forked from github/wulkanowy-mirror
Add in-app updates support (#914)
This commit is contained in:
parent
ca67e144e4
commit
db9c2640c7
@ -112,6 +112,7 @@ play {
|
||||
serviceAccountCredentials = file('key.p12')
|
||||
defaultToAppBundles = false
|
||||
track = 'alpha'
|
||||
updatePriority = 0
|
||||
}
|
||||
|
||||
ext {
|
||||
@ -183,11 +184,12 @@ dependencies {
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
||||
|
||||
playImplementation 'com.google.firebase:firebase-analytics:17.5.0'
|
||||
playImplementation 'com.google.firebase:firebase-analytics:17.6.0'
|
||||
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.1'
|
||||
playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.1"
|
||||
playImplementation 'com.google.firebase:firebase-messaging:20.3.0'
|
||||
playImplementation 'com.google.firebase:firebase-crashlytics:17.2.2'
|
||||
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
|
||||
|
||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||
|
@ -0,0 +1,17 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
class UpdateHelper @Inject constructor() {
|
||||
|
||||
lateinit var messageContainer: View
|
||||
|
||||
fun checkAndInstallUpdates(activity: Activity) {}
|
||||
|
||||
fun onActivityResult(requestCode: Int, resultCode: Int) {}
|
||||
|
||||
fun onResume(activity: Activity) {}
|
||||
}
|
@ -14,6 +14,7 @@ import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
|
||||
import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment
|
||||
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
|
||||
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
|
||||
import io.github.wulkanowy.utils.UpdateHelper
|
||||
import io.github.wulkanowy.utils.setOnSelectPageListener
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -25,6 +26,9 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
||||
|
||||
private val loginAdapter = BaseFragmentPagerAdapter(supportFragmentManager)
|
||||
|
||||
@Inject
|
||||
lateinit var updateHelper: UpdateHelper
|
||||
|
||||
companion object {
|
||||
|
||||
fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java)
|
||||
@ -37,8 +41,20 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
||||
setContentView(ActivityLoginBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
setSupportActionBar(binding.loginToolbar)
|
||||
messageContainer = binding.loginContainer
|
||||
updateHelper.messageContainer = binding.loginContainer
|
||||
|
||||
presenter.onAttachView(this)
|
||||
updateHelper.checkAndInstallUpdates(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateHelper.onResume(this)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
updateHelper.onActivityResult(requestCode, resultCode)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
|
@ -40,6 +40,7 @@ import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.UpdateHelper
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.safelyPopFragments
|
||||
@ -56,6 +57,9 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
@Inject
|
||||
lateinit var analytics: FirebaseAnalyticsHelper
|
||||
|
||||
@Inject
|
||||
lateinit var updateHelper: UpdateHelper
|
||||
|
||||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
@ -100,6 +104,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
setSupportActionBar(binding.mainToolbar)
|
||||
messageContainer = binding.mainFragmentContainer
|
||||
updateHelper.messageContainer = binding.mainFragmentContainer
|
||||
|
||||
presenter.onAttachView(this, MainView.Section.values().singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) })
|
||||
|
||||
@ -107,6 +112,18 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
initialize(startMenuIndex, savedInstanceState)
|
||||
pushFragment(moreMenuFragments[startMenuMoreIndex])
|
||||
}
|
||||
updateHelper.checkAndInstallUpdates(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateHelper.onResume(this)
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
updateHelper.onActivityResult(requestCode, resultCode)
|
||||
if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) initShortcuts()
|
||||
}
|
||||
|
||||
|
@ -472,6 +472,12 @@
|
||||
<string name="all_copied">Copied</string>
|
||||
<string name="all_undo">Undo</string>
|
||||
|
||||
<!--Update helper-->
|
||||
<string name="update_download_started">Start downloading update…</string>
|
||||
<string name="update_download_success">An update has just been downloaded.</string>
|
||||
<string name="update_download_success_button">Restart</string>
|
||||
<string name="update_failed">Update failed! Wulkanowy may not function properly. Consider updating</string>
|
||||
|
||||
|
||||
<!--Errors-->
|
||||
<string name="error_no_internet">No internet connection</string>
|
||||
|
104
app/src/play/java/io/github/wulkanowy/utils/UpdateHelper.kt
Normal file
104
app/src/play/java/io/github/wulkanowy/utils/UpdateHelper.kt
Normal file
@ -0,0 +1,104 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.play.core.appupdate.AppUpdateInfo
|
||||
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
|
||||
import com.google.android.play.core.install.InstallStateUpdatedListener
|
||||
import com.google.android.play.core.install.model.AppUpdateType.FLEXIBLE
|
||||
import com.google.android.play.core.install.model.AppUpdateType.IMMEDIATE
|
||||
import com.google.android.play.core.install.model.InstallStatus.DOWNLOADED
|
||||
import com.google.android.play.core.install.model.InstallStatus.PENDING
|
||||
import com.google.android.play.core.install.model.UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
|
||||
import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_AVAILABLE
|
||||
import com.google.android.play.core.ktx.isFlexibleUpdateAllowed
|
||||
import com.google.android.play.core.ktx.isImmediateUpdateAllowed
|
||||
import io.github.wulkanowy.R
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class UpdateHelper @Inject constructor(private val context: Context) {
|
||||
|
||||
lateinit var messageContainer: View
|
||||
|
||||
companion object {
|
||||
const val IN_APP_UPDATE_REQUEST_CODE = 1721
|
||||
|
||||
const val DAYS_FOR_FLEXIBLE_UPDATE = 7
|
||||
const val HIGH_PRIORITY_UPDATE = 4
|
||||
}
|
||||
|
||||
private val appUpdateManager by lazy { AppUpdateManagerFactory.create(context) }
|
||||
|
||||
private val flexibleUpdateListener = InstallStateUpdatedListener { state ->
|
||||
when (state.installStatus()) {
|
||||
PENDING -> Toast.makeText(context, R.string.update_download_started, Toast.LENGTH_SHORT).show()
|
||||
DOWNLOADED -> popupSnackBarForCompleteUpdate()
|
||||
else -> Timber.d("Update state: ${state.installStatus()}")
|
||||
}
|
||||
}
|
||||
|
||||
private inline val AppUpdateInfo.isImmediateUpdateAvailable: Boolean
|
||||
get() = updateAvailability() == UPDATE_AVAILABLE && isImmediateUpdateAllowed &&
|
||||
updatePriority() >= HIGH_PRIORITY_UPDATE
|
||||
|
||||
private inline val AppUpdateInfo.isFlexibleUpdateAvailable: Boolean
|
||||
get() = updateAvailability() == UPDATE_AVAILABLE && isFlexibleUpdateAllowed &&
|
||||
clientVersionStalenessDays() ?: 0 >= DAYS_FOR_FLEXIBLE_UPDATE
|
||||
|
||||
fun checkAndInstallUpdates(activity: Activity) {
|
||||
Timber.d("Checking for updates...")
|
||||
appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
|
||||
when {
|
||||
appUpdateInfo.isImmediateUpdateAvailable -> startUpdate(activity, appUpdateInfo, IMMEDIATE)
|
||||
appUpdateInfo.isFlexibleUpdateAvailable -> {
|
||||
appUpdateManager.registerListener(flexibleUpdateListener)
|
||||
startUpdate(activity, appUpdateInfo, FLEXIBLE)
|
||||
}
|
||||
else -> Timber.d("No update available")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startUpdate(activity: Activity, appUpdateInfo: AppUpdateInfo, updateType: Int) {
|
||||
appUpdateManager.startUpdateFlowForResult(
|
||||
appUpdateInfo, updateType, activity, IN_APP_UPDATE_REQUEST_CODE
|
||||
)
|
||||
}
|
||||
|
||||
fun onActivityResult(requestCode: Int, resultCode: Int) {
|
||||
if (requestCode == IN_APP_UPDATE_REQUEST_CODE && resultCode != RESULT_OK) {
|
||||
Timber.e("Update failed! Result code: $resultCode")
|
||||
Toast.makeText(context, R.string.update_failed, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun onResume(activity: Activity) {
|
||||
appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
|
||||
Timber.d("InAppUpdate.onResume() listener: $info")
|
||||
|
||||
when {
|
||||
DOWNLOADED == info.installStatus() -> popupSnackBarForCompleteUpdate()
|
||||
DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS == info.updateAvailability() -> {
|
||||
startUpdate(activity, info, if (info.isImmediateUpdateAvailable) IMMEDIATE else FLEXIBLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun popupSnackBarForCompleteUpdate() {
|
||||
Snackbar.make(messageContainer, R.string.update_download_success, Snackbar.LENGTH_INDEFINITE).apply {
|
||||
setAction(R.string.update_download_success_button) {
|
||||
appUpdateManager.completeUpdate()
|
||||
appUpdateManager.unregisterListener(flexibleUpdateListener)
|
||||
}
|
||||
show()
|
||||
}
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ buildscript {
|
||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||
classpath 'com.google.gms:google-services:4.3.4'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
|
||||
classpath "com.github.triplet.gradle:play-publisher:2.7.5"
|
||||
classpath "com.github.triplet.gradle:play-publisher:2.8.0"
|
||||
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0"
|
||||
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
|
||||
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries"
|
||||
|
Loading…
x
Reference in New Issue
Block a user