Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,6 +24,7 @@ class AndroidApplicationPlugin : Plugin<Project> {
configureKotlinAndroid(this)
configureComposeAndroid(this)
configureAppVersion()
configureApplicationId()
with(defaultConfig) {
targetSdk = libs.findVersion("targetSdk").get().requiredVersion.toInt()
versionCode = libs.findVersion("versionCode").get().requiredVersion.toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -19,6 +20,7 @@ class AndroidLibraryPlugin : Plugin<Project> {
configureKotlinAndroid(this)
configureKotlinCoroutine(this)
configureAppVersion()
configureApplicationId()
}
}
}
Original file line number Diff line number Diff line change
@@ -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<ApplicationExtension>()?.let { appExtension ->
appExtension.defaultConfig {
this.applicationId = applicationId
}
}

extensions.findByType<LibraryExtension>()?.let { libExtension ->
libExtension.apply {
defaultConfig {
buildConfigField("String", "APPLICATION_ID", "\"$applicationId\"")
}
buildFeatures {
buildConfig = true
}
}
}
}
12 changes: 12 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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" }
Expand Down Expand Up @@ -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" }
Expand Down
1 change: 1 addition & 0 deletions presentation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.threegap.bitnagil.presentation.common.playstore

enum class UpdateAvailableState {
LATEST, NEED_UPDATE, NONE
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.threegap.bitnagil.presentation.common.version

interface VersionNameProvider {
fun getVersionName(): String
}
Loading