Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Image [#59] #74

Merged
merged 1 commit into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file 작업 코드는 repository에서 직접 하는거보단 하위에 DataStoreUtil에서 하는게 맞아보입니다. 다음 페이즈에서 변경해봅시다~

}
}

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