From 2fa1cd6c0a187924a7cf653f3a4297033507a973 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Thu, 2 May 2024 21:21:32 +0900 Subject: [PATCH 01/25] #82 chore : add feature:setting --- feature/setting/build.gradle.kts | 10 +++++++++ .../setting/ExampleInstrumentedTest.kt | 22 +++++++++++++++++++ feature/setting/src/main/AndroidManifest.xml | 4 ++++ .../app/feature/setting/ExampleUnitTest.kt | 16 ++++++++++++++ settings.gradle.kts | 1 + 5 files changed, 53 insertions(+) create mode 100644 feature/setting/build.gradle.kts create mode 100644 feature/setting/src/androidTest/kotlin/com/titi/app/feature/setting/ExampleInstrumentedTest.kt create mode 100644 feature/setting/src/main/AndroidManifest.xml create mode 100644 feature/setting/src/test/kotlin/com/titi/app/feature/setting/ExampleUnitTest.kt diff --git a/feature/setting/build.gradle.kts b/feature/setting/build.gradle.kts new file mode 100644 index 00000000..63109244 --- /dev/null +++ b/feature/setting/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("titi.android.feature") +} + +android { + namespace = "com.titi.app.feature.setting" +} + +dependencies { +} diff --git a/feature/setting/src/androidTest/kotlin/com/titi/app/feature/setting/ExampleInstrumentedTest.kt b/feature/setting/src/androidTest/kotlin/com/titi/app/feature/setting/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..1f19630a --- /dev/null +++ b/feature/setting/src/androidTest/kotlin/com/titi/app/feature/setting/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package com.titi.app.feature.setting + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.titi.app.feature.setting.test", appContext.packageName) + } +} diff --git a/feature/setting/src/main/AndroidManifest.xml b/feature/setting/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/setting/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/setting/src/test/kotlin/com/titi/app/feature/setting/ExampleUnitTest.kt b/feature/setting/src/test/kotlin/com/titi/app/feature/setting/ExampleUnitTest.kt new file mode 100644 index 00000000..ef0f6baa --- /dev/null +++ b/feature/setting/src/test/kotlin/com/titi/app/feature/setting/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package com.titi.app.feature.setting + +import org.junit.Assert.assertEquals +import org.junit.Test + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 93be0a03..d196e10d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -46,3 +46,4 @@ include(":feature:popup") include(":feature:log") include(":data:graph:impl") include(":data:graph:api") +include(":feature:setting") From 4494e579eb91a472ca064a1b76d0498857abbe51 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Thu, 2 May 2024 22:35:44 +0900 Subject: [PATCH 02/25] #82 feat : add grouped color --- .../kotlin/com/titi/app/core/designsystem/theme/Color.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/designsystem/src/main/kotlin/com/titi/app/core/designsystem/theme/Color.kt b/core/designsystem/src/main/kotlin/com/titi/app/core/designsystem/theme/Color.kt index ae959416..233ccfa1 100644 --- a/core/designsystem/src/main/kotlin/com/titi/app/core/designsystem/theme/Color.kt +++ b/core/designsystem/src/main/kotlin/com/titi/app/core/designsystem/theme/Color.kt @@ -21,6 +21,7 @@ data class TdsColorsPalette( val textColor: Color = Color.Unspecified, val backgroundColor: Color = Color.Unspecified, val secondaryBackgroundColor: Color = Color.Unspecified, + val groupedBackgroundColor: Color = Color.Unspecified, val switchBackgroundColor: Color = Color.Unspecified, val alertBackgroundColor: Color = Color.Unspecified, val tertiaryBackgroundColor: Color = Color.Unspecified, @@ -52,7 +53,8 @@ val TdsLightColorsPalette = TdsColorsPalette( d12 = Color(0xFF928AEA), textColor = Color(0xFF000000), backgroundColor = Color(0xFFFFFFFF), - secondaryBackgroundColor = Color(0xFFF2F2F7), + secondaryBackgroundColor = Color(0xFFFFFFFF), + groupedBackgroundColor = Color(0xFFF2F2F7), switchBackgroundColor = Color(0xFFE9E9EB), alertBackgroundColor = Color(0xD1EEEEEE), tertiaryBackgroundColor = Color(0xFFFFFFFF), @@ -85,6 +87,7 @@ val TdsDarkColorsPalette = TdsColorsPalette( textColor = Color(0xFFFFFFFF), backgroundColor = Color(0xFF000000), secondaryBackgroundColor = Color(0xFF1C1C1E), + groupedBackgroundColor = Color(0xFF000000), switchBackgroundColor = Color(0xFF39393D), alertBackgroundColor = Color(0xD12B2B2B), tertiaryBackgroundColor = Color(0xFF2C2C2E), @@ -118,6 +121,7 @@ enum class TdsColor { TEXT, BACKGROUND, SECONDARY_BACKGROUND, + GROUPED_BACKGROUND, SWITCH_BACKGROUND, ALERT_BACKGROUND, TERTIARY_BACKGROUND, @@ -154,6 +158,7 @@ enum class TdsColor { TEXT -> TiTiTheme.colors.textColor BACKGROUND -> TiTiTheme.colors.backgroundColor SECONDARY_BACKGROUND -> TiTiTheme.colors.secondaryBackgroundColor + GROUPED_BACKGROUND -> TiTiTheme.colors.groupedBackgroundColor SWITCH_BACKGROUND -> TiTiTheme.colors.switchBackgroundColor ALERT_BACKGROUND -> TiTiTheme.colors.alertBackgroundColor TERTIARY_BACKGROUND -> TiTiTheme.colors.tertiaryBackgroundColor From bbc252ed9d4822a2ca5454efaeaf7f66accbe7a5 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Thu, 2 May 2024 22:37:22 +0900 Subject: [PATCH 03/25] #82 feat : SettingScreen --- .../titi/app/feature/setting/SettingScreen.kt | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/SettingScreen.kt diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/SettingScreen.kt new file mode 100644 index 00000000..779e7600 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/SettingScreen.kt @@ -0,0 +1,235 @@ +package com.titi.app.feature.setting + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +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.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Switch +import androidx.compose.runtime.Composable +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.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.titi.app.core.designsystem.R +import com.titi.app.core.designsystem.component.TdsText +import com.titi.app.core.designsystem.theme.TdsColor +import com.titi.app.core.designsystem.theme.TdsTextStyle +import com.titi.app.core.designsystem.theme.TiTiTheme + +@Composable +fun SettingScreen() { + val scrollState = rememberScrollState() + + Scaffold(containerColor = Color.Transparent) { + Column( + modifier = Modifier + .fillMaxSize() + .background(color = TdsColor.GROUPED_BACKGROUND.getColor()) + .padding(it) + .verticalScroll(scrollState), + ) { + Spacer(modifier = Modifier.height(64.dp)) + + TdsText( + modifier = Modifier.padding(start = 16.dp), + text = "Setting", + textStyle = TdsTextStyle.EXTRA_BOLD_TEXT_STYLE, + fontSize = 34.sp, + color = TdsColor.TEXT, + ) + + Spacer(modifier = Modifier.height(35.dp)) + + SettingServiceSection() + + Spacer(modifier = Modifier.height(35.dp)) + + SettingNotificationSection() + + Spacer(modifier = Modifier.height(35.dp)) + + SettingVersionSection() + + Spacer(modifier = Modifier.height(35.dp)) + } + } +} + +@Composable +private fun SettingServiceSection() { + TdsText( + modifier = Modifier.padding(start = 16.dp), + text = "서비스", + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + fontSize = 14.sp, + color = TdsColor.TEXT, + ) + + Spacer(modifier = Modifier.height(4.dp)) + + SettingRowContent( + title = "TiTi 기능들", + rightAreaContent = { + Icon( + painter = painterResource(id = R.drawable.arrow_right_icon), + contentDescription = "", + tint = TdsColor.LIGHT_GRAY.getColor(), + ) + }, + onClick = {}, + ) +} + +@Composable +private fun SettingNotificationSection() { + TdsText( + modifier = Modifier.padding(start = 16.dp), + text = "알림", + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + fontSize = 14.sp, + color = TdsColor.TEXT, + ) + + Spacer(modifier = Modifier.height(4.dp)) + + SettingRowContent( + title = "타이머", + description = "종료 5분전 알림", + rightAreaContent = { + Switch( + checked = true, + onCheckedChange = {}, + ) + }, + onClick = {}, + ) + + Spacer(modifier = Modifier.height(1.dp)) + + SettingRowContent( + title = "타이머", + description = "종료 알림", + rightAreaContent = { + Switch( + checked = true, + onCheckedChange = {}, + ) + }, + onClick = {}, + ) + + Spacer(modifier = Modifier.height(1.dp)) + + SettingRowContent( + title = "스톱워치", + description = "1시간단위 경과시 알림", + rightAreaContent = { + Switch( + checked = true, + onCheckedChange = {}, + ) + }, + onClick = {}, + ) +} + +@Composable +private fun SettingVersionSection() { + TdsText( + modifier = Modifier.padding(start = 16.dp), + text = "버전 및 업데이트 내역", + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + fontSize = 14.sp, + color = TdsColor.TEXT, + ) + + Spacer(modifier = Modifier.height(4.dp)) + + SettingRowContent( + title = "버전 정보", + description = "최신버전: 7.17.1", + rightAreaContent = { + Icon( + painter = painterResource(id = R.drawable.arrow_right_icon), + contentDescription = "", + tint = TdsColor.LIGHT_GRAY.getColor(), + ) + }, + onClick = {}, + ) + + Spacer(modifier = Modifier.height(1.dp)) + + SettingRowContent( + title = "업데이트 내역", + rightAreaContent = { + Icon( + painter = painterResource(id = R.drawable.arrow_right_icon), + contentDescription = "", + tint = TdsColor.LIGHT_GRAY.getColor(), + ) + }, + onClick = {}, + ) +} + +@Composable +private fun SettingRowContent( + title: String, + description: String? = null, + rightAreaContent: @Composable () -> Unit, + onClick: () -> Unit, +) { + Row( + modifier = Modifier + .fillMaxSize() + .background(TdsColor.SECONDARY_BACKGROUND.getColor()) + .clickable { onClick() } + .padding( + horizontal = 16.dp, + vertical = 14.dp, + ), + verticalAlignment = Alignment.CenterVertically, + ) { + Column(modifier = Modifier.weight(1f)) { + TdsText( + text = title, + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + fontSize = 17.sp, + color = TdsColor.TEXT, + ) + + Spacer(modifier = Modifier.height(4.dp)) + + description?.let { + TdsText( + text = it, + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + fontSize = 11.sp, + color = TdsColor.LIGHT_GRAY, + ) + } + } + + rightAreaContent() + } +} + +@PreviewLightDark +@Composable +private fun SettingScreenPreview() { + TiTiTheme { + SettingScreen() + } +} From adf833b8faf32f5f17a9bb5f2b722b6e675bc7aa Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Thu, 2 May 2024 22:57:03 +0900 Subject: [PATCH 04/25] #82 feat : navigate setting --- .../src/main/res/values/strings.xml | 1 + feature/main/build.gradle.kts | 1 + .../feature/main/navigation/TiTiNavHost.kt | 3 +++ .../main/navigation/TopLevelDestination.kt | 4 ++++ .../titi/app/feature/main/ui/TiTiAppState.kt | 5 +++++ .../setting/navigation/SettingNavigation.kt | 20 +++++++++++++++++++ .../feature/setting/{ => ui}/SettingScreen.kt | 4 +--- 7 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt rename feature/setting/src/main/kotlin/com/titi/app/feature/setting/{ => ui}/SettingScreen.kt (98%) diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index c62fe299..4293cc48 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -3,6 +3,7 @@ Timer Stopwatch Log + Setting 누적 시간 타이머 diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts index d6b9fd69..de53ea5a 100644 --- a/feature/main/build.gradle.kts +++ b/feature/main/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation(project(":feature:time")) implementation(project(":feature:log")) implementation(project(":feature:popup")) + implementation(project(":feature:setting")) implementation(libs.androidx.splashscreen) implementation(libs.androidx.material3.window.size) diff --git a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt index cda2d56f..5a5c0d49 100644 --- a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt +++ b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt @@ -14,6 +14,7 @@ import com.titi.app.feature.main.ui.TiTiAppState import com.titi.app.feature.popup.PopUpActivity import com.titi.app.feature.popup.PopUpActivity.Companion.COLOR_RECORDING_MODE_KEY import com.titi.app.feature.popup.PopUpActivity.Companion.MEASURE_SPLASH_RESULT_KEY +import com.titi.app.feature.setting.navigation.settingGraph import com.titi.app.feature.time.navigation.STOPWATCH_SCREEN import com.titi.app.feature.time.navigation.TIMER_SCREEN import com.titi.app.feature.time.navigation.timeGraph @@ -61,5 +62,7 @@ fun TiTiNavHost( ) logGraph() + + settingGraph() } } diff --git a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TopLevelDestination.kt b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TopLevelDestination.kt index 9ebcf6fe..436574a3 100644 --- a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TopLevelDestination.kt +++ b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TopLevelDestination.kt @@ -20,4 +20,8 @@ enum class TopLevelDestination( titleTextId = R.string.bottom_log_text, iconResourceId = R.drawable.log_icon, ), + SETTING( + titleTextId = R.string.bottom_setting_text, + iconResourceId = R.drawable.setting_icon, + ), } diff --git a/feature/main/src/main/kotlin/com/titi/app/feature/main/ui/TiTiAppState.kt b/feature/main/src/main/kotlin/com/titi/app/feature/main/ui/TiTiAppState.kt index bceb18f0..5ab4e70f 100644 --- a/feature/main/src/main/kotlin/com/titi/app/feature/main/ui/TiTiAppState.kt +++ b/feature/main/src/main/kotlin/com/titi/app/feature/main/ui/TiTiAppState.kt @@ -15,6 +15,8 @@ import com.titi.app.domain.color.usecase.GetTimeColorFlowUseCase import com.titi.app.feature.log.navigation.LOG_ROUTE import com.titi.app.feature.log.navigation.navigateToLog import com.titi.app.feature.main.navigation.TopLevelDestination +import com.titi.app.feature.setting.navigation.SETTING_ROUTE +import com.titi.app.feature.setting.navigation.navigateToSetting import com.titi.app.feature.time.navigation.STOPWATCH_ROUTE import com.titi.app.feature.time.navigation.TIMER_ROUTE import com.titi.app.feature.time.navigation.navigateToStopWatch @@ -64,6 +66,7 @@ class TiTiAppState( TIMER_ROUTE -> TopLevelDestination.TIMER STOPWATCH_ROUTE -> TopLevelDestination.STOPWATCH LOG_ROUTE -> TopLevelDestination.LOG + SETTING_ROUTE -> TopLevelDestination.SETTING else -> null } @@ -82,6 +85,7 @@ class TiTiAppState( TIMER_ROUTE -> timeColor.timerBackgroundColor STOPWATCH_ROUTE -> timeColor.stopwatchBackgroundColor LOG_ROUTE -> if (isSystemDarkTheme) 0xFF000000 else 0xFFFFFFFF + SETTING_ROUTE -> if (isSystemDarkTheme) 0xFF000000 else 0xFFFFFFFF else -> 0xFF000000 } } @@ -106,6 +110,7 @@ class TiTiAppState( TopLevelDestination.TIMER -> navController.navigateToTimer(topLevelNavOptions) TopLevelDestination.STOPWATCH -> navController.navigateToStopWatch(topLevelNavOptions) TopLevelDestination.LOG -> navController.navigateToLog(topLevelNavOptions) + TopLevelDestination.SETTING -> navController.navigateToSetting(topLevelNavOptions) } } } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt new file mode 100644 index 00000000..ecdf66dc --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt @@ -0,0 +1,20 @@ +package com.titi.app.feature.setting.navigation + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.titi.app.feature.setting.ui.SettingScreen + +private const val SETTING_SCREEN = "setting" +const val SETTING_ROUTE = SETTING_SCREEN + +fun NavController.navigateToSetting(navOptions: NavOptions) { + navigate(SETTING_ROUTE, navOptions) +} + +fun NavGraphBuilder.settingGraph() { + composable(route = SETTING_ROUTE) { + SettingScreen() + } +} diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt similarity index 98% rename from feature/setting/src/main/kotlin/com/titi/app/feature/setting/SettingScreen.kt rename to feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index 779e7600..509d37e5 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -1,4 +1,4 @@ -package com.titi.app.feature.setting +package com.titi.app.feature.setting.ui import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -39,8 +39,6 @@ fun SettingScreen() { .padding(it) .verticalScroll(scrollState), ) { - Spacer(modifier = Modifier.height(64.dp)) - TdsText( modifier = Modifier.padding(start = 16.dp), text = "Setting", From b4c417e174bca51d13a641385e6406899e4bae8a Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Fri, 3 May 2024 15:40:34 +0900 Subject: [PATCH 05/25] #82 feat : SettingUiState --- .../app/feature/setting/di/ViewModelModule.kt | 19 +++ .../feature/setting/model/SettingUiState.kt | 19 +++ .../app/feature/setting/ui/SettingScreen.kt | 112 +++++++++++------- .../feature/setting/ui/SettingViewModel.kt | 24 ++++ 4 files changed, 133 insertions(+), 41 deletions(-) create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/di/ViewModelModule.kt create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/SettingUiState.kt create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/di/ViewModelModule.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/di/ViewModelModule.kt new file mode 100644 index 00000000..df833131 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/di/ViewModelModule.kt @@ -0,0 +1,19 @@ +package com.titi.app.feature.setting.di + +import com.airbnb.mvrx.hilt.AssistedViewModelFactory +import com.airbnb.mvrx.hilt.MavericksViewModelComponent +import com.airbnb.mvrx.hilt.ViewModelKey +import com.titi.app.feature.setting.ui.SettingViewModel +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.multibindings.IntoMap + +@Module +@InstallIn(MavericksViewModelComponent::class) +internal interface ViewModelModule { + @Binds + @IntoMap + @ViewModelKey(SettingViewModel::class) + fun settingViewModelFactory(factory: SettingViewModel.Factory): AssistedViewModelFactory<*, *> +} diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/SettingUiState.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/SettingUiState.kt new file mode 100644 index 00000000..2885ca44 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/SettingUiState.kt @@ -0,0 +1,19 @@ +package com.titi.app.feature.setting.model + +import com.airbnb.mvrx.MavericksState + +data class SettingUiState( + val switchState: SwitchState = SwitchState(), + val versionState: VersionState = VersionState(), +) : MavericksState { + data class SwitchState( + val timerFiveMinutesBeforeTheEnd: Boolean = true, + val timerBeforeTheEnd: Boolean = true, + val stopwatch: Boolean = true, + ) + + data class VersionState( + val currentVersion: String = "", + val newVersion: String = "", + ) +} diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index 509d37e5..fd665e67 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -8,12 +8,14 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -21,46 +23,63 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.airbnb.mvrx.compose.collectAsState +import com.airbnb.mvrx.compose.mavericksViewModel import com.titi.app.core.designsystem.R import com.titi.app.core.designsystem.component.TdsText import com.titi.app.core.designsystem.theme.TdsColor import com.titi.app.core.designsystem.theme.TdsTextStyle import com.titi.app.core.designsystem.theme.TiTiTheme +import com.titi.app.feature.setting.model.SettingUiState @Composable -fun SettingScreen() { - val scrollState = rememberScrollState() +fun SettingScreen(viewModel: SettingViewModel = mavericksViewModel()) { + val uiState by viewModel.collectAsState() Scaffold(containerColor = Color.Transparent) { - Column( - modifier = Modifier - .fillMaxSize() - .background(color = TdsColor.GROUPED_BACKGROUND.getColor()) - .padding(it) - .verticalScroll(scrollState), - ) { - TdsText( - modifier = Modifier.padding(start = 16.dp), - text = "Setting", - textStyle = TdsTextStyle.EXTRA_BOLD_TEXT_STYLE, - fontSize = 34.sp, - color = TdsColor.TEXT, - ) + SettingScreen( + modifier = Modifier.padding(it), + uiState = uiState, + ) + } +} - Spacer(modifier = Modifier.height(35.dp)) +@Composable +private fun SettingScreen(modifier: Modifier, uiState: SettingUiState) { + val scrollState = rememberScrollState() - SettingServiceSection() + Column( + modifier = Modifier + .fillMaxSize() + .background(color = TdsColor.GROUPED_BACKGROUND.getColor()) + .then(modifier) + .verticalScroll(scrollState), + ) { + TdsText( + modifier = Modifier.padding(start = 16.dp), + text = "Setting", + textStyle = TdsTextStyle.EXTRA_BOLD_TEXT_STYLE, + fontSize = 34.sp, + color = TdsColor.TEXT, + ) - Spacer(modifier = Modifier.height(35.dp)) + Spacer(modifier = Modifier.height(35.dp)) - SettingNotificationSection() + SettingServiceSection() - Spacer(modifier = Modifier.height(35.dp)) + Spacer(modifier = Modifier.height(35.dp)) - SettingVersionSection() + SettingNotificationSection( + switchState = uiState.switchState, + ) - Spacer(modifier = Modifier.height(35.dp)) - } + Spacer(modifier = Modifier.height(35.dp)) + + SettingVersionSection( + versionState = uiState.versionState, + ) + + Spacer(modifier = Modifier.height(35.dp)) } } @@ -90,7 +109,7 @@ private fun SettingServiceSection() { } @Composable -private fun SettingNotificationSection() { +private fun SettingNotificationSection(switchState: SettingUiState.SwitchState) { TdsText( modifier = Modifier.padding(start = 16.dp), text = "알림", @@ -106,11 +125,10 @@ private fun SettingNotificationSection() { description = "종료 5분전 알림", rightAreaContent = { Switch( - checked = true, + checked = switchState.timerFiveMinutesBeforeTheEnd, onCheckedChange = {}, ) }, - onClick = {}, ) Spacer(modifier = Modifier.height(1.dp)) @@ -120,11 +138,10 @@ private fun SettingNotificationSection() { description = "종료 알림", rightAreaContent = { Switch( - checked = true, + checked = switchState.timerBeforeTheEnd, onCheckedChange = {}, ) }, - onClick = {}, ) Spacer(modifier = Modifier.height(1.dp)) @@ -134,16 +151,15 @@ private fun SettingNotificationSection() { description = "1시간단위 경과시 알림", rightAreaContent = { Switch( - checked = true, + checked = switchState.stopwatch, onCheckedChange = {}, ) }, - onClick = {}, ) } @Composable -private fun SettingVersionSection() { +private fun SettingVersionSection(versionState: SettingUiState.VersionState) { TdsText( modifier = Modifier.padding(start = 16.dp), text = "버전 및 업데이트 내역", @@ -156,7 +172,7 @@ private fun SettingVersionSection() { SettingRowContent( title = "버전 정보", - description = "최신버전: 7.17.1", + description = "최신버전: ${versionState.newVersion}", rightAreaContent = { Icon( painter = painterResource(id = R.drawable.arrow_right_icon), @@ -172,11 +188,22 @@ private fun SettingVersionSection() { SettingRowContent( title = "업데이트 내역", rightAreaContent = { - Icon( - painter = painterResource(id = R.drawable.arrow_right_icon), - contentDescription = "", - tint = TdsColor.LIGHT_GRAY.getColor(), - ) + Row(verticalAlignment = Alignment.CenterVertically) { + TdsText( + text = versionState.currentVersion, + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + color = TdsColor.LIGHT_GRAY, + fontSize = 14.sp, + ) + + Spacer(modifier = Modifier.width(4.dp)) + + Icon( + painter = painterResource(id = R.drawable.arrow_right_icon), + contentDescription = "", + tint = TdsColor.LIGHT_GRAY.getColor(), + ) + } }, onClick = {}, ) @@ -187,13 +214,13 @@ private fun SettingRowContent( title: String, description: String? = null, rightAreaContent: @Composable () -> Unit, - onClick: () -> Unit, + onClick: (() -> Unit)? = null, ) { Row( modifier = Modifier .fillMaxSize() .background(TdsColor.SECONDARY_BACKGROUND.getColor()) - .clickable { onClick() } + .clickable { onClick?.invoke() } .padding( horizontal = 16.dp, vertical = 14.dp, @@ -228,6 +255,9 @@ private fun SettingRowContent( @Composable private fun SettingScreenPreview() { TiTiTheme { - SettingScreen() + SettingScreen( + modifier = Modifier, + uiState = SettingUiState(), + ) } } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt new file mode 100644 index 00000000..4e069f81 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt @@ -0,0 +1,24 @@ +package com.titi.app.feature.setting.ui + +import com.airbnb.mvrx.MavericksViewModel +import com.airbnb.mvrx.MavericksViewModelFactory +import com.airbnb.mvrx.hilt.AssistedViewModelFactory +import com.airbnb.mvrx.hilt.hiltMavericksViewModelFactory +import com.titi.app.feature.setting.model.SettingUiState +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class SettingViewModel @AssistedInject constructor( + @Assisted initialState: SettingUiState, +) : MavericksViewModel(initialState) { + + @AssistedFactory + interface Factory : AssistedViewModelFactory { + override fun create(state: SettingUiState): SettingViewModel + } + + companion object : + MavericksViewModelFactory + by hiltMavericksViewModelFactory() +} From ae297619e02e2b396b58ac6ef49376f9e2b617c6 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Fri, 3 May 2024 15:57:22 +0900 Subject: [PATCH 06/25] #82 feat : SettingActions --- .../feature/setting/model/SettingActions.kt | 18 ++++ .../app/feature/setting/ui/SettingScreen.kt | 90 ++++++++++++++++--- 2 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/SettingActions.kt diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/SettingActions.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/SettingActions.kt new file mode 100644 index 00000000..9e4a8890 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/SettingActions.kt @@ -0,0 +1,18 @@ +package com.titi.app.feature.setting.model + +sealed interface SettingActions { + + sealed interface Navigates : SettingActions { + data object FeaturesList : Navigates + data object PlayStore : Navigates + data object UpdatesList : Navigates + } + + sealed interface Updates : SettingActions { + @JvmInline + value class Switch(val switchState: SettingUiState.SwitchState) : Updates + + @JvmInline + value class Version(val versionState: SettingUiState.VersionState) : Updates + } +} diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index fd665e67..c7f96f90 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -1,5 +1,6 @@ package com.titi.app.feature.setting.ui +import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -30,6 +31,7 @@ import com.titi.app.core.designsystem.component.TdsText import com.titi.app.core.designsystem.theme.TdsColor import com.titi.app.core.designsystem.theme.TdsTextStyle import com.titi.app.core.designsystem.theme.TiTiTheme +import com.titi.app.feature.setting.model.SettingActions import com.titi.app.feature.setting.model.SettingUiState @Composable @@ -40,12 +42,38 @@ fun SettingScreen(viewModel: SettingViewModel = mavericksViewModel()) { SettingScreen( modifier = Modifier.padding(it), uiState = uiState, + onSettingActions = { settingActions -> + when (settingActions) { + is SettingActions.Navigates -> { + when (settingActions) { + is SettingActions.Navigates.FeaturesList -> { + Log.e("ABC", settingActions.toString()) + } + + is SettingActions.Navigates.UpdatesList -> { + Log.e("ABC", settingActions.toString()) + } + + is SettingActions.Navigates.PlayStore -> { + Log.e("ABC", settingActions.toString()) + } + } + } + + is SettingActions.Updates -> { + } + } + }, ) } } @Composable -private fun SettingScreen(modifier: Modifier, uiState: SettingUiState) { +private fun SettingScreen( + modifier: Modifier, + uiState: SettingUiState, + onSettingActions: (SettingActions) -> Unit, +) { val scrollState = rememberScrollState() Column( @@ -65,18 +93,20 @@ private fun SettingScreen(modifier: Modifier, uiState: SettingUiState) { Spacer(modifier = Modifier.height(35.dp)) - SettingServiceSection() + SettingServiceSection(onSettingActions = onSettingActions) Spacer(modifier = Modifier.height(35.dp)) SettingNotificationSection( switchState = uiState.switchState, + onSettingActions = onSettingActions, ) Spacer(modifier = Modifier.height(35.dp)) SettingVersionSection( versionState = uiState.versionState, + onSettingActions = onSettingActions, ) Spacer(modifier = Modifier.height(35.dp)) @@ -84,7 +114,7 @@ private fun SettingScreen(modifier: Modifier, uiState: SettingUiState) { } @Composable -private fun SettingServiceSection() { +private fun SettingServiceSection(onSettingActions: (SettingActions) -> Unit) { TdsText( modifier = Modifier.padding(start = 16.dp), text = "서비스", @@ -104,12 +134,17 @@ private fun SettingServiceSection() { tint = TdsColor.LIGHT_GRAY.getColor(), ) }, - onClick = {}, + onClick = { + onSettingActions(SettingActions.Navigates.FeaturesList) + }, ) } @Composable -private fun SettingNotificationSection(switchState: SettingUiState.SwitchState) { +private fun SettingNotificationSection( + switchState: SettingUiState.SwitchState, + onSettingActions: (SettingActions) -> Unit, +) { TdsText( modifier = Modifier.padding(start = 16.dp), text = "알림", @@ -126,7 +161,16 @@ private fun SettingNotificationSection(switchState: SettingUiState.SwitchState) rightAreaContent = { Switch( checked = switchState.timerFiveMinutesBeforeTheEnd, - onCheckedChange = {}, + onCheckedChange = { + onSettingActions( + SettingActions.Updates.Switch( + switchState = switchState.copy( + timerFiveMinutesBeforeTheEnd = + !switchState.timerFiveMinutesBeforeTheEnd, + ), + ), + ) + }, ) }, ) @@ -139,7 +183,15 @@ private fun SettingNotificationSection(switchState: SettingUiState.SwitchState) rightAreaContent = { Switch( checked = switchState.timerBeforeTheEnd, - onCheckedChange = {}, + onCheckedChange = { + onSettingActions( + SettingActions.Updates.Switch( + switchState = switchState.copy( + timerBeforeTheEnd = !switchState.timerBeforeTheEnd, + ), + ), + ) + }, ) }, ) @@ -152,14 +204,25 @@ private fun SettingNotificationSection(switchState: SettingUiState.SwitchState) rightAreaContent = { Switch( checked = switchState.stopwatch, - onCheckedChange = {}, + onCheckedChange = { + onSettingActions( + SettingActions.Updates.Switch( + switchState = switchState.copy( + stopwatch = !switchState.stopwatch, + ), + ), + ) + }, ) }, ) } @Composable -private fun SettingVersionSection(versionState: SettingUiState.VersionState) { +private fun SettingVersionSection( + versionState: SettingUiState.VersionState, + onSettingActions: (SettingActions) -> Unit, +) { TdsText( modifier = Modifier.padding(start = 16.dp), text = "버전 및 업데이트 내역", @@ -180,7 +243,9 @@ private fun SettingVersionSection(versionState: SettingUiState.VersionState) { tint = TdsColor.LIGHT_GRAY.getColor(), ) }, - onClick = {}, + onClick = { + onSettingActions(SettingActions.Navigates.PlayStore) + }, ) Spacer(modifier = Modifier.height(1.dp)) @@ -205,7 +270,9 @@ private fun SettingVersionSection(versionState: SettingUiState.VersionState) { ) } }, - onClick = {}, + onClick = { + onSettingActions(SettingActions.Navigates.UpdatesList) + }, ) } @@ -258,6 +325,7 @@ private fun SettingScreenPreview() { SettingScreen( modifier = Modifier, uiState = SettingUiState(), + onSettingActions = {}, ) } } From a74fc43c3928bb47e3bb334a25ee550ad44d7689 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Fri, 3 May 2024 16:01:10 +0900 Subject: [PATCH 07/25] #82 feat : handleUpdateActions --- .../app/feature/setting/ui/SettingScreen.kt | 3 +-- .../feature/setting/ui/SettingViewModel.kt | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index c7f96f90..a887a23c 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -60,8 +60,7 @@ fun SettingScreen(viewModel: SettingViewModel = mavericksViewModel()) { } } - is SettingActions.Updates -> { - } + is SettingActions.Updates -> viewModel.handleUpdateActions(settingActions) } }, ) diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt index 4e069f81..f83136e2 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt @@ -4,6 +4,7 @@ import com.airbnb.mvrx.MavericksViewModel import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.hilt.AssistedViewModelFactory import com.airbnb.mvrx.hilt.hiltMavericksViewModelFactory +import com.titi.app.feature.setting.model.SettingActions import com.titi.app.feature.setting.model.SettingUiState import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -13,6 +14,25 @@ class SettingViewModel @AssistedInject constructor( @Assisted initialState: SettingUiState, ) : MavericksViewModel(initialState) { + fun handleUpdateActions(updateActions: SettingActions.Updates) { + when (updateActions) { + is SettingActions.Updates.Switch -> updateSwitch(updateActions.switchState) + is SettingActions.Updates.Version -> updateVersion(updateActions.versionState) + } + } + + private fun updateSwitch(switchState: SettingUiState.SwitchState) { + setState { + copy(switchState = switchState) + } + } + + private fun updateVersion(versionState: SettingUiState.VersionState) { + setState { + copy(versionState = versionState) + } + } + @AssistedFactory interface Factory : AssistedViewModelFactory { override fun create(state: SettingUiState): SettingViewModel From 2e5f8f0e2cda45e7e83b65c72a4739f3a50bc324 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Fri, 3 May 2024 16:36:55 +0900 Subject: [PATCH 08/25] =?UTF-8?q?#82=20feat=20:=20handleUpdateActions=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/main/navigation/TiTiNavHost.kt | 16 +++++++- .../setting/navigation/SettingNavigation.kt | 41 ++++++++++++++++++- .../feature/setting/ui/FeaturesListScreen.kt | 22 ++++++++++ .../app/feature/setting/ui/SettingScreen.kt | 22 +++------- .../feature/setting/ui/UpdatesListScreen.kt | 22 ++++++++++ 5 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt diff --git a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt index 5a5c0d49..00a3c6c3 100644 --- a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt +++ b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt @@ -6,6 +6,7 @@ import androidx.activity.result.ActivityResult import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.core.net.toUri import androidx.navigation.compose.NavHost import com.titi.app.feature.log.navigation.logGraph import com.titi.app.feature.main.model.SplashResultState @@ -14,6 +15,8 @@ import com.titi.app.feature.main.ui.TiTiAppState import com.titi.app.feature.popup.PopUpActivity import com.titi.app.feature.popup.PopUpActivity.Companion.COLOR_RECORDING_MODE_KEY import com.titi.app.feature.popup.PopUpActivity.Companion.MEASURE_SPLASH_RESULT_KEY +import com.titi.app.feature.setting.navigation.navigateToFeatures +import com.titi.app.feature.setting.navigation.navigateToUpdates import com.titi.app.feature.setting.navigation.settingGraph import com.titi.app.feature.time.navigation.STOPWATCH_SCREEN import com.titi.app.feature.time.navigation.TIMER_SCREEN @@ -63,6 +66,17 @@ fun TiTiNavHost( logGraph() - settingGraph() + settingGraph( + onNavigateToFeatures = { navController.navigateToFeatures() }, + onNavigateToUpdates = { navController.navigateToUpdates() }, + onNavigateToPlayStore = { + val intent = Intent( + Intent.ACTION_VIEW, + "https://play.google.com/store/apps/details?id=com.titi.app".toUri(), + ) + + context.startActivity(intent) + }, + ) } } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt index ecdf66dc..f56a4264 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt @@ -4,17 +4,54 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable +import com.titi.app.feature.setting.model.SettingActions +import com.titi.app.feature.setting.ui.FeaturesListScreen import com.titi.app.feature.setting.ui.SettingScreen +import com.titi.app.feature.setting.ui.UpdatesListScreen private const val SETTING_SCREEN = "setting" const val SETTING_ROUTE = SETTING_SCREEN +private const val FEATURES_SCREEN = "features" +const val FEATURES_ROUTE = FEATURES_SCREEN + +private const val UPDATES_SCREEN = "updates" +const val UPDATES_ROUTE = UPDATES_SCREEN + fun NavController.navigateToSetting(navOptions: NavOptions) { navigate(SETTING_ROUTE, navOptions) } -fun NavGraphBuilder.settingGraph() { +fun NavController.navigateToFeatures() { + navigate(FEATURES_ROUTE) +} + +fun NavController.navigateToUpdates() { + navigate(UPDATES_ROUTE) +} + +fun NavGraphBuilder.settingGraph( + onNavigateToFeatures: () -> Unit, + onNavigateToUpdates: () -> Unit, + onNavigateToPlayStore: () -> Unit, +) { composable(route = SETTING_ROUTE) { - SettingScreen() + SettingScreen( + handleNavigateActions = { + when (it) { + SettingActions.Navigates.FeaturesList -> onNavigateToFeatures() + SettingActions.Navigates.PlayStore -> onNavigateToPlayStore() + SettingActions.Navigates.UpdatesList -> onNavigateToUpdates() + } + }, + ) + } + + composable(route = FEATURES_ROUTE) { + FeaturesListScreen() + } + + composable(route = UPDATES_ROUTE) { + UpdatesListScreen() } } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt new file mode 100644 index 00000000..d660399e --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt @@ -0,0 +1,22 @@ +package com.titi.app.feature.setting.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.sp +import com.titi.app.core.designsystem.component.TdsText +import com.titi.app.core.designsystem.theme.TdsColor +import com.titi.app.core.designsystem.theme.TdsTextStyle + +@Composable +fun FeaturesListScreen() { + Column(modifier = Modifier.fillMaxSize()) { + TdsText( + text = "Features", + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + color = TdsColor.TEXT, + fontSize = 24.sp, + ) + } +} diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index a887a23c..db6f7f5a 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -1,6 +1,5 @@ package com.titi.app.feature.setting.ui -import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -35,7 +34,10 @@ import com.titi.app.feature.setting.model.SettingActions import com.titi.app.feature.setting.model.SettingUiState @Composable -fun SettingScreen(viewModel: SettingViewModel = mavericksViewModel()) { +fun SettingScreen( + viewModel: SettingViewModel = mavericksViewModel(), + handleNavigateActions: (SettingActions.Navigates) -> Unit, +) { val uiState by viewModel.collectAsState() Scaffold(containerColor = Color.Transparent) { @@ -44,21 +46,7 @@ fun SettingScreen(viewModel: SettingViewModel = mavericksViewModel()) { uiState = uiState, onSettingActions = { settingActions -> when (settingActions) { - is SettingActions.Navigates -> { - when (settingActions) { - is SettingActions.Navigates.FeaturesList -> { - Log.e("ABC", settingActions.toString()) - } - - is SettingActions.Navigates.UpdatesList -> { - Log.e("ABC", settingActions.toString()) - } - - is SettingActions.Navigates.PlayStore -> { - Log.e("ABC", settingActions.toString()) - } - } - } + is SettingActions.Navigates -> handleNavigateActions(settingActions) is SettingActions.Updates -> viewModel.handleUpdateActions(settingActions) } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt new file mode 100644 index 00000000..30aab799 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt @@ -0,0 +1,22 @@ +package com.titi.app.feature.setting.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.sp +import com.titi.app.core.designsystem.component.TdsText +import com.titi.app.core.designsystem.theme.TdsColor +import com.titi.app.core.designsystem.theme.TdsTextStyle + +@Composable +fun UpdatesListScreen() { + Column(modifier = Modifier.fillMaxSize()) { + TdsText( + text = "Updates", + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + color = TdsColor.TEXT, + fontSize = 24.sp, + ) + } +} From 460b388103895f5ba5d64498d82c1b1f45f79ed8 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 14:47:14 +0900 Subject: [PATCH 09/25] #82 update : setting topbar --- .../app/feature/setting/ui/SettingScreen.kt | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index db6f7f5a..ff04be12 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -11,9 +11,12 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -33,6 +36,7 @@ import com.titi.app.core.designsystem.theme.TiTiTheme import com.titi.app.feature.setting.model.SettingActions import com.titi.app.feature.setting.model.SettingUiState +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingScreen( viewModel: SettingViewModel = mavericksViewModel(), @@ -40,7 +44,24 @@ fun SettingScreen( ) { val uiState by viewModel.collectAsState() - Scaffold(containerColor = Color.Transparent) { + Scaffold( + containerColor = Color.Transparent, + topBar = { + TopAppBar( + colors = TopAppBarDefaults.topAppBarColors( + containerColor = TdsColor.GROUPED_BACKGROUND.getColor(), + ), + title = { + TdsText( + text = "Setting", + textStyle = TdsTextStyle.EXTRA_BOLD_TEXT_STYLE, + fontSize = 24.sp, + color = TdsColor.TEXT, + ) + }, + ) + }, + ) { SettingScreen( modifier = Modifier.padding(it), uiState = uiState, @@ -70,16 +91,6 @@ private fun SettingScreen( .then(modifier) .verticalScroll(scrollState), ) { - TdsText( - modifier = Modifier.padding(start = 16.dp), - text = "Setting", - textStyle = TdsTextStyle.EXTRA_BOLD_TEXT_STYLE, - fontSize = 34.sp, - color = TdsColor.TEXT, - ) - - Spacer(modifier = Modifier.height(35.dp)) - SettingServiceSection(onSettingActions = onSettingActions) Spacer(modifier = Modifier.height(35.dp)) From 437e595999813aa32f4a06f74a56badb99ba8918 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 15:15:31 +0900 Subject: [PATCH 10/25] =?UTF-8?q?#82=20feat=20:=20=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=EB=93=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/main/navigation/TiTiNavHost.kt | 4 + .../app/feature/setting/di/ViewModelModule.kt | 8 ++ .../feature/setting/model/FeaturesUiState.kt | 42 +++++++ .../setting/navigation/SettingNavigation.kt | 7 +- .../feature/setting/ui/FeaturesListScreen.kt | 106 ++++++++++++++++-- .../setting/ui/FeaturesListViewModel.kt | 24 ++++ .../app/feature/setting/ui/SettingScreen.kt | 17 +-- 7 files changed, 192 insertions(+), 16 deletions(-) create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/FeaturesUiState.kt create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListViewModel.kt diff --git a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt index 00a3c6c3..2e2e1c1f 100644 --- a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt +++ b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt @@ -77,6 +77,10 @@ fun TiTiNavHost( context.startActivity(intent) }, + onNavigateUp = { navController.navigateUp() }, + onNavigateToWebView = { + // TODO WebView 연결 + }, ) } } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/di/ViewModelModule.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/di/ViewModelModule.kt index df833131..1631bc74 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/di/ViewModelModule.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/di/ViewModelModule.kt @@ -3,6 +3,7 @@ package com.titi.app.feature.setting.di import com.airbnb.mvrx.hilt.AssistedViewModelFactory import com.airbnb.mvrx.hilt.MavericksViewModelComponent import com.airbnb.mvrx.hilt.ViewModelKey +import com.titi.app.feature.setting.ui.FeaturesListViewModel import com.titi.app.feature.setting.ui.SettingViewModel import dagger.Binds import dagger.Module @@ -16,4 +17,11 @@ internal interface ViewModelModule { @IntoMap @ViewModelKey(SettingViewModel::class) fun settingViewModelFactory(factory: SettingViewModel.Factory): AssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @ViewModelKey(FeaturesListViewModel::class) + fun featuresViewModelFactory( + factory: FeaturesListViewModel.Factory, + ): AssistedViewModelFactory<*, *> } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/FeaturesUiState.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/FeaturesUiState.kt new file mode 100644 index 00000000..6c6d2429 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/FeaturesUiState.kt @@ -0,0 +1,42 @@ +package com.titi.app.feature.setting.model + +import com.airbnb.mvrx.MavericksState + +data class FeaturesUiState( + val features: List = makeFeatures(), +) : MavericksState { + data class Feature( + val title: String, + val url: String, + ) +} + +internal fun makeFeatures(): List { + return listOf( + FeaturesUiState.Feature( + title = "새로운 기록 설정", + url = "https://www.notion.so/timertiti/2501881bb0ef49c29a1c2cee29b7f48e?pvs=4", + ), + FeaturesUiState.Feature( + title = "Task", + url = "https://www.notion.so/timertiti/Task-5fbd947fe3994ce09dd3d87051861005?pvs=4", + ), + FeaturesUiState.Feature( + title = "Timer", + url = "https://www.notion.so/timertiti/Timer-0083c63a3a464fc69b6c255930690ae8?pvs=4", + ), + FeaturesUiState.Feature( + title = "Stopwatch", + url = + "https://www.notion.so/timertiti/Stopwatch-41984a8ab11444cba79fb94984f799bb?pvs=4", + ), + FeaturesUiState.Feature( + title = "Log", + url = "https://www.notion.so/timertiti/Log-362d4cffb3e74f1686dd4e603fba8496?pvs=4", + ), + FeaturesUiState.Feature( + title = "Daily", + url = "https://www.notion.so/timertiti/Daily-d60dc90f3c104744a74985ea221e5691?pvs=4", + ), + ) +} diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt index f56a4264..27d1dbf3 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt @@ -34,6 +34,8 @@ fun NavGraphBuilder.settingGraph( onNavigateToFeatures: () -> Unit, onNavigateToUpdates: () -> Unit, onNavigateToPlayStore: () -> Unit, + onNavigateUp: () -> Unit, + onNavigateToWebView: (String) -> Unit, ) { composable(route = SETTING_ROUTE) { SettingScreen( @@ -48,7 +50,10 @@ fun NavGraphBuilder.settingGraph( } composable(route = FEATURES_ROUTE) { - FeaturesListScreen() + FeaturesListScreen( + onNavigateUp = onNavigateUp, + onNavigateWebView = onNavigateToWebView, + ) } composable(route = UPDATES_ROUTE) { diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt index d660399e..2222efc9 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt @@ -1,22 +1,114 @@ package com.titi.app.feature.setting.ui import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.airbnb.mvrx.compose.collectAsState +import com.airbnb.mvrx.compose.mavericksViewModel +import com.titi.app.core.designsystem.R +import com.titi.app.core.designsystem.component.TdsIconButton import com.titi.app.core.designsystem.component.TdsText import com.titi.app.core.designsystem.theme.TdsColor import com.titi.app.core.designsystem.theme.TdsTextStyle +import com.titi.app.core.designsystem.theme.TiTiTheme +import com.titi.app.feature.setting.model.FeaturesUiState +import com.titi.app.feature.setting.model.makeFeatures +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun FeaturesListScreen() { - Column(modifier = Modifier.fillMaxSize()) { - TdsText( - text = "Features", - textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, - color = TdsColor.TEXT, - fontSize = 24.sp, +fun FeaturesListScreen( + viewModel: FeaturesListViewModel = mavericksViewModel(), + onNavigateUp: () -> Unit, + onNavigateWebView: (String) -> Unit, +) { + val uiState by viewModel.collectAsState() + + Scaffold( + containerColor = TdsColor.GROUPED_BACKGROUND.getColor(), + topBar = { + TopAppBar( + colors = TopAppBarDefaults.topAppBarColors( + containerColor = TdsColor.GROUPED_BACKGROUND.getColor(), + ), + navigationIcon = { + TdsIconButton(onClick = onNavigateUp) { + Icon( + painter = painterResource(id = R.drawable.arrow_left_icon), + contentDescription = "back", + tint = TdsColor.TEXT.getColor(), + ) + } + }, + title = { + TdsText( + text = "Setting", + textStyle = TdsTextStyle.EXTRA_BOLD_TEXT_STYLE, + fontSize = 24.sp, + color = TdsColor.TEXT, + ) + }, + ) + }, + ) { + FeaturesListScreen( + modifier = Modifier + .fillMaxSize() + .padding(it), + uiState = uiState, + onClick = onNavigateWebView, + ) + } +} + +@Composable +fun FeaturesListScreen( + modifier: Modifier = Modifier, + uiState: FeaturesUiState, + onClick: (String) -> Unit, +) { + Column(modifier = modifier) { + uiState.features.forEachIndexed { index, feature -> + ListContent( + title = feature.title, + rightAreaContent = { + Icon( + painter = painterResource(id = R.drawable.arrow_right_icon), + contentDescription = "", + tint = TdsColor.LIGHT_GRAY.getColor(), + ) + }, + onClick = { onClick(feature.url) }, + ) + + if (index != uiState.features.size - 1) { + Spacer(modifier = Modifier.height(1.dp)) + } + } + } +} + +@Composable +@Preview +private fun FeaturesListScreenPreview() { + TiTiTheme { + FeaturesListScreen( + modifier = Modifier.fillMaxSize(), + uiState = FeaturesUiState(features = makeFeatures()), + onClick = {}, ) } } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListViewModel.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListViewModel.kt new file mode 100644 index 00000000..b3ccf8eb --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListViewModel.kt @@ -0,0 +1,24 @@ +package com.titi.app.feature.setting.ui + +import com.airbnb.mvrx.MavericksViewModel +import com.airbnb.mvrx.MavericksViewModelFactory +import com.airbnb.mvrx.hilt.AssistedViewModelFactory +import com.airbnb.mvrx.hilt.hiltMavericksViewModelFactory +import com.titi.app.feature.setting.model.FeaturesUiState +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class FeaturesListViewModel @AssistedInject constructor( + @Assisted initialState: FeaturesUiState, +) : MavericksViewModel(initialState) { + + @AssistedFactory + interface Factory : AssistedViewModelFactory { + override fun create(state: FeaturesUiState): FeaturesListViewModel + } + + companion object : + MavericksViewModelFactory + by hiltMavericksViewModelFactory() +} diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index ff04be12..e6ccb31e 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -6,6 +6,7 @@ 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.width @@ -123,7 +124,7 @@ private fun SettingServiceSection(onSettingActions: (SettingActions) -> Unit) { Spacer(modifier = Modifier.height(4.dp)) - SettingRowContent( + ListContent( title = "TiTi 기능들", rightAreaContent = { Icon( @@ -153,7 +154,7 @@ private fun SettingNotificationSection( Spacer(modifier = Modifier.height(4.dp)) - SettingRowContent( + ListContent( title = "타이머", description = "종료 5분전 알림", rightAreaContent = { @@ -175,7 +176,7 @@ private fun SettingNotificationSection( Spacer(modifier = Modifier.height(1.dp)) - SettingRowContent( + ListContent( title = "타이머", description = "종료 알림", rightAreaContent = { @@ -196,7 +197,7 @@ private fun SettingNotificationSection( Spacer(modifier = Modifier.height(1.dp)) - SettingRowContent( + ListContent( title = "스톱워치", description = "1시간단위 경과시 알림", rightAreaContent = { @@ -231,7 +232,7 @@ private fun SettingVersionSection( Spacer(modifier = Modifier.height(4.dp)) - SettingRowContent( + ListContent( title = "버전 정보", description = "최신버전: ${versionState.newVersion}", rightAreaContent = { @@ -248,7 +249,7 @@ private fun SettingVersionSection( Spacer(modifier = Modifier.height(1.dp)) - SettingRowContent( + ListContent( title = "업데이트 내역", rightAreaContent = { Row(verticalAlignment = Alignment.CenterVertically) { @@ -275,7 +276,7 @@ private fun SettingVersionSection( } @Composable -private fun SettingRowContent( +internal fun ListContent( title: String, description: String? = null, rightAreaContent: @Composable () -> Unit, @@ -283,7 +284,7 @@ private fun SettingRowContent( ) { Row( modifier = Modifier - .fillMaxSize() + .fillMaxWidth() .background(TdsColor.SECONDARY_BACKGROUND.getColor()) .clickable { onClick?.invoke() } .padding( From f92dfc96937ec2b6592181219129a703dd996ea2 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 15:21:46 +0900 Subject: [PATCH 11/25] #82 chore : data notification module setting --- data/notification/api/build.gradle.kts | 7 +++++++ data/notification/api/src/main/AndroidManifest.xml | 4 ++++ data/notification/impl/build.gradle.kts | 11 +++++++++++ data/notification/impl/src/main/AndroidManifest.xml | 4 ++++ settings.gradle.kts | 2 ++ 5 files changed, 28 insertions(+) create mode 100644 data/notification/api/build.gradle.kts create mode 100644 data/notification/api/src/main/AndroidManifest.xml create mode 100644 data/notification/impl/build.gradle.kts create mode 100644 data/notification/impl/src/main/AndroidManifest.xml diff --git a/data/notification/api/build.gradle.kts b/data/notification/api/build.gradle.kts new file mode 100644 index 00000000..c1dd973b --- /dev/null +++ b/data/notification/api/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + id("titi.android.library-no-hilt") +} + +android { + namespace = "com.titi.app.data.notification.api" +} diff --git a/data/notification/api/src/main/AndroidManifest.xml b/data/notification/api/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/data/notification/api/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/data/notification/impl/build.gradle.kts b/data/notification/impl/build.gradle.kts new file mode 100644 index 00000000..c249701a --- /dev/null +++ b/data/notification/impl/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("titi.android.data.local") +} + +android { + namespace = "com.titi.app.data.notification.impl" +} + +dependencies { + implementation(project(":data:notification:api")) +} diff --git a/data/notification/impl/src/main/AndroidManifest.xml b/data/notification/impl/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/data/notification/impl/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index d196e10d..e9a06e60 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -47,3 +47,5 @@ include(":feature:log") include(":data:graph:impl") include(":data:graph:api") include(":feature:setting") +include(":data:notification:api") +include(":data:notification:impl") From b249782bd13e8607af2d61cfd2780b6b429b83bf Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 15:31:26 +0900 Subject: [PATCH 12/25] #82 feat : NotificationDataStore --- .../notification/impl/di/DataStoreModule.kt | 19 +++++++++++ .../impl/local/NotificationDataStore.kt | 33 +++++++++++++++++++ .../impl/local/model/NotificationEntity.kt | 10 ++++++ 3 files changed, 62 insertions(+) create mode 100644 data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/di/DataStoreModule.kt create mode 100644 data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/NotificationDataStore.kt create mode 100644 data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/model/NotificationEntity.kt diff --git a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/di/DataStoreModule.kt b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/di/DataStoreModule.kt new file mode 100644 index 00000000..cdd567a0 --- /dev/null +++ b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/di/DataStoreModule.kt @@ -0,0 +1,19 @@ +package com.titi.app.data.notification.impl.di + +import android.content.Context +import com.titi.app.data.notification.impl.local.NotificationDataStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object DataStoreModule { + @Provides + @Singleton + fun provideNotificationDataStore(@ApplicationContext context: Context) = + NotificationDataStore(context) +} diff --git a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/NotificationDataStore.kt b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/NotificationDataStore.kt new file mode 100644 index 00000000..6eed21be --- /dev/null +++ b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/NotificationDataStore.kt @@ -0,0 +1,33 @@ +package com.titi.app.data.notification.impl.local + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import com.titi.app.core.util.fromJson +import com.titi.app.core.util.readFlowValue +import com.titi.app.core.util.storeValue +import com.titi.app.core.util.toJson +import com.titi.app.data.notification.impl.local.model.NotificationEntity +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +internal class NotificationDataStore(context: Context) { + private val dataStore: DataStore = context.dataStore + + suspend fun setNotification(notificationEntity: NotificationEntity) { + dataStore.storeValue(NOTIFICATION_KEY, notificationEntity.toJson()) + } + + fun getNotificationFlow(): Flow = + dataStore.readFlowValue(NOTIFICATION_KEY).map { it?.fromJson() } + + companion object { + private const val NOTIFICATION_PREF_NAME = "notificationPrefName" + private val NOTIFICATION_KEY = stringPreferencesKey("notificationKey") + + private val Context.dataStore: DataStore + by preferencesDataStore(NOTIFICATION_PREF_NAME) + } +} diff --git a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/model/NotificationEntity.kt b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/model/NotificationEntity.kt new file mode 100644 index 00000000..4a34a759 --- /dev/null +++ b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/model/NotificationEntity.kt @@ -0,0 +1,10 @@ +package com.titi.app.data.notification.impl.local.model + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class NotificationEntity( + val timerFiveMinutesBeforeTheEnd: Boolean, + val timerBeforeTheEnd: Boolean, + val stopwatch: Boolean, +) From 3d3321e6701a0574fac4bee88d6216e915be832a Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 15:34:43 +0900 Subject: [PATCH 13/25] #82 feat : NotificationRepositoryModel --- .../api/model/NotificationRepositoryModel.kt | 7 +++++++ .../impl/mapper/LocalToRepositoryMapper.kt | 10 ++++++++++ .../impl/mapper/RepositoryToLocalMapper.kt | 10 ++++++++++ 3 files changed, 27 insertions(+) create mode 100644 data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/model/NotificationRepositoryModel.kt create mode 100644 data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/mapper/LocalToRepositoryMapper.kt create mode 100644 data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/mapper/RepositoryToLocalMapper.kt diff --git a/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/model/NotificationRepositoryModel.kt b/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/model/NotificationRepositoryModel.kt new file mode 100644 index 00000000..d048899a --- /dev/null +++ b/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/model/NotificationRepositoryModel.kt @@ -0,0 +1,7 @@ +package com.titi.app.data.notification.api.model + +data class NotificationRepositoryModel( + val timerFiveMinutesBeforeTheEnd: Boolean, + val timerBeforeTheEnd: Boolean, + val stopwatch: Boolean, +) diff --git a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/mapper/LocalToRepositoryMapper.kt b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/mapper/LocalToRepositoryMapper.kt new file mode 100644 index 00000000..54e4680e --- /dev/null +++ b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/mapper/LocalToRepositoryMapper.kt @@ -0,0 +1,10 @@ +package com.titi.app.data.notification.impl.mapper + +import com.titi.app.data.notification.api.model.NotificationRepositoryModel +import com.titi.app.data.notification.impl.local.model.NotificationEntity + +internal fun NotificationEntity.toRepositoryModel() = NotificationRepositoryModel( + timerFiveMinutesBeforeTheEnd = timerFiveMinutesBeforeTheEnd, + timerBeforeTheEnd = timerBeforeTheEnd, + stopwatch = stopwatch, +) diff --git a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/mapper/RepositoryToLocalMapper.kt b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/mapper/RepositoryToLocalMapper.kt new file mode 100644 index 00000000..7c4902e2 --- /dev/null +++ b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/mapper/RepositoryToLocalMapper.kt @@ -0,0 +1,10 @@ +package com.titi.app.data.notification.impl.mapper + +import com.titi.app.data.notification.api.model.NotificationRepositoryModel +import com.titi.app.data.notification.impl.local.model.NotificationEntity + +internal fun NotificationRepositoryModel.toLocalModel() = NotificationEntity( + timerFiveMinutesBeforeTheEnd = timerFiveMinutesBeforeTheEnd, + timerBeforeTheEnd = timerBeforeTheEnd, + stopwatch = stopwatch, +) From 9e1c5c3b76ed625e381cc4b833adb39170dd0e10 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 15:39:15 +0900 Subject: [PATCH 14/25] #82 feat : NotificationRepository --- .../api/NotificationRepository.kt | 11 ++++++++++ .../api/model/NotificationRepositoryModel.kt | 6 ++--- .../notification/impl/di/RepositoryModule.kt | 19 ++++++++++++++++ .../repository/NotificationRepositoryImpl.kt | 22 +++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/NotificationRepository.kt create mode 100644 data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/di/RepositoryModule.kt create mode 100644 data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/repository/NotificationRepositoryImpl.kt diff --git a/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/NotificationRepository.kt b/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/NotificationRepository.kt new file mode 100644 index 00000000..015e2d1c --- /dev/null +++ b/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/NotificationRepository.kt @@ -0,0 +1,11 @@ +package com.titi.app.data.notification.api + +import com.titi.app.data.notification.api.model.NotificationRepositoryModel +import kotlinx.coroutines.flow.Flow + +interface NotificationRepository { + + suspend fun setNotification(notificationRepositoryModel: NotificationRepositoryModel) + + fun getNotificationFlow(): Flow +} diff --git a/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/model/NotificationRepositoryModel.kt b/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/model/NotificationRepositoryModel.kt index d048899a..40eca73f 100644 --- a/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/model/NotificationRepositoryModel.kt +++ b/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/model/NotificationRepositoryModel.kt @@ -1,7 +1,7 @@ package com.titi.app.data.notification.api.model data class NotificationRepositoryModel( - val timerFiveMinutesBeforeTheEnd: Boolean, - val timerBeforeTheEnd: Boolean, - val stopwatch: Boolean, + val timerFiveMinutesBeforeTheEnd: Boolean = true, + val timerBeforeTheEnd: Boolean = true, + val stopwatch: Boolean = true, ) diff --git a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/di/RepositoryModule.kt b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/di/RepositoryModule.kt new file mode 100644 index 00000000..2ec2b335 --- /dev/null +++ b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/di/RepositoryModule.kt @@ -0,0 +1,19 @@ +package com.titi.app.data.notification.impl.di + +import com.titi.app.data.notification.api.NotificationRepository +import com.titi.app.data.notification.impl.repository.NotificationRepositoryImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface RepositoryModule { + @Binds + @Singleton + fun provideNotificationRepository( + notificationRepositoryImpl: NotificationRepositoryImpl, + ): NotificationRepository +} diff --git a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/repository/NotificationRepositoryImpl.kt b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/repository/NotificationRepositoryImpl.kt new file mode 100644 index 00000000..2f5cc819 --- /dev/null +++ b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/repository/NotificationRepositoryImpl.kt @@ -0,0 +1,22 @@ +package com.titi.app.data.notification.impl.repository + +import com.titi.app.data.notification.api.NotificationRepository +import com.titi.app.data.notification.api.model.NotificationRepositoryModel +import com.titi.app.data.notification.impl.local.NotificationDataStore +import com.titi.app.data.notification.impl.mapper.toLocalModel +import com.titi.app.data.notification.impl.mapper.toRepositoryModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +internal class NotificationRepositoryImpl @Inject constructor( + private val notificationDataStore: NotificationDataStore, +) : NotificationRepository { + override suspend fun setNotification(notificationRepositoryModel: NotificationRepositoryModel) { + notificationDataStore.setNotification(notificationRepositoryModel.toLocalModel()) + } + + override fun getNotificationFlow(): Flow = + notificationDataStore.getNotificationFlow() + .map { it?.toRepositoryModel() ?: NotificationRepositoryModel() } +} From 7cffd4a98508face4c9190d4618f8b71ec4307c2 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 15:50:16 +0900 Subject: [PATCH 15/25] =?UTF-8?q?#82=20feat=20:=20Notification=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85=20=ED=99=94=EB=A9=B4=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + feature/setting/build.gradle.kts | 1 + .../mapper/FeatureToRepositoryMapper.kt | 10 ++++++++ .../mapper/RepositoryToFeatureMapper.kt | 10 ++++++++ .../feature/setting/ui/SettingViewModel.kt | 23 ++++++++++++++++++- 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/mapper/FeatureToRepositoryMapper.kt create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/mapper/RepositoryToFeatureMapper.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e8217229..f23126a4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -72,6 +72,7 @@ dependencies { implementation(project(":data:sleep:impl")) implementation(project(":data:alarm:impl")) implementation(project(":data:graph:impl")) + implementation(project(":data:notification:impl")) implementation(platform(libs.firebase.bom)) implementation(libs.firebase.analytics) diff --git a/feature/setting/build.gradle.kts b/feature/setting/build.gradle.kts index 63109244..3f7168ba 100644 --- a/feature/setting/build.gradle.kts +++ b/feature/setting/build.gradle.kts @@ -7,4 +7,5 @@ android { } dependencies { + implementation(project(":data:notification:api")) } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/mapper/FeatureToRepositoryMapper.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/mapper/FeatureToRepositoryMapper.kt new file mode 100644 index 00000000..393b9d85 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/mapper/FeatureToRepositoryMapper.kt @@ -0,0 +1,10 @@ +package com.titi.app.feature.setting.mapper + +import com.titi.app.data.notification.api.model.NotificationRepositoryModel +import com.titi.app.feature.setting.model.SettingUiState + +fun SettingUiState.SwitchState.toRepositoryModel() = NotificationRepositoryModel( + timerFiveMinutesBeforeTheEnd = timerFiveMinutesBeforeTheEnd, + timerBeforeTheEnd = timerBeforeTheEnd, + stopwatch = stopwatch, +) diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/mapper/RepositoryToFeatureMapper.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/mapper/RepositoryToFeatureMapper.kt new file mode 100644 index 00000000..c261b703 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/mapper/RepositoryToFeatureMapper.kt @@ -0,0 +1,10 @@ +package com.titi.app.feature.setting.mapper + +import com.titi.app.data.notification.api.model.NotificationRepositoryModel +import com.titi.app.feature.setting.model.SettingUiState + +fun NotificationRepositoryModel.toFeatureModel() = SettingUiState.SwitchState( + timerFiveMinutesBeforeTheEnd = timerFiveMinutesBeforeTheEnd, + timerBeforeTheEnd = timerBeforeTheEnd, + stopwatch = stopwatch, +) diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt index f83136e2..d0b399f1 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingViewModel.kt @@ -4,19 +4,40 @@ import com.airbnb.mvrx.MavericksViewModel import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.hilt.AssistedViewModelFactory import com.airbnb.mvrx.hilt.hiltMavericksViewModelFactory +import com.titi.app.data.notification.api.NotificationRepository +import com.titi.app.feature.setting.mapper.toFeatureModel +import com.titi.app.feature.setting.mapper.toRepositoryModel import com.titi.app.feature.setting.model.SettingActions import com.titi.app.feature.setting.model.SettingUiState import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch class SettingViewModel @AssistedInject constructor( @Assisted initialState: SettingUiState, + private val notificationRepository: NotificationRepository, ) : MavericksViewModel(initialState) { + init { + viewModelScope.launch { + notificationRepository.getNotificationFlow() + .collectLatest { + updateSwitch(it.toFeatureModel()) + } + } + } + fun handleUpdateActions(updateActions: SettingActions.Updates) { when (updateActions) { - is SettingActions.Updates.Switch -> updateSwitch(updateActions.switchState) + is SettingActions.Updates.Switch -> { + viewModelScope.launch { + notificationRepository + .setNotification(updateActions.switchState.toRepositoryModel()) + } + } + is SettingActions.Updates.Version -> updateVersion(updateActions.versionState) } } From 8af00d9add5202ea5f51b1c659f99ffa6dd335bb Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 16:36:24 +0900 Subject: [PATCH 16/25] #82 feat : getNotification --- .../titi/app/data/notification/api/NotificationRepository.kt | 3 ++- .../data/notification/impl/local/NotificationDataStore.kt | 4 ++++ .../impl/repository/NotificationRepositoryImpl.kt | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/NotificationRepository.kt b/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/NotificationRepository.kt index 015e2d1c..e41edeb5 100644 --- a/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/NotificationRepository.kt +++ b/data/notification/api/src/main/kotlin/com/titi/app/data/notification/api/NotificationRepository.kt @@ -4,8 +4,9 @@ import com.titi.app.data.notification.api.model.NotificationRepositoryModel import kotlinx.coroutines.flow.Flow interface NotificationRepository { - suspend fun setNotification(notificationRepositoryModel: NotificationRepositoryModel) fun getNotificationFlow(): Flow + + suspend fun getNotification(): NotificationRepositoryModel } diff --git a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/NotificationDataStore.kt b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/NotificationDataStore.kt index 6eed21be..c620857f 100644 --- a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/NotificationDataStore.kt +++ b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/local/NotificationDataStore.kt @@ -7,6 +7,7 @@ import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import com.titi.app.core.util.fromJson import com.titi.app.core.util.readFlowValue +import com.titi.app.core.util.readValue import com.titi.app.core.util.storeValue import com.titi.app.core.util.toJson import com.titi.app.data.notification.impl.local.model.NotificationEntity @@ -23,6 +24,9 @@ internal class NotificationDataStore(context: Context) { fun getNotificationFlow(): Flow = dataStore.readFlowValue(NOTIFICATION_KEY).map { it?.fromJson() } + suspend fun getNotification(): NotificationEntity? = + dataStore.readValue(NOTIFICATION_KEY)?.fromJson() + companion object { private const val NOTIFICATION_PREF_NAME = "notificationPrefName" private val NOTIFICATION_KEY = stringPreferencesKey("notificationKey") diff --git a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/repository/NotificationRepositoryImpl.kt b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/repository/NotificationRepositoryImpl.kt index 2f5cc819..bc1852d7 100644 --- a/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/repository/NotificationRepositoryImpl.kt +++ b/data/notification/impl/src/main/kotlin/com/titi/app/data/notification/impl/repository/NotificationRepositoryImpl.kt @@ -19,4 +19,9 @@ internal class NotificationRepositoryImpl @Inject constructor( override fun getNotificationFlow(): Flow = notificationDataStore.getNotificationFlow() .map { it?.toRepositoryModel() ?: NotificationRepositoryModel() } + + override suspend fun getNotification(): NotificationRepositoryModel { + return notificationDataStore.getNotification()?.toRepositoryModel() + ?: NotificationRepositoryModel() + } } From 455003338e0ea8c7162a404a2e3d0643fbeec9c1 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 16:45:25 +0900 Subject: [PATCH 17/25] =?UTF-8?q?#82=20feat=20:=20notification=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=95=8C=EB=9E=8C=20?= =?UTF-8?q?=EB=B3=B4=EB=82=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- domain/alarm/build.gradle.kts | 1 + .../alarm/usecase/SetStopWatchAlarmUseCase.kt | 37 +++++++++++-------- .../alarm/usecase/SetTimerAlarmUseCase.kt | 24 ++++++++---- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/domain/alarm/build.gradle.kts b/domain/alarm/build.gradle.kts index 2cb7b4b4..02e1e7e3 100644 --- a/domain/alarm/build.gradle.kts +++ b/domain/alarm/build.gradle.kts @@ -8,6 +8,7 @@ android { dependencies { implementation(project(":data:alarm:api")) + implementation(project(":data:notification:api")) implementation(libs.threetenabp) } diff --git a/domain/alarm/src/main/kotlin/com/titi/app/domain/alarm/usecase/SetStopWatchAlarmUseCase.kt b/domain/alarm/src/main/kotlin/com/titi/app/domain/alarm/usecase/SetStopWatchAlarmUseCase.kt index 2ae28ec3..b00e41d7 100644 --- a/domain/alarm/src/main/kotlin/com/titi/app/domain/alarm/usecase/SetStopWatchAlarmUseCase.kt +++ b/domain/alarm/src/main/kotlin/com/titi/app/domain/alarm/usecase/SetStopWatchAlarmUseCase.kt @@ -1,6 +1,7 @@ package com.titi.app.domain.alarm.usecase import com.titi.app.data.alarm.api.AlarmRepository +import com.titi.app.data.notification.api.NotificationRepository import com.titi.app.domain.alarm.mapper.toRepositoryModel import com.titi.app.domain.alarm.model.Alarm import com.titi.app.domain.alarm.model.Alarms @@ -9,29 +10,33 @@ import org.threeten.bp.ZonedDateTime class SetStopWatchAlarmUseCase @Inject constructor( private val alarmRepository: AlarmRepository, + private val notificationRepository: NotificationRepository, ) { suspend operator fun invoke(title: String, finishMessage: String, measureTime: Long) { alarmRepository.cancelAlarms() val now = ZonedDateTime.now() val finishTimeRange = (measureTime / ONE_HOUR_SECONDS) + 1..TWENTY_FOUR_HOURS - val alarms = - Alarms( - alarms = finishTimeRange.map { - Alarm( - title = title, - message = "$it$finishMessage", - finishTime = now.plusSeconds( - it * ONE_HOUR_SECONDS - measureTime, - ).toString(), - ) - }, - ) + val alarms = Alarms( + alarms = finishTimeRange.map { + Alarm( + title = title, + message = "$it$finishMessage", + finishTime = now.plusSeconds( + it * ONE_HOUR_SECONDS - measureTime, + ).toString(), + ) + }, + ) - if (alarmRepository.canScheduleExactAlarms()) { - alarmRepository.setExactAlarms(alarms.toRepositoryModel()) - } else { - alarmRepository.addExactAlarms(alarms.toRepositoryModel()) + val notification = notificationRepository.getNotification() + + if (notification.stopwatch) { + if (alarmRepository.canScheduleExactAlarms()) { + alarmRepository.setExactAlarms(alarms.toRepositoryModel()) + } else { + alarmRepository.addExactAlarms(alarms.toRepositoryModel()) + } } } diff --git a/domain/alarm/src/main/kotlin/com/titi/app/domain/alarm/usecase/SetTimerAlarmUseCase.kt b/domain/alarm/src/main/kotlin/com/titi/app/domain/alarm/usecase/SetTimerAlarmUseCase.kt index 2dffac3c..f27497dc 100644 --- a/domain/alarm/src/main/kotlin/com/titi/app/domain/alarm/usecase/SetTimerAlarmUseCase.kt +++ b/domain/alarm/src/main/kotlin/com/titi/app/domain/alarm/usecase/SetTimerAlarmUseCase.kt @@ -1,6 +1,7 @@ package com.titi.app.domain.alarm.usecase import com.titi.app.data.alarm.api.AlarmRepository +import com.titi.app.data.notification.api.NotificationRepository import com.titi.app.domain.alarm.mapper.toRepositoryModel import com.titi.app.domain.alarm.model.Alarm import com.titi.app.domain.alarm.model.Alarms @@ -9,6 +10,7 @@ import org.threeten.bp.ZonedDateTime class SetTimerAlarmUseCase @Inject constructor( private val alarmRepository: AlarmRepository, + private val notificationRepository: NotificationRepository, ) { suspend operator fun invoke( title: String, @@ -26,17 +28,23 @@ class SetTimerAlarmUseCase @Inject constructor( null } + val notification = notificationRepository.getNotification() val alarms = Alarms( alarms = mutableListOf().apply { - add( - Alarm( - title = title, - message = finishMessage, - finishTime = finishTime, - ), - ) + if (notification.timerBeforeTheEnd) { + add( + Alarm( + title = title, + message = finishMessage, + finishTime = finishTime, + ), + ) + } - if (fiveMinutesBeforeFinishTime != null) { + if ( + fiveMinutesBeforeFinishTime != null && + notification.timerFiveMinutesBeforeTheEnd + ) { add( Alarm( title = title, From 4283d9a54fc1d2017ce52858b94538628f35f9ab Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 18:57:22 +0900 Subject: [PATCH 18/25] =?UTF-8?q?#82=20feat=20:=20firebase=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=ED=98=84=EC=9E=AC=20=EC=B5=9C=EC=8B=A0=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/setting/build.gradle.kts | 7 ++++ .../app/feature/setting/model/Versions.kt | 15 +++++++++ .../app/feature/setting/ui/SettingScreen.kt | 32 +++++++++++++++++++ gradle/libs.versions.toml | 1 + 4 files changed, 55 insertions(+) create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Versions.kt diff --git a/feature/setting/build.gradle.kts b/feature/setting/build.gradle.kts index 3f7168ba..1e260fa8 100644 --- a/feature/setting/build.gradle.kts +++ b/feature/setting/build.gradle.kts @@ -8,4 +8,11 @@ android { dependencies { implementation(project(":data:notification:api")) + + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.database) + + implementation(libs.moshi) + implementation(libs.moshi.kotlin) + ksp(libs.moshi.codegen) } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Versions.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Versions.kt new file mode 100644 index 00000000..2357f9e1 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Versions.kt @@ -0,0 +1,15 @@ +package com.titi.app.feature.setting.model + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class Versions( + val versions: List = emptyList(), +) { + @JsonClass(generateAdapter = true) + data class Version( + val currentVersion: String = "", + val date: String = "", + val description: String = "", + ) +} diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index e6ccb31e..cf0a1a2b 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -1,5 +1,6 @@ package com.titi.app.feature.setting.ui +import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -29,6 +30,11 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.airbnb.mvrx.compose.collectAsState import com.airbnb.mvrx.compose.mavericksViewModel +import com.google.firebase.database.DataSnapshot +import com.google.firebase.database.DatabaseError +import com.google.firebase.database.FirebaseDatabase +import com.google.firebase.database.ValueEventListener +import com.google.firebase.database.getValue import com.titi.app.core.designsystem.R import com.titi.app.core.designsystem.component.TdsText import com.titi.app.core.designsystem.theme.TdsColor @@ -36,6 +42,7 @@ import com.titi.app.core.designsystem.theme.TdsTextStyle import com.titi.app.core.designsystem.theme.TiTiTheme import com.titi.app.feature.setting.model.SettingActions import com.titi.app.feature.setting.model.SettingUiState +import com.titi.app.feature.setting.model.Versions @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -45,6 +52,31 @@ fun SettingScreen( ) { val uiState by viewModel.collectAsState() + val firebaseDatabase = FirebaseDatabase.getInstance() + val databaseReference = firebaseDatabase.getReference("versions") + + databaseReference.addValueEventListener( + object : ValueEventListener { + override fun onDataChange(snapshot: DataSnapshot) { + val latestVersion = snapshot + .children + .lastOrNull() + ?.getValue() + ?.currentVersion + + latestVersion?.let { + viewModel.handleUpdateActions( + SettingActions.Updates.Version(uiState.versionState.copy(newVersion = it)), + ) + } + } + + override fun onCancelled(error: DatabaseError) { + Log.e("SettingScreen", error.message) + } + }, + ) + Scaffold( containerColor = Color.Transparent, topBar = { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8c89aab3..1370c2b6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -117,6 +117,7 @@ androidx-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version. firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics" } firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics" } +firebase-database = { group = "com.google.firebase", name = "firebase-database-ktx" } calendar = { group = "com.kizitonwose.calendar", name = "compose", version.ref = "calendar" } From 07f1b57a634502ca672248555036c709e93bad56 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 19:01:45 +0900 Subject: [PATCH 19/25] =?UTF-8?q?#82=20feat=20:=20=ED=98=84=EC=9E=AC=20?= =?UTF-8?q?=EB=B2=84=EC=A0=BC=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/setting/ui/SettingScreen.kt | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index cf0a1a2b..0c8823fa 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp @@ -55,6 +56,8 @@ fun SettingScreen( val firebaseDatabase = FirebaseDatabase.getInstance() val databaseReference = firebaseDatabase.getReference("versions") + val context = LocalContext.current + databaseReference.addValueEventListener( object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { @@ -64,9 +67,19 @@ fun SettingScreen( ?.getValue() ?.currentVersion - latestVersion?.let { + val currentVersion = context + .packageManager + .getPackageInfo(context.packageName, 0) + .versionName + + latestVersion?.let { safeLatestVersion -> viewModel.handleUpdateActions( - SettingActions.Updates.Version(uiState.versionState.copy(newVersion = it)), + SettingActions.Updates.Version( + uiState.versionState.copy( + newVersion = safeLatestVersion, + currentVersion = currentVersion, + ), + ), ) } } @@ -268,6 +281,15 @@ private fun SettingVersionSection( title = "버전 정보", description = "최신버전: ${versionState.newVersion}", rightAreaContent = { + TdsText( + text = versionState.currentVersion, + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + color = TdsColor.LIGHT_GRAY, + fontSize = 14.sp, + ) + + Spacer(modifier = Modifier.width(4.dp)) + Icon( painter = painterResource(id = R.drawable.arrow_right_icon), contentDescription = "", @@ -285,15 +307,6 @@ private fun SettingVersionSection( title = "업데이트 내역", rightAreaContent = { Row(verticalAlignment = Alignment.CenterVertically) { - TdsText( - text = versionState.currentVersion, - textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, - color = TdsColor.LIGHT_GRAY, - fontSize = 14.sp, - ) - - Spacer(modifier = Modifier.width(4.dp)) - Icon( painter = painterResource(id = R.drawable.arrow_right_icon), contentDescription = "", From e3e0b61bea2b3d33274da2d86bdaa459a2d3f301 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 19:03:44 +0900 Subject: [PATCH 20/25] =?UTF-8?q?#82=20feat=20:=20=ED=8C=8C=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=EB=B2=A0=EC=9D=B4=EC=8A=A4=EC=9A=A9=20model=20proguar?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/proguard-rules.pro | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 28c16c03..141b1628 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -69,4 +69,7 @@ -keepclassmembers class * { @com.squareup.moshi.FromJson ; @com.squareup.moshi.ToJson ; -} \ No newline at end of file +} + +-keepattributes Signature +-keepclassmembers class com.titi.app.feature.setting.model.** {*;} \ No newline at end of file From e5c89a5f84747980c4a2ca06f55eb466765656f1 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 20:01:28 +0900 Subject: [PATCH 21/25] #82 update : versions -> versionuistate --- .../feature/setting/model/{Versions.kt => VersionUiState.kt} | 2 +- .../kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/{Versions.kt => VersionUiState.kt} (92%) diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Versions.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/VersionUiState.kt similarity index 92% rename from feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Versions.kt rename to feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/VersionUiState.kt index 2357f9e1..4897091c 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Versions.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/VersionUiState.kt @@ -3,7 +3,7 @@ package com.titi.app.feature.setting.model import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class Versions( +data class VersionUiState( val versions: List = emptyList(), ) { @JsonClass(generateAdapter = true) diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index 0c8823fa..fe30ae9b 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -43,7 +43,7 @@ import com.titi.app.core.designsystem.theme.TdsTextStyle import com.titi.app.core.designsystem.theme.TiTiTheme import com.titi.app.feature.setting.model.SettingActions import com.titi.app.feature.setting.model.SettingUiState -import com.titi.app.feature.setting.model.Versions +import com.titi.app.feature.setting.model.VersionUiState @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -64,7 +64,7 @@ fun SettingScreen( val latestVersion = snapshot .children .lastOrNull() - ?.getValue() + ?.getValue() ?.currentVersion val currentVersion = context From fbb0ef433a9a78b5773dc021b8d4e1683a1a7790 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sat, 4 May 2024 21:37:09 +0900 Subject: [PATCH 22/25] #82 feat : UpdateListScreen --- .../titi/app/feature/setting/model/Version.kt | 10 ++ .../feature/setting/model/VersionUiState.kt | 15 -- .../setting/navigation/SettingNavigation.kt | 2 +- .../feature/setting/ui/FeaturesListScreen.kt | 2 +- .../app/feature/setting/ui/SettingScreen.kt | 61 ++++--- .../feature/setting/ui/UpdatesListScreen.kt | 167 +++++++++++++++++- 6 files changed, 205 insertions(+), 52 deletions(-) create mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Version.kt delete mode 100644 feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/VersionUiState.kt diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Version.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Version.kt new file mode 100644 index 00000000..0d0edba2 --- /dev/null +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/Version.kt @@ -0,0 +1,10 @@ +package com.titi.app.feature.setting.model + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class Version( + val currentVersion: String = "", + val date: String = "", + val description: String = "", +) diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/VersionUiState.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/VersionUiState.kt deleted file mode 100644 index 4897091c..00000000 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/model/VersionUiState.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.titi.app.feature.setting.model - -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -data class VersionUiState( - val versions: List = emptyList(), -) { - @JsonClass(generateAdapter = true) - data class Version( - val currentVersion: String = "", - val date: String = "", - val description: String = "", - ) -} diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt index 27d1dbf3..71f0a026 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt @@ -57,6 +57,6 @@ fun NavGraphBuilder.settingGraph( } composable(route = UPDATES_ROUTE) { - UpdatesListScreen() + UpdatesListScreen(onNavigateUp = onNavigateUp) } } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt index 2222efc9..5264a10f 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt @@ -55,7 +55,7 @@ fun FeaturesListScreen( }, title = { TdsText( - text = "Setting", + text = "TiTi 기능들", textStyle = TdsTextStyle.EXTRA_BOLD_TEXT_STYLE, fontSize = 24.sp, color = TdsColor.TEXT, diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt index fe30ae9b..5d7435b9 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/SettingScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.material3.Switch import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -43,7 +44,7 @@ import com.titi.app.core.designsystem.theme.TdsTextStyle import com.titi.app.core.designsystem.theme.TiTiTheme import com.titi.app.feature.setting.model.SettingActions import com.titi.app.feature.setting.model.SettingUiState -import com.titi.app.feature.setting.model.VersionUiState +import com.titi.app.feature.setting.model.Version @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -58,37 +59,39 @@ fun SettingScreen( val context = LocalContext.current - databaseReference.addValueEventListener( - object : ValueEventListener { - override fun onDataChange(snapshot: DataSnapshot) { - val latestVersion = snapshot - .children - .lastOrNull() - ?.getValue() - ?.currentVersion - - val currentVersion = context - .packageManager - .getPackageInfo(context.packageName, 0) - .versionName - - latestVersion?.let { safeLatestVersion -> - viewModel.handleUpdateActions( - SettingActions.Updates.Version( - uiState.versionState.copy( - newVersion = safeLatestVersion, - currentVersion = currentVersion, + LaunchedEffect(Unit) { + databaseReference.addValueEventListener( + object : ValueEventListener { + override fun onDataChange(snapshot: DataSnapshot) { + val latestVersion = snapshot + .children + .lastOrNull() + ?.getValue() + ?.currentVersion + + val currentVersion = context + .packageManager + .getPackageInfo(context.packageName, 0) + .versionName + + latestVersion?.let { safeLatestVersion -> + viewModel.handleUpdateActions( + SettingActions.Updates.Version( + uiState.versionState.copy( + newVersion = safeLatestVersion, + currentVersion = currentVersion, + ), ), - ), - ) + ) + } } - } - override fun onCancelled(error: DatabaseError) { - Log.e("SettingScreen", error.message) - } - }, - ) + override fun onCancelled(error: DatabaseError) { + Log.e("SettingScreen", error.message) + } + }, + ) + } Scaffold( containerColor = Color.Transparent, diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt index 30aab799..82ed1cc3 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt @@ -1,22 +1,177 @@ package com.titi.app.feature.setting.ui +import android.util.Log +import androidx.compose.foundation.background +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.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.google.firebase.database.DataSnapshot +import com.google.firebase.database.DatabaseError +import com.google.firebase.database.FirebaseDatabase +import com.google.firebase.database.ValueEventListener +import com.google.firebase.database.getValue +import com.titi.app.core.designsystem.R +import com.titi.app.core.designsystem.component.TdsIconButton import com.titi.app.core.designsystem.component.TdsText import com.titi.app.core.designsystem.theme.TdsColor import com.titi.app.core.designsystem.theme.TdsTextStyle +import com.titi.app.feature.setting.model.Version +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun UpdatesListScreen() { - Column(modifier = Modifier.fillMaxSize()) { +fun UpdatesListScreen(onNavigateUp: () -> Unit) { + val firebaseDatabase = FirebaseDatabase.getInstance() + val databaseReference = firebaseDatabase.getReference("versions") + + val updates = remember { mutableStateListOf() } + + LaunchedEffect(Unit) { + databaseReference.addValueEventListener( + object : ValueEventListener { + override fun onDataChange(snapshot: DataSnapshot) { + for (data in snapshot.children) { + data.getValue()?.let { + updates.add(it) + } + } + } + + override fun onCancelled(error: DatabaseError) { + Log.e("UpdateListScreen", error.message) + } + }, + ) + } + + Scaffold( + containerColor = TdsColor.GROUPED_BACKGROUND.getColor(), + topBar = { + TopAppBar( + colors = TopAppBarDefaults.topAppBarColors( + containerColor = TdsColor.GROUPED_BACKGROUND.getColor(), + ), + navigationIcon = { + TdsIconButton(onClick = onNavigateUp) { + Icon( + painter = painterResource(id = R.drawable.arrow_left_icon), + contentDescription = "back", + tint = TdsColor.TEXT.getColor(), + ) + } + }, + title = { + TdsText( + text = "업데이트 내역", + textStyle = TdsTextStyle.EXTRA_BOLD_TEXT_STYLE, + fontSize = 24.sp, + color = TdsColor.TEXT, + ) + }, + ) + }, + ) { + UpdateListScreen( + modifier = Modifier + .fillMaxSize() + .padding(it), + updates = updates.reversed(), + ) + } +} + +@Composable +private fun UpdateListScreen(modifier: Modifier = Modifier, updates: List) { + LazyColumn(modifier) { + items(updates) { + VersionContent(version = it) + } + } +} + +@Composable +private fun VersionContent(version: Version) { + Column { + Row( + modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + TdsText( + text = "ver ${version.currentVersion}", + textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, + color = TdsColor.TEXT, + fontSize = 16.sp, + ) + + TdsText( + text = version.date, + textStyle = TdsTextStyle.NORMAL_TEXT_STYLE, + color = TdsColor.LIGHT_GRAY, + fontSize = 16.sp, + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + TdsText( - text = "Updates", - textStyle = TdsTextStyle.SEMI_BOLD_TEXT_STYLE, - color = TdsColor.TEXT, - fontSize = 24.sp, + modifier = Modifier + .fillMaxWidth() + .background(color = TdsColor.SECONDARY_BACKGROUND.getColor()) + .padding( + horizontal = 8.dp, + vertical = 12.dp, + ), + text = version.description, + textStyle = TdsTextStyle.NORMAL_TEXT_STYLE, + color = TdsColor.LIGHT_GRAY, + fontSize = 16.sp, ) + + Spacer(modifier = Modifier.height(8.dp)) } } + +@Preview +@Composable +private fun UpdatesListScreenPreview() { + UpdateListScreen( + modifier = Modifier.fillMaxSize(), + updates = listOf( + Version( + currentVersion = "repudiandae", + date = "utinam", + description = "magnis", + ), + Version( + currentVersion = "repudiandae", + date = "utinam", + description = "magnis", + ), + Version( + currentVersion = "repudiandae", + date = "utinam", + description = "magnis", + ), + ), + ) +} From 6972f4070b475686d147565bbbaf7dba929e7610 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sun, 5 May 2024 12:37:39 +0900 Subject: [PATCH 23/25] #82 chore : feature:webview --- feature/webview/build.gradle.kts | 10 ++++++++++ feature/webview/src/main/AndroidManifest.xml | 4 ++++ settings.gradle.kts | 1 + 3 files changed, 15 insertions(+) create mode 100644 feature/webview/build.gradle.kts create mode 100644 feature/webview/src/main/AndroidManifest.xml diff --git a/feature/webview/build.gradle.kts b/feature/webview/build.gradle.kts new file mode 100644 index 00000000..6f8ab32b --- /dev/null +++ b/feature/webview/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("titi.android.feature") +} + +android { + namespace = "com.titi.app.feature.webview" +} + +dependencies { +} diff --git a/feature/webview/src/main/AndroidManifest.xml b/feature/webview/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/webview/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index e9a06e60..7911b62d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -49,3 +49,4 @@ include(":data:graph:api") include(":feature:setting") include(":data:notification:api") include(":data:notification:impl") +include(":feature:webview") From a2e23611d50e89dda395468970c1ebabe975d30d Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sun, 5 May 2024 13:18:15 +0900 Subject: [PATCH 24/25] #82 feat : webview --- app/src/main/AndroidManifest.xml | 1 + feature/main/build.gradle.kts | 1 + .../feature/main/navigation/TiTiNavHost.kt | 11 ++- .../setting/navigation/SettingNavigation.kt | 2 +- .../feature/setting/ui/FeaturesListScreen.kt | 8 +- .../app/feature/webview/WebViewNavigation.kt | 37 ++++++++ .../titi/app/feature/webview/WebViewScreen.kt | 90 +++++++++++++++++++ 7 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 feature/webview/src/main/kotlin/com/titi/app/feature/webview/WebViewNavigation.kt create mode 100644 feature/webview/src/main/kotlin/com/titi/app/feature/webview/WebViewScreen.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a78ba903..257b2b88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ android:label="${appName}" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" + android:usesCleartextTraffic="true" android:theme="@style/Theme.TiTi" tools:targetApi="31"> diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts index de53ea5a..a5eb135b 100644 --- a/feature/main/build.gradle.kts +++ b/feature/main/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(project(":feature:log")) implementation(project(":feature:popup")) implementation(project(":feature:setting")) + implementation(project(":feature:webview")) implementation(libs.androidx.splashscreen) implementation(libs.androidx.material3.window.size) diff --git a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt index 2e2e1c1f..3b9ea0dc 100644 --- a/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt +++ b/feature/main/src/main/kotlin/com/titi/app/feature/main/navigation/TiTiNavHost.kt @@ -21,6 +21,8 @@ import com.titi.app.feature.setting.navigation.settingGraph import com.titi.app.feature.time.navigation.STOPWATCH_SCREEN import com.titi.app.feature.time.navigation.TIMER_SCREEN import com.titi.app.feature.time.navigation.timeGraph +import com.titi.app.feature.webview.navigateToWebView +import com.titi.app.feature.webview.webViewGraph @Composable fun TiTiNavHost( @@ -78,9 +80,14 @@ fun TiTiNavHost( context.startActivity(intent) }, onNavigateUp = { navController.navigateUp() }, - onNavigateToWebView = { - // TODO WebView 연결 + onNavigateToWebView = { title, url -> + navController.navigateToWebView( + title = title, + url = url, + ) }, ) + + webViewGraph(onNavigateUp = { navController.navigateUp() }) } } diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt index 71f0a026..dc0f0d73 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/navigation/SettingNavigation.kt @@ -35,7 +35,7 @@ fun NavGraphBuilder.settingGraph( onNavigateToUpdates: () -> Unit, onNavigateToPlayStore: () -> Unit, onNavigateUp: () -> Unit, - onNavigateToWebView: (String) -> Unit, + onNavigateToWebView: (title: String, url: String) -> Unit, ) { composable(route = SETTING_ROUTE) { SettingScreen( diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt index 5264a10f..243d52ca 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt @@ -33,7 +33,7 @@ import com.titi.app.feature.setting.model.makeFeatures fun FeaturesListScreen( viewModel: FeaturesListViewModel = mavericksViewModel(), onNavigateUp: () -> Unit, - onNavigateWebView: (String) -> Unit, + onNavigateWebView: (title: String, url: String) -> Unit, ) { val uiState by viewModel.collectAsState() @@ -78,7 +78,7 @@ fun FeaturesListScreen( fun FeaturesListScreen( modifier: Modifier = Modifier, uiState: FeaturesUiState, - onClick: (String) -> Unit, + onClick: (title: String, url: String) -> Unit, ) { Column(modifier = modifier) { uiState.features.forEachIndexed { index, feature -> @@ -91,7 +91,7 @@ fun FeaturesListScreen( tint = TdsColor.LIGHT_GRAY.getColor(), ) }, - onClick = { onClick(feature.url) }, + onClick = { onClick(feature.title, feature.url) }, ) if (index != uiState.features.size - 1) { @@ -108,7 +108,7 @@ private fun FeaturesListScreenPreview() { FeaturesListScreen( modifier = Modifier.fillMaxSize(), uiState = FeaturesUiState(features = makeFeatures()), - onClick = {}, + onClick = { title, url -> }, ) } } diff --git a/feature/webview/src/main/kotlin/com/titi/app/feature/webview/WebViewNavigation.kt b/feature/webview/src/main/kotlin/com/titi/app/feature/webview/WebViewNavigation.kt new file mode 100644 index 00000000..477bcd39 --- /dev/null +++ b/feature/webview/src/main/kotlin/com/titi/app/feature/webview/WebViewNavigation.kt @@ -0,0 +1,37 @@ +package com.titi.app.feature.webview + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavType +import androidx.navigation.compose.composable +import androidx.navigation.navArgument + +private const val WEBVIEW_SCREEN = "webview" +const val WEBVIEW_TITLE_ARG = "title" +const val WEBVIEW_URL_ARG = "url" +const val WEBVIEW_ROUTE = + "$WEBVIEW_SCREEN?$WEBVIEW_TITLE_ARG={$WEBVIEW_TITLE_ARG}&$WEBVIEW_URL_ARG={$WEBVIEW_URL_ARG}" + +fun NavController.navigateToWebView(title: String, url: String) { + navigate("$WEBVIEW_SCREEN?$WEBVIEW_TITLE_ARG=$title&$WEBVIEW_URL_ARG=$url") +} + +fun NavGraphBuilder.webViewGraph(onNavigateUp: () -> Unit) { + composable( + route = WEBVIEW_ROUTE, + arguments = listOf( + navArgument(WEBVIEW_TITLE_ARG) { + type = NavType.StringType + }, + navArgument(WEBVIEW_URL_ARG) { + type = NavType.StringType + }, + ), + ) { + WebViewScreen( + title = it.arguments?.getString(WEBVIEW_TITLE_ARG) ?: "", + url = it.arguments?.getString(WEBVIEW_URL_ARG) ?: "", + onNavigateUp = onNavigateUp, + ) + } +} diff --git a/feature/webview/src/main/kotlin/com/titi/app/feature/webview/WebViewScreen.kt b/feature/webview/src/main/kotlin/com/titi/app/feature/webview/WebViewScreen.kt new file mode 100644 index 00000000..2b28535a --- /dev/null +++ b/feature/webview/src/main/kotlin/com/titi/app/feature/webview/WebViewScreen.kt @@ -0,0 +1,90 @@ +package com.titi.app.feature.webview + +import android.view.ViewGroup +import android.webkit.WebChromeClient +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.sp +import androidx.compose.ui.viewinterop.AndroidView +import com.titi.app.core.designsystem.R +import com.titi.app.core.designsystem.component.TdsIconButton +import com.titi.app.core.designsystem.component.TdsText +import com.titi.app.core.designsystem.theme.TdsColor +import com.titi.app.core.designsystem.theme.TdsTextStyle + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun WebViewScreen(title: String, url: String, onNavigateUp: () -> Unit) { + Scaffold( + containerColor = Color.White, + modifier = Modifier.fillMaxSize(), + topBar = { + TopAppBar( + colors = TopAppBarDefaults.topAppBarColors( + containerColor = TdsColor.GROUPED_BACKGROUND.getColor(), + ), + navigationIcon = { + TdsIconButton(onClick = onNavigateUp) { + Icon( + painter = painterResource(id = R.drawable.arrow_left_icon), + contentDescription = "back", + tint = TdsColor.TEXT.getColor(), + ) + } + }, + title = { + TdsText( + text = title, + textStyle = TdsTextStyle.EXTRA_BOLD_TEXT_STYLE, + fontSize = 24.sp, + color = TdsColor.TEXT, + ) + }, + ) + }, + ) { + WebViewScreen( + modifier = Modifier + .fillMaxSize() + .padding(it), + url = url, + ) + } +} + +@Composable +private fun WebViewScreen(modifier: Modifier, url: String) { + AndroidView( + modifier = modifier, + factory = { context -> + WebView(context).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ) + webViewClient = WebViewClient() + webChromeClient = WebChromeClient() + settings.apply { + javaScriptEnabled = true + domStorageEnabled = true + loadWithOverviewMode = true + useWideViewPort = true + } + } + }, + update = { webView -> + webView.loadUrl(url) + }, + ) +} From 2f4bc3641327121432d2b046dfaee04261763d48 Mon Sep 17 00:00:00 2001 From: koreatlwls Date: Sun, 5 May 2024 13:31:00 +0900 Subject: [PATCH 25/25] =?UTF-8?q?#82=20feat=20:=20color=20=EB=A7=9E?= =?UTF-8?q?=EC=B6=94=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/titi/app/feature/main/ui/TiTiAppState.kt | 13 ++++++++++--- .../app/feature/setting/ui/FeaturesListScreen.kt | 3 ++- .../app/feature/setting/ui/UpdatesListScreen.kt | 3 ++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/feature/main/src/main/kotlin/com/titi/app/feature/main/ui/TiTiAppState.kt b/feature/main/src/main/kotlin/com/titi/app/feature/main/ui/TiTiAppState.kt index 5ab4e70f..7efc322d 100644 --- a/feature/main/src/main/kotlin/com/titi/app/feature/main/ui/TiTiAppState.kt +++ b/feature/main/src/main/kotlin/com/titi/app/feature/main/ui/TiTiAppState.kt @@ -15,12 +15,15 @@ import com.titi.app.domain.color.usecase.GetTimeColorFlowUseCase import com.titi.app.feature.log.navigation.LOG_ROUTE import com.titi.app.feature.log.navigation.navigateToLog import com.titi.app.feature.main.navigation.TopLevelDestination +import com.titi.app.feature.setting.navigation.FEATURES_ROUTE import com.titi.app.feature.setting.navigation.SETTING_ROUTE +import com.titi.app.feature.setting.navigation.UPDATES_ROUTE import com.titi.app.feature.setting.navigation.navigateToSetting import com.titi.app.feature.time.navigation.STOPWATCH_ROUTE import com.titi.app.feature.time.navigation.TIMER_ROUTE import com.titi.app.feature.time.navigation.navigateToStopWatch import com.titi.app.feature.time.navigation.navigateToTimer +import com.titi.app.feature.webview.WEBVIEW_ROUTE import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -84,9 +87,13 @@ class TiTiAppState( when (route) { TIMER_ROUTE -> timeColor.timerBackgroundColor STOPWATCH_ROUTE -> timeColor.stopwatchBackgroundColor - LOG_ROUTE -> if (isSystemDarkTheme) 0xFF000000 else 0xFFFFFFFF - SETTING_ROUTE -> if (isSystemDarkTheme) 0xFF000000 else 0xFFFFFFFF - else -> 0xFF000000 + SETTING_ROUTE, + FEATURES_ROUTE, + UPDATES_ROUTE, + WEBVIEW_ROUTE, + -> if (isSystemDarkTheme) 0xFF000000 else 0xFFF2F2F7 + + else -> if (isSystemDarkTheme) 0xFF000000 else 0xFFFFFFFF } } .stateIn( diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt index 243d52ca..4bd8639e 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/FeaturesListScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -38,7 +39,7 @@ fun FeaturesListScreen( val uiState by viewModel.collectAsState() Scaffold( - containerColor = TdsColor.GROUPED_BACKGROUND.getColor(), + containerColor = Color.Transparent, topBar = { TopAppBar( colors = TopAppBarDefaults.topAppBarColors( diff --git a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt index 82ed1cc3..6ee051f5 100644 --- a/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt +++ b/feature/setting/src/main/kotlin/com/titi/app/feature/setting/ui/UpdatesListScreen.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -65,7 +66,7 @@ fun UpdatesListScreen(onNavigateUp: () -> Unit) { } Scaffold( - containerColor = TdsColor.GROUPED_BACKGROUND.getColor(), + containerColor = Color.Transparent, topBar = { TopAppBar( colors = TopAppBarDefaults.topAppBarColors(