diff --git a/data/src/main/java/com/foke/together/data/repository/AppPreferencesRepository.kt b/data/src/main/java/com/foke/together/data/repository/AppPreferencesRepository.kt index 017af39..1eeaa78 100644 --- a/data/src/main/java/com/foke/together/data/repository/AppPreferencesRepository.kt +++ b/data/src/main/java/com/foke/together/data/repository/AppPreferencesRepository.kt @@ -11,6 +11,9 @@ import com.foke.together.domain.output.AppPreferenceInterface import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject +import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.toDuration // TODO: datasource -> local / remote 기준으로 module 구분하는게 어떨지? class AppPreferencesRepository @Inject constructor( @@ -98,6 +101,19 @@ class AppPreferencesRepository @Inject constructor( } } + override fun getCaptureDuration(): Flow = appPreferencesFlow.map { + it.cameraDuration.toDuration(DurationUnit.MILLISECONDS) + } + + override suspend fun setCaptureDuration(duration: Duration) { + appPreferences.updateData { + it.toBuilder() + .setCameraDuration(duration.toLong(DurationUnit.MILLISECONDS)) + .build() + } + } + + override suspend fun clearAll() { appPreferences.updateData { diff --git a/data/src/main/proto/app_preferences.proto b/data/src/main/proto/app_preferences.proto index e996d68..5aef4e1 100644 --- a/data/src/main/proto/app_preferences.proto +++ b/data/src/main/proto/app_preferences.proto @@ -20,6 +20,8 @@ message AppPreferences { int32 internal_camera_capture_mode = 22; int32 internal_camera_aspect_ratio = 23; + int64 camera_duration = 24; + // TODO: sample code. remove later. string sample_id = 999997; string sample_title = 999998; diff --git a/domain/src/main/java/com/foke/together/domain/GetCaptureDurationUseCase.kt b/domain/src/main/java/com/foke/together/domain/GetCaptureDurationUseCase.kt new file mode 100644 index 0000000..a8802d6 --- /dev/null +++ b/domain/src/main/java/com/foke/together/domain/GetCaptureDurationUseCase.kt @@ -0,0 +1,12 @@ +package com.foke.together.domain + +import com.foke.together.domain.output.AppPreferenceInterface +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject +import kotlin.time.Duration + +class GetCaptureDurationUseCase @Inject constructor( + private val appPreferenceInterface: AppPreferenceInterface +) { + operator fun invoke() : Flow = appPreferenceInterface.getCaptureDuration() +} \ No newline at end of file diff --git a/domain/src/main/java/com/foke/together/domain/SetCaptureDurationUseCase.kt b/domain/src/main/java/com/foke/together/domain/SetCaptureDurationUseCase.kt new file mode 100644 index 0000000..11f7ae7 --- /dev/null +++ b/domain/src/main/java/com/foke/together/domain/SetCaptureDurationUseCase.kt @@ -0,0 +1,11 @@ +package com.foke.together.domain + +import com.foke.together.domain.output.AppPreferenceInterface +import javax.inject.Inject +import kotlin.time.Duration + +class SetCaptureDurationUseCase @Inject constructor( + private val appPreferenceInterface: AppPreferenceInterface +) { + suspend operator fun invoke(duration: Duration) = appPreferenceInterface.setCaptureDuration(duration) +} \ No newline at end of file diff --git a/domain/src/main/java/com/foke/together/domain/interactor/GetInternalCameraLensFacingUseCase.kt b/domain/src/main/java/com/foke/together/domain/interactor/GetInternalCameraLensFacingUseCase.kt index 7df746a..b4ef1cd 100644 --- a/domain/src/main/java/com/foke/together/domain/interactor/GetInternalCameraLensFacingUseCase.kt +++ b/domain/src/main/java/com/foke/together/domain/interactor/GetInternalCameraLensFacingUseCase.kt @@ -9,9 +9,5 @@ import javax.inject.Inject class GetInternalCameraLensFacingUseCase @Inject constructor( private val appPreferenceRepository: AppPreferenceInterface ) { - operator fun invoke() : Flow = appPreferenceRepository.getInternalCameraLensFacing().map { cameraSelectorIdx -> - CameraSelector.Builder() - .requireLensFacing(cameraSelectorIdx) - .build() - } + operator fun invoke() : Flow = appPreferenceRepository.getInternalCameraLensFacing() } \ No newline at end of file diff --git a/domain/src/main/java/com/foke/together/domain/interactor/SetInternalCameraLensFacingUseCase.kt b/domain/src/main/java/com/foke/together/domain/interactor/SetInternalCameraLensFacingUseCase.kt new file mode 100644 index 0000000..09fd9df --- /dev/null +++ b/domain/src/main/java/com/foke/together/domain/interactor/SetInternalCameraLensFacingUseCase.kt @@ -0,0 +1,10 @@ +package com.foke.together.domain.interactor + +import com.foke.together.domain.output.AppPreferenceInterface +import javax.inject.Inject + +class SetInternalCameraLensFacingUseCase @Inject constructor( + private val appPreferenceRepository: AppPreferenceInterface +) { + suspend operator fun invoke(lensFacing: Int) = appPreferenceRepository.setInternalCameraLensFacing(lensFacing) +} \ No newline at end of file diff --git a/domain/src/main/java/com/foke/together/domain/interactor/entity/CutFrameType.kt b/domain/src/main/java/com/foke/together/domain/interactor/entity/CutFrameType.kt index 232294e..a0a6c68 100644 --- a/domain/src/main/java/com/foke/together/domain/interactor/entity/CutFrameType.kt +++ b/domain/src/main/java/com/foke/together/domain/interactor/entity/CutFrameType.kt @@ -165,4 +165,18 @@ sealed class DefaultCutFrameSet ( ), dateStringHeight = 495 ) + + companion object { + val entries = listOf( + FourCutLight, + FourCurDark, + MakerFaire, + Wedding1, + Wedding2, + Bride1, + Bride2, + Groom1, + Groom2, + ).sortedBy { it.index } + } } \ No newline at end of file diff --git a/domain/src/main/java/com/foke/together/domain/output/AppPreferenceInterface.kt b/domain/src/main/java/com/foke/together/domain/output/AppPreferenceInterface.kt index 5e600ab..e8cafa5 100644 --- a/domain/src/main/java/com/foke/together/domain/output/AppPreferenceInterface.kt +++ b/domain/src/main/java/com/foke/together/domain/output/AppPreferenceInterface.kt @@ -5,6 +5,7 @@ import androidx.camera.core.CameraSelector import com.foke.together.domain.interactor.entity.CameraSourceType import com.foke.together.domain.interactor.entity.ExternalCameraIP import kotlinx.coroutines.flow.Flow +import kotlin.time.Duration interface AppPreferenceInterface { fun getCameraSourceType(): Flow @@ -28,5 +29,11 @@ interface AppPreferenceInterface { @IntRange(from = 0, to = 2) captureMode: Int ) + fun getCaptureDuration(): Flow + + suspend fun setCaptureDuration( + duration: Duration + ) + suspend fun clearAll() } \ No newline at end of file diff --git a/external/src/main/java/com/foke/together/external/repository/InternalCameraRepository.kt b/external/src/main/java/com/foke/together/external/repository/InternalCameraRepository.kt index b38c179..79c3a1c 100644 --- a/external/src/main/java/com/foke/together/external/repository/InternalCameraRepository.kt +++ b/external/src/main/java/com/foke/together/external/repository/InternalCameraRepository.kt @@ -26,7 +26,6 @@ import javax.inject.Singleton @Singleton class InternalCameraRepository @Inject constructor( ): InternalCameraRepositoryInterface{ - private lateinit var previewView: PreviewView private lateinit var cameraController: LifecycleCameraController private lateinit var imageCapture: ImageCapture diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8056bc4..a0f5dec 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -96,6 +96,7 @@ androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-ui-text-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation-compose" } diff --git a/presenter/build.gradle.kts b/presenter/build.gradle.kts index 4746940..205276b 100644 --- a/presenter/build.gradle.kts +++ b/presenter/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation(libs.androidx.ui) implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.ui.text.google.fonts) implementation(libs.androidx.material3) implementation(libs.androidx.hilt.compiler) implementation(libs.androidx.material.icons.extended) diff --git a/presenter/src/main/java/com/foke/together/presenter/MainActivity.kt b/presenter/src/main/java/com/foke/together/presenter/MainActivity.kt index 006f609..c013a36 100644 --- a/presenter/src/main/java/com/foke/together/presenter/MainActivity.kt +++ b/presenter/src/main/java/com/foke/together/presenter/MainActivity.kt @@ -17,7 +17,10 @@ class MainActivity : ComponentActivity() { // TODO: temporary remove this option // enableEdgeToEdge() setContent { - MainScreen() + FourCutTogetherTheme { + val navController = rememberNavController() + NavGraph(navController) + } } } } diff --git a/presenter/src/main/java/com/foke/together/presenter/component/Basic.kt b/presenter/src/main/java/com/foke/together/presenter/component/Basic.kt new file mode 100644 index 0000000..6ca99b9 --- /dev/null +++ b/presenter/src/main/java/com/foke/together/presenter/component/Basic.kt @@ -0,0 +1,434 @@ +package com.foke.together.presenter.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Remove +import androidx.compose.material.icons.filled.Start +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Slider +import androidx.compose.material3.SliderColors +import androidx.compose.material3.SliderDefaults +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldColors +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.isUnspecified +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension +import com.foke.together.presenter.theme.AppTheme + +@Composable +fun OptionButton( + modifier: Modifier = Modifier, + title: String, + selected: Boolean, + onSelected: () -> Unit, +){ + Surface( + modifier = modifier, + color = when{ + selected -> AppTheme.colorScheme.top + else -> AppTheme.colorScheme.bottom + }, + shape = AppTheme.shapes.container, + onClick = onSelected, + ){ + Text( + modifier = Modifier + .wrapContentSize(Alignment.Center) + .padding(AppTheme.size.buttonPadding), + text = title, + color = when{ + selected -> AppTheme.colorScheme.tint + else -> AppTheme.colorScheme.border + }, + style = AppTheme.typography.body, + textAlign = TextAlign.Center + ) + } +} + +@Composable +fun MultiOptionsButton( + modifier: Modifier = Modifier, + optionList : List, + selectedOptionIndex : Int, + onChangeOption : (Int) -> Unit +){ + Row( + modifier = modifier, + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ){ + optionList.forEachIndexed { index, title -> + OptionButton( + modifier = Modifier.wrapContentSize(), + title = title, + selected = selectedOptionIndex == index, + onSelected = { onChangeOption(index) } + ) + } + } +} + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SliderPreference( + modifier: Modifier = Modifier, + title: String, + sliderText: String, + iconVector: ImageVector = Icons.Default.Start, + sliderValue: Float, + sliderRange: ClosedFloatingPointRange, + onSliderValueChange: (Float) -> Unit +){ + ConstraintLayout( + modifier = modifier, + ){ + val ( + iconConstraint, + titleConstraint, + sliderValueConstraint, + sliderPlusConstraint, + sliderMinusConstraint, + sliderConstraint + ) = createRefs() + + + Icon( + modifier = Modifier.constrainAs(iconConstraint) { + top.linkTo(parent.top) + bottom.linkTo(sliderConstraint.top) + start.linkTo(parent.start) + end.linkTo(titleConstraint.start) + }, + imageVector = iconVector, + tint = AppTheme.colorScheme.bottom, + contentDescription = "icon" + ) + + Text( + modifier = Modifier.constrainAs(titleConstraint){ + top.linkTo(parent.top) + bottom.linkTo(sliderConstraint.top) + start.linkTo(iconConstraint.end) + end.linkTo(sliderValueConstraint.start) + width = Dimension.fillToConstraints + height = Dimension.wrapContent + width = Dimension.fillToConstraints + height = Dimension.wrapContent + } + .padding(start = AppTheme.size.buttonPadding), + text = title, + style = AppTheme.typography.body, + color = AppTheme.colorScheme.border + ) + + Text( + modifier = Modifier.constrainAs(sliderValueConstraint) { + top.linkTo(titleConstraint.top) + bottom.linkTo(titleConstraint.bottom) + start.linkTo(titleConstraint.end) + end.linkTo(parent.end) + width = Dimension.wrapContent + height = Dimension.wrapContent + horizontalChainWeight = 2f + } + .padding(AppTheme.size.buttonPadding), + text = sliderText, + color = AppTheme.colorScheme.border, + style = AppTheme.typography.body + ) + + + createHorizontalChain( + sliderMinusConstraint, + sliderConstraint, + sliderPlusConstraint, + ) + + Icon( + modifier = Modifier.size(AppTheme.size.icon).constrainAs(sliderMinusConstraint) { + top.linkTo(iconConstraint.bottom) + bottom.linkTo(parent.bottom) + start.linkTo(parent.start) + end.linkTo(sliderConstraint.start) + }, + imageVector = Icons.Default.Remove, + contentDescription = "slider minus", + tint = AppTheme.colorScheme.border + ) + + Icon( + modifier = Modifier.size(AppTheme.size.icon).constrainAs(sliderPlusConstraint) { + top.linkTo(sliderValueConstraint.bottom) + bottom.linkTo(parent.bottom) + start.linkTo(sliderConstraint.end) + end.linkTo(parent.end) + }, + imageVector = Icons.Default.Add, + contentDescription = "slider minus", + tint = AppTheme.colorScheme.border + ) + + Slider( + modifier = Modifier.constrainAs(sliderConstraint) { + top.linkTo(sliderMinusConstraint.top) + bottom.linkTo(parent.bottom) + start.linkTo(sliderMinusConstraint.end) + end.linkTo(sliderPlusConstraint.start) + width = Dimension.fillToConstraints + height = Dimension.fillToConstraints + }, + value = sliderValue, + onValueChange = onSliderValueChange, + valueRange = sliderRange, + track = { sliderState -> + SliderDefaults.Track( + modifier = Modifier.scale(scaleX = 1f, scaleY = 0.3f), + sliderState = sliderState, + colors = SliderDefaults.colors( + activeTrackColor = AppTheme.colorScheme.middle, + inactiveTrackColor = AppTheme.colorScheme.border + ) + ) + }, + thumb = { + Card( + modifier = Modifier.size(AppTheme.size.buttonPadding), + elevation = CardDefaults.cardElevation(AppTheme.size.buttonPadding), + shape = CircleShape, + colors = CardDefaults.cardColors( + containerColor = AppTheme.colorScheme.bottom + ), + content = {} + ) + }, + colors = SliderColors( + thumbColor = AppTheme.colorScheme.top, + activeTrackColor = AppTheme.colorScheme.middle, + activeTickColor = AppTheme.colorScheme.bottom, + inactiveTrackColor = AppTheme.colorScheme.middle, + inactiveTickColor = AppTheme.colorScheme.bottom, + disabledThumbColor = AppTheme.colorScheme.top, + disabledActiveTrackColor = AppTheme.colorScheme.middle, + disabledActiveTickColor = AppTheme.colorScheme.bottom, + disabledInactiveTrackColor = AppTheme.colorScheme.middle, + disabledInactiveTickColor = AppTheme.colorScheme.bottom, + ) + ) + } +} + +@Composable +fun BasicScaffold( + modifier: Modifier, + topBar: @Composable () -> Unit = {}, + bottomBar: @Composable () -> Unit = {}, + floatingActionButton: @Composable () -> Unit = {}, + content: @Composable (PaddingValues) -> Unit, + + ){ + Scaffold( + modifier = modifier + .background(AppTheme.colorScheme.bottom), + topBar = topBar, + bottomBar = bottomBar, + floatingActionButton = floatingActionButton, + content = content, + containerColor = AppTheme.colorScheme.bottom, + contentColor = AppTheme.colorScheme.top, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AppTopBar( + title: String, + alignment : Alignment.Horizontal = Alignment.Start, + titleStyle: TextStyle = AppTheme.typography.head, + leftIcon : @Composable () -> Unit = {}, + rightIcon : @Composable RowScope.() -> Unit = {} +){ + when(alignment){ + Alignment.CenterHorizontally -> { + CenterAlignedTopAppBar( + title = { + Text( + text = title, + style = titleStyle, + textAlign = TextAlign.Center, + color = AppTheme.colorScheme.border + ) + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = AppTheme.colorScheme.bottom, + scrolledContainerColor = AppTheme.colorScheme.bottom, + titleContentColor = AppTheme.colorScheme.border, + navigationIconContentColor = AppTheme.colorScheme.border, + actionIconContentColor = AppTheme.colorScheme.border + ), + navigationIcon = leftIcon, + actions = rightIcon + ) + } + Alignment.Start -> { + TopAppBar( + title = { + Text( + text = title, + style = titleStyle, + textAlign = TextAlign.Center, + color = AppTheme.colorScheme.border + ) + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = AppTheme.colorScheme.bottom, + scrolledContainerColor = AppTheme.colorScheme.bottom, + titleContentColor = AppTheme.colorScheme.border, + navigationIconContentColor = AppTheme.colorScheme.border, + actionIconContentColor = AppTheme.colorScheme.border + ), + navigationIcon = leftIcon, + actions = rightIcon + ) + } + } +} + +@Composable +fun AppButton( + modifier: Modifier = Modifier, + containerColor : Color = AppTheme.colorScheme.top, + contentColor : Color = AppTheme.colorScheme.tint, + disabledContainerColor : Color = AppTheme.colorScheme.bottom, + disabledContentColor : Color = AppTheme.colorScheme.border, + onClick: () -> Unit, + content : @Composable RowScope.() -> Unit +){ + Button( + modifier = modifier, + onClick = onClick, + shape = AppTheme.shapes.button, + colors = ButtonColors( + containerColor = containerColor, + contentColor = contentColor, + disabledContainerColor = disabledContainerColor, + disabledContentColor = disabledContentColor, + ), + content = content + ) +} + + +@Composable +fun AppText( + text: String, + style : TextStyle = AppTheme.typography.body, + textAlign: TextAlign = TextAlign.Start, + modifier: Modifier = Modifier, + color : Color = AppTheme.colorScheme.border +){ + var resizedTextStyle by remember { + mutableStateOf(style) + } + var shouldDraw by remember { + mutableStateOf(false) + } + + val defaultFontSize = style + + Text( + text = text, + color = color, + textAlign = textAlign, + modifier = modifier.drawWithContent { + if (shouldDraw) { + drawContent() + } + }, + softWrap = false, + style = resizedTextStyle, + onTextLayout = { result -> + if (result.didOverflowWidth) { + if (style.fontSize.isUnspecified) { + resizedTextStyle = resizedTextStyle.copy( + fontSize = defaultFontSize.fontSize + ) + } + resizedTextStyle = resizedTextStyle.copy( + fontSize = resizedTextStyle.fontSize * 0.95 + ) + } else { + shouldDraw = true + } + } + ) +} + +@Composable +fun AppTextField( + modifier : Modifier = Modifier, + value : String, + onValueChange : (String) -> Unit, + label : String, + enabled: Boolean = true, +){ + TextField( + modifier = modifier, + value = value, + onValueChange = onValueChange, + label = { + Text( + text = label, + style = AppTheme.typography.label, + color = AppTheme.colorScheme.border + ) + }, + colors = TextFieldDefaults.colors( + focusedTextColor = AppTheme.colorScheme.border, + unfocusedTextColor = AppTheme.colorScheme.border, + focusedContainerColor = AppTheme.colorScheme.middle, + unfocusedContainerColor = AppTheme.colorScheme.middle, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent + ), + enabled = enabled + ) +} \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/frame/FourCutFrame.kt b/presenter/src/main/java/com/foke/together/presenter/frame/FourCutFrame.kt index 4b468c7..efd588d 100644 --- a/presenter/src/main/java/com/foke/together/presenter/frame/FourCutFrame.kt +++ b/presenter/src/main/java/com/foke/together/presenter/frame/FourCutFrame.kt @@ -41,114 +41,114 @@ import coil.request.ImageRequest import com.foke.together.domain.interactor.entity.FramePosition 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 +@Deprecated("Deprecated") @Composable fun FourCutFrame( // TODO: Need to refactoring. separate frame design with application theme - designColorScheme: ColorScheme = mediumContrastLightColorScheme, +// designColorScheme: ColorScheme = mediumContrastLightColorScheme, cameraImageUrlList : List? = null, position: FramePosition? = null ) { - ConstraintLayout( - modifier = Modifier - .aspectRatio(ratio = 0.3333f) - .background(color = designColorScheme.surface) - .border(1.dp, designColorScheme.inverseSurface) - .padding( - start = position.let { - when(it){ - FramePosition.LEFT -> 20.dp - FramePosition.RIGHT -> 0.dp - null -> 10.dp - } - }, - end = position.let { - when(it){ - FramePosition.LEFT -> 0.dp - FramePosition.RIGHT -> 20.dp - null -> 10.dp - } - }, - top = 40.dp - ) - ) { - val (cameraColumn, decorateRow) = createRefs() - LazyColumn( - state = rememberLazyListState(), - verticalArrangement = Arrangement.spacedBy(10.dp), - contentPadding = PaddingValues(horizontal = 15.dp, vertical = 15.dp), - modifier = Modifier - .constrainAs(cameraColumn) { - top.linkTo(parent.top) - bottom.linkTo(decorateRow.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - } - .wrapContentSize() - ) { - 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() - - ) - } - } - } - Column( - modifier = Modifier - .constrainAs(decorateRow){ - top.linkTo(cameraColumn.bottom) - bottom.linkTo(parent.bottom) - start.linkTo(parent.start) - end.linkTo(parent.end) - height = Dimension.fillToConstraints - width = Dimension.fillToConstraints - }, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Icon( - imageVector = ImageVector.vectorResource( - id = R.drawable.fourcut_together - ), - contentDescription = "for decorate", - modifier = Modifier.weight(1f), - tint = designColorScheme.primaryContainer - ) - Text( - text = TimeUtil.getCurrentDisplayTime(), - modifier = Modifier.weight(1f), - color = designColorScheme.inverseSurface, - fontSize = 15.sp, - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold - ) - } - } + // TODO(세션 고도화 작업에 사용할 예정) +// ConstraintLayout( +// modifier = Modifier +// .aspectRatio(ratio = 0.3333f) +// .background(color = designColorScheme.surface) +// .border(1.dp, designColorScheme.inverseSurface) +// .padding( +// start = position.let { +// when(it){ +// FramePosition.LEFT -> 20.dp +// FramePosition.RIGHT -> 0.dp +// null -> 10.dp +// } +// }, +// end = position.let { +// when(it){ +// FramePosition.LEFT -> 0.dp +// FramePosition.RIGHT -> 20.dp +// null -> 10.dp +// } +// }, +// top = 40.dp +// ) +// ) { +// val (cameraColumn, decorateRow) = createRefs() +// LazyColumn( +// state = rememberLazyListState(), +// verticalArrangement = Arrangement.spacedBy(10.dp), +// contentPadding = PaddingValues(horizontal = 15.dp, vertical = 15.dp), +// modifier = Modifier +// .constrainAs(cameraColumn) { +// top.linkTo(parent.top) +// bottom.linkTo(decorateRow.top) +// start.linkTo(parent.start) +// end.linkTo(parent.end) +// } +// .wrapContentSize() +// ) { +// 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() +// +// ) +// } +// } +// } +// Column( +// modifier = Modifier +// .constrainAs(decorateRow){ +// top.linkTo(cameraColumn.bottom) +// bottom.linkTo(parent.bottom) +// start.linkTo(parent.start) +// end.linkTo(parent.end) +// height = Dimension.fillToConstraints +// width = Dimension.fillToConstraints +// }, +// horizontalAlignment = Alignment.CenterHorizontally +// ) { +// Icon( +// imageVector = ImageVector.vectorResource( +// id = R.drawable.fourcut_together +// ), +// contentDescription = "for decorate", +// modifier = Modifier.weight(1f), +// tint = designColorScheme.primaryContainer +// ) +// Text( +// text = TimeUtil.getCurrentDisplayTime(), +// modifier = Modifier.weight(1f), +// color = designColorScheme.inverseSurface, +// fontSize = 15.sp, +// textAlign = TextAlign.Center, +// fontWeight = FontWeight.Bold +// ) +// } +// } } -@Preview(showBackground = true) -@Composable -private fun DefaultFrame() { - FourCutTogetherTheme { - FourCutFrame( - designColorScheme = highContrastDarkColorScheme - ) - } -} \ No newline at end of file +//@Preview(showBackground = true) +//@Composable +//private fun DefaultFrame() { +// FourCutTogetherTheme { +// FourCutFrame( +// designColorScheme = highContrastDarkColorScheme +// ) +// } +//} \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/screen/GenerateImageScreen.kt b/presenter/src/main/java/com/foke/together/presenter/screen/GenerateImageScreen.kt index f70a48d..a16157c 100644 --- a/presenter/src/main/java/com/foke/together/presenter/screen/GenerateImageScreen.kt +++ b/presenter/src/main/java/com/foke/together/presenter/screen/GenerateImageScreen.kt @@ -9,9 +9,9 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -24,18 +24,19 @@ import androidx.compose.ui.graphics.layer.GraphicsLayer import androidx.compose.ui.graphics.layer.drawLayer import androidx.compose.ui.graphics.rememberGraphicsLayer import androidx.compose.ui.res.colorResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.foke.together.domain.interactor.entity.DefaultCutFrameSet import com.foke.together.presenter.R +import com.foke.together.presenter.component.AppTopBar +import com.foke.together.presenter.component.BasicScaffold import com.foke.together.presenter.frame.DefaultCutFrame +import com.foke.together.presenter.theme.AppTheme import com.foke.together.presenter.theme.FourCutTogetherTheme import com.foke.together.presenter.viewmodel.GenerateImageViewModel import com.foke.together.util.AppLog @@ -49,6 +50,7 @@ fun GenerateImageScreen( viewModel: GenerateImageViewModel = hiltViewModel() ) { val TAG = "GenerateImageScreen" + val curFrame = viewModel.cutFrame as DefaultCutFrameSet val graphicsLayer1 = rememberGraphicsLayer() val graphicsLayer2 = rememberGraphicsLayer() val coroutineScope = rememberCoroutineScope() @@ -70,49 +72,13 @@ fun GenerateImageScreen( navigateToShare() } } - - Column ( - modifier = Modifier - .fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Text( - text = "이미지를 생성중입니다", - fontWeight = FontWeight.Bold, - fontSize = 48.sp, - modifier = Modifier - .padding(bottom = 48.dp) - ) - - Row { - if (isFirstState.value) { - // TODO: 나중에 다른 CutFrameSet 어떻게 처리해야 할지? - GetDefaultFrame( - cutFrame = viewModel.cutFrame as DefaultCutFrameSet, - imageUri = imageUri, - graphicsLayer = graphicsLayer1, - ) - } else { - GetDefaultFrame( - cutFrame = viewModel.cutFrame as DefaultCutFrameSet, - imageUri = imageUri, - graphicsLayer = graphicsLayer2, - isForPrint = true, - isPaddingHorizontal = 16.dp - ) - } - } - - CircularProgressIndicator( - modifier = Modifier - .width(128.dp) - .height(128.dp) - .padding(top = 48.dp), - color = colorResource(R.color.app_primary_color), - strokeWidth = 24.dp - ) - } + GenerateImageContent( + cutFrame = curFrame, + imageUri = imageUri, + isFirstState = isFirstState.value, + graphicsLayer1 = graphicsLayer1, + graphicsLayer2 = graphicsLayer2 + ) } // TODO: !!!!! left / right 20dp 마진 있었음 @@ -146,6 +112,63 @@ fun GetDefaultFrame( } } +@Composable +fun GenerateImageContent( + cutFrame: DefaultCutFrameSet, + imageUri: List, + isFirstState: Boolean, + graphicsLayer1: GraphicsLayer, + graphicsLayer2: GraphicsLayer, + isForPrint: Boolean = true, + +){ + BasicScaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + AppTopBar( + title = "사진을 생성중입니다...", + alignment = Alignment.CenterHorizontally + ) + }, + bottomBar = { + CircularProgressIndicator( + modifier = Modifier + .size(AppTheme.size.button), + color = AppTheme.colorScheme.top, + strokeWidth = AppTheme.size.icon + ) + }, + ) { paddingValues -> + Row( + modifier = Modifier + .fillMaxSize() + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding() + ), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ){ + if (isFirstState) { + // TODO: 나중에 다른 CutFrameSet 어떻게 처리해야 할지? + GetDefaultFrame( + cutFrame = cutFrame, + imageUri = imageUri, + graphicsLayer = graphicsLayer1, + ) + } else { + GetDefaultFrame( + cutFrame = cutFrame, + imageUri = imageUri, + graphicsLayer = graphicsLayer2, + isForPrint = isForPrint, + isPaddingHorizontal = 16.dp + ) + } + } + } +} + @Composable private fun WhiteBox( size: Dp @@ -162,9 +185,13 @@ private fun WhiteBox( @Composable private fun DefaultFrame() { FourCutTogetherTheme { - GenerateImageScreen( - navigateToShare = {}, - popBackStack = {} + GenerateImageContent( + cutFrame = DefaultCutFrameSet.FourCutLight, + imageUri = listOf(Uri.EMPTY, Uri.EMPTY, Uri.EMPTY, Uri.EMPTY), + isFirstState = true, + graphicsLayer1 = rememberGraphicsLayer(), + graphicsLayer2 = rememberGraphicsLayer() + ) } } \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/screen/HomeScreen.kt b/presenter/src/main/java/com/foke/together/presenter/screen/HomeScreen.kt index 3320e4e..2494786 100644 --- a/presenter/src/main/java/com/foke/together/presenter/screen/HomeScreen.kt +++ b/presenter/src/main/java/com/foke/together/presenter/screen/HomeScreen.kt @@ -1,17 +1,31 @@ package com.foke.together.presenter.screen import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Settings -import androidx.compose.material3.* +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -19,6 +33,10 @@ import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import androidx.hilt.navigation.compose.hiltViewModel import com.foke.together.presenter.R +import com.foke.together.presenter.component.AppButton +import com.foke.together.presenter.component.AppTopBar +import com.foke.together.presenter.component.BasicScaffold +import com.foke.together.presenter.theme.AppTheme import com.foke.together.presenter.theme.FourCutTogetherTheme import com.foke.together.presenter.viewmodel.HomeViewModel @@ -34,73 +52,97 @@ fun HomeScreen( onDispose { } } - FourCutTogetherTheme() { - ConstraintLayout( - modifier = Modifier.fillMaxSize() - ) { - val (topBarrier, bottomBarrier, startBarrier, endBarrier, play, setting, summary) = createRefs() + HomeScreenContent( + navigationSelectFrame = navigationSelectFrame, + navigateToSetting = navigateToSetting, + ) +} +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeScreenContent( + navigationSelectFrame: () -> Unit, + navigateToSetting: () -> Unit, +){ + BasicScaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + AppTopBar( + title = "", + alignment = Alignment.CenterHorizontally, + rightIcon = { + IconButton( + onClick = { navigateToSetting() }, + modifier = Modifier.wrapContentSize(), + ) { + Icon( + modifier = Modifier.size(AppTheme.size.icon), + imageVector = Icons.Filled.Settings, + contentDescription = "Setting", + tint = AppTheme.colorScheme.top + ) + } + } + ) + } + ) { paddingValues -> + Column( + modifier = Modifier.fillMaxSize() + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding(), + start = AppTheme.size.layoutPadding, + end = AppTheme.size.layoutPadding + ), + verticalArrangement = Arrangement.SpaceEvenly, + horizontalAlignment = Alignment.CenterHorizontally + ){ Image( painter = painterResource( id = R.drawable.fourcut_together ), contentDescription = "FourCuts Together", - - modifier = Modifier.constrainAs(play) { - centerTo(parent) - height = Dimension.wrapContent - width = Dimension.wrapContent - }, + modifier = Modifier.wrapContentSize(), ) - - IconButton( - onClick = { navigateToSetting() }, - modifier = Modifier.constrainAs(setting) { - top.linkTo(parent.top, margin = 24.dp) - end.linkTo(parent.end, margin = 24.dp) - height = Dimension.wrapContent - width = Dimension.wrapContent - }, - ) { - Icon( - modifier = Modifier.size(85.dp), - imageVector = Icons.Filled.Settings, - contentDescription = "Setting", - tint = MaterialTheme.colorScheme.primary - ) - } - - Button( + AppButton( onClick = { navigationSelectFrame() }, - modifier = Modifier - .constrainAs(summary) { - top.linkTo(play.bottom) - start.linkTo(parent.start, margin = 64.dp) - end.linkTo(parent.end, margin = 64.dp) - bottom.linkTo(parent.bottom) - height = Dimension.wrapContent - width = Dimension.fillToConstraints - } + modifier = Modifier.wrapContentSize() + .padding(AppTheme.size.buttonPadding) ) { Text( text = stringResource(id = R.string.home_button_start), - style = MaterialTheme.typography.displayLarge, - fontWeight = FontWeight.Bold, - fontSize = 36.sp + style = AppTheme.typography.title, + color = AppTheme.colorScheme.tint ) } } } } -@Preview(showBackground = true) +@Preview( + showBackground = true, + device = Devices.PHONE +) +@Composable +private fun HomeScreenPortraitPreview() { + FourCutTogetherTheme() { + HomeScreenContent( + navigationSelectFrame = {}, + navigateToSetting = {}, + ) + } +} + +@Preview( + showBackground = true, + device = Devices.TABLET +) @Composable -private fun DefaultPreview() { +private fun HomeScreenLandScapePreview() { FourCutTogetherTheme() { - HomeScreen( + HomeScreenContent( navigationSelectFrame = {}, navigateToSetting = {}, - popBackStack = {} ) } } \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/screen/InternalCameraScreen.kt b/presenter/src/main/java/com/foke/together/presenter/screen/InternalCameraScreen.kt index 87b4527..0d16822 100644 --- a/presenter/src/main/java/com/foke/together/presenter/screen/InternalCameraScreen.kt +++ b/presenter/src/main/java/com/foke/together/presenter/screen/InternalCameraScreen.kt @@ -12,9 +12,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material3.BottomAppBar -import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -36,6 +34,8 @@ import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.foke.together.presenter.R +import com.foke.together.presenter.component.AppTopBar +import com.foke.together.presenter.component.BasicScaffold import com.foke.together.presenter.screen.state.InternalCameraState import com.foke.together.presenter.theme.FourCutTogetherTheme import com.foke.together.presenter.viewmodel.InternelCameraViewModel @@ -89,18 +89,12 @@ fun InternalCameraScreen( initialPreview : (Context, PreviewView) -> Unit, releasePreview : (Context) -> Unit, ){ - Scaffold( + BasicScaffold( modifier = Modifier.fillMaxSize(), topBar = { - CenterAlignedTopAppBar( - title = { - Text( - text = "${captureCount}번째 촬영", - fontWeight = FontWeight.Bold, - fontSize = 24.sp, - textAlign = TextAlign.Center - ) - } + AppTopBar( + title = "${captureCount}번째 촬영", + alignment = Alignment.CenterHorizontally ) }, bottomBar = { diff --git a/presenter/src/main/java/com/foke/together/presenter/screen/SelectFrameScreen.kt b/presenter/src/main/java/com/foke/together/presenter/screen/SelectFrameScreen.kt index 042191d..78614ef 100644 --- a/presenter/src/main/java/com/foke/together/presenter/screen/SelectFrameScreen.kt +++ b/presenter/src/main/java/com/foke/together/presenter/screen/SelectFrameScreen.kt @@ -1,16 +1,25 @@ package com.foke.together.presenter.screen +import android.content.res.Configuration import android.net.Uri +import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.clickable +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.Row +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PageSize import androidx.compose.foundation.pager.rememberPagerState @@ -18,29 +27,41 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.Checkbox +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarColors import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.UiMode import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import androidx.hilt.navigation.compose.hiltViewModel +import com.foke.together.domain.interactor.entity.CutFrame import com.foke.together.domain.interactor.entity.DefaultCutFrameSet import com.foke.together.presenter.R +import com.foke.together.presenter.component.AppTopBar +import com.foke.together.presenter.component.BasicScaffold import com.foke.together.presenter.frame.DefaultCutFrame +import com.foke.together.presenter.theme.AppTheme import com.foke.together.presenter.theme.FourCutTogetherTheme import com.foke.together.presenter.viewmodel.SelectFrameViewModel @@ -50,192 +71,131 @@ fun SelectFrameScreen( popBackStack: () -> Unit, viewModel: SelectFrameViewModel = hiltViewModel() ) { - val cutFrames = remember { mutableStateOf>(emptyList()) } - val isDateDisplay = remember { mutableStateOf(false) } + val cutFrames by viewModel.cutFrames.collectAsState() + val isDateDisplay by viewModel.isDateDisplay.collectAsState() DisposableEffect(Unit) { viewModel.updateSessionStatus() - // get frames - cutFrames.value = DefaultCutFrameSet::class.sealedSubclasses.mapNotNull { classes -> - val cutFrame = classes.objectInstance - cutFrame?.isDateString = isDateDisplay.value - cutFrame - }.sortedBy { it.index } - onDispose { } } - - FourCutTogetherTheme { - val pagerState = rememberPagerState( - initialPage = 0 - ) { - cutFrames.value.size // 총 페이지 수 설정 + SelectFrameContent( + cutFrames = cutFrames, + isDateDisplay = isDateDisplay, + selectFrame = { + viewModel.setCutFrameType(it) + navigateToMethod() + }, + onChangeData = { + viewModel.updateDateDisplay() } - ConstraintLayout( - modifier = Modifier.fillMaxSize() - ) { - val (backKey, title, pager, isDateDisplayCheckBox, frameSelectButton) = createRefs() - val topGuideLine = createGuidelineFromTop(0.1f) - val bottomGuideLine = createGuidelineFromBottom(0.1f) - val startGuideLine = createGuidelineFromStart(0.1f) - val endGuideLine = createGuidelineFromEnd(0.1f) - - IconButton( - onClick = { popBackStack() }, - modifier = Modifier.constrainAs(backKey) { - top.linkTo(topGuideLine) - start.linkTo(parent.start) - end.linkTo(title.start) - bottom.linkTo(pager.top) - height = Dimension.wrapContent - width = Dimension.wrapContent - }, - ) { - Icon( - modifier = Modifier.size(24.dp), - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "backKey", - tint = MaterialTheme.colorScheme.primary - ) - } + ) +} - Text( - text = "프레임을 선택하세요", - modifier = Modifier.constrainAs(title) { - top.linkTo(topGuideLine) - start.linkTo(startGuideLine) - end.linkTo(endGuideLine) - bottom.linkTo(pager.top) - }, - fontWeight = FontWeight.Bold, - fontSize = 25.sp +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SelectFrameContent( + cutFrames: List, + isDateDisplay: Boolean, + selectFrame: (cutFrame: CutFrame) -> Unit, + onChangeData : ( Boolean ) -> Unit +){ + BasicScaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + AppTopBar( + title = "프레임을 선택하세요", + alignment = Alignment.CenterHorizontally, ) - - HorizontalPager( - modifier = Modifier - .padding(top = 30.dp, bottom = 30.dp) - .constrainAs(pager) { - top.linkTo(title.bottom) - start.linkTo(startGuideLine) - end.linkTo(endGuideLine) - bottom.linkTo(isDateDisplayCheckBox.top) - width = Dimension.wrapContent - height = Dimension.wrapContent - }, - verticalAlignment = Alignment.Top, - state = pagerState, - pageSize = PageSize.Fixed(250.dp), - contentPadding = PaddingValues( - start = 120.dp, - end = 120.dp, - ) - ) { page -> - Column ( - modifier = Modifier.fillMaxWidth(), + }, + bottomBar = { + Row( + modifier = Modifier.fillMaxWidth().wrapContentHeight(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly + ){ + Column( + modifier = Modifier.wrapContentSize(), + verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally - ) { - Box( - contentAlignment = Alignment.Center, - modifier = Modifier - .clickable { - viewModel.setCutFrameType(cutFrames.value[page]) - navigateToMethod() - } - ) { - DefaultCutFrame( - cutFrames.value[page], - listOf( - // !!!!! TODO: empty 혹은 다른 이미지로 교체 - Uri.parse("file:///android_asset/sample_cut.png"), - Uri.parse("file:///android_asset/sample_cut.png"), - Uri.parse("file:///android_asset/sample_cut.png"), - Uri.parse("file:///android_asset/sample_cut.png"), - ), - - ) - } + ){ + Checkbox( + checked = isDateDisplay, + onCheckedChange = { isChecked -> + onChangeData(isChecked) + } + ) Text( - text = cutFrames.value[page].frameTitle, - fontWeight = FontWeight.Bold, - color = Color(200,200,200), - fontSize = 24.sp, - modifier = Modifier.padding(top = 16.dp) + text = "날짜 표시하기", + style = AppTheme.typography.title ) } - } - Row ( - modifier = Modifier - .constrainAs(isDateDisplayCheckBox) { - top.linkTo(pager.bottom) - start.linkTo(startGuideLine) - end.linkTo(endGuideLine) - bottom.linkTo(frameSelectButton.top) - width = Dimension.wrapContent - height = Dimension.wrapContent - } - .padding(bottom = 30.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Checkbox( - checked = isDateDisplay.value, - onCheckedChange = { isChecked -> - isDateDisplay.value = isChecked - cutFrames.value = cutFrames.value.map { - it.isDateString = isChecked - it + Column( + modifier = Modifier.wrapContentSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ){ + Checkbox( + checked = isDateDisplay, + onCheckedChange = { isChecked -> + onChangeData(isChecked) } - } - ) - Text( - text = "날짜 표시하기", - fontWeight = FontWeight.Bold, - color = Color.Gray, - fontSize = 16.sp, - modifier = Modifier.padding(start = 10.dp) - ) + ) + Text( + text = "QR 표시하기", + style = AppTheme.typography.title + ) + } } - - Button( - onClick = { - viewModel.setCutFrameType(cutFrames.value[pagerState.currentPage]) - navigateToMethod() - }, - modifier = Modifier - .constrainAs(frameSelectButton) { - top.linkTo(isDateDisplayCheckBox.bottom) - start.linkTo(startGuideLine) - end.linkTo(endGuideLine) - bottom.linkTo(bottomGuideLine) - height = Dimension.wrapContent - width = Dimension.wrapContent - } - .width(200.dp), - ) { - Text( - text = stringResource(id = R.string.select_frame_button_next), - style = MaterialTheme.typography.displayLarge, - fontWeight = FontWeight.Bold, - fontSize = 36.sp + } + ){ paddingValues -> + LazyVerticalGrid( + modifier = Modifier + .fillMaxSize() + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding(), + start = AppTheme.size.layoutPadding, + end = AppTheme.size.layoutPadding + ), + userScrollEnabled = true, + horizontalArrangement = Arrangement.spacedBy(AppTheme.size.layoutPadding), + verticalArrangement = Arrangement.spacedBy(AppTheme.size.layoutPadding), + columns = GridCells.Fixed(6) + ){ + items(cutFrames.size){ index -> + Image( + modifier = Modifier.aspectRatio(0.3333f) + .clickable( + true, + onClick = { + selectFrame(cutFrames[index]) + } + ), + painter = painterResource(id = cutFrames[index].frameImageSrc), + contentDescription = cutFrames[index].frameTitle ) } } } } -@Preview(showBackground = true) +@Preview( + showBackground = true, + device = Devices.TABLET, + uiMode = Configuration.ORIENTATION_LANDSCAPE +) @Composable private fun DefaultPreview() { FourCutTogetherTheme { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - SelectFrameScreen( - navigateToMethod = {}, - popBackStack = {} - ) - } + val cutFrames by remember { mutableStateOf(DefaultCutFrameSet.entries) } + var isDateDisplay by remember { mutableStateOf(false) } + SelectFrameContent( + cutFrames = cutFrames, + isDateDisplay = isDateDisplay, + selectFrame = {}, + onChangeData = { isDateDisplay = it } + ) } } \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/screen/SelectMethodScreen.kt b/presenter/src/main/java/com/foke/together/presenter/screen/SelectMethodScreen.kt index 05b803c..1c07bf5 100644 --- a/presenter/src/main/java/com/foke/together/presenter/screen/SelectMethodScreen.kt +++ b/presenter/src/main/java/com/foke/together/presenter/screen/SelectMethodScreen.kt @@ -32,6 +32,7 @@ import com.foke.together.presenter.R import com.foke.together.presenter.theme.FourCutTogetherTheme import com.foke.together.presenter.viewmodel.SelectMethodViewModel +@Deprecated("Deprecated") @Composable fun SelectMethodScreen( navigateToCamera: () -> Unit, diff --git a/presenter/src/main/java/com/foke/together/presenter/screen/SettingScreen.kt b/presenter/src/main/java/com/foke/together/presenter/screen/SettingScreen.kt index 96a9309..3384690 100644 --- a/presenter/src/main/java/com/foke/together/presenter/screen/SettingScreen.kt +++ b/presenter/src/main/java/com/foke/together/presenter/screen/SettingScreen.kt @@ -1,14 +1,20 @@ package com.foke.together.presenter.screen +import androidx.camera.core.CameraSelector import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack @@ -19,6 +25,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout @@ -26,9 +33,17 @@ import androidx.constraintlayout.compose.Dimension import com.foke.together.presenter.theme.FourCutTogetherTheme import androidx.hilt.navigation.compose.hiltViewModel import com.foke.together.domain.interactor.entity.CameraSourceType +import com.foke.together.presenter.component.AppTextField +import com.foke.together.presenter.component.AppTopBar +import com.foke.together.presenter.component.BasicScaffold +import com.foke.together.presenter.component.MultiOptionsButton +import com.foke.together.presenter.component.SliderPreference +import com.foke.together.presenter.theme.AppTheme import com.foke.together.presenter.viewmodel.SettingViewModel import com.foke.together.util.AppPolicy import kotlinx.coroutines.flow.map +import kotlin.time.Duration.Companion.seconds +import kotlin.time.DurationUnit private const val CameraSourceTypeError = -1 @@ -37,181 +52,119 @@ fun SettingScreen( popBackStack: () -> Unit, viewModel: SettingViewModel = hiltViewModel() ) { - val cameraSelectedIndex by remember { - viewModel.cameraSourceType.map { CameraSourceType.entries.indexOf(it) } - }.collectAsState(CameraSourceTypeError) + val cameraTypeIndex by viewModel.cameraTypeIndex.collectAsState() val cameraTypeList = CameraSourceType.entries.map { it.name } + val cameraSelector by viewModel.cameraSelector.collectAsState() + val cameraIPAddress by viewModel.cameraIPAddress.collectAsState() + val email by viewModel.email.collectAsState() + val password by viewModel.password.collectAsState() + val captureDuration by viewModel.captureDuration.collectAsState() - FourCutTogetherTheme { - ConstraintLayout( - modifier = Modifier.fillMaxSize() - ) { - val (backKey, content) = createRefs() - - val topGuideLine = createGuidelineFromTop(0.1f) - val bottomGuideLine = createGuidelineFromBottom(0.1f) - val startGuideLine = createGuidelineFromStart(0.2f) - val endGuideLine = createGuidelineFromEnd(0.2f) - - IconButton( - onClick = { popBackStack() }, - modifier = Modifier.constrainAs(backKey) { - top.linkTo(topGuideLine) - start.linkTo(startGuideLine) - height = Dimension.wrapContent - width = Dimension.wrapContent - }, - ) { - Icon( - modifier = Modifier.size(24.dp), - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "backKey", - tint = MaterialTheme.colorScheme.primary - ) - } - Column( - modifier = Modifier.constrainAs(content) { - top.linkTo(backKey.bottom) - start.linkTo(startGuideLine) - end.linkTo(endGuideLine) - } - .padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - // Camera Source Selection - Row( - modifier = Modifier.width(300.dp) // Make the row full width - ) { - val cornerRadius = 16.dp - cameraTypeList.forEachIndexed { index, item -> - OutlinedButton( - onClick = { - viewModel.setCameraSourceType(index) - }, - modifier = Modifier.weight(1f), - shape = when (index) { - 0 -> RoundedCornerShape( - topStart = cornerRadius, - topEnd = 0.dp, - bottomStart = cornerRadius, - bottomEnd = 0.dp - ) - - cameraTypeList.size - 1 -> RoundedCornerShape( - topStart = 0.dp, - topEnd = cornerRadius, - bottomStart = 0.dp, - bottomEnd = cornerRadius - ) - - else -> RoundedCornerShape(0.dp) - }, - border = BorderStroke( - 1.dp, if (cameraSelectedIndex == index) { - MaterialTheme.colorScheme.primary - } else { - MaterialTheme.colorScheme.secondary - } - ), - colors = if (cameraSelectedIndex == index) { // selected - ButtonDefaults.outlinedButtonColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - contentColor = MaterialTheme.colorScheme.primary - ) - } else { // not selected - ButtonDefaults.outlinedButtonColors( - containerColor = MaterialTheme.colorScheme.onPrimary, - contentColor = MaterialTheme.colorScheme.primary - ) - } - ) { - Text(item) - } - } - } - Spacer(modifier = Modifier.height(16.dp)) + SettingContent( + popBackStack = popBackStack, + cameraTypeList = cameraTypeList, + cameraTypeIndex = cameraTypeIndex, + onChangeCameraSourceType = { viewModel.setCameraSourceType(it) }, - TextField( - modifier = Modifier.width(300.dp), // Make the TextField full width - value = viewModel.cameraIPAddressState, - onValueChange = { - viewModel.cameraIPAddressState = it - viewModel.setCameraIPAddress(it) - }, - label = { Text(text = "Camera Server URL") }, - ) + cameraSelectorIndex = cameraSelector, + onChangeCameraSelector = { viewModel.setCameraSelector(it) }, - HorizontalDivider( - modifier = Modifier.padding(vertical = 16.dp).width(300.dp), - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), - thickness = 1.dp - ) + ipAddress = cameraIPAddress, + onIPAddressChange = { viewModel.setCameraIPAddress(it) }, - Column(modifier = Modifier.width(300.dp)) { - Text(text = "Account Info", style = MaterialTheme.typography.bodySmall) - Text(text = "user", style = MaterialTheme.typography.bodyLarge) - } + email = email, + onEmailChange = {viewModel.setEmail(it)}, + password = password, + onPasswordChange = { viewModel.setPassword(it) }, + captureDuration = captureDuration, + onCaptureDurationChange = {viewModel.setCaptureDuration(it)} + ) +} - Spacer(modifier = Modifier.height(16.dp)) +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SettingContent( + popBackStack: () -> Unit, - Column(modifier = Modifier.width(300.dp)) { - Text(text = "Bearer Token", style = MaterialTheme.typography.bodySmall) - Text(text = "YourBearerTokenValueHere", style = MaterialTheme.typography.bodyLarge) - } + cameraTypeList : List, + cameraTypeIndex: Int, + onChangeCameraSourceType : (Int) -> Unit, - HorizontalDivider( - modifier = Modifier.padding(vertical = 16.dp).width(300.dp), - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), - thickness = 1.dp - ) + cameraSelectorIndex: Int, + onChangeCameraSelector : (Int) -> Unit, - TextField( - modifier = Modifier.width(300.dp), - value = "", - onValueChange = { }, - label = { Text(text = "Email") }, - enabled = false - ) + ipAddress : String, + onIPAddressChange : (String) -> Unit, + email : String, + password : String, + onEmailChange : (String) -> Unit, + onPasswordChange : (String) -> Unit, - Spacer(modifier = Modifier.height(16.dp)) + captureDuration : Long, + onCaptureDurationChange : (Long) -> Unit, - TextField( - modifier = Modifier.width(300.dp), - value = "", - onValueChange = { }, - label = { Text(text = "Password") }, - enabled = false - ) +){ + BasicScaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + AppTopBar( + title = "", + leftIcon = { + Icon( + modifier = Modifier.size(AppTheme.size.icon) + .clickable(true, onClick = popBackStack), + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "backKey", + tint = AppTheme.colorScheme.top + ) + } + ) + } + ){ paddingValues -> + Column( + modifier = Modifier.fillMaxSize() + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding(), + start = AppTheme.size.layoutPadding, + end = AppTheme.size.layoutPadding + ), + verticalArrangement = Arrangement.SpaceAround, + horizontalAlignment = Alignment.CenterHorizontally, + ){ + MultiOptionsButton( + modifier = Modifier.wrapContentSize(), + optionList = cameraTypeList, + selectedOptionIndex = cameraTypeIndex, + onChangeOption = onChangeCameraSourceType + ) - TextButton( - onClick = { /* Handle login action */ }, - modifier = Modifier - .padding(vertical = 16.dp) // Add some padding - .width(300.dp) - .height(48.dp), // Optional: Set a fixed height for the button - shape = RoundedCornerShape(16.dp), // Slightly rounded corners - colors = ButtonDefaults.textButtonColors( - containerColor = MaterialTheme.colorScheme.primary, // Button background color - contentColor = MaterialTheme.colorScheme.onPrimary // Text color - ), - enabled = false - ) { - Text(text = "Log In") + SliderPreference( + modifier = Modifier.fillMaxWidth().wrapContentHeight(), + title = "Capture Duration", + sliderText = "${captureDuration / 1000}s", + sliderValue = captureDuration.toFloat(), + sliderRange = 1f..10f, + onSliderValueChange = { sliderValue -> + onCaptureDurationChange(sliderValue.toLong()) } + ) - HorizontalDivider( - modifier = Modifier.padding(vertical = 16.dp).width(300.dp), - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), - thickness = 1.dp + if(cameraTypeIndex == CameraSourceType.INTERNAL.ordinal){ + InternalSettingContent( + cameraSelectorIndex = cameraSelectorIndex, + onChangeCameraSelector = onChangeCameraSelector, ) - - TextField( - modifier = Modifier.width(300.dp), - value = AppPolicy.CAPTURE_INTERVAL.toString(), - onValueChange = { }, - label = { Text(text = "Capture Interval") }, - enabled = false + } + if(cameraTypeIndex == CameraSourceType.EXTERNAL.ordinal){ + ExternalSettingContent( + ipAddress = ipAddress, + onIPAddressChange = onIPAddressChange, + email = email, + onEmailChange = onEmailChange, + password = password, + onPasswordChange = onPasswordChange, ) } } @@ -219,18 +172,100 @@ fun SettingScreen( } @Composable -fun PhoneCamSetting(){ - // TODO: implement in phase 3 +fun ExternalSettingContent( + ipAddress : String, + onIPAddressChange : (String) -> Unit, + email : String, + password : String, + onEmailChange : (String) -> Unit, + onPasswordChange : (String) -> Unit, +){ + AppTextField( + modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(AppTheme.size.buttonPadding), + value = ipAddress, + onValueChange = onIPAddressChange, + label = "Camera Server URL", + ) + + Text(text = "Account Info", style = AppTheme.typography.label) + Text(text = "user", style = AppTheme.typography.label) + + Text(text = "Bearer Token", style = AppTheme.typography.label) + Text(text = "YourBearerTokenValueHere", style = AppTheme.typography.label) + + + AppTextField( + modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(AppTheme.size.buttonPadding), + value = email, + onValueChange = onEmailChange, + label = "Email", + enabled = false + ) + + AppTextField( + modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(AppTheme.size.buttonPadding), + value = password, + onValueChange = onPasswordChange, + label = "Password", + enabled = false + ) + + TextButton( + onClick = { /* Handle login action */ }, + modifier = Modifier.fillMaxWidth().height(AppTheme.size.button), + shape = AppTheme.shapes.button, // Slightly rounded corners + colors = ButtonDefaults.textButtonColors( + containerColor = AppTheme.colorScheme.top, // Button background color + contentColor = AppTheme.colorScheme.tint // Text color + ), + enabled = false + ) { + Text(text = "Log In") + } } @Composable -fun ExternalCamSetting( modifier: Modifier) { +fun InternalSettingContent( + cameraSelectorIndex: Int, + onChangeCameraSelector : (Int) -> Unit, +){ + MultiOptionsButton( + modifier = Modifier.wrapContentSize(), + optionList = listOf("Front", "Back"), + selectedOptionIndex = cameraSelectorIndex, + onChangeOption = onChangeCameraSelector + ) } -@Preview(showBackground = true) +@Preview( + showBackground = true, + device = Devices.TABLET +) @Composable -private fun DefaultPreview() { - SettingScreen( - popBackStack = {} - ) +private fun SettingContentPreview() { + var cameraTypeList by remember { mutableStateOf(listOf("Internal", "External"))} + var cameraTypeIndex by remember { mutableIntStateOf(CameraSourceType.EXTERNAL.ordinal ) } + var cameraSelectorIndex by remember { mutableIntStateOf(CameraSelector.LENS_FACING_FRONT) } + var ipAddress by remember { mutableStateOf("192.168.0.1")} + var email by remember { mutableStateOf("4cuts@foke.io") } + var password by remember { mutableStateOf("password") } + var captureDuration by remember { mutableLongStateOf(5.seconds.toLong(DurationUnit.MILLISECONDS)) } + FourCutTogetherTheme { + SettingContent( + popBackStack = {}, + cameraTypeList = cameraTypeList, + cameraTypeIndex = cameraTypeIndex, + onChangeCameraSourceType = { cameraTypeIndex = it }, + cameraSelectorIndex = cameraSelectorIndex, + onChangeCameraSelector = { cameraSelectorIndex = it }, + ipAddress = ipAddress, + onIPAddressChange = { ipAddress = it }, + email = email, + onEmailChange = {email = it}, + password = password, + onPasswordChange = {password = it}, + captureDuration = captureDuration, + onCaptureDurationChange = {captureDuration = it} + ) + } } \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/screen/ShareScreen.kt b/presenter/src/main/java/com/foke/together/presenter/screen/ShareScreen.kt index 1d80abd..49b1c3b 100644 --- a/presenter/src/main/java/com/foke/together/presenter/screen/ShareScreen.kt +++ b/presenter/src/main/java/com/foke/together/presenter/screen/ShareScreen.kt @@ -1,26 +1,40 @@ package com.foke.together.presenter.screen import android.content.Context +import android.graphics.Bitmap +import android.net.Uri import android.widget.Toast +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Print import androidx.compose.material.icons.filled.Share +import androidx.compose.material.icons.outlined.Download +import androidx.compose.material.icons.outlined.Print +import androidx.compose.material.icons.outlined.Share import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout @@ -31,9 +45,13 @@ import com.foke.together.presenter.theme.FourCutTogetherTheme import androidx.hilt.navigation.compose.hiltViewModel import coil.compose.AsyncImage import coil.request.ImageRequest +import com.foke.together.presenter.component.AppTopBar +import com.foke.together.presenter.component.BasicScaffold +import com.foke.together.presenter.theme.AppTheme import com.foke.together.presenter.viewmodel.ShareViewModel import com.foke.together.util.AppLog import com.foke.together.util.ImageFileUtil +import androidx.core.graphics.createBitmap @Composable fun ShareScreen( @@ -41,139 +59,127 @@ fun ShareScreen( viewModel: ShareViewModel = hiltViewModel() ) { val context = LocalContext.current - + val captureImageUri = viewModel.singleImageUri + val qrImageBitmap by viewModel.qrCodeBitmap.collectAsState() DisposableEffect(Unit) { viewModel.updateSessionStatus() onDispose { } } - ConstraintLayout( - modifier = Modifier.fillMaxSize() - ) { - val (finalPic, buttonColumn, printButton, shareButton, downloadButton, homeButton ) = createRefs() - val frameType = 0 - val topGuideLine = createGuidelineFromTop(0.1f) - val bottomGuideLine = createGuidelineFromBottom(0.1f) - val startGuideLine = createGuidelineFromStart(0.1f) - val endGuideLine = createGuidelineFromEnd(0.1f) - - DisposableEffect(Unit) { - saveToLocal(context, viewModel) - onDispose { } - } + ShareContent( + context = context, + captureImageUri = captureImageUri, + qrImageBitmap = qrImageBitmap + ) +} - // TODO: need check to change single ImageView - AsyncImage( - model = ImageRequest.Builder(context) - .data(viewModel.singleImageUri) - .build(), - contentDescription = "", - modifier = Modifier - .constrainAs(finalPic) { - top.linkTo(topGuideLine) - bottom.linkTo(bottomGuideLine) - start.linkTo(startGuideLine) - end.linkTo(buttonColumn.start) - width = Dimension.wrapContent - height = Dimension.fillToConstraints +@Composable +fun ShareContent( + context: Context, + captureImageUri : Uri, + qrImageBitmap : Bitmap? +){ + BasicScaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + AppTopBar( + title = "이미지 저장", + alignment = Alignment.CenterHorizontally + ) + }, + bottomBar = { + Row( + modifier = Modifier.fillMaxWidth().padding( + start = AppTheme.size.layoutPadding, + end = AppTheme.size.layoutPadding, + bottom = AppTheme.size.layoutPadding + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly + ){ + Column( + modifier = Modifier.wrapContentSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ){ + Icon( + imageVector = Icons.Outlined.Print, + contentDescription = "print", + tint = AppTheme.colorScheme.border + ) + Text( + text = "출력", + style = AppTheme.typography.title, + color = AppTheme.colorScheme.border + ) } - .aspectRatio(0.3333f) - ) - - Column( - modifier = Modifier.constrainAs(buttonColumn){ - top.linkTo(topGuideLine) - bottom.linkTo(bottomGuideLine) - start.linkTo(finalPic.end) - end.linkTo(endGuideLine) - width = Dimension.fillToConstraints - height = Dimension.fillToConstraints - }, - horizontalAlignment = Alignment.CenterHorizontally - ) { - IconButton( - onClick = { - viewModel.closeSession() - popBackStack() - }, - modifier = Modifier - .width(70.dp) - .weight(1f) - ) { - Icon( - modifier = Modifier.fillMaxSize(), - imageVector = Icons.Filled.Home, - contentDescription = "Home", - tint = MaterialTheme.colorScheme.primary - ) - } - IconButton( - onClick = { - AppLog.e("", "", "asdf: ${viewModel.twoImageUri}") - ImageFileUtil.printFromUri(context, viewModel.twoImageUri) - }, - modifier = Modifier - .width(70.dp) - .weight(1f) - ) { - Icon( - modifier = Modifier.fillMaxSize(), - imageVector = Icons.Filled.Print, - contentDescription = "Print", - tint = MaterialTheme.colorScheme.primary - ) - } + Column( + modifier = Modifier.wrapContentSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ){ + Icon( + imageVector = Icons.Outlined.Download, + contentDescription = "download", + tint = AppTheme.colorScheme.border + ) + Text( + text = "저장", + style = AppTheme.typography.title, + color = AppTheme.colorScheme.border + ) + } - IconButton( - onClick = { - val contentUri = FileProvider.getUriForFile( - context, - "com.foke.together.fileprovider", - viewModel.singleImageUri.toFile() + Column( + modifier = Modifier.wrapContentSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ){ + Icon( + imageVector = Icons.Outlined.Share, + contentDescription = "sharing", + tint = AppTheme.colorScheme.border ) - ImageFileUtil.shareUri(context, contentUri) - }, - modifier = Modifier - .width(70.dp) - .weight(1f) - ) { - Icon( - modifier = Modifier.fillMaxSize(), - imageVector = Icons.Filled.Share, - contentDescription = "Share", - tint = MaterialTheme.colorScheme.primary - ) + Text( + text = "공유", + style = AppTheme.typography.title, + color = AppTheme.colorScheme.border + ) + } } - - // TODO: add android native share button - IconButton( - onClick = { - saveToLocal(context, viewModel) - }, + }, + ){ paddingValues -> + Row( + modifier = Modifier.fillMaxSize().padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding(), + start = AppTheme.size.layoutPadding, + end = AppTheme.size.layoutPadding + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly + ){ + // TODO: need check to change single ImageView + AsyncImage( + model = ImageRequest.Builder(context) + .data(captureImageUri) + .build(), + contentDescription = "", modifier = Modifier - .width(70.dp) - .weight(1f) - ) { - Icon( - modifier = Modifier.fillMaxSize(), - imageVector = Icons.Filled.Download, - contentDescription = "Download", - tint = MaterialTheme.colorScheme.primary - ) - } + .aspectRatio(0.4f) + .fillMaxHeight(0.6f) + ) - Box( - modifier = Modifier.weight(1f) - ){ - AsyncImage( - model = ImageRequest.Builder(context) - .data(viewModel.qrCodeBitmap) - .build(), - contentDescription = "qr code", - modifier = Modifier.fillMaxSize() - ) - } + AsyncImage( + model = ImageRequest.Builder(context) + .data(qrImageBitmap) + .build(), + contentDescription = "", + modifier = Modifier + .fillMaxHeight(0.5f) + .aspectRatio(1f) + ) } } } @@ -188,17 +194,18 @@ private fun saveToLocal(context: Context, viewModel: ShareViewModel) { } } -@Preview(showBackground = true) +@Preview( + showBackground = true, + device = Devices.TABLET +) @Composable -private fun DefaultPreview() { +private fun ShareScreenPreview() { + val qrBitmap = createBitmap(125, 125, Bitmap.Config.ARGB_8888) FourCutTogetherTheme { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - ShareScreen( - popBackStack = {} - ) - } + ShareContent( + context = LocalContext.current, + captureImageUri = Uri.EMPTY, + qrImageBitmap = qrBitmap + ) } } \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/theme/AppDesignSystem.kt b/presenter/src/main/java/com/foke/together/presenter/theme/AppDesignSystem.kt new file mode 100644 index 0000000..a95ac1e --- /dev/null +++ b/presenter/src/main/java/com/foke/together/presenter/theme/AppDesignSystem.kt @@ -0,0 +1,63 @@ +package com.foke.together.presenter.theme + +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.Dp + +data class AppColorScheme( + val top : Color = Color.Unspecified, + val bottom : Color = Color.Unspecified, + val middle : Color = Color.Unspecified, + + val border : Color = Color.Unspecified, + // 에러 컬러 + val tint : Color = Color.Unspecified, +) + + +data class AppTypography( + // Top App Bar Screen Name + val head : TextStyle = TextStyle.Default, + + // 타이틀 바로 아래 + val title : TextStyle = TextStyle.Default, + + // 요소의 제목 + val body : TextStyle = TextStyle.Default, + + // 요소 제목의 하위 요소 + val label : TextStyle = TextStyle.Default, +) + + +data class AppShape( + val container : Shape = RectangleShape, + val surface : Shape = RectangleShape, + val icon : Shape = RectangleShape, + val button : Shape = RectangleShape, +) + +data class AppSize( + val layoutPadding : Dp = Dp.Unspecified, + val buttonPadding : Dp = Dp.Unspecified, + val icon : Dp = Dp.Unspecified, + val button : Dp = Dp.Unspecified, +) + +val LocalAppColorScheme = staticCompositionLocalOf { + AppColorScheme() +} + +val LocalAppShape = staticCompositionLocalOf { + AppShape() +} + +val LocalAppTypography = staticCompositionLocalOf { + AppTypography() +} +val LocalAppSize = staticCompositionLocalOf { + AppSize() +} \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/theme/Color.kt b/presenter/src/main/java/com/foke/together/presenter/theme/Color.kt index 4b34d7f..198ab0f 100644 --- a/presenter/src/main/java/com/foke/together/presenter/theme/Color.kt +++ b/presenter/src/main/java/com/foke/together/presenter/theme/Color.kt @@ -1,225 +1,8 @@ package com.foke.together.presenter.theme import androidx.compose.ui.graphics.Color -val primaryLight = Color(0xFF00696B) -val onPrimaryLight = Color(0xFFFFFFFF) -val primaryContainerLight = Color(0xFF9CF1F3) -val onPrimaryContainerLight = Color(0xFF002021) -val secondaryLight = Color(0xFF4A6363) -val onSecondaryLight = Color(0xFFFFFFFF) -val secondaryContainerLight = Color(0xFFCCE8E8) -val onSecondaryContainerLight = Color(0xFF041F20) -val tertiaryLight = Color(0xFF4C5F7C) -val onTertiaryLight = Color(0xFFFFFFFF) -val tertiaryContainerLight = Color(0xFFD4E3FF) -val onTertiaryContainerLight = Color(0xFF061C36) -val errorLight = Color(0xFFBA1A1A) -val onErrorLight = Color(0xFFFFFFFF) -val errorContainerLight = Color(0xFFFFDAD6) -val onErrorContainerLight = Color(0xFF410002) -val backgroundLight = Color(0xFFF4FBFA) -val onBackgroundLight = Color(0xFF161D1D) -val surfaceLight = Color(0xFFF4FBFA) -val onSurfaceLight = Color(0xFF161D1D) -val surfaceVariantLight = Color(0xFFDAE4E4) -val onSurfaceVariantLight = Color(0xFF3F4949) -val outlineLight = Color(0xFF6F7979) -val outlineVariantLight = Color(0xFFBEC8C8) -val scrimLight = Color(0xFF000000) -val inverseSurfaceLight = Color(0xFF2B3232) -val inverseOnSurfaceLight = Color(0xFFECF2F1) -val inversePrimaryLight = Color(0xFF80D4D6) -val surfaceDimLight = Color(0xFFD5DBDB) -val surfaceBrightLight = Color(0xFFF4FBFA) -val surfaceContainerLowestLight = Color(0xFFFFFFFF) -val surfaceContainerLowLight = Color(0xFFEFF5F4) -val surfaceContainerLight = Color(0xFFE9EFEF) -val surfaceContainerHighLight = Color(0xFFE3E9E9) -val surfaceContainerHighestLight = Color(0xFFDDE4E3) - -val primaryLightMediumContrast = Color(0xFF004B4D) -val onPrimaryLightMediumContrast = Color(0xFFFFFFFF) -val primaryContainerLightMediumContrast = Color(0xFF238183) -val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF) -val secondaryLightMediumContrast = Color(0xFF2E4748) -val onSecondaryLightMediumContrast = Color(0xFFFFFFFF) -val secondaryContainerLightMediumContrast = Color(0xFF5F797A) -val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF) -val tertiaryLightMediumContrast = Color(0xFF31445F) -val onTertiaryLightMediumContrast = Color(0xFFFFFFFF) -val tertiaryContainerLightMediumContrast = Color(0xFF627693) -val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF) -val errorLightMediumContrast = Color(0xFF8C0009) -val onErrorLightMediumContrast = Color(0xFFFFFFFF) -val errorContainerLightMediumContrast = Color(0xFFDA342E) -val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF) -val backgroundLightMediumContrast = Color(0xFFF4FBFA) -val onBackgroundLightMediumContrast = Color(0xFF161D1D) -val surfaceLightMediumContrast = Color(0xFFF4FBFA) -val onSurfaceLightMediumContrast = Color(0xFF161D1D) -val surfaceVariantLightMediumContrast = Color(0xFFDAE4E4) -val onSurfaceVariantLightMediumContrast = Color(0xFF3B4545) -val outlineLightMediumContrast = Color(0xFF576161) -val outlineVariantLightMediumContrast = Color(0xFF737D7D) -val scrimLightMediumContrast = Color(0xFF000000) -val inverseSurfaceLightMediumContrast = Color(0xFF2B3232) -val inverseOnSurfaceLightMediumContrast = Color(0xFFECF2F1) -val inversePrimaryLightMediumContrast = Color(0xFF80D4D6) -val surfaceDimLightMediumContrast = Color(0xFFD5DBDB) -val surfaceBrightLightMediumContrast = Color(0xFFF4FBFA) -val surfaceContainerLowestLightMediumContrast = Color(0xFFFFFFFF) -val surfaceContainerLowLightMediumContrast = Color(0xFFEFF5F4) -val surfaceContainerLightMediumContrast = Color(0xFFE9EFEF) -val surfaceContainerHighLightMediumContrast = Color(0xFFE3E9E9) -val surfaceContainerHighestLightMediumContrast = Color(0xFFDDE4E3) - -val primaryLightHighContrast = Color(0xFF002728) -val onPrimaryLightHighContrast = Color(0xFFFFFFFF) -val primaryContainerLightHighContrast = Color(0xFF004B4D) -val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF) -val secondaryLightHighContrast = Color(0xFF0C2627) -val onSecondaryLightHighContrast = Color(0xFFFFFFFF) -val secondaryContainerLightHighContrast = Color(0xFF2E4748) -val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF) -val tertiaryLightHighContrast = Color(0xFF0E233D) -val onTertiaryLightHighContrast = Color(0xFFFFFFFF) -val tertiaryContainerLightHighContrast = Color(0xFF31445F) -val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF) -val errorLightHighContrast = Color(0xFF4E0002) -val onErrorLightHighContrast = Color(0xFFFFFFFF) -val errorContainerLightHighContrast = Color(0xFF8C0009) -val onErrorContainerLightHighContrast = Color(0xFFFFFFFF) -val backgroundLightHighContrast = Color(0xFFF4FBFA) -val onBackgroundLightHighContrast = Color(0xFF161D1D) -val surfaceLightHighContrast = Color(0xFFF4FBFA) -val onSurfaceLightHighContrast = Color(0xFF000000) -val surfaceVariantLightHighContrast = Color(0xFFDAE4E4) -val onSurfaceVariantLightHighContrast = Color(0xFF1C2626) -val outlineLightHighContrast = Color(0xFF3B4545) -val outlineVariantLightHighContrast = Color(0xFF3B4545) -val scrimLightHighContrast = Color(0xFF000000) -val inverseSurfaceLightHighContrast = Color(0xFF2B3232) -val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF) -val inversePrimaryLightHighContrast = Color(0xFFA6FBFC) -val surfaceDimLightHighContrast = Color(0xFFD5DBDB) -val surfaceBrightLightHighContrast = Color(0xFFF4FBFA) -val surfaceContainerLowestLightHighContrast = Color(0xFFFFFFFF) -val surfaceContainerLowLightHighContrast = Color(0xFFEFF5F4) -val surfaceContainerLightHighContrast = Color(0xFFE9EFEF) -val surfaceContainerHighLightHighContrast = Color(0xFFE3E9E9) -val surfaceContainerHighestLightHighContrast = Color(0xFFDDE4E3) - -val primaryDark = Color(0xFF80D4D6) -val onPrimaryDark = Color(0xFF003738) -val primaryContainerDark = Color(0xFF004F51) -val onPrimaryContainerDark = Color(0xFF9CF1F3) -val secondaryDark = Color(0xFFB0CCCC) -val onSecondaryDark = Color(0xFF1B3435) -val secondaryContainerDark = Color(0xFF324B4C) -val onSecondaryContainerDark = Color(0xFFCCE8E8) -val tertiaryDark = Color(0xFFB4C8E9) -val onTertiaryDark = Color(0xFF1D314C) -val tertiaryContainerDark = Color(0xFF354863) -val onTertiaryContainerDark = Color(0xFFD4E3FF) -val errorDark = Color(0xFFFFB4AB) -val onErrorDark = Color(0xFF690005) -val errorContainerDark = Color(0xFF93000A) -val onErrorContainerDark = Color(0xFFFFDAD6) -val backgroundDark = Color(0xFF0E1415) -val onBackgroundDark = Color(0xFFDDE4E3) -val surfaceDark = Color(0xFF0E1415) -val onSurfaceDark = Color(0xFFDDE4E3) -val surfaceVariantDark = Color(0xFF3F4949) -val onSurfaceVariantDark = Color(0xFFBEC8C8) -val outlineDark = Color(0xFF899392) -val outlineVariantDark = Color(0xFF3F4949) -val scrimDark = Color(0xFF000000) -val inverseSurfaceDark = Color(0xFFDDE4E3) -val inverseOnSurfaceDark = Color(0xFF2B3232) -val inversePrimaryDark = Color(0xFF00696B) -val surfaceDimDark = Color(0xFF0E1415) -val surfaceBrightDark = Color(0xFF343A3A) -val surfaceContainerLowestDark = Color(0xFF090F0F) -val surfaceContainerLowDark = Color(0xFF161D1D) -val surfaceContainerDark = Color(0xFF1A2121) -val surfaceContainerHighDark = Color(0xFF252B2B) -val surfaceContainerHighestDark = Color(0xFF2F3636) - -val primaryDarkMediumContrast = Color(0xFF84D9DA) -val onPrimaryDarkMediumContrast = Color(0xFF001A1B) -val primaryContainerDarkMediumContrast = Color(0xFF479D9F) -val onPrimaryContainerDarkMediumContrast = Color(0xFF000000) -val secondaryDarkMediumContrast = Color(0xFFB5D0D0) -val onSecondaryDarkMediumContrast = Color(0xFF001A1B) -val secondaryContainerDarkMediumContrast = Color(0xFF7B9696) -val onSecondaryContainerDarkMediumContrast = Color(0xFF000000) -val tertiaryDarkMediumContrast = Color(0xFFB8CCED) -val onTertiaryDarkMediumContrast = Color(0xFF011730) -val tertiaryContainerDarkMediumContrast = Color(0xFF7E92B1) -val onTertiaryContainerDarkMediumContrast = Color(0xFF000000) -val errorDarkMediumContrast = Color(0xFFFFBAB1) -val onErrorDarkMediumContrast = Color(0xFF370001) -val errorContainerDarkMediumContrast = Color(0xFFFF5449) -val onErrorContainerDarkMediumContrast = Color(0xFF000000) -val backgroundDarkMediumContrast = Color(0xFF0E1415) -val onBackgroundDarkMediumContrast = Color(0xFFDDE4E3) -val surfaceDarkMediumContrast = Color(0xFF0E1415) -val onSurfaceDarkMediumContrast = Color(0xFFF6FCFB) -val surfaceVariantDarkMediumContrast = Color(0xFF3F4949) -val onSurfaceVariantDarkMediumContrast = Color(0xFFC2CDCC) -val outlineDarkMediumContrast = Color(0xFF9BA5A5) -val outlineVariantDarkMediumContrast = Color(0xFF7B8585) -val scrimDarkMediumContrast = Color(0xFF000000) -val inverseSurfaceDarkMediumContrast = Color(0xFFDDE4E3) -val inverseOnSurfaceDarkMediumContrast = Color(0xFF252B2B) -val inversePrimaryDarkMediumContrast = Color(0xFF005152) -val surfaceDimDarkMediumContrast = Color(0xFF0E1415) -val surfaceBrightDarkMediumContrast = Color(0xFF343A3A) -val surfaceContainerLowestDarkMediumContrast = Color(0xFF090F0F) -val surfaceContainerLowDarkMediumContrast = Color(0xFF161D1D) -val surfaceContainerDarkMediumContrast = Color(0xFF1A2121) -val surfaceContainerHighDarkMediumContrast = Color(0xFF252B2B) -val surfaceContainerHighestDarkMediumContrast = Color(0xFF2F3636) - -val primaryDarkHighContrast = Color(0xFFE9FFFF) -val onPrimaryDarkHighContrast = Color(0xFF000000) -val primaryContainerDarkHighContrast = Color(0xFF84D9DA) -val onPrimaryContainerDarkHighContrast = Color(0xFF000000) -val secondaryDarkHighContrast = Color(0xFFE9FFFF) -val onSecondaryDarkHighContrast = Color(0xFF000000) -val secondaryContainerDarkHighContrast = Color(0xFFB5D0D0) -val onSecondaryContainerDarkHighContrast = Color(0xFF000000) -val tertiaryDarkHighContrast = Color(0xFFFBFAFF) -val onTertiaryDarkHighContrast = Color(0xFF000000) -val tertiaryContainerDarkHighContrast = Color(0xFFB8CCED) -val onTertiaryContainerDarkHighContrast = Color(0xFF000000) -val errorDarkHighContrast = Color(0xFFFFF9F9) -val onErrorDarkHighContrast = Color(0xFF000000) -val errorContainerDarkHighContrast = Color(0xFFFFBAB1) -val onErrorContainerDarkHighContrast = Color(0xFF000000) -val backgroundDarkHighContrast = Color(0xFF0E1415) -val onBackgroundDarkHighContrast = Color(0xFFDDE4E3) -val surfaceDarkHighContrast = Color(0xFF0E1415) -val onSurfaceDarkHighContrast = Color(0xFFFFFFFF) -val surfaceVariantDarkHighContrast = Color(0xFF3F4949) -val onSurfaceVariantDarkHighContrast = Color(0xFFF3FDFC) -val outlineDarkHighContrast = Color(0xFFC2CDCC) -val outlineVariantDarkHighContrast = Color(0xFFC2CDCC) -val scrimDarkHighContrast = Color(0xFF000000) -val inverseSurfaceDarkHighContrast = Color(0xFFDDE4E3) -val inverseOnSurfaceDarkHighContrast = Color(0xFF000000) -val inversePrimaryDarkHighContrast = Color(0xFF003031) -val surfaceDimDarkHighContrast = Color(0xFF0E1415) -val surfaceBrightDarkHighContrast = Color(0xFF343A3A) -val surfaceContainerLowestDarkHighContrast = Color(0xFF090F0F) -val surfaceContainerLowDarkHighContrast = Color(0xFF161D1D) -val surfaceContainerDarkHighContrast = Color(0xFF1A2121) -val surfaceContainerHighDarkHighContrast = Color(0xFF252B2B) -val surfaceContainerHighestDarkHighContrast = Color(0xFF2F3636) - - - - - - - +val middleColor = Color(0x3200A3A6) +val topColor = Color(0xFF00A3A6) +val bottomColor =Color(0xFFF1F1F1) +val borderColor = Color(0xFF000000) +val tintColor = Color(0xFFFFFFFF) \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/theme/Shape.kt b/presenter/src/main/java/com/foke/together/presenter/theme/Shape.kt index 22aabf3..ec1d4f8 100644 --- a/presenter/src/main/java/com/foke/together/presenter/theme/Shape.kt +++ b/presenter/src/main/java/com/foke/together/presenter/theme/Shape.kt @@ -1,4 +1,11 @@ package com.foke.together.presenter.theme +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.unit.dp + // Defines commonly used or recycled values // e.g. https://github.com/android/compose-samples/blob/main/JetNews/app/src/main/java/com/example/jetnews/ui/theme/Shape.kt +val container = RoundedCornerShape(16.dp) +val surface = RoundedCornerShape(8.dp) +val icon = RoundedCornerShape(8.dp) +val button = RoundedCornerShape(8.dp) \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/theme/Size.kt b/presenter/src/main/java/com/foke/together/presenter/theme/Size.kt new file mode 100644 index 0000000..e4576d8 --- /dev/null +++ b/presenter/src/main/java/com/foke/together/presenter/theme/Size.kt @@ -0,0 +1,10 @@ +package com.foke.together.presenter.theme + +import androidx.compose.ui.unit.dp + +val portrait = AppSize( + layoutPadding = 16.dp, + buttonPadding = 8.dp, + icon = 36.dp, + button = 32.dp +) diff --git a/presenter/src/main/java/com/foke/together/presenter/theme/Theme.kt b/presenter/src/main/java/com/foke/together/presenter/theme/Theme.kt index b949f60..bf81362 100644 --- a/presenter/src/main/java/com/foke/together/presenter/theme/Theme.kt +++ b/presenter/src/main/java/com/foke/together/presenter/theme/Theme.kt @@ -1,255 +1,27 @@ package com.foke.together.presenter.theme +import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.unit.dp -val lightScheme = lightColorScheme( - primary = primaryLight, - onPrimary = onPrimaryLight, - primaryContainer = primaryContainerLight, - onPrimaryContainer = onPrimaryContainerLight, - secondary = secondaryLight, - onSecondary = onSecondaryLight, - secondaryContainer = secondaryContainerLight, - onSecondaryContainer = onSecondaryContainerLight, - tertiary = tertiaryLight, - onTertiary = onTertiaryLight, - tertiaryContainer = tertiaryContainerLight, - onTertiaryContainer = onTertiaryContainerLight, - error = errorLight, - onError = onErrorLight, - errorContainer = errorContainerLight, - onErrorContainer = onErrorContainerLight, - background = backgroundLight, - onBackground = onBackgroundLight, - surface = surfaceLight, - onSurface = onSurfaceLight, - surfaceVariant = surfaceVariantLight, - onSurfaceVariant = onSurfaceVariantLight, - outline = outlineLight, - outlineVariant = outlineVariantLight, - scrim = scrimLight, - inverseSurface = inverseSurfaceLight, - inverseOnSurface = inverseOnSurfaceLight, - inversePrimary = inversePrimaryLight, - surfaceDim = surfaceDimLight, - surfaceBright = surfaceBrightLight, - surfaceContainerLowest = surfaceContainerLowestLight, - surfaceContainerLow = surfaceContainerLowLight, - surfaceContainer = surfaceContainerLight, - surfaceContainerHigh = surfaceContainerHighLight, - surfaceContainerHighest = surfaceContainerHighestLight, -) - -val darkScheme = darkColorScheme( - primary = primaryDark, - onPrimary = onPrimaryDark, - primaryContainer = primaryContainerDark, - onPrimaryContainer = onPrimaryContainerDark, - secondary = secondaryDark, - onSecondary = onSecondaryDark, - secondaryContainer = secondaryContainerDark, - onSecondaryContainer = onSecondaryContainerDark, - tertiary = tertiaryDark, - onTertiary = onTertiaryDark, - tertiaryContainer = tertiaryContainerDark, - onTertiaryContainer = onTertiaryContainerDark, - error = errorDark, - onError = onErrorDark, - errorContainer = errorContainerDark, - onErrorContainer = onErrorContainerDark, - background = backgroundDark, - onBackground = onBackgroundDark, - surface = surfaceDark, - onSurface = onSurfaceDark, - surfaceVariant = surfaceVariantDark, - onSurfaceVariant = onSurfaceVariantDark, - outline = outlineDark, - outlineVariant = outlineVariantDark, - scrim = scrimDark, - inverseSurface = inverseSurfaceDark, - inverseOnSurface = inverseOnSurfaceDark, - inversePrimary = inversePrimaryDark, - surfaceDim = surfaceDimDark, - surfaceBright = surfaceBrightDark, - surfaceContainerLowest = surfaceContainerLowestDark, - surfaceContainerLow = surfaceContainerLowDark, - surfaceContainer = surfaceContainerDark, - surfaceContainerHigh = surfaceContainerHighDark, - surfaceContainerHighest = surfaceContainerHighestDark, -) - -val mediumContrastLightColorScheme = lightColorScheme( - primary = primaryLightMediumContrast, - onPrimary = onPrimaryLightMediumContrast, - primaryContainer = primaryContainerLightMediumContrast, - onPrimaryContainer = onPrimaryContainerLightMediumContrast, - secondary = secondaryLightMediumContrast, - onSecondary = onSecondaryLightMediumContrast, - secondaryContainer = secondaryContainerLightMediumContrast, - onSecondaryContainer = onSecondaryContainerLightMediumContrast, - tertiary = tertiaryLightMediumContrast, - onTertiary = onTertiaryLightMediumContrast, - tertiaryContainer = tertiaryContainerLightMediumContrast, - onTertiaryContainer = onTertiaryContainerLightMediumContrast, - error = errorLightMediumContrast, - onError = onErrorLightMediumContrast, - errorContainer = errorContainerLightMediumContrast, - onErrorContainer = onErrorContainerLightMediumContrast, - background = backgroundLightMediumContrast, - onBackground = onBackgroundLightMediumContrast, - surface = surfaceLightMediumContrast, - onSurface = onSurfaceLightMediumContrast, - surfaceVariant = surfaceVariantLightMediumContrast, - onSurfaceVariant = onSurfaceVariantLightMediumContrast, - outline = outlineLightMediumContrast, - outlineVariant = outlineVariantLightMediumContrast, - scrim = scrimLightMediumContrast, - inverseSurface = inverseSurfaceLightMediumContrast, - inverseOnSurface = inverseOnSurfaceLightMediumContrast, - inversePrimary = inversePrimaryLightMediumContrast, - surfaceDim = surfaceDimLightMediumContrast, - surfaceBright = surfaceBrightLightMediumContrast, - surfaceContainerLowest = surfaceContainerLowestLightMediumContrast, - surfaceContainerLow = surfaceContainerLowLightMediumContrast, - surfaceContainer = surfaceContainerLightMediumContrast, - surfaceContainerHigh = surfaceContainerHighLightMediumContrast, - surfaceContainerHighest = surfaceContainerHighestLightMediumContrast, -) - -val highContrastLightColorScheme = lightColorScheme( - primary = primaryLightHighContrast, - onPrimary = onPrimaryLightHighContrast, - primaryContainer = primaryContainerLightHighContrast, - onPrimaryContainer = onPrimaryContainerLightHighContrast, - secondary = secondaryLightHighContrast, - onSecondary = onSecondaryLightHighContrast, - secondaryContainer = secondaryContainerLightHighContrast, - onSecondaryContainer = onSecondaryContainerLightHighContrast, - tertiary = tertiaryLightHighContrast, - onTertiary = onTertiaryLightHighContrast, - tertiaryContainer = tertiaryContainerLightHighContrast, - onTertiaryContainer = onTertiaryContainerLightHighContrast, - error = errorLightHighContrast, - onError = onErrorLightHighContrast, - errorContainer = errorContainerLightHighContrast, - onErrorContainer = onErrorContainerLightHighContrast, - background = backgroundLightHighContrast, - onBackground = onBackgroundLightHighContrast, - surface = surfaceLightHighContrast, - onSurface = onSurfaceLightHighContrast, - surfaceVariant = surfaceVariantLightHighContrast, - onSurfaceVariant = onSurfaceVariantLightHighContrast, - outline = outlineLightHighContrast, - outlineVariant = outlineVariantLightHighContrast, - scrim = scrimLightHighContrast, - inverseSurface = inverseSurfaceLightHighContrast, - inverseOnSurface = inverseOnSurfaceLightHighContrast, - inversePrimary = inversePrimaryLightHighContrast, - surfaceDim = surfaceDimLightHighContrast, - surfaceBright = surfaceBrightLightHighContrast, - surfaceContainerLowest = surfaceContainerLowestLightHighContrast, - surfaceContainerLow = surfaceContainerLowLightHighContrast, - surfaceContainer = surfaceContainerLightHighContrast, - surfaceContainerHigh = surfaceContainerHighLightHighContrast, - surfaceContainerHighest = surfaceContainerHighestLightHighContrast, -) - -val mediumContrastDarkColorScheme = darkColorScheme( - primary = primaryDarkMediumContrast, - onPrimary = onPrimaryDarkMediumContrast, - primaryContainer = primaryContainerDarkMediumContrast, - onPrimaryContainer = onPrimaryContainerDarkMediumContrast, - secondary = secondaryDarkMediumContrast, - onSecondary = onSecondaryDarkMediumContrast, - secondaryContainer = secondaryContainerDarkMediumContrast, - onSecondaryContainer = onSecondaryContainerDarkMediumContrast, - tertiary = tertiaryDarkMediumContrast, - onTertiary = onTertiaryDarkMediumContrast, - tertiaryContainer = tertiaryContainerDarkMediumContrast, - onTertiaryContainer = onTertiaryContainerDarkMediumContrast, - error = errorDarkMediumContrast, - onError = onErrorDarkMediumContrast, - errorContainer = errorContainerDarkMediumContrast, - onErrorContainer = onErrorContainerDarkMediumContrast, - background = backgroundDarkMediumContrast, - onBackground = onBackgroundDarkMediumContrast, - surface = surfaceDarkMediumContrast, - onSurface = onSurfaceDarkMediumContrast, - surfaceVariant = surfaceVariantDarkMediumContrast, - onSurfaceVariant = onSurfaceVariantDarkMediumContrast, - outline = outlineDarkMediumContrast, - outlineVariant = outlineVariantDarkMediumContrast, - scrim = scrimDarkMediumContrast, - inverseSurface = inverseSurfaceDarkMediumContrast, - inverseOnSurface = inverseOnSurfaceDarkMediumContrast, - inversePrimary = inversePrimaryDarkMediumContrast, - surfaceDim = surfaceDimDarkMediumContrast, - surfaceBright = surfaceBrightDarkMediumContrast, - surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast, - surfaceContainerLow = surfaceContainerLowDarkMediumContrast, - surfaceContainer = surfaceContainerDarkMediumContrast, - surfaceContainerHigh = surfaceContainerHighDarkMediumContrast, - surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast, -) - -val highContrastDarkColorScheme = darkColorScheme( - primary = primaryDarkHighContrast, - onPrimary = onPrimaryDarkHighContrast, - primaryContainer = primaryContainerDarkHighContrast, - onPrimaryContainer = onPrimaryContainerDarkHighContrast, - secondary = secondaryDarkHighContrast, - onSecondary = onSecondaryDarkHighContrast, - secondaryContainer = secondaryContainerDarkHighContrast, - onSecondaryContainer = onSecondaryContainerDarkHighContrast, - tertiary = tertiaryDarkHighContrast, - onTertiary = onTertiaryDarkHighContrast, - tertiaryContainer = tertiaryContainerDarkHighContrast, - onTertiaryContainer = onTertiaryContainerDarkHighContrast, - error = errorDarkHighContrast, - onError = onErrorDarkHighContrast, - errorContainer = errorContainerDarkHighContrast, - onErrorContainer = onErrorContainerDarkHighContrast, - background = backgroundDarkHighContrast, - onBackground = onBackgroundDarkHighContrast, - surface = surfaceDarkHighContrast, - onSurface = onSurfaceDarkHighContrast, - surfaceVariant = surfaceVariantDarkHighContrast, - onSurfaceVariant = onSurfaceVariantDarkHighContrast, - outline = outlineDarkHighContrast, - outlineVariant = outlineVariantDarkHighContrast, - scrim = scrimDarkHighContrast, - inverseSurface = inverseSurfaceDarkHighContrast, - inverseOnSurface = inverseOnSurfaceDarkHighContrast, - inversePrimary = inversePrimaryDarkHighContrast, - surfaceDim = surfaceDimDarkHighContrast, - surfaceBright = surfaceBrightDarkHighContrast, - surfaceContainerLowest = surfaceContainerLowestDarkHighContrast, - surfaceContainerLow = surfaceContainerLowDarkHighContrast, - surfaceContainer = surfaceContainerDarkHighContrast, - surfaceContainerHigh = surfaceContainerHighDarkHighContrast, - surfaceContainerHighest = surfaceContainerHighestDarkHighContrast, -) -@Immutable -data class ColorFamily( - val color: Color, - val onColor: Color, - val colorContainer: Color, - val onColorContainer: Color +private val topColorScheme = AppColorScheme( + top = topColor, + bottom = bottomColor, + middle = middleColor, + border = borderColor, + tint = tintColor ) -val unspecified_scheme = ColorFamily( - Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified +private val bottomColorScheme = AppColorScheme( + top = bottomColor, + bottom = topColor, + middle = middleColor, + border = tintColor, + tint = borderColor ) - @Composable fun FourCutTogetherTheme( darkTheme: Boolean = isSystemInDarkTheme(), @@ -257,20 +29,41 @@ fun FourCutTogetherTheme( dynamicColor: Boolean = true, content: @Composable () -> Unit ) { - val colorScheme = when { - dynamicColor -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkTheme -> darkScheme - else -> lightScheme - } - - MaterialTheme( - colorScheme = colorScheme, - typography = AppTypography, - content = content - ) + val colorScheme = if(darkTheme) bottomColorScheme else topColorScheme + CompositionLocalProvider( + LocalAppColorScheme provides colorScheme, + LocalAppShape provides AppShape( + container = container, + surface = surface, + icon = icon, + button = button + ), + LocalAppTypography provides localTypography, + LocalAppSize provides AppSize( + layoutPadding = 24.dp, + buttonPadding = 12.dp, + icon = 36.dp, + button = 32.dp + ), + LocalIndication provides ripple(), + content = content, + ) } +object AppTheme { + val colorScheme: AppColorScheme + @Composable + get() = LocalAppColorScheme.current + + val typography : AppTypography + @Composable + get() = LocalAppTypography.current + + val shapes : AppShape + @Composable + get() = LocalAppShape.current + + val size : AppSize + @Composable + get() = LocalAppSize.current +} \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/theme/Type.kt b/presenter/src/main/java/com/foke/together/presenter/theme/Type.kt index 494422b..19d938c 100644 --- a/presenter/src/main/java/com/foke/together/presenter/theme/Type.kt +++ b/presenter/src/main/java/com/foke/together/presenter/theme/Type.kt @@ -1,5 +1,42 @@ package com.foke.together.presenter.theme -import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.googlefonts.Font +import androidx.compose.ui.text.googlefonts.GoogleFont +import androidx.compose.ui.unit.sp +import com.foke.together.presenter.R -val AppTypography = Typography() +val provider = GoogleFont.Provider( + providerAuthority = "com.google.android.gms.fonts", + providerPackage = "com.google.android.gms", + certificates = R.array.com_google_android_gms_fonts_certs +) +val interFont = FontFamily( + Font(googleFont = GoogleFont("inter"), fontProvider = provider) +) + +val localTypography = AppTypography( + head = TextStyle( + fontFamily = interFont, + fontWeight = FontWeight.SemiBold, + fontSize = 40.sp, + ), + + title = TextStyle( + fontFamily = interFont, + fontWeight = FontWeight.Medium, + fontSize = 24.sp, + ), + body = TextStyle( + fontFamily = interFont, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + ), + label = TextStyle( + fontFamily = interFont, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + ), +) \ No newline at end of file diff --git a/presenter/src/main/java/com/foke/together/presenter/viewmodel/InternelCameraViewModel.kt b/presenter/src/main/java/com/foke/together/presenter/viewmodel/InternelCameraViewModel.kt index 0c7a64c..be504f1 100644 --- a/presenter/src/main/java/com/foke/together/presenter/viewmodel/InternelCameraViewModel.kt +++ b/presenter/src/main/java/com/foke/together/presenter/viewmodel/InternelCameraViewModel.kt @@ -27,6 +27,7 @@ import com.foke.together.util.SoundUtil import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject @@ -46,7 +47,11 @@ class InternelCameraViewModel @Inject constructor( val progressState = MutableStateFlow(1f) private var captureTimer: CountDownTimer? = null private var mTimerState = false - private val cameraSelector = getInternalCameraLensFacingUseCase().stateIn( + private val cameraSelector = getInternalCameraLensFacingUseCase().map { cameraSelectorIdx -> + CameraSelector.Builder() + .requireLensFacing(cameraSelectorIdx) + .build() + }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(), initialValue = CameraSelector.DEFAULT_FRONT_CAMERA diff --git a/presenter/src/main/java/com/foke/together/presenter/viewmodel/SelectFrameViewModel.kt b/presenter/src/main/java/com/foke/together/presenter/viewmodel/SelectFrameViewModel.kt index a585926..78a86e1 100644 --- a/presenter/src/main/java/com/foke/together/presenter/viewmodel/SelectFrameViewModel.kt +++ b/presenter/src/main/java/com/foke/together/presenter/viewmodel/SelectFrameViewModel.kt @@ -3,9 +3,12 @@ package com.foke.together.presenter.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.foke.together.domain.interactor.entity.CutFrame +import com.foke.together.domain.interactor.entity.DefaultCutFrameSet import com.foke.together.domain.interactor.entity.Status import com.foke.together.domain.interactor.session.UpdateSessionStatusUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import javax.inject.Inject @@ -14,10 +17,20 @@ class SelectFrameViewModel @Inject constructor( private val updateSessionStatusUseCase: UpdateSessionStatusUseCase ): ViewModel() { + val cutFrames = MutableStateFlow(DefaultCutFrameSet.entries) + val isDateDisplay = MutableStateFlow(false) fun updateSessionStatus() { updateSessionStatusUseCase(Status.SELECT_FRAME) } + fun updateDateDisplay() = viewModelScope.launch{ + isDateDisplay.value = !isDateDisplay.value + cutFrames.value = cutFrames.value.map { + it.isDateString = isDateDisplay.value + it + } + } + fun setCutFrameType(cutFrame: CutFrame) = viewModelScope.launch { updateSessionStatusUseCase(cutFrame) } diff --git a/presenter/src/main/java/com/foke/together/presenter/viewmodel/SettingViewModel.kt b/presenter/src/main/java/com/foke/together/presenter/viewmodel/SettingViewModel.kt index d1d3b50..243ed98 100644 --- a/presenter/src/main/java/com/foke/together/presenter/viewmodel/SettingViewModel.kt +++ b/presenter/src/main/java/com/foke/together/presenter/viewmodel/SettingViewModel.kt @@ -1,70 +1,111 @@ package com.foke.together.presenter.viewmodel +import androidx.camera.core.CameraSelector import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.foke.together.domain.GetCaptureDurationUseCase +import com.foke.together.domain.SetCaptureDurationUseCase import com.foke.together.domain.interactor.GetCameraSourceTypeUseCase import com.foke.together.domain.interactor.GetExternalCameraIPUseCase +import com.foke.together.domain.interactor.GetInternalCameraLensFacingUseCase import com.foke.together.domain.interactor.SetCameraSourceTypeUseCase import com.foke.together.domain.interactor.SetExternalCameraIPUseCase +import com.foke.together.domain.interactor.SetInternalCameraLensFacingUseCase import com.foke.together.domain.interactor.entity.CameraSourceType import com.foke.together.domain.interactor.entity.ExternalCameraIP import com.foke.together.util.AppLog import com.foke.together.util.di.IODispatcher import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds +import kotlin.time.DurationUnit +import kotlin.time.toDuration @HiltViewModel class SettingViewModel @Inject constructor( @IODispatcher private val ioDispatcher: CoroutineDispatcher, - getCameraSourceTypeUseCase: GetCameraSourceTypeUseCase, + private val getCameraSourceTypeUseCase: GetCameraSourceTypeUseCase, private val setCameraSourceTypeUseCase: SetCameraSourceTypeUseCase, - getExternalCameraIPUseCase: GetExternalCameraIPUseCase, - private val setExternalCameraIPUseCase: SetExternalCameraIPUseCase + private val getExternalCameraIPUseCase: GetExternalCameraIPUseCase, + private val setExternalCameraIPUseCase: SetExternalCameraIPUseCase, + private val getInternalCameraLensFacingUseCase: GetInternalCameraLensFacingUseCase, + private val setInternalCameraLensFacingUseCase: SetInternalCameraLensFacingUseCase, + private val getCaptureDurationUseCase: GetCaptureDurationUseCase, + private val setCaptureDurationUseCase: SetCaptureDurationUseCase, ): ViewModel() { - val cameraSourceType = getCameraSourceTypeUseCase().shareIn( + val cameraSourceType = getCameraSourceTypeUseCase().stateIn( scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - replay = 1 + started = SharingStarted.Lazily, + initialValue = CameraSourceType.INTERNAL ) - // TODO: check to remove - val cameraIPAddress = getExternalCameraIPUseCase().shareIn( + val cameraTypeIndex = getCameraSourceTypeUseCase().map { sourceType -> + sourceType.ordinal + }.stateIn( scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - replay = 1 + started = SharingStarted.Lazily, + initialValue = CameraSourceType.INTERNAL.ordinal ) - var cameraIPAddressState by mutableStateOf("") - init { - viewModelScope.launch(ioDispatcher) { - cameraIPAddress.collectLatest { - cameraIPAddressState = it.address - } - } + val cameraSelector = getInternalCameraLensFacingUseCase().stateIn( + scope = viewModelScope, + started = SharingStarted.Lazily, + initialValue = CameraSelector.LENS_FACING_FRONT + ) + + val cameraIPAddress = getExternalCameraIPUseCase().map{ ipClass -> + ipClass.address + }.stateIn( + scope = viewModelScope, + started = SharingStarted.Lazily, + initialValue = "" + ) + + val email = MutableStateFlow("4cuts@foke.io") + val password = MutableStateFlow("password") + + val captureDuration = getCaptureDurationUseCase().map{ duration -> + duration.toLong(DurationUnit.MILLISECONDS) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.Lazily, + initialValue = 5.seconds.toLong(DurationUnit.MILLISECONDS) + ) + + fun setCameraSourceType(index: Int) = viewModelScope.launch { + AppLog.e(TAG, "setCameraSourceType", "type: $index") + setCameraSourceTypeUseCase(CameraSourceType.entries[index]) + } + + fun setCameraIPAddress(address: String) = viewModelScope.launch { + setExternalCameraIPUseCase(ExternalCameraIP(address)) + } + + fun setCameraSelector(index: Int) = viewModelScope.launch{ + setInternalCameraLensFacingUseCase(index) } - fun setCameraSourceType(index: Int){ - setCameraSourceType(CameraSourceType.entries[index]) + fun setEmail(address: String){ + email.value = address } - fun setCameraSourceType(type: CameraSourceType){ - viewModelScope.launch { - AppLog.e(TAG, "setCameraSourceType", "type: $type") - setCameraSourceTypeUseCase(type) - } + fun setPassword(address: String){ + password.value = address } - fun setCameraIPAddress(address: String){ - viewModelScope.launch { - setExternalCameraIPUseCase(ExternalCameraIP(address)) - } + fun setCaptureDuration(duration : Long) = viewModelScope.launch { + setCaptureDurationUseCase(duration.toDuration(DurationUnit.MILLISECONDS)) } companion object { diff --git a/presenter/src/main/java/com/foke/together/presenter/viewmodel/ShareViewModel.kt b/presenter/src/main/java/com/foke/together/presenter/viewmodel/ShareViewModel.kt index 657a6ee..5ca5dd2 100644 --- a/presenter/src/main/java/com/foke/together/presenter/viewmodel/ShareViewModel.kt +++ b/presenter/src/main/java/com/foke/together/presenter/viewmodel/ShareViewModel.kt @@ -23,6 +23,7 @@ import com.foke.together.util.AppPolicy import com.foke.together.util.ImageFileUtil import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -37,7 +38,7 @@ class ShareViewModel @Inject constructor( private val updateSessionStatusUseCase: UpdateSessionStatusUseCase, private val clearSessionUseCase: ClearSessionUseCase ): ViewModel() { - var qrCodeBitmap by mutableStateOf(null) + val qrCodeBitmap = MutableStateFlow(null) val singleImageUri: Uri = generatePhotoFrameUseCaseV1.getFinalSingleImageUri() val twoImageUri: Uri = generatePhotoFrameUseCaseV1.getFinalTwoImageUri() @@ -75,7 +76,7 @@ class ShareViewModel @Inject constructor( AppLog.e(TAG, "generateQRcode", "sessionKey: $sessionKey") AppLog.e(TAG, "generateQRcode", "downloadUrl: $downloadUrl") } - qrCodeBitmap = getQRCodeUseCase(sessionKey, downloadUrl).getOrNull() + qrCodeBitmap.value = getQRCodeUseCase(sessionKey, downloadUrl).getOrNull() } } diff --git a/presenter/src/main/res/values/font_certs.xml b/presenter/src/main/res/values/font_certs.xml new file mode 100644 index 0000000..5ece3f5 --- /dev/null +++ b/presenter/src/main/res/values/font_certs.xml @@ -0,0 +1,32 @@ + + + + + @array/com_google_android_gms_fonts_certs_dev + @array/com_google_android_gms_fonts_certs_prod + + + + MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= + + + + + MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK + + + \ No newline at end of file