1+ package com.nmc.android.appupdate
2+
3+ import android.app.Activity
4+ import androidx.activity.result.ActivityResult
5+ import androidx.activity.result.ActivityResultLauncher
6+ import androidx.activity.result.IntentSenderRequest
7+ import androidx.activity.result.contract.ActivityResultContracts
8+ import androidx.appcompat.app.AppCompatActivity
9+ import com.google.android.material.snackbar.Snackbar
10+ import com.google.android.play.core.appupdate.AppUpdateInfo
11+ import com.google.android.play.core.appupdate.AppUpdateManagerFactory
12+ import com.google.android.play.core.appupdate.AppUpdateOptions
13+ import com.google.android.play.core.install.InstallState
14+ import com.google.android.play.core.install.InstallStateUpdatedListener
15+ import com.google.android.play.core.install.model.ActivityResult.RESULT_IN_APP_UPDATE_FAILED
16+ import com.google.android.play.core.install.model.AppUpdateType
17+ import com.google.android.play.core.install.model.InstallStatus
18+ import com.google.android.play.core.install.model.UpdateAvailability
19+ import com.google.firebase.ktx.Firebase
20+ import com.google.firebase.remoteconfig.ktx.remoteConfig
21+ import com.nmc.android.remoteconfig.RemoteConfigInit.Companion.APP_VERSION_KEY
22+ import com.nmc.android.remoteconfig.RemoteConfigInit.Companion.FORCE_UPDATE_KEY
23+ import com.owncloud.android.R
24+ import com.owncloud.android.lib.common.utils.Log_OC
25+ import com.owncloud.android.utils.DisplayUtils
26+
27+ class InAppUpdateHelperImpl (private val activity : AppCompatActivity ) : InAppUpdateHelper, InstallStateUpdatedListener {
28+
29+ companion object {
30+ private val TAG = InAppUpdateHelperImpl ::class .java.simpleName
31+ }
32+
33+ private val remoteConfig = Firebase .remoteConfig
34+ private val isForceUpdate = remoteConfig.getBoolean(FORCE_UPDATE_KEY )
35+ private val appVersionCode = remoteConfig.getLong(APP_VERSION_KEY )
36+
37+ private val appUpdateManager = AppUpdateManagerFactory .create(activity)
38+
39+ @AppUpdateType
40+ private var updateType = if (isForceUpdate) AppUpdateType .IMMEDIATE else AppUpdateType .FLEXIBLE
41+
42+ init {
43+ Log_OC .d(TAG , " App Update Remote Config Values : Force Update- $isForceUpdate -- Version Code- $appVersionCode " )
44+
45+ val appUpdateInfoTask = appUpdateManager.appUpdateInfo
46+
47+ appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
48+ if (appUpdateInfo.updateAvailability() == UpdateAvailability .UPDATE_AVAILABLE ) {
49+ Log_OC .d(TAG , " App update is available." )
50+
51+ // if app version in remote config is not equal to the latest app version code in play store
52+ // then do the flexible update instead of reading the value from remote config
53+ if (appUpdateInfo.availableVersionCode() != appVersionCode.toInt()) {
54+ Log_OC .d(
55+ TAG ,
56+ " Available app version code mismatch with remote config. Setting update type to optional."
57+ )
58+ updateType = AppUpdateType .FLEXIBLE
59+ }
60+
61+ if (appUpdateInfo.isUpdateTypeAllowed(updateType)) {
62+ // Request the update.
63+ startAppUpdate(
64+ appUpdateInfo,
65+ updateType
66+ )
67+ }
68+ } else {
69+ Log_OC .d(TAG , " No app update available." )
70+ }
71+ }
72+ }
73+
74+ private fun startAppUpdate (
75+ appUpdateInfo : AppUpdateInfo ,
76+ @AppUpdateType updateType : Int
77+ ) {
78+
79+ if (updateType == AppUpdateType .FLEXIBLE ) {
80+ // Before starting an update, register a listener for updates.
81+ appUpdateManager.registerListener(this )
82+ }
83+
84+ Log_OC .d(TAG , " App update dialog showing to the user." )
85+
86+ appUpdateManager.startUpdateFlowForResult(
87+ appUpdateInfo,
88+ appUpdateResultLauncher,
89+ AppUpdateOptions .newBuilder(updateType).build()
90+ )
91+ }
92+
93+ private val appUpdateResultLauncher: ActivityResultLauncher <IntentSenderRequest > =
94+ activity.registerForActivityResult(ActivityResultContracts .StartIntentSenderForResult ()) { result: ActivityResult ->
95+ when (result.resultCode) {
96+ Activity .RESULT_OK -> {
97+ Log_OC .d(TAG , " The user has accepted to download the update or the download finished." )
98+ }
99+
100+ Activity .RESULT_CANCELED -> {
101+ Log_OC .e(TAG , " Update flow failed: The user has denied or canceled the update." )
102+ }
103+
104+ RESULT_IN_APP_UPDATE_FAILED -> {
105+ Log_OC .e(
106+ TAG ,
107+ " Update flow failed: Some other error prevented either the user from providing consent or the update from proceeding."
108+ )
109+ }
110+ }
111+
112+ }
113+
114+ private fun flexibleUpdateDownloadCompleted () {
115+ DisplayUtils .createSnackbar(
116+ activity.findViewById(android.R .id.content),
117+ R .string.app_update_downloaded,
118+ Snackbar .LENGTH_INDEFINITE
119+ ).apply {
120+ setAction(R .string.common_restart) { appUpdateManager.completeUpdate() }
121+ show()
122+ }
123+ }
124+
125+ override fun onResume () {
126+ appUpdateManager
127+ .appUpdateInfo
128+ .addOnSuccessListener { appUpdateInfo: AppUpdateInfo ->
129+ // for AppUpdateType.IMMEDIATE only, already executing updater
130+ if (updateType == AppUpdateType .IMMEDIATE ) {
131+ if (appUpdateInfo.updateAvailability()
132+ == UpdateAvailability .DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
133+ ) {
134+ Log_OC .d(TAG , " Resume the Immediate update if in-app update is already running." )
135+ // If an in-app update is already running, resume the update.
136+ startAppUpdate(
137+ appUpdateInfo,
138+ AppUpdateType .IMMEDIATE
139+ )
140+ }
141+ } else if (updateType == AppUpdateType .FLEXIBLE ) {
142+ // If the update is downloaded but not installed,
143+ // notify the user to complete the update.
144+ if (appUpdateInfo.installStatus() == InstallStatus .DOWNLOADED ) {
145+ Log_OC .d(TAG , " Resume: Flexible update is downloaded but not installed. User is notified." )
146+ flexibleUpdateDownloadCompleted()
147+ }
148+ }
149+ }
150+ }
151+
152+ override fun onDestroy () {
153+ appUpdateManager.unregisterListener(this )
154+ }
155+
156+ override fun onStateUpdate (state : InstallState ) {
157+ if (state.installStatus() == InstallStatus .DOWNLOADED ) {
158+ Log_OC .d(TAG , " Flexible update is downloaded. User is notified to restart the app." )
159+
160+ // After the update is downloaded, notifying user via snackbar
161+ // and request user confirmation to restart the app.
162+ flexibleUpdateDownloadCompleted()
163+ }
164+ }
165+ }
0 commit comments