Skip to content

Commit cb08f79

Browse files
committed
Add ClearCapturedImageUriUseCase and implement related functionality
[#162] - Introduced ClearCapturedImageUriUseCase to clear captured image URI. - Updated InternalCameraRepositoryInterface to include clearCapturedImageUri method. - Implemented clearCapturedImageUri in InternalCameraRepository. - Added new cut frame types: FourCutFrame25 and Halloween25 in CutFrameType. - Enhanced SelectFrameScreen layout for better UI/UX. - Added close icon functionality in ShareScreen. - Integrated ClearCapturedImageUriUseCase in InternelCameraViewModel for session management.
1 parent a0fb7a1 commit cb08f79

File tree

9 files changed

+162
-33
lines changed

9 files changed

+162
-33
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.foke.together.domain.interactor
2+
3+
import android.net.Uri
4+
import com.foke.together.domain.output.InternalCameraRepositoryInterface
5+
import kotlinx.coroutines.flow.Flow
6+
import javax.inject.Inject
7+
8+
class ClearCapturedImageUriUseCase @Inject constructor(
9+
private val internalCameraRepository: InternalCameraRepositoryInterface
10+
) {
11+
operator fun invoke() = internalCameraRepository.clearCapturedImageUri()
12+
}

domain/src/main/java/com/foke/together/domain/interactor/entity/CutFrameType.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,44 @@ data object FourCurDark: DefaultCutFrameSet(
163163
qrCodePosition = QrStickerPosition(30,145,515)
164164
)
165165

166+
data object FourCutFrame25: DefaultCutFrameSet(
167+
9,
168+
"FourCutFrame Dark 2025",
169+
4, 190, 570,
170+
R.drawable.fourcut_frame_dark_25,
171+
listOf(
172+
PhotoPosition(163, 110, 13, 29),
173+
PhotoPosition(163, 110, 13, 144),
174+
PhotoPosition(163, 110, 13, 259),
175+
PhotoPosition(163, 110, 13, 373),
176+
),
177+
listOf(
178+
R.drawable.fourcut_frame_dark_25
179+
),
180+
datePosition = TextStickerPosition(80, 10, 92,546,R.font.cascadia_mono, color = Color(187, 187, 187), fontWeight = FontWeight.ExtraBold, fontSize = 6, textAlign = TextAlign.End),
181+
copyrightPosition = TextStickerPosition(80, 10, 92,551,R.font.cascadia_mono, color = Color(187, 187, 187), fontWeight = FontWeight.ExtraBold, fontSize = 6, textAlign = TextAlign.End),
182+
qrCodePosition = QrStickerPosition(30,145,515)
183+
)
184+
185+
data object Halloween25: DefaultCutFrameSet(
186+
10,
187+
"Halloween 2025",
188+
4, 190, 570,
189+
R.drawable.holloween,
190+
listOf(
191+
PhotoPosition(163, 110, 13, 29),
192+
PhotoPosition(163, 110, 13, 144),
193+
PhotoPosition(163, 110, 13, 259),
194+
PhotoPosition(163, 110, 13, 373),
195+
),
196+
listOf(
197+
R.drawable.holloween
198+
),
199+
datePosition = TextStickerPosition(80, 10, 92,546,R.font.cascadia_mono, color = Color(187, 187, 187), fontWeight = FontWeight.ExtraBold, fontSize = 6, textAlign = TextAlign.End),
200+
copyrightPosition = TextStickerPosition(80, 10, 92,551,R.font.cascadia_mono, color = Color(187, 187, 187), fontWeight = FontWeight.ExtraBold, fontSize = 6, textAlign = TextAlign.End),
201+
qrCodePosition = QrStickerPosition(30,145,515)
202+
)
203+
166204
companion object {
167205
val entries = listOf(
168206
FourCutLight,
@@ -172,6 +210,8 @@ data object FourCurDark: DefaultCutFrameSet(
172210
MakerFaire25_2,
173211
MakerFaire25_3,
174212
MakerFaire25_4,
213+
FourCutFrame25,
214+
Halloween25,
175215
).sortedBy { it.index }
176216
}
177217
}

domain/src/main/java/com/foke/together/domain/output/InternalCameraRepositoryInterface.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ interface InternalCameraRepositoryInterface {
1717
context: Context,
1818
fileName : String,
1919
)
20+
21+
fun clearCapturedImageUri()
2022
suspend fun initial(
2123
context: Context,
2224
lifecycleOwner: LifecycleOwner,
360 KB
Loading

external/src/main/java/com/foke/together/external/repository/InternalCameraRepository.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ class InternalCameraRepository @Inject constructor(
7070
}
7171
}
7272

73+
override fun clearCapturedImageUri() {
74+
capturedImageUri.value = null
75+
}
76+
7377
override suspend fun initial(
7478
context: Context,
7579
lifecycleOwner: LifecycleOwner,

presenter/src/main/java/com/foke/together/presenter/screen/SelectFrameScreen.kt

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import androidx.compose.foundation.layout.Row
1111
import androidx.compose.foundation.layout.aspectRatio
1212
import androidx.compose.foundation.layout.fillMaxSize
1313
import androidx.compose.foundation.layout.fillMaxWidth
14+
import androidx.compose.foundation.layout.height
1415
import androidx.compose.foundation.layout.padding
16+
import androidx.compose.foundation.layout.width
1517
import androidx.compose.foundation.layout.wrapContentHeight
1618
import androidx.compose.foundation.layout.wrapContentSize
1719
import androidx.compose.foundation.lazy.grid.GridCells
@@ -31,8 +33,10 @@ import androidx.compose.ui.Alignment
3133
import androidx.compose.ui.Modifier
3234
import androidx.compose.ui.res.painterResource
3335
import androidx.compose.ui.text.font.FontWeight
36+
import androidx.compose.ui.text.style.TextAlign
3437
import androidx.compose.ui.tooling.preview.Devices
3538
import androidx.compose.ui.tooling.preview.Preview
39+
import androidx.compose.ui.unit.dp
3640
import androidx.hilt.navigation.compose.hiltViewModel
3741
import com.foke.together.domain.interactor.entity.CutFrame
3842
import com.foke.together.domain.interactor.entity.DefaultCutFrameSet
@@ -148,27 +152,45 @@ fun SelectFrameContent(
148152
){
149153
items(cutFrames.size){ index ->
150154
Box(
151-
modifier = Modifier.fillMaxSize()
155+
modifier = Modifier
156+
.height(450.dp)
152157
.clickable{
153158
selectFrame(cutFrames[index])
154159
},
155-
contentAlignment = Alignment.Center,
156160
){
157-
DefaultCutFrame(
158-
cutFrame = cutFrames[index],
159-
imageUrlList = listOf(
160-
"file:///android_asset/sample_cut.png".toUri(),
161-
"file:///android_asset/sample_cut.png".toUri(),
162-
"file:///android_asset/sample_cut.png".toUri(),
163-
"file:///android_asset/sample_cut.png".toUri(),
164-
),
165-
)
166-
Text(
167-
modifier = Modifier.align(Alignment.TopCenter),
168-
text = cutFrames[index].frameTitle,
169-
style = AppTheme.typography.body.copy(fontWeight = FontWeight.Bold),
170-
color = AppTheme.colorScheme.top
171-
)
161+
Box(
162+
modifier = Modifier
163+
.align(Alignment.TopCenter)
164+
.height(400.dp)
165+
.width(130.dp)
166+
.clickable{
167+
selectFrame(cutFrames[index])
168+
},
169+
) {
170+
DefaultCutFrame(
171+
cutFrame = cutFrames[index],
172+
imageUrlList = listOf(
173+
// "file:///android_asset/sample_cut.png".toUri(),
174+
// "file:///android_asset/sample_cut.png".toUri(),
175+
// "file:///android_asset/sample_cut.png".toUri(),
176+
// "file:///android_asset/sample_cut.png".toUri(),
177+
Uri.EMPTY,
178+
Uri.EMPTY,
179+
Uri.EMPTY,
180+
Uri.EMPTY,
181+
),
182+
)
183+
Text(
184+
modifier = Modifier
185+
.align(Alignment.TopCenter)
186+
.padding(top = 410.dp)
187+
.width(150.dp),
188+
text = cutFrames[index].frameTitle,
189+
style = AppTheme.typography.body.copy(fontWeight = FontWeight.Bold),
190+
color = AppTheme.colorScheme.top,
191+
textAlign = TextAlign.Center
192+
)
193+
}
172194
}
173195
}
174196
}

presenter/src/main/java/com/foke/together/presenter/screen/ShareScreen.kt

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.padding
1515
import androidx.compose.foundation.layout.size
1616
import androidx.compose.foundation.layout.wrapContentSize
1717
import androidx.compose.material.icons.Icons
18+
import androidx.compose.material.icons.filled.Close
1819
import androidx.compose.material.icons.filled.Download
1920
import androidx.compose.material.icons.filled.Home
2021
import androidx.compose.material.icons.filled.Print
@@ -49,7 +50,13 @@ fun ShareScreen(
4950
val qrImageBitmap = viewModel.qrCodeBitmap
5051
DisposableEffect(Unit) {
5152
viewModel.updateSessionStatus()
52-
onDispose { }
53+
viewModel.setupTimer(
54+
finished = popBackStack
55+
)
56+
viewModel.startTimer()
57+
onDispose {
58+
viewModel.closeTimer()
59+
}
5360
}
5461

5562
ShareContent(
@@ -91,15 +98,6 @@ fun ShareContent(
9198
AppTopBar(
9299
title = "이미지 저장",
93100
alignment = Alignment.CenterHorizontally,
94-
leftIcon = {
95-
Icon(
96-
modifier = Modifier.size(AppTheme.size.icon)
97-
.clickable(true, onClick = popBackStack),
98-
imageVector = Icons.Filled.Home,
99-
contentDescription = "home",
100-
tint = AppTheme.colorScheme.top
101-
)
102-
}
103101
)
104102
},
105103
bottomBar = {
@@ -163,6 +161,25 @@ fun ShareContent(
163161
color = AppTheme.colorScheme.border
164162
)
165163
}
164+
165+
Column(
166+
modifier = Modifier.wrapContentSize()
167+
.clickable(true, onClick = popBackStack),
168+
verticalArrangement = Arrangement.Center,
169+
horizontalAlignment = Alignment.CenterHorizontally
170+
){
171+
Icon(
172+
modifier = Modifier.size(AppTheme.size.icon),
173+
imageVector = Icons.Filled.Close,
174+
contentDescription = "close",
175+
tint = AppTheme.colorScheme.top
176+
)
177+
Text(
178+
text = "닫기",
179+
style = AppTheme.typography.title,
180+
color = AppTheme.colorScheme.border
181+
)
182+
}
166183
}
167184
},
168185
){ paddingValues ->

presenter/src/main/java/com/foke/together/presenter/viewmodel/InternelCameraViewModel.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import androidx.camera.view.PreviewView
88
import androidx.lifecycle.LifecycleOwner
99
import androidx.lifecycle.ViewModel
1010
import androidx.lifecycle.viewModelScope
11+
import com.foke.together.domain.interactor.ClearCapturedImageUriUseCase
1112
import com.foke.together.domain.interactor.GeneratePhotoFrameUseCaseV1
1213
import com.foke.together.domain.interactor.GetCaptureDurationUseCase
1314
import com.foke.together.domain.interactor.GetCapturedImageUriUseCase
@@ -35,12 +36,13 @@ import kotlin.time.DurationUnit
3536
class InternelCameraViewModel @Inject constructor(
3637
private val internalCameraUseCase: InternalCameraUseCase,
3738
private val generatePhotoFrameUseCaseV1: GeneratePhotoFrameUseCaseV1,
38-
getCaptureDurationUseCase: GetCaptureDurationUseCase,
39-
getInternalCameraLensFacingUseCase: GetInternalCameraLensFacingUseCase,
40-
getInternalCameraFlashModeUseCase: GetInternalCameraFlashModeUseCase,
41-
getInternalCameraCaptureModeUseCase: GetInternalCameraCaptureModeUseCase,
42-
getCapturedImageUriUseCase: GetCapturedImageUriUseCase,
43-
getSessionUseCase : GetCurrentSessionUseCase,
39+
private val getCaptureDurationUseCase: GetCaptureDurationUseCase,
40+
private val getInternalCameraLensFacingUseCase: GetInternalCameraLensFacingUseCase,
41+
private val getInternalCameraFlashModeUseCase: GetInternalCameraFlashModeUseCase,
42+
private val getInternalCameraCaptureModeUseCase: GetInternalCameraCaptureModeUseCase,
43+
private val getCapturedImageUriUseCase: GetCapturedImageUriUseCase,
44+
private val clearCapturedImageUriUseCase: ClearCapturedImageUriUseCase,
45+
private val getSessionUseCase : GetCurrentSessionUseCase,
4446
): ViewModel() {
4547

4648
// TODO(MutableStateFlow 로 처리하기)
@@ -141,6 +143,7 @@ class InternelCameraViewModel @Inject constructor(
141143

142144
captureTimer = object : CountDownTimer(captureDuration.value.toLong(DurationUnit.MILLISECONDS), AppPolicy.COUNTDOWN_INTERVAL) {
143145
override fun onTick(millisUntilFinished: Long) {
146+
clearCapturedImageUriUseCase()
144147
countdownSeconds.value = ((millisUntilFinished / 1000) + 1).toInt()
145148
}
146149
override fun onFinish() {

presenter/src/main/java/com/foke/together/presenter/viewmodel/ShareViewModel.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.foke.together.presenter.viewmodel
22

33
import android.content.Context
44
import android.net.Uri
5+
import android.os.CountDownTimer
56
import androidx.core.content.FileProvider
67
import androidx.core.net.toFile
78
import androidx.lifecycle.ViewModel
@@ -11,11 +12,14 @@ import com.foke.together.domain.interactor.entity.Status
1112
import com.foke.together.domain.interactor.session.ClearSessionUseCase
1213
import com.foke.together.domain.interactor.session.GetCurrentSessionUseCase
1314
import com.foke.together.domain.interactor.session.UpdateSessionStatusUseCase
15+
import com.foke.together.util.AppPolicy
1416
import com.foke.together.util.ImageFileUtil
1517
import dagger.hilt.android.lifecycle.HiltViewModel
1618
import dagger.hilt.android.qualifiers.ApplicationContext
1719
import kotlinx.coroutines.launch
1820
import javax.inject.Inject
21+
import kotlin.time.Duration.Companion.minutes
22+
import kotlin.time.DurationUnit
1923

2024
@HiltViewModel
2125
class ShareViewModel @Inject constructor(
@@ -29,6 +33,8 @@ class ShareViewModel @Inject constructor(
2933
val singleImageUri: Uri = generatePhotoFrameUseCaseV1.getFinalSingleImageUri()
3034
val twoImageUri: Uri = generatePhotoFrameUseCaseV1.getFinalTwoImageUri()
3135

36+
private var returnHomeTimer: CountDownTimer? = null
37+
3238
fun downloadImage(): Result<Unit> {
3339
return getCurrentSessionUseCase()?.let { session ->
3440
val imageBitmap = ImageFileUtil.getBitmapFromUri(context, singleImageUri)
@@ -62,6 +68,29 @@ class ShareViewModel @Inject constructor(
6268
updateSessionStatusUseCase(Status.SHARE)
6369
}
6470

71+
fun setupTimer(
72+
finished : () -> Unit
73+
){
74+
returnHomeTimer = object : CountDownTimer(1.minutes.toLong(DurationUnit.MILLISECONDS), AppPolicy.COUNTDOWN_INTERVAL){
75+
override fun onFinish() {
76+
finished()
77+
}
78+
override fun onTick(millisUntilFinished: Long) {
79+
//
80+
}
81+
}
82+
}
83+
fun startTimer() {
84+
returnHomeTimer?.start()
85+
}
86+
87+
fun closeTimer(
88+
89+
){
90+
returnHomeTimer?.cancel()
91+
returnHomeTimer = null
92+
}
93+
6594
fun closeSession() {
6695
clearSessionUseCase()
6796
}

0 commit comments

Comments
 (0)