Skip to content

Commit

Permalink
add playstore flexible updates
Browse files Browse the repository at this point in the history
  • Loading branch information
odaridavid committed Mar 5, 2024
1 parent 82e6ca0 commit b5f4e62
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 2 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ dependencies {

// Memory Leak Detection
debugImplementation(libs.leakcanary)

// In-app update
implementation(libs.bundles.google.play)
}

kapt {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.github.odaridavid.weatherapp.ui

import android.annotation.SuppressLint
import android.app.Activity
import android.location.Location
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
Expand All @@ -25,17 +25,23 @@ import com.github.odaridavid.weatherapp.designsystem.EnableLocationSettingScreen
import com.github.odaridavid.weatherapp.designsystem.LoadingScreen
import com.github.odaridavid.weatherapp.designsystem.RequiresPermissionsScreen
import com.github.odaridavid.weatherapp.designsystem.theme.WeatherAppTheme
import com.github.odaridavid.weatherapp.ui.update.UpdateManager
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

private val mainViewModel: MainViewModel by viewModels()

@Inject
lateinit var updateManager: UpdateManager

private val locationRequestLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
if (result.resultCode == RESULT_OK) {
mainViewModel.processIntent(MainViewIntent.CheckLocationSettings(isEnabled = true))
} else {
mainViewModel.processIntent(MainViewIntent.CheckLocationSettings(isEnabled = false))
Expand All @@ -46,10 +52,22 @@ class MainActivity : ComponentActivity() {
mainViewModel.processIntent(MainViewIntent.GrantPermission(isGranted = isGranted))
}

private val updateRequestLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
if (result.resultCode == RESULT_OK) {
// TODO Trigger a UI event
Log.d("MainActivity", "Update successful")
} else {
Log.e("MainActivity", "Update failed")
}
}

private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

updateManager.checkForUpdates(activityResultLauncher = updateRequestLauncher)

createLocationRequest(
activity = this@MainActivity,
locationRequestLauncher = locationRequestLauncher
Expand Down Expand Up @@ -115,5 +133,10 @@ class MainActivity : ComponentActivity() {
else -> LoadingScreen()
}
}

override fun onDestroy() {
super.onDestroy()
updateManager.unregisterListeners()
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.github.odaridavid.weatherapp.ui.update

data class UpdateAppException(val throwable: Throwable) : Exception()
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.github.odaridavid.weatherapp.ui.update

import android.content.Context
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import com.github.odaridavid.weatherapp.core.api.Logger
import com.google.android.play.core.appupdate.AppUpdateInfo
import com.google.android.play.core.appupdate.AppUpdateManager
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.appupdate.AppUpdateOptions
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.UpdateAvailability
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject

class UpdateManager @Inject constructor(
@ApplicationContext private val context: Context,
private val logger: Logger,
private val updateStateFactory: UpdateStateFactory,
) {

private val appUpdateManager: AppUpdateManager by lazy {
AppUpdateManagerFactory.create(context)
}

fun checkForUpdates(
activityResultLauncher: ActivityResultLauncher<IntentSenderRequest>
) {
val appUpdateInfoTask = appUpdateManager.appUpdateInfo

appUpdateManager.registerListener(updateStateFactory.getUpdateStateListener(
onDownloaded = {
// TODO: Notify the user that the update is ready to be installed.Don't do it this way.
appUpdateManager.completeUpdate()
}
))

appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
) {
update(
appUpdateManager = appUpdateManager,
appUpdateInfo = appUpdateInfo,
activityResultLauncher = activityResultLauncher,
)
}
}.addOnFailureListener { exception ->
logger.logException(UpdateAppException(exception))
}
}

fun unregisterListeners() {
appUpdateManager.unregisterListener(updateStateFactory.getUpdateStateListener())
}

private fun update(
appUpdateManager: AppUpdateManager,
appUpdateInfo: AppUpdateInfo,
activityResultLauncher: ActivityResultLauncher<IntentSenderRequest>
) {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
activityResultLauncher,
AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build()
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.github.odaridavid.weatherapp.ui.update

import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.InstallStatus
import javax.inject.Inject

class UpdateStateFactory @Inject constructor() {

fun getUpdateStateListener(
onDownloading: ((bytesDownloaded: Long, totalBytesToDownload: Long) -> Unit)? = null,
onDownloaded: (() -> Unit)? = null,
) = InstallStateUpdatedListener { state ->
when (state.installStatus()) {
InstallStatus.DOWNLOADING -> {
val bytesDownloaded = state.bytesDownloaded()
val totalBytesToDownload = state.totalBytesToDownload()
if (onDownloading != null) {
onDownloading(bytesDownloaded, totalBytesToDownload)
}
// Show update progress bar.
}
InstallStatus.DOWNLOADED -> {
// Notify the user that the update is ready to be installed.
if (onDownloaded != null) {
onDownloaded()
}

}
InstallStatus.INSTALLING,
InstallStatus.INSTALLED,
InstallStatus.FAILED,
InstallStatus.CANCELED,
InstallStatus.PENDING,
InstallStatus.UNKNOWN -> {
// No-op
}
else -> {
// No-op
}
}
}

}
8 changes: 8 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ retrofit = "2.9.0"
truth = "1.4.2"
turbine = "1.0.0"
leakcanary = "3.0-alpha-1"
#InAppUpdate
inappupdate = "2.1.0"

[libraries]
activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" }
Expand Down Expand Up @@ -90,6 +92,8 @@ retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit
truth = { module = "com.google.truth:truth", version.ref = "truth" }
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
inapp-update = { module = "com.google.android.play:app-update", version.ref = "inappupdate" }
inapp-update-ktx = { module = "com.google.android.play:app-update-ktx", version.ref = "inappupdate" }

[plugins]
com-android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
Expand Down Expand Up @@ -120,3 +124,7 @@ firebase = [
"firebase-crashlytics",
"firebase-perfomance-monitoring",
]
google-play = [
"inapp-update",
"inapp-update-ktx",
]

0 comments on commit b5f4e62

Please sign in to comment.