diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2519d233..92520175 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/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/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 + } + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4384ec3b..b11bd079 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 = "10.0.0" @@ -125,6 +128,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" } @@ -190,6 +197,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 2e5a2663..0c51effa 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(libs.lottie.compose) implementation(libs.bundles.coil) implementation(libs.accompanist.permissions) + 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 new file mode 100644 index 00000000..3a12e2c9 --- /dev/null +++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt @@ -0,0 +1,119 @@ +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 kotlinx.coroutines.suspendCancellableCoroutine +import kotlin.coroutines.resume +import kotlin.system.exitProcess + +private const val PACKAGE_NAME = BuildConfig.APPLICATION_ID +private const val GOOGLE_PLAY_PACKAGE = "com.android.vending" +private const val APP_EXIT_DELAY = 500L + +fun openAppInPlayStore( + activity: ComponentActivity?, + shouldFinishApp: Boolean = true, +) { + activity?.let { + val isSuccess = tryOpenPlayStore(it) || tryOpenWebBrowser(it) + + if (isSuccess && shouldFinishApp) { + finishAppWithDelay(it) + } + } +} + +private fun tryOpenPlayStore(activity: ComponentActivity): Boolean = + try { + val intent = createPlayStoreIntent() + activity.startActivity(intent) + true + } catch (e: ActivityNotFoundException) { + false + } + +private fun tryOpenWebBrowser(activity: ComponentActivity): Boolean = + try { + val intent = createWebIntent() + activity.startActivity(intent) + true + } catch (e: Exception) { + false + } + +private fun createPlayStoreIntent(): Intent = + Intent( + Intent.ACTION_VIEW, + "market://details?id=$PACKAGE_NAME".toUri(), + ).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + setPackage(GOOGLE_PLAY_PACKAGE) + } + +private fun createWebIntent(): Intent = + Intent( + Intent.ACTION_VIEW, + "https://play.google.com/store/apps/details?id=$PACKAGE_NAME".toUri(), + ).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + +private fun finishAppWithDelay(activity: ComponentActivity) { + activity.lifecycleScope.launch { + delay(APP_EXIT_DELAY) + activity.finishAffinity() + 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 = suspendCancellableCoroutine { continuation -> + val appUpdateManager = AppUpdateManagerFactory.create(this) + val appUpdateInfoTask = appUpdateManager.appUpdateInfo + + appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + if (!continuation.isActive) return@addOnSuccessListener + + 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 { + if (continuation.isActive) { + 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..59b633c7 --- /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/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 +} 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 342b36f1..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 @@ -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 @@ -16,10 +17,10 @@ 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 +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,11 +29,14 @@ import com.threegap.bitnagil.designsystem.component.block.BitnagilAlertDialog 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.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( @@ -43,9 +47,12 @@ fun SettingScreenContainer( navigateToLogin: () -> Unit, navigateToWithdrawal: () -> Unit, ) { - val state by viewModel.stateFlow.collectAsState() + val context = LocalContext.current + val activity = context as? ComponentActivity + val state by viewModel.collectAsState() + val updateAvailableState = updateAvailable() - viewModel.sideEffectFlow.collectAsEffect { sideEffect -> + viewModel.collectSideEffect { sideEffect -> when (sideEffect) { SettingSideEffect.NavigateToLogin -> navigateToLogin() SettingSideEffect.NavigateToWithdrawal -> navigateToWithdrawal() @@ -65,20 +72,22 @@ fun SettingScreenContainer( SettingScreen( state = state, + updateAvailableState = updateAvailableState, toggleServiceAlarm = viewModel::toggleServiceAlarm, togglePushAlarm = viewModel::togglePushAlarm, - onClickUpdate = {}, + onClickUpdate = { openAppInPlayStore(activity = activity, shouldFinishApp = false) }, onClickBack = navigateToBack, onClickTermsOfService = navigateToTermsOfService, onClickPrivacyPolicy = navigateToPrivacyPolicy, onClickLogout = viewModel::showLogoutDialog, - onClickWithdrawal = { viewModel.sendIntent(SettingIntent.OnWithdrawalClick) }, + onClickWithdrawal = viewModel::navigateToWithdrawal, ) } @Composable private fun SettingScreen( state: SettingState, + updateAvailableState: UpdateAvailableState, toggleServiceAlarm: () -> Unit, togglePushAlarm: () -> Unit, onClickUpdate: () -> Unit, @@ -131,19 +140,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 (updateAvailableState) { + UpdateAvailableState.LATEST -> Text( + text = "최신", + color = BitnagilTheme.colors.coolGray70, + style = BitnagilTheme.typography.button2, + modifier = Modifier + .background( + color = BitnagilTheme.colors.coolGray98, + shape = RoundedCornerShape(8.dp), + ) + .padding(horizontal = 10.dp, vertical = 5.dp), + ) + UpdateAvailableState.NEED_UPDATE -> Text( + text = "업데이트", + color = BitnagilTheme.colors.orange500, + style = BitnagilTheme.typography.button2, + modifier = Modifier + .background( + color = BitnagilTheme.colors.orange50, + shape = RoundedCornerShape(8.dp), + ) + .clickableWithoutRipple(onClick = onClickUpdate) + .padding(horizontal = 10.dp, vertical = 5.dp), + ) + UpdateAvailableState.NONE -> {} + } } BitnagilOptionButton( @@ -185,10 +207,10 @@ fun SettingScreenPreview() { useServiceAlarm = true, usePushAlarm = false, version = "1.0.1", - latestVersion = "1.0.0", loading = false, logoutConfirmDialogVisible = false, ), + updateAvailableState = UpdateAvailableState.LATEST, toggleServiceAlarm = {}, togglePushAlarm = {}, onClickUpdate = {}, 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 2f2fbce4..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 @@ -1,116 +1,94 @@ 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.setting.model.mvi.SettingIntent +import com.threegap.bitnagil.presentation.common.version.VersionNameProvider 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.Syntax +import org.orbitmvi.orbit.Container +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.viewmodel.container import javax.inject.Inject @HiltViewModel class SettingViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val logoutUseCase: LogoutUseCase, -) : MviViewModel( - initState = SettingState.Init, - savedStateHandle = savedStateHandle, -) { + versionNameProvider: VersionNameProvider, +) : ContainerHost, ViewModel() { private var setServiceAlarmJob: Job? = null private var setPushAlarmJob: Job? = null - override suspend fun Syntax.reduceState( - intent: SettingIntent, - state: SettingState, - ): SettingState? { - when (intent) { - is SettingIntent.LoadSettingSuccess -> { - return state.copy( - useServiceAlarm = intent.useServiceAlarm, - usePushAlarm = intent.usePushAlarm, - version = intent.version, - latestVersion = intent.latestVersion, - ) - } - 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 47b30630..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 @@ -8,16 +8,14 @@ data class SettingState( val useServiceAlarm: Boolean, val usePushAlarm: Boolean, val version: String, - val latestVersion: String, val loading: Boolean, val logoutConfirmDialogVisible: Boolean, -) : MviState { +) : Parcelable { companion object { val Init = SettingState( useServiceAlarm = false, usePushAlarm = false, version = "", - latestVersion = "", loading = false, logoutConfirmDialogVisible = false, ) 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 diff --git a/presentation/src/main/java/com/threegap/bitnagil/presentation/splash/util/PlayStoreUtils.kt b/presentation/src/main/java/com/threegap/bitnagil/presentation/splash/util/PlayStoreUtils.kt deleted file mode 100644 index 8b60652a..00000000 --- a/presentation/src/main/java/com/threegap/bitnagil/presentation/splash/util/PlayStoreUtils.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.threegap.bitnagil.presentation.splash.util - -import android.content.ActivityNotFoundException -import android.content.Intent -import androidx.activity.ComponentActivity -import androidx.core.net.toUri -import androidx.lifecycle.lifecycleScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlin.system.exitProcess - -private const val PACKAGE_NAME = "com.threegap.bitnagil" -private const val GOOGLE_PLAY_PACKAGE = "com.android.vending" -private const val APP_EXIT_DELAY = 500L - -fun openAppInPlayStore( - activity: ComponentActivity?, - shouldFinishApp: Boolean = true, -) { - activity?.let { - val isSuccess = tryOpenPlayStore(it) || tryOpenWebBrowser(it) - - if (isSuccess && shouldFinishApp) { - finishAppWithDelay(it) - } - } -} - -private fun tryOpenPlayStore(activity: ComponentActivity): Boolean = - try { - val intent = createPlayStoreIntent() - activity.startActivity(intent) - true - } catch (e: ActivityNotFoundException) { - false - } - -private fun tryOpenWebBrowser(activity: ComponentActivity): Boolean = - try { - val intent = createWebIntent() - activity.startActivity(intent) - true - } catch (e: Exception) { - false - } - -private fun createPlayStoreIntent(): Intent = - Intent( - Intent.ACTION_VIEW, - "market://details?id=$PACKAGE_NAME".toUri(), - ).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - setPackage(GOOGLE_PLAY_PACKAGE) - } - -private fun createWebIntent(): Intent = - Intent( - Intent.ACTION_VIEW, - "https://play.google.com/store/apps/details?id=$PACKAGE_NAME".toUri(), - ).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - -private fun finishAppWithDelay(activity: ComponentActivity) { - activity.lifecycleScope.launch { - delay(APP_EXIT_DELAY) - activity.finishAffinity() - exitProcess(0) - } -}