Skip to content

Commit

Permalink
Merge pull request #1 from allaboutapps/forceupdater
Browse files Browse the repository at this point in the history
Force Update mode added
  • Loading branch information
mgursch authored Jun 19, 2019
2 parents 6b0edff + 8ddd620 commit cdb566f
Show file tree
Hide file tree
Showing 16 changed files with 499 additions and 251 deletions.
43 changes: 40 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,25 @@

This library aims to simplify Android [In App Updates](https://developer.android.com/guide/app-bundle/in-app-updates). You can force your users to update to latest app version with just a few lines of code.



### DEMO VIDEOS

![Flexible update](assets/flexible_update_small.gif)

[(YOUTUBE) Demo type FLEXIBLE](https://youtu.be/c4IGhj5E7eI)

[(YOUTUBE) Demo type IMMEDIATE](https://youtu.be/mr5SGbXs3ec)

[(YOUTUBE) Demo Forced update](https://youtu.be/9R7vMctW87I)

# Usage


## Add to project

```Gradle
implementation 'at.allaboutapps.inappupdater:inappupdater:1.0.6'
implementation 'at.allaboutapps.inappupdater:inappupdater:{latest_version}'
```

## Initialize the InAppUpdateManager
Expand Down Expand Up @@ -54,12 +61,42 @@ There are two modes
```


## Forced updates
There are some reasons when an update is mandatory. For this case you can implement a provider interface to decide if an update is a forced update

```Kotlin
class DemoForceUpdateProvider : ForceUpdateProvider {

override fun requestUpdateShouldBeImmediate(availableVersionCode: Int, doUpdate: () -> Unit) {

// place your logic here

// if a forced update is needed, just call doUpdate
doUpdate()

}
```

Just provide to the InAppUpdateManager a second, optional parameter
```Kotlin
inAppUpdateManager = InAppUpdateManager(this, DemoForceUpdateProvider())
```

The force update activity screen provided by Google Play Core library can be closed through the back button. So we need to override onActivityResult to force the update again
```Kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
inAppUpdateManager.onActivityResult(requestCode, resultCode)
super.onActivityResult(requestCode, resultCode, data)
}
```


### How to setup the demo app

In App Updates needs a newer version of the app available in Play Store.

1. Add a SigningConfig to build.gradle
2. Set versionCode to 9000
2. Set versionCode to 9000 // or any other high number
3. Set a unique application id
4. Generate APK / AAB
5. Upload APK / AAB to your play store account
Expand All @@ -71,5 +108,5 @@ In App Updates needs a newer version of the app available in Play Store.

### Any Troubles?

* In-app updates are available only to user accounts that own the app. So, make sure the account you’re using has downloaded your app from Google Play at least once before using the account to test in-app updates.
* In-app updates are available only to user accounts that own the app. So, make sure the account you’re using has downloaded your app from Google Play *at least once before* using the account to test in-app updates.
* Make sure that the app that you are testing in-app updates with has the same application ID and is signed with the same signing key as the one available from Google Play.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ android {
applicationId "at.allaboutapps.inappupdater.demo"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "V1.0.0"
versionCode 3
versionName "V1.0.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
Expand Down
29 changes: 19 additions & 10 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="at.allaboutapps.inappupdaterdemo">
package="at.allaboutapps.inappupdaterdemo">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="at.allaboutapps.inappupdaterdemo.MainActivity"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".ForceUpdateDemoActivity"
android:screenOrientation="portrait" />
<activity
android:name=".UpdateTypeDemoActivity"
android:screenOrientation="portrait" />

</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package at.allaboutapps.inappupdaterdemo

import at.allaboutapps.inappupdater.ForceUpdateProvider

/**
* Implementation of ForceUpdateProvider to start a forced update
*/
class DemoForceUpdateProvider : ForceUpdateProvider {

override fun requestUpdateShouldBeImmediate(availableVersionCode: Int, doUpdate: () -> Unit) {

// place your logic here

// if a forced update is needed, just call doUpdate
doUpdate()

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package at.allaboutapps.inappupdaterdemo

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import at.allaboutapps.inappupdater.InAppUpdateManager
import at.allaboutapps.inappupdater.InAppUpdateStatus
import io.reactivex.disposables.Disposables

class ForceUpdateDemoActivity : AppCompatActivity() {

companion object {
fun newIntent(context: Context) = Intent(context, ForceUpdateDemoActivity::class.java)
}

private lateinit var inAppUpdateManager: InAppUpdateManager
private var inAppUpdateStatusDisposable = Disposables.empty()


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContentView(R.layout.activity_force_update)
initInAppUpdate()
}

private fun initInAppUpdate() {
inAppUpdateManager = InAppUpdateManager(this, DemoForceUpdateProvider())
inAppUpdateStatusDisposable = inAppUpdateManager.observeInAppUpdateStatus()
.subscribe { currentStatus ->
if (currentStatus.isUpdatePending()) {
inAppUpdateManager.startUpdate()
}
updateUI(currentStatus)
}
}

fun updateUI(currentStatus: InAppUpdateStatus) {
//Do nothing -> just a demo for forced update
}

/**
* Immediate update type page is not uncloseable
* Just recall startUpdate at onActivityResult
*/

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
inAppUpdateManager.onActivityResult(requestCode, resultCode)
super.onActivityResult(requestCode, resultCode, data)
}

override fun onDestroy() {
if (!inAppUpdateStatusDisposable.isDisposed)
inAppUpdateStatusDisposable.dispose()
super.onDestroy()
}
}
81 changes: 6 additions & 75 deletions app/src/main/java/at/allaboutapps/inappupdaterdemo/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,91 +1,22 @@
package at.allaboutapps.inappupdaterdemo

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import at.allaboutapps.inappupdater.InAppUpdateManager
import at.allaboutapps.inappupdater.InAppUpdateStatus
import io.reactivex.disposables.Disposables
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

private lateinit var inAppUpdateManager: InAppUpdateManager
private var inAppUpdateStatusDisposable = Disposables.empty()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

tvPackageName.text = packageName
tvCurrentVersionCode.text = BuildConfig.VERSION_CODE.toString()

initInAppUpdate()

btUpdate.setOnClickListener {
inAppUpdateManager.startUpdate(getSelectedUpdateType())
}

btRestart.setOnClickListener {
inAppUpdateManager.completeUpdate()
}
}

@InAppUpdateManager.InAppUpdateType
private fun getSelectedUpdateType() =
if (updateTypeGroup.checkedButtonId == R.id.btImmediate) InAppUpdateManager.UPDATE_TYPE_IMMEDIATE else InAppUpdateManager.UPDATE_TYPE_FLEXIBLE

private fun initInAppUpdate() {
inAppUpdateManager = InAppUpdateManager(this)
inAppUpdateStatusDisposable = inAppUpdateManager.observeInAppUpdateStatus()
.subscribe { currentStatus ->
if (currentStatus.isUpdatePending()) {
inAppUpdateManager.startUpdate()
}
updateUI(currentStatus)
}
}
setContentView(R.layout.activity_main)

private fun updateUI(currentStatus: InAppUpdateStatus) {
if (currentStatus.isUpdateInProgress) {
showUpdateInProgressState(currentStatus)
} else {
tvAvailableAppVersion.text = if (currentStatus.availableVersionCode == 0) {
"App not published in Play Store yet!"
} else {
currentStatus.availableVersionCode.toString()
}
if (currentStatus.isUpdateAvailable()) {
tvUpdateAvailable.text = "YES"
showUpdateAvailableState()
} else {
tvUpdateAvailable.text = "NO"
}
btForceUpdate.setOnClickListener {
startActivity(ForceUpdateDemoActivity.newIntent(this))
}
}

private fun showUpdateAvailableState() {
btUpdate.visibility = View.VISIBLE
vgUpdateInProgress.visibility = View.GONE
vgUpdateAvailable.visibility = View.VISIBLE
vgUpdateFinished.visibility = View.GONE
}

private fun showUpdateInProgressState(currentStatus: InAppUpdateStatus) {
if (currentStatus.isDownloading) {
vgUpdateInProgress.visibility = View.VISIBLE
vgUpdateAvailable.visibility = View.GONE
vgUpdateFinished.visibility = View.GONE
} else if (currentStatus.isDownloaded) {
vgUpdateInProgress.visibility = View.GONE
vgUpdateAvailable.visibility = View.GONE
vgUpdateFinished.visibility = View.VISIBLE
btUpdateType.setOnClickListener {
startActivity(UpdateTypeDemoActivity.newIntent(this))
}
}

override fun onDestroy() {
if (!inAppUpdateStatusDisposable.isDisposed)
inAppUpdateStatusDisposable.dispose()
super.onDestroy()
}
}
}
Loading

0 comments on commit cdb566f

Please sign in to comment.