Skip to content

Commit

Permalink
Generate Image [#59]
Browse files Browse the repository at this point in the history
  • Loading branch information
ing03201 committed Oct 10, 2024
1 parent 3123d86 commit c88b989
Show file tree
Hide file tree
Showing 24 changed files with 696 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.foke.together.domain.interactor

import android.content.Context
import com.foke.together.domain.output.ExternalCameraRepositoryInterface
import com.foke.together.domain.output.ImageRepositoryInterface
import com.foke.together.util.AppLog
import com.foke.together.util.ImageFileUtil
import com.foke.together.util.TimeUtil
Expand All @@ -10,13 +11,18 @@ import javax.inject.Inject

class CaptureWithExternalCameraUseCase @Inject constructor(
@ApplicationContext private val context: Context,
private val externalCameraRepository: ExternalCameraRepositoryInterface
private val externalCameraRepository: ExternalCameraRepositoryInterface,
private val imageRepository: ImageRepositoryInterface
) {
suspend operator fun invoke(filepath: String): Result<Unit> {
suspend operator fun invoke(fileName: String): Result<Unit> {
externalCameraRepository.capture()
.onSuccess {
// TODO: create file module in phase4
ImageFileUtil.saveBitmap(context, it, filepath, TimeUtil.getCurrentTimeMillis())
// TODO: save Bitmap to internal storage
AppLog.i(TAG, "capture", "success: $it")
if(it == null) {
return Result.failure(Exception("Bitmap is null"))
}
imageRepository.saveToStorage(it, fileName)
return Result.success(Unit)
}
.onFailure {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.foke.together.domain.interactor

import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import com.foke.together.domain.interactor.entity.CutFrameType
import com.foke.together.domain.output.ImageRepositoryInterface
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class GeneratePhotoFrameUseCase @Inject constructor(
@ApplicationContext private val context: Context,
private val imageRepositoryInterface: ImageRepositoryInterface
){
fun getCutFrameType(): CutFrameType = imageRepositoryInterface.getCutFrameType()
suspend fun setCutFrameType(type: Int) = imageRepositoryInterface.setCutFrameType(type)

fun getCapturedImageListUri(): List<Uri> = imageRepositoryInterface.getCachedImageUriList()
suspend fun clearCapturedImageList() = imageRepositoryInterface.clearCacheDir()
suspend fun saveGraphicsLayerImage(image: Bitmap, fileName: String) = imageRepositoryInterface.cachingImage(image, fileName)
suspend fun saveFinalImage(image: Bitmap, fileName: String) = imageRepositoryInterface.saveToStorage(image, fileName)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.foke.together.domain.interactor.entity

enum class CutFrameType {
MAKER_FAIRE,
FOURCUT_LIGHT,
FOURCUT_DARK;

companion object {
fun findBy(name: String): CutFrameType {
return when (name) {
MAKER_FAIRE.name -> MAKER_FAIRE
FOURCUT_LIGHT.name -> FOURCUT_LIGHT
FOURCUT_DARK.name -> FOURCUT_DARK
else -> throw IllegalArgumentException("Unknown value: $name")
}
}
fun findBy(ordinal: Int): CutFrameType {
return when (ordinal) {
MAKER_FAIRE.ordinal -> MAKER_FAIRE
FOURCUT_LIGHT.ordinal -> FOURCUT_LIGHT
FOURCUT_DARK.ordinal -> FOURCUT_DARK
else -> throw IllegalArgumentException("Unknown value: $ordinal")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.foke.together.domain.output

import android.graphics.Bitmap
import android.net.Uri
import android.os.Environment
import com.foke.together.domain.interactor.entity.CutFrameType
import kotlinx.coroutines.flow.Flow

interface ImageRepositoryInterface {
fun getCutFrameType(): CutFrameType
suspend fun setCutFrameType(type: Int)
// 촬영한 사진들 모음
suspend fun cachingImage(image: Bitmap, fileName: String) : Uri
fun getCachedImageUriList() : List<Uri>
suspend fun clearCacheDir()

// 완성된 프레임 모음
suspend fun saveToStorage(image: Bitmap, fileName :String) : Uri
fun getUriToBitmap(imageUri: Uri): Bitmap
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.foke.together.external.repository

import android.content.Context
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.net.Uri
import android.provider.MediaStore
import com.foke.together.domain.interactor.entity.CutFrameType
import com.foke.together.domain.output.ImageRepositoryInterface
import com.foke.together.util.AppPolicy
import com.foke.together.util.ImageFileUtil
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

class ImageRepository @Inject constructor(
@ApplicationContext private val context: Context
): ImageRepositoryInterface{
private var cutFrameType: CutFrameType = CutFrameType.MAKER_FAIRE

override fun getCutFrameType(): CutFrameType = cutFrameType
override suspend fun setCutFrameType(type: Int) {
cutFrameType = CutFrameType.findBy(type)
}

override suspend fun cachingImage(image: Bitmap, fileName: String): Uri {
return ImageFileUtil.cacheBitmap(context, image, fileName)
}

override fun getCachedImageUriList(): List<Uri> {
var uriList = mutableListOf<Uri>()
context.cacheDir.listFiles().forEach {
if(it.name.contains(AppPolicy.CAPTURED_FOUR_CUT_IMAGE_NAME)){
// capture로 시작하는 파일만 반환
uriList.add(Uri.fromFile(it))
}
}
return uriList
}

override suspend fun clearCacheDir() {
context.cacheDir.listFiles().forEach {
if(it.name.contains(".jpg")){
it.delete()
}
}
}

override suspend fun saveToStorage(image: Bitmap, fileName: String): Uri {
return ImageFileUtil.saveBitmapToStorage(context, image, fileName)
}

override fun getUriToBitmap(imageUri: Uri): Bitmap {
return ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.contentResolver, imageUri))
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.foke.together.external.repository.di

import com.foke.together.domain.output.ExternalCameraRepositoryInterface
import com.foke.together.domain.output.ImageRepositoryInterface
import com.foke.together.external.repository.ExternalCameraRepository
import com.foke.together.external.repository.ImageRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -16,4 +18,9 @@ abstract class RepositoryModule {
abstract fun bindAppPreferenceRepository(
externalCameraRepository: ExternalCameraRepository
): ExternalCameraRepositoryInterface

@Binds
abstract fun bindImageRepository(
imageRepository: ImageRepository
): ImageRepositoryInterface
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ ui-graphics-android = "1.7.3"
junit = "4.13.2"
junit-version = "1.2.1"
espresso-core = "3.6.1"
coilCompose = "2.7.0"



Expand Down Expand Up @@ -128,6 +129,7 @@ junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit-version" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coilCompose" }



Expand Down
1 change: 1 addition & 0 deletions presenter/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies {
// navigation
implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.androidx.navigation.compose)
implementation(libs.coil.compose)

// test
testImplementation(libs.junit)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,97 +1,122 @@
package com.foke.together.presenter.frame

import android.net.Uri
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer
import androidx.compose.ui.graphics.rememberGraphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.foke.together.presenter.R
import com.foke.together.presenter.theme.FourCutTogetherTheme
import com.foke.together.presenter.theme.highContrastDarkColorScheme
import com.foke.together.presenter.theme.mediumContrastLightColorScheme
import com.foke.together.util.AppPolicy
import com.foke.together.util.ImageFileUtil
import com.foke.together.util.TimeUtil
import kotlinx.coroutines.launch

@Composable
fun FourCutFrame(
// TODO: Need to refactoring. separate frame design with application theme
designColorScheme: ColorScheme = mediumContrastLightColorScheme,
cameraImageUrlList : List<String>? = null,
cameraImageUrlList : List<Uri>? = null
) {
ConstraintLayout(
modifier = Modifier
.aspectRatio(ratio = 0.3333f)
.background(color = designColorScheme.surface)
.padding(10.dp)
.border(1.dp, designColorScheme.inverseSurface)
) {
val (startBarrier, endBarrier, cameraColumn, background, decorateImage, curTime) = createRefs()
val (cameraColumn, decorateRow) = createRefs()
LazyColumn(
state = rememberLazyListState(),
verticalArrangement = Arrangement.spacedBy(10.dp),
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 10.dp),
contentPadding = PaddingValues(horizontal = 15.dp, vertical = 15.dp),
modifier = Modifier
.constrainAs(cameraColumn) {
top.linkTo(parent.top, margin = 15.dp)
bottom.linkTo(decorateImage.top)
top.linkTo(parent.top)
bottom.linkTo(decorateRow.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
width = Dimension.fillToConstraints
height = Dimension.wrapContent
}
) {
items(4){
items(AppPolicy.CAPTURE_COUNT){
//TODO: add camera image
// change Box -> ImageView
Box(
modifier = Modifier
.aspectRatio(1.5f)
.background(color = designColorScheme.inverseSurface)
)
){
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(cameraImageUrlList?.get(it))
.build(),
contentDescription = "",
modifier = Modifier.fillParentMaxSize()

)
}
}
}

Icon(
imageVector = ImageVector.vectorResource(
id = R.drawable.fourcut_together
),
contentDescription = "for decorate",
Column(
modifier = Modifier
.constrainAs(decorateImage) {
.constrainAs(decorateRow){
top.linkTo(cameraColumn.bottom)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
.size(
width = 48.dp,
height = 48.dp
height = Dimension.fillToConstraints
width = Dimension.fillToConstraints
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = ImageVector.vectorResource(
id = R.drawable.fourcut_together
),
tint = designColorScheme.primaryContainer
)
Text(
text = TimeUtil.getCurrentDisplayTime(),
modifier = Modifier.constrainAs(curTime){
top.linkTo(decorateImage.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
},
color = designColorScheme.inverseSurface,
fontSize = 12.sp
)
contentDescription = "for decorate",
modifier = Modifier.weight(1f),
tint = designColorScheme.primaryContainer
)
Text(
text = TimeUtil.getCurrentDisplayTime(),
modifier = Modifier.weight(1f),
color = designColorScheme.inverseSurface,
fontSize = 12.sp
)
}
}
}

Expand Down
Loading

0 comments on commit c88b989

Please sign in to comment.