From b555706dc0deb7cfb69a5aa3c2ba66584669965b Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Tue, 16 Sep 2025 19:10:07 +0900 Subject: [PATCH 1/9] =?UTF-8?q?FEAT:=20Presentation=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=20=EB=82=B4=EC=97=90=EC=84=9C=20=EB=B2=84=EC=A0=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20=EA=B0=80=EC=A0=B8=EC=98=AC=20=EB=96=84=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=A0=20VersionNameProvider=20=EB=B0=8F?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=EC=B2=B4=20=EC=A0=95=EC=9D=98=20=EB=B0=8F?= =?UTF-8?q?=20DI=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/VersionNameProviderModule.kt | 17 +++++++++++++++++ .../AndroidApplicationVersionNameProvider.kt | 14 ++++++++++++++ .../common/version/VersionNameProvider.kt | 5 +++++ 3 files changed, 36 insertions(+) create mode 100644 app/src/main/java/com/threegap/bitnagil/di/presentation/VersionNameProviderModule.kt create mode 100644 presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/AndroidApplicationVersionNameProvider.kt create mode 100644 presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/VersionNameProvider.kt diff --git a/app/src/main/java/com/threegap/bitnagil/di/presentation/VersionNameProviderModule.kt b/app/src/main/java/com/threegap/bitnagil/di/presentation/VersionNameProviderModule.kt new file mode 100644 index 00000000..2aa4fa0f --- /dev/null +++ b/app/src/main/java/com/threegap/bitnagil/di/presentation/VersionNameProviderModule.kt @@ -0,0 +1,17 @@ +package com.threegap.bitnagil.di.presentation + +import com.threegap.bitnagil.presentation.common.version.AndroidApplicationVersionNameProvider +import com.threegap.bitnagil.presentation.common.version.VersionNameProvider +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +abstract class VersionNameProviderModule { + @Binds + @Singleton + abstract fun bindVersionNameProvider(androidApplicationVersionNameProvider: AndroidApplicationVersionNameProvider): VersionNameProvider +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/AndroidApplicationVersionNameProvider.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/AndroidApplicationVersionNameProvider.kt new file mode 100644 index 00000000..8a147c47 --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/AndroidApplicationVersionNameProvider.kt @@ -0,0 +1,14 @@ +package com.threegap.bitnagil.presentation.common.version + +import com.threegap.bitnagil.presentation.BuildConfig +import javax.inject.Inject + +class AndroidApplicationVersionNameProvider @Inject constructor() : VersionNameProvider { + override fun getVersionName(): String { + val majorVersion = BuildConfig.VERSION_MAJOR + val minorVersion = BuildConfig.VERSION_MINOR + val patchVersion = BuildConfig.VERSION_PATCH + + return "$majorVersion.$minorVersion.$patchVersion" + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/VersionNameProvider.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/VersionNameProvider.kt new file mode 100644 index 00000000..1d8b7649 --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/VersionNameProvider.kt @@ -0,0 +1,5 @@ +package com.threegap.bitnagil.presentation.common.version + +interface VersionNameProvider { + fun getVersionName(): String +} From 6fae144e342cae165657edc42f437f00f34cbc01 Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Tue, 16 Sep 2025 19:13:13 +0900 Subject: [PATCH 2/9] =?UTF-8?q?FIX:=20=EC=84=A4=EC=A0=95=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=EC=84=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8/=EC=B5=9C=EC=8B=A0=20=ED=83=9C=EA=B7=B8=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EB=B0=A9=EC=8B=9D=EA=B3=BC=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=EB=90=9C=20=EB=B3=80=EC=88=98=EB=A5=BC=20=EB=B3=84=EB=8F=84?= =?UTF-8?q?=EC=9D=98=20enum=20class=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/setting/SettingScreen.kt | 42 ++++++++++++------- .../presentation/setting/SettingViewModel.kt | 5 ++- .../setting/model/VersionStateUiModel.kt | 5 +++ .../setting/model/mvi/SettingState.kt | 5 ++- 4 files changed, 39 insertions(+), 18 deletions(-) create mode 100644 presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/VersionStateUiModel.kt diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt index 6794af98..160dab7a 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt @@ -30,6 +30,7 @@ import com.threegap.bitnagil.designsystem.modifier.clickableWithoutRipple import com.threegap.bitnagil.presentation.common.flow.collectAsEffect import com.threegap.bitnagil.presentation.setting.component.atom.settingtitle.SettingTitle import com.threegap.bitnagil.presentation.setting.component.block.LogoutConfirmDialog +import com.threegap.bitnagil.presentation.setting.model.VersionStateUiModel import com.threegap.bitnagil.presentation.setting.model.mvi.SettingIntent import com.threegap.bitnagil.presentation.setting.model.mvi.SettingSideEffect import com.threegap.bitnagil.presentation.setting.model.mvi.SettingState @@ -127,19 +128,32 @@ private fun SettingScreen( ) } - val isLatest = state.version == state.latestVersion - Text( - text = if (isLatest) "최신" else "업데이트", - color = if (isLatest) BitnagilTheme.colors.coolGray70 else BitnagilTheme.colors.orange500, - style = BitnagilTheme.typography.button2, - modifier = Modifier - .background( - color = if (isLatest) BitnagilTheme.colors.coolGray98 else BitnagilTheme.colors.orange50, - shape = RoundedCornerShape(8.dp), - ) - .let { if (!isLatest) it.clickableWithoutRipple(onClick = onClickUpdate) else it } - .padding(horizontal = 10.dp, vertical = 5.dp), - ) + when(state.versionState) { + VersionStateUiModel.Latest -> Text( + text = "최신", + color = BitnagilTheme.colors.coolGray70, + style = BitnagilTheme.typography.button2, + modifier = Modifier + .background( + color = BitnagilTheme.colors.coolGray98, + shape = RoundedCornerShape(8.dp), + ) + .clickableWithoutRipple(onClick = onClickUpdate) + .padding(horizontal = 10.dp, vertical = 5.dp), + ) + VersionStateUiModel.NEED_UPDATE -> Text( + text = "업데이트", + color = BitnagilTheme.colors.orange500, + style = BitnagilTheme.typography.button2, + modifier = Modifier + .background( + color = BitnagilTheme.colors.orange50, + shape = RoundedCornerShape(8.dp), + ) + .padding(horizontal = 10.dp, vertical = 5.dp), + ) + VersionStateUiModel.NONE -> {} + } } BitnagilOptionButton( @@ -181,7 +195,7 @@ fun SettingScreenPreview() { useServiceAlarm = true, usePushAlarm = false, version = "1.0.1", - latestVersion = "1.0.0", + versionState = VersionStateUiModel.NONE, loading = false, logoutConfirmDialogVisible = false, ), diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt index 9dba8c28..74db47d1 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.threegap.bitnagil.domain.auth.usecase.LogoutUseCase import com.threegap.bitnagil.presentation.common.mviviewmodel.MviViewModel +import com.threegap.bitnagil.presentation.common.version.VersionNameProvider import com.threegap.bitnagil.presentation.setting.model.mvi.SettingIntent import com.threegap.bitnagil.presentation.setting.model.mvi.SettingSideEffect import com.threegap.bitnagil.presentation.setting.model.mvi.SettingState @@ -18,8 +19,9 @@ import javax.inject.Inject class SettingViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val logoutUseCase: LogoutUseCase, + versionNameProvider: VersionNameProvider, ) : MviViewModel( - initState = SettingState.Init, + initState = SettingState.Init.copy(version = versionNameProvider.getVersionName()), savedStateHandle = savedStateHandle, ) { private var setServiceAlarmJob: Job? = null @@ -35,7 +37,6 @@ class SettingViewModel @Inject constructor( useServiceAlarm = intent.useServiceAlarm, usePushAlarm = intent.usePushAlarm, version = intent.version, - latestVersion = intent.latestVersion, ) } SettingIntent.TogglePushAlarm -> { diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/VersionStateUiModel.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/VersionStateUiModel.kt new file mode 100644 index 00000000..2161c8a2 --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/VersionStateUiModel.kt @@ -0,0 +1,5 @@ +package com.threegap.bitnagil.presentation.setting.model + +enum class VersionStateUiModel { + Latest, NEED_UPDATE, NONE +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt index 47b30630..8eb34b44 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt @@ -1,6 +1,7 @@ package com.threegap.bitnagil.presentation.setting.model.mvi import com.threegap.bitnagil.presentation.common.mviviewmodel.MviState +import com.threegap.bitnagil.presentation.setting.model.VersionStateUiModel import kotlinx.parcelize.Parcelize @Parcelize @@ -8,7 +9,7 @@ data class SettingState( val useServiceAlarm: Boolean, val usePushAlarm: Boolean, val version: String, - val latestVersion: String, + val versionState: VersionStateUiModel, val loading: Boolean, val logoutConfirmDialogVisible: Boolean, ) : MviState { @@ -17,7 +18,7 @@ data class SettingState( useServiceAlarm = false, usePushAlarm = false, version = "", - latestVersion = "", + versionState = VersionStateUiModel.NONE, loading = false, logoutConfirmDialogVisible = false, ) From 1c1846557001e101628478aa8a125dc56a636af1 Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Tue, 23 Sep 2025 21:24:48 +0900 Subject: [PATCH 3/9] =?UTF-8?q?FIX:=20app=EC=9D=98=20applicationId?= =?UTF-8?q?=EB=A5=BC=20build-logic=20convention=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 -- .../convention/AndroidApplicationPlugin.kt | 2 ++ .../convention/AndroidLibraryPlugin.kt | 2 ++ .../convention/extension/ApplicationId.kt | 27 +++++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 build-logic/convention/src/main/java/com/threegap/bitnagil/convention/extension/ApplicationId.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1bf1f6a0..123ffceb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,8 +33,6 @@ android { } defaultConfig { - applicationId = "com.threegap.bitnagil" - val kakaoNativeAppKey = (properties["kakao.native.app.key"] as? String) ?: System.getenv("KAKAO_NATIVE_APP_KEY") diff --git a/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidApplicationPlugin.kt b/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidApplicationPlugin.kt index a70606ad..9e628f0d 100644 --- a/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidApplicationPlugin.kt +++ b/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidApplicationPlugin.kt @@ -2,6 +2,7 @@ package com.threegap.bitnagil.convention import com.android.build.api.dsl.ApplicationExtension import com.threegap.bitnagil.convention.extension.configureAppVersion +import com.threegap.bitnagil.convention.extension.configureApplicationId import com.threegap.bitnagil.convention.extension.configureComposeAndroid import com.threegap.bitnagil.convention.extension.configureKotlinAndroid import org.gradle.api.Plugin @@ -23,6 +24,7 @@ class AndroidApplicationPlugin : Plugin { configureKotlinAndroid(this) configureComposeAndroid(this) configureAppVersion() + configureApplicationId() with(defaultConfig) { targetSdk = libs.findVersion("targetSdk").get().requiredVersion.toInt() versionCode = libs.findVersion("versionCode").get().requiredVersion.toInt() diff --git a/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidLibraryPlugin.kt b/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidLibraryPlugin.kt index ddcbf9fa..b3504c54 100644 --- a/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidLibraryPlugin.kt +++ b/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidLibraryPlugin.kt @@ -2,6 +2,7 @@ package com.threegap.bitnagil.convention import com.android.build.gradle.LibraryExtension import com.threegap.bitnagil.convention.extension.configureAppVersion +import com.threegap.bitnagil.convention.extension.configureApplicationId import com.threegap.bitnagil.convention.extension.configureKotlinAndroid import com.threegap.bitnagil.convention.extension.configureKotlinCoroutine import org.gradle.api.Plugin @@ -19,6 +20,7 @@ class AndroidLibraryPlugin : Plugin { configureKotlinAndroid(this) configureKotlinCoroutine(this) configureAppVersion() + configureApplicationId() } } } diff --git a/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/extension/ApplicationId.kt b/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/extension/ApplicationId.kt new file mode 100644 index 00000000..dd06bf50 --- /dev/null +++ b/build-logic/convention/src/main/java/com/threegap/bitnagil/convention/extension/ApplicationId.kt @@ -0,0 +1,27 @@ +package com.threegap.bitnagil.convention.extension + +import com.android.build.api.dsl.ApplicationExtension +import com.android.build.api.dsl.LibraryExtension +import org.gradle.api.Project +import org.gradle.kotlin.dsl.findByType + +internal fun Project.configureApplicationId() { + val applicationId = "com.threegap.bitnagil" + + extensions.findByType()?.let { appExtension -> + appExtension.defaultConfig { + this.applicationId = applicationId + } + } + + extensions.findByType()?.let { libExtension -> + libExtension.apply { + defaultConfig { + buildConfigField("String", "APPLICATION_ID", "\"$applicationId\"") + } + buildFeatures { + buildConfig = true + } + } + } +} From 6c2408b7a5336272d24482a295215354fb8d906a Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Tue, 23 Sep 2025 21:27:28 +0900 Subject: [PATCH 4/9] =?UTF-8?q?FIX:=20PlayStoreUtils=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=9D=84=20splash/utils=EC=97=90=EC=84=9C=20common/utils?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20=EB=B0=8F=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=ED=99=94=EB=A9=B4=EB=82=B4=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EB=B2=84=ED=8A=BC=EC=97=90=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=ED=86=A0=EC=96=B4=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util => common/playstore}/PlayStoreUtils.kt | 5 +++-- .../bitnagil/presentation/setting/SettingScreen.kt | 11 ++++++++--- .../bitnagil/presentation/splash/SplashScreen.kt | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) rename presentation/src/main/java/com/threegap/bitnagil/presentation/{splash/util => common/playstore}/PlayStoreUtils.kt (91%) diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/splash/util/PlayStoreUtils.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt similarity index 91% rename from presentation/src/main/java/com/threegap/bitnagil/presentation/splash/util/PlayStoreUtils.kt rename to presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt index 8b60652a..1caeb9ca 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/splash/util/PlayStoreUtils.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt @@ -1,15 +1,16 @@ -package com.threegap.bitnagil.presentation.splash.util +package com.threegap.bitnagil.presentation.common.playstore import android.content.ActivityNotFoundException import android.content.Intent import androidx.activity.ComponentActivity import androidx.core.net.toUri import androidx.lifecycle.lifecycleScope +import com.threegap.bitnagil.presentation.BuildConfig import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlin.system.exitProcess -private const val PACKAGE_NAME = "com.threegap.bitnagil" +private const val PACKAGE_NAME = BuildConfig.APPLICATION_ID private const val GOOGLE_PLAY_PACKAGE = "com.android.vending" private const val APP_EXIT_DELAY = 500L diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt index 160dab7a..6edf8623 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt @@ -1,5 +1,6 @@ package com.threegap.bitnagil.presentation.setting +import androidx.activity.ComponentActivity import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -20,6 +21,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -28,6 +30,7 @@ import com.threegap.bitnagil.designsystem.component.block.BitnagilOptionButton import com.threegap.bitnagil.designsystem.component.block.BitnagilTopBar import com.threegap.bitnagil.designsystem.modifier.clickableWithoutRipple import com.threegap.bitnagil.presentation.common.flow.collectAsEffect +import com.threegap.bitnagil.presentation.common.playstore.openAppInPlayStore import com.threegap.bitnagil.presentation.setting.component.atom.settingtitle.SettingTitle import com.threegap.bitnagil.presentation.setting.component.block.LogoutConfirmDialog import com.threegap.bitnagil.presentation.setting.model.VersionStateUiModel @@ -44,6 +47,8 @@ fun SettingScreenContainer( navigateToLogin: () -> Unit, navigateToWithdrawal: () -> Unit, ) { + val context = LocalContext.current + val activity = context as? ComponentActivity val state by viewModel.stateFlow.collectAsState() viewModel.sideEffectFlow.collectAsEffect { sideEffect -> @@ -64,7 +69,7 @@ fun SettingScreenContainer( state = state, toggleServiceAlarm = viewModel::toggleServiceAlarm, togglePushAlarm = viewModel::togglePushAlarm, - onClickUpdate = {}, + onClickUpdate = { openAppInPlayStore(activity = activity, shouldFinishApp = false) }, onClickBack = navigateToBack, onClickTermsOfService = navigateToTermsOfService, onClickPrivacyPolicy = navigateToPrivacyPolicy, @@ -128,7 +133,7 @@ private fun SettingScreen( ) } - when(state.versionState) { + when (state.versionState) { VersionStateUiModel.Latest -> Text( text = "최신", color = BitnagilTheme.colors.coolGray70, @@ -138,7 +143,6 @@ private fun SettingScreen( color = BitnagilTheme.colors.coolGray98, shape = RoundedCornerShape(8.dp), ) - .clickableWithoutRipple(onClick = onClickUpdate) .padding(horizontal = 10.dp, vertical = 5.dp), ) VersionStateUiModel.NEED_UPDATE -> Text( @@ -150,6 +154,7 @@ private fun SettingScreen( color = BitnagilTheme.colors.orange50, shape = RoundedCornerShape(8.dp), ) + .clickableWithoutRipple(onClick = onClickUpdate) .padding(horizontal = 10.dp, vertical = 5.dp), ) VersionStateUiModel.NONE -> {} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/splash/SplashScreen.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/splash/SplashScreen.kt index 5801baa4..2586d595 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/splash/SplashScreen.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/splash/SplashScreen.kt @@ -25,10 +25,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.threegap.bitnagil.designsystem.BitnagilTheme import com.threegap.bitnagil.designsystem.R import com.threegap.bitnagil.designsystem.component.atom.BitnagilIcon +import com.threegap.bitnagil.presentation.common.playstore.openAppInPlayStore import com.threegap.bitnagil.presentation.splash.component.template.BitnagilLottieAnimation import com.threegap.bitnagil.presentation.splash.component.template.ForceUpdateDialog import com.threegap.bitnagil.presentation.splash.model.SplashSideEffect -import com.threegap.bitnagil.presentation.splash.util.openAppInPlayStore import org.orbitmvi.orbit.compose.collectSideEffect import kotlin.system.exitProcess From fb7f036fe10be2a177b60184289a0e5864085be8 Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Mon, 1 Dec 2025 21:40:23 +0900 Subject: [PATCH 5/9] =?UTF-8?q?Feat:=20app-update=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EC=96=B4=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/libs.versions.toml | 12 ++++++ presentation/build.gradle.kts | 1 + .../common/playstore/PlayStoreUtils.kt | 42 +++++++++++++++++++ .../common/playstore/UpdateAvailableState.kt | 5 +++ .../presentation/setting/SettingScreen.kt | 16 ++++--- .../setting/model/VersionStateUiModel.kt | 5 --- .../setting/model/mvi/SettingState.kt | 3 -- 7 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt delete mode 100644 presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/VersionStateUiModel.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 44f52366..d239aed8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,6 +48,9 @@ junit = "4.13.2" junitVersion = "1.2.1" espressoCore = "3.6.1" +## app-update +app-update = "2.1.0" + ## Other material = "1.12.0" orbit = "6.1.0" @@ -122,6 +125,10 @@ kotlin-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coro coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" } coil-network = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" } +## app-update +app-update = { group = "com.google.android.play", name = "app-update", version.ref = "app-update" } +app-update-ktx = { group = "com.google.android.play", name = "app-update-ktx", version.ref = "app-update" } + ## Other material = { group = "com.google.android.material", name = "material", version.ref = "material" } kakao-v2-user = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakaoLogin" } @@ -183,6 +190,11 @@ coil = [ "coil-network" ] +app-update = [ + "app-update", + "app-update-ktx" +] + [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index 6f8d0b1b..1a3bcd15 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { implementation(libs.kotlinx.serialization.json) implementation(libs.lottie.compose) implementation(libs.bundles.coil) + implementation(libs.bundles.app.update) testImplementation(libs.junit) testImplementation(libs.kotlin.coroutines.test) diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt index 1caeb9ca..e3330793 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt @@ -1,13 +1,26 @@ package com.threegap.bitnagil.presentation.common.playstore import android.content.ActivityNotFoundException +import android.content.Context import android.content.Intent import androidx.activity.ComponentActivity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext import androidx.core.net.toUri import androidx.lifecycle.lifecycleScope +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.UpdateAvailability import com.threegap.bitnagil.presentation.BuildConfig import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine import kotlin.system.exitProcess private const val PACKAGE_NAME = BuildConfig.APPLICATION_ID @@ -69,3 +82,32 @@ private fun finishAppWithDelay(activity: ComponentActivity) { exitProcess(0) } } + +@Composable +fun updateAvailable(): UpdateAvailableState { + val context = LocalContext.current + var isUpdateAvailable by remember { mutableStateOf(UpdateAvailableState.NONE) } + + LaunchedEffect(Unit) { + isUpdateAvailable = context.checkForUpdateAvailability() + } + + return isUpdateAvailable +} + +private suspend fun Context.checkForUpdateAvailability(): UpdateAvailableState = suspendCoroutine { continuation -> + val appUpdateManager = AppUpdateManagerFactory.create(this) + val appUpdateInfoTask = appUpdateManager.appUpdateInfo + + appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + val isAvailable = appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && + appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + + val state = if (isAvailable) UpdateAvailableState.NEED_UPDATE else UpdateAvailableState.Latest + continuation.resume(state) + } + + appUpdateInfoTask.addOnFailureListener { + continuation.resume(UpdateAvailableState.NONE) + } +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt new file mode 100644 index 00000000..d245b952 --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt @@ -0,0 +1,5 @@ +package com.threegap.bitnagil.presentation.common.playstore + +enum class UpdateAvailableState { + Latest, NEED_UPDATE, NONE +} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt index 6edf8623..ea248f1d 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt @@ -30,10 +30,11 @@ import com.threegap.bitnagil.designsystem.component.block.BitnagilOptionButton import com.threegap.bitnagil.designsystem.component.block.BitnagilTopBar import com.threegap.bitnagil.designsystem.modifier.clickableWithoutRipple import com.threegap.bitnagil.presentation.common.flow.collectAsEffect +import com.threegap.bitnagil.presentation.common.playstore.UpdateAvailableState import com.threegap.bitnagil.presentation.common.playstore.openAppInPlayStore +import com.threegap.bitnagil.presentation.common.playstore.updateAvailable import com.threegap.bitnagil.presentation.setting.component.atom.settingtitle.SettingTitle import com.threegap.bitnagil.presentation.setting.component.block.LogoutConfirmDialog -import com.threegap.bitnagil.presentation.setting.model.VersionStateUiModel import com.threegap.bitnagil.presentation.setting.model.mvi.SettingIntent import com.threegap.bitnagil.presentation.setting.model.mvi.SettingSideEffect import com.threegap.bitnagil.presentation.setting.model.mvi.SettingState @@ -50,6 +51,7 @@ fun SettingScreenContainer( val context = LocalContext.current val activity = context as? ComponentActivity val state by viewModel.stateFlow.collectAsState() + val updateAvailableState = updateAvailable() viewModel.sideEffectFlow.collectAsEffect { sideEffect -> when (sideEffect) { @@ -67,6 +69,7 @@ fun SettingScreenContainer( SettingScreen( state = state, + updateAvailableState = updateAvailableState, toggleServiceAlarm = viewModel::toggleServiceAlarm, togglePushAlarm = viewModel::togglePushAlarm, onClickUpdate = { openAppInPlayStore(activity = activity, shouldFinishApp = false) }, @@ -81,6 +84,7 @@ fun SettingScreenContainer( @Composable private fun SettingScreen( state: SettingState, + updateAvailableState: UpdateAvailableState, toggleServiceAlarm: () -> Unit, togglePushAlarm: () -> Unit, onClickUpdate: () -> Unit, @@ -133,8 +137,8 @@ private fun SettingScreen( ) } - when (state.versionState) { - VersionStateUiModel.Latest -> Text( + when (updateAvailableState) { + UpdateAvailableState.Latest -> Text( text = "최신", color = BitnagilTheme.colors.coolGray70, style = BitnagilTheme.typography.button2, @@ -145,7 +149,7 @@ private fun SettingScreen( ) .padding(horizontal = 10.dp, vertical = 5.dp), ) - VersionStateUiModel.NEED_UPDATE -> Text( + UpdateAvailableState.NEED_UPDATE -> Text( text = "업데이트", color = BitnagilTheme.colors.orange500, style = BitnagilTheme.typography.button2, @@ -157,7 +161,7 @@ private fun SettingScreen( .clickableWithoutRipple(onClick = onClickUpdate) .padding(horizontal = 10.dp, vertical = 5.dp), ) - VersionStateUiModel.NONE -> {} + UpdateAvailableState.NONE -> {} } } @@ -200,10 +204,10 @@ fun SettingScreenPreview() { useServiceAlarm = true, usePushAlarm = false, version = "1.0.1", - versionState = VersionStateUiModel.NONE, loading = false, logoutConfirmDialogVisible = false, ), + updateAvailableState = UpdateAvailableState.Latest, toggleServiceAlarm = {}, togglePushAlarm = {}, onClickUpdate = {}, diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/VersionStateUiModel.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/VersionStateUiModel.kt deleted file mode 100644 index 2161c8a2..00000000 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/VersionStateUiModel.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.threegap.bitnagil.presentation.setting.model - -enum class VersionStateUiModel { - Latest, NEED_UPDATE, NONE -} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt index 8eb34b44..0cddea5d 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt @@ -1,7 +1,6 @@ package com.threegap.bitnagil.presentation.setting.model.mvi import com.threegap.bitnagil.presentation.common.mviviewmodel.MviState -import com.threegap.bitnagil.presentation.setting.model.VersionStateUiModel import kotlinx.parcelize.Parcelize @Parcelize @@ -9,7 +8,6 @@ data class SettingState( val useServiceAlarm: Boolean, val usePushAlarm: Boolean, val version: String, - val versionState: VersionStateUiModel, val loading: Boolean, val logoutConfirmDialogVisible: Boolean, ) : MviState { @@ -18,7 +16,6 @@ data class SettingState( useServiceAlarm = false, usePushAlarm = false, version = "", - versionState = VersionStateUiModel.NONE, loading = false, logoutConfirmDialogVisible = false, ) From 7318fffb10aa81121f5f774d1170bc5e073f42e4 Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Mon, 1 Dec 2025 21:52:38 +0900 Subject: [PATCH 6/9] =?UTF-8?q?Refactor:=20SettingViewModel=EC=97=90?= =?UTF-8?q?=EC=84=9C=20MviViewModel=20=EA=B5=AC=ED=98=84=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20orbit=20ContainerHost=20=EC=A7=81?= =?UTF-8?q?=EC=A0=91=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/setting/SettingScreen.kt | 11 +- .../presentation/setting/SettingViewModel.kt | 110 +++++++----------- .../setting/model/mvi/SettingIntent.kt | 21 ---- .../setting/model/mvi/SettingState.kt | 4 +- 4 files changed, 52 insertions(+), 94 deletions(-) delete mode 100644 presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingIntent.kt diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt index ea248f1d..a4666baa 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -29,15 +28,15 @@ import com.threegap.bitnagil.designsystem.BitnagilTheme import com.threegap.bitnagil.designsystem.component.block.BitnagilOptionButton import com.threegap.bitnagil.designsystem.component.block.BitnagilTopBar import com.threegap.bitnagil.designsystem.modifier.clickableWithoutRipple -import com.threegap.bitnagil.presentation.common.flow.collectAsEffect import com.threegap.bitnagil.presentation.common.playstore.UpdateAvailableState import com.threegap.bitnagil.presentation.common.playstore.openAppInPlayStore import com.threegap.bitnagil.presentation.common.playstore.updateAvailable import com.threegap.bitnagil.presentation.setting.component.atom.settingtitle.SettingTitle import com.threegap.bitnagil.presentation.setting.component.block.LogoutConfirmDialog -import com.threegap.bitnagil.presentation.setting.model.mvi.SettingIntent import com.threegap.bitnagil.presentation.setting.model.mvi.SettingSideEffect import com.threegap.bitnagil.presentation.setting.model.mvi.SettingState +import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect @Composable fun SettingScreenContainer( @@ -50,10 +49,10 @@ fun SettingScreenContainer( ) { val context = LocalContext.current val activity = context as? ComponentActivity - val state by viewModel.stateFlow.collectAsState() + val state by viewModel.collectAsState() val updateAvailableState = updateAvailable() - viewModel.sideEffectFlow.collectAsEffect { sideEffect -> + viewModel.collectSideEffect { sideEffect -> when (sideEffect) { SettingSideEffect.NavigateToLogin -> navigateToLogin() SettingSideEffect.NavigateToWithdrawal -> navigateToWithdrawal() @@ -77,7 +76,7 @@ fun SettingScreenContainer( onClickTermsOfService = navigateToTermsOfService, onClickPrivacyPolicy = navigateToPrivacyPolicy, onClickLogout = viewModel::showLogoutDialog, - onClickWithdrawal = { viewModel.sendIntent(SettingIntent.OnWithdrawalClick) }, + onClickWithdrawal = viewModel::navigateToWithdrawal, ) } diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt index 74db47d1..bc7cd065 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt @@ -1,18 +1,22 @@ package com.threegap.bitnagil.presentation.setting import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.threegap.bitnagil.domain.auth.usecase.LogoutUseCase -import com.threegap.bitnagil.presentation.common.mviviewmodel.MviViewModel import com.threegap.bitnagil.presentation.common.version.VersionNameProvider -import com.threegap.bitnagil.presentation.setting.model.mvi.SettingIntent import com.threegap.bitnagil.presentation.setting.model.mvi.SettingSideEffect import com.threegap.bitnagil.presentation.setting.model.mvi.SettingState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import org.orbitmvi.orbit.syntax.simple.SimpleSyntax +import org.orbitmvi.orbit.Container +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect +import org.orbitmvi.orbit.syntax.simple.reduce +import org.orbitmvi.orbit.viewmodel.container import javax.inject.Inject @HiltViewModel @@ -20,98 +24,74 @@ class SettingViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val logoutUseCase: LogoutUseCase, versionNameProvider: VersionNameProvider, -) : MviViewModel( - initState = SettingState.Init.copy(version = versionNameProvider.getVersionName()), - savedStateHandle = savedStateHandle, -) { +) : ContainerHost, ViewModel() { private var setServiceAlarmJob: Job? = null private var setPushAlarmJob: Job? = null - override suspend fun SimpleSyntax.reduceState( - intent: SettingIntent, - state: SettingState, - ): SettingState? { - when (intent) { - is SettingIntent.LoadSettingSuccess -> { - return state.copy( - useServiceAlarm = intent.useServiceAlarm, - usePushAlarm = intent.usePushAlarm, - version = intent.version, - ) - } - SettingIntent.TogglePushAlarm -> { - return state.copy(usePushAlarm = !state.usePushAlarm) - } - SettingIntent.ToggleServiceAlarm -> { - return state.copy(useServiceAlarm = !state.useServiceAlarm) - } + override val container: Container = container( + initialState = SettingState.Init.copy(version = versionNameProvider.getVersionName()), + savedStateHandle = savedStateHandle, + ) - is SettingIntent.ShowLogoutConfirmDialog -> { - return state.copy(logoutConfirmDialogVisible = true) - } - - is SettingIntent.HideConfirmDialog -> { - return state.copy(logoutConfirmDialogVisible = false) - } - - SettingIntent.LogoutSuccess -> { - sendSideEffect(SettingSideEffect.NavigateToLogin) - return null - } - - SettingIntent.LogoutLoading -> { - return state.copy(loading = true) - } - - SettingIntent.LogoutFailure -> { - return state.copy(loading = false) - } - - SettingIntent.OnWithdrawalClick -> { - sendSideEffect(SettingSideEffect.NavigateToWithdrawal) - return null - } + fun showLogoutDialog() = intent { + reduce { + state.copy(logoutConfirmDialogVisible = true) } } - fun showLogoutDialog() { - sendIntent(SettingIntent.ShowLogoutConfirmDialog) + fun hideConfirmDialog() = intent { + reduce { + state.copy(logoutConfirmDialogVisible = false) + } } - fun hideConfirmDialog() { - sendIntent(SettingIntent.HideConfirmDialog) - } + fun logout() = intent { + if (container.stateFlow.value.loading) return@intent - fun logout() { - if (container.stateFlow.value.loading) return + reduce { + state.copy( + logoutConfirmDialogVisible = false, + loading = true, + ) + } - sendIntent(SettingIntent.HideConfirmDialog) - sendIntent(SettingIntent.LogoutLoading) viewModelScope.launch { logoutUseCase().fold( onSuccess = { - sendIntent(SettingIntent.LogoutSuccess) + postSideEffect(SettingSideEffect.NavigateToLogin) }, onFailure = { - sendIntent(SettingIntent.LogoutFailure) + reduce { + state.copy(loading = false) + } }, ) } } - fun toggleServiceAlarm() { - sendIntent(SettingIntent.ToggleServiceAlarm) + fun toggleServiceAlarm() = intent { + reduce { + state.copy(useServiceAlarm = !state.useServiceAlarm) + } + setServiceAlarmJob?.cancel() setServiceAlarmJob = viewModelScope.launch { delay(1000L) } } - fun togglePushAlarm() { - sendIntent(SettingIntent.TogglePushAlarm) + fun togglePushAlarm() = intent { + reduce { + state.copy(usePushAlarm = !state.usePushAlarm) + } + setPushAlarmJob?.cancel() setPushAlarmJob = viewModelScope.launch { delay(1000L) } } + + fun navigateToWithdrawal() = intent { + postSideEffect(SettingSideEffect.NavigateToWithdrawal) + } } diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingIntent.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingIntent.kt deleted file mode 100644 index 97b77f65..00000000 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingIntent.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.threegap.bitnagil.presentation.setting.model.mvi - -import com.threegap.bitnagil.presentation.common.mviviewmodel.MviIntent - -sealed class SettingIntent : MviIntent { - data class LoadSettingSuccess( - val useServiceAlarm: Boolean, - val usePushAlarm: Boolean, - val version: String, - val latestVersion: String, - ) : SettingIntent() - - data object ShowLogoutConfirmDialog : SettingIntent() - data object HideConfirmDialog : SettingIntent() - data object ToggleServiceAlarm : SettingIntent() - data object TogglePushAlarm : SettingIntent() - data object LogoutLoading : SettingIntent() - data object LogoutSuccess : SettingIntent() - data object LogoutFailure : SettingIntent() - data object OnWithdrawalClick : SettingIntent() -} diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt index 0cddea5d..aa29a342 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt @@ -1,6 +1,6 @@ package com.threegap.bitnagil.presentation.setting.model.mvi -import com.threegap.bitnagil.presentation.common.mviviewmodel.MviState +import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize @@ -10,7 +10,7 @@ data class SettingState( val version: String, val loading: Boolean, val logoutConfirmDialogVisible: Boolean, -) : MviState { +) : Parcelable { companion object { val Init = SettingState( useServiceAlarm = false, From 0ae59c224618c1a34025774937f6ef05254e0b4c Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Wed, 3 Dec 2025 18:55:51 +0900 Subject: [PATCH 7/9] =?UTF-8?q?Chore:=20=EB=B3=91=ED=95=A9=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=95=EC=97=90=EC=84=9C=20=EC=A0=9C=EA=B1=B0=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EB=AA=BB=ED=95=9C=20import=EB=AC=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../threegap/bitnagil/presentation/setting/SettingScreen.kt | 2 -- .../threegap/bitnagil/presentation/setting/SettingViewModel.kt | 3 --- 2 files changed, 5 deletions(-) diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt index c07eb370..e2a6d19f 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt @@ -33,8 +33,6 @@ import com.threegap.bitnagil.presentation.common.playstore.UpdateAvailableState import com.threegap.bitnagil.presentation.common.playstore.openAppInPlayStore import com.threegap.bitnagil.presentation.common.playstore.updateAvailable import com.threegap.bitnagil.presentation.setting.component.atom.settingtitle.SettingTitle -import com.threegap.bitnagil.presentation.setting.model.mvi.SettingIntent -import com.threegap.bitnagil.presentation.setting.component.block.LogoutConfirmDialog import com.threegap.bitnagil.presentation.setting.model.mvi.SettingSideEffect import com.threegap.bitnagil.presentation.setting.model.mvi.SettingState import org.orbitmvi.orbit.compose.collectAsState diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt index bc7cd065..7c4e91e2 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt @@ -13,9 +13,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.orbitmvi.orbit.Container import org.orbitmvi.orbit.ContainerHost -import org.orbitmvi.orbit.syntax.simple.intent -import org.orbitmvi.orbit.syntax.simple.postSideEffect -import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container import javax.inject.Inject From 814b10bdb00c5954633f5db348e10e7d85ea92a2 Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Wed, 3 Dec 2025 19:21:14 +0900 Subject: [PATCH 8/9] =?UTF-8?q?Fix:=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90=EC=84=9C=20suspendCorou?= =?UTF-8?q?tine=20=EB=8C=80=EC=8B=A0=20suspendCancellableCoroutine=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9,=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=8C=90=EB=8B=A8=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/playstore/PlayStoreUtils.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt index e3330793..f7c6f434 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt @@ -19,8 +19,8 @@ import com.google.android.play.core.install.model.UpdateAvailability import com.threegap.bitnagil.presentation.BuildConfig import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine import kotlin.system.exitProcess private const val PACKAGE_NAME = BuildConfig.APPLICATION_ID @@ -95,19 +95,25 @@ fun updateAvailable(): UpdateAvailableState { return isUpdateAvailable } -private suspend fun Context.checkForUpdateAvailability(): UpdateAvailableState = suspendCoroutine { continuation -> +private suspend fun Context.checkForUpdateAvailability(): UpdateAvailableState = suspendCancellableCoroutine { continuation -> val appUpdateManager = AppUpdateManagerFactory.create(this) val appUpdateInfoTask = appUpdateManager.appUpdateInfo appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> - val isAvailable = appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && - appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + if (!continuation.isActive) return@addOnSuccessListener - val state = if (isAvailable) UpdateAvailableState.NEED_UPDATE else UpdateAvailableState.Latest + val isAvailable = appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE + + val isAllowed = appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) || + appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) + + val state = if (isAvailable && isAllowed) UpdateAvailableState.NEED_UPDATE else UpdateAvailableState.Latest continuation.resume(state) } appUpdateInfoTask.addOnFailureListener { - continuation.resume(UpdateAvailableState.NONE) + if (continuation.isActive) { + continuation.resume(UpdateAvailableState.NONE) + } } } From 94a2d2714bb42924187b06bd86e5b232ef0cfb0e Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Thu, 4 Dec 2025 19:01:18 +0900 Subject: [PATCH 9/9] =?UTF-8?q?Chore:=20UpdateAvailableState=EC=9D=98=20La?= =?UTF-8?q?test=EB=A5=BC=20=EB=8C=80=EB=AC=B8=EC=9E=90=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bitnagil/presentation/common/playstore/PlayStoreUtils.kt | 2 +- .../presentation/common/playstore/UpdateAvailableState.kt | 2 +- .../threegap/bitnagil/presentation/setting/SettingScreen.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt index f7c6f434..3a12e2c9 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt @@ -107,7 +107,7 @@ private suspend fun Context.checkForUpdateAvailability(): UpdateAvailableState = val isAllowed = appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) || appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) - val state = if (isAvailable && isAllowed) UpdateAvailableState.NEED_UPDATE else UpdateAvailableState.Latest + val state = if (isAvailable && isAllowed) UpdateAvailableState.NEED_UPDATE else UpdateAvailableState.LATEST continuation.resume(state) } diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt index d245b952..59b633c7 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt @@ -1,5 +1,5 @@ package com.threegap.bitnagil.presentation.common.playstore enum class UpdateAvailableState { - Latest, NEED_UPDATE, NONE + LATEST, NEED_UPDATE, NONE } diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt index e2a6d19f..5b69cd58 100644 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt @@ -141,7 +141,7 @@ private fun SettingScreen( } when (updateAvailableState) { - UpdateAvailableState.Latest -> Text( + UpdateAvailableState.LATEST -> Text( text = "최신", color = BitnagilTheme.colors.coolGray70, style = BitnagilTheme.typography.button2, @@ -210,7 +210,7 @@ fun SettingScreenPreview() { loading = false, logoutConfirmDialogVisible = false, ), - updateAvailableState = UpdateAvailableState.Latest, + updateAvailableState = UpdateAvailableState.LATEST, toggleServiceAlarm = {}, togglePushAlarm = {}, onClickUpdate = {},