In-App update (IMMEDIATE) not restarting an app - android

Facing an issue with IMMEDIATE app update mode. After successful completion of app update, everything is closed and not restarting the app. That is the issue.
But android documentation says:
A full screen user experience that requires the user to update and
restart the app in order to continue using the app. This UX is best
for cases where an update is critical for continued use of the app.
After a user accepts an immediate update, Google Play handles the
update installation and app restart.
implementation 'com.google.android.play:core:1.9.1'
implementation 'com.google.android.play:core-ktx:1.8.1'
code
class MainActivity : AppCompatActivity() {
companion object {
const val UPDATE_REQUEST_CODE = 112
const val TAG = "MainActivity"
}
private var appUpdateManager: AppUpdateManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<TextView>(R.id.tv_text).text = "Version " + BuildConfig.VERSION_NAME
// Returns an intent object that you use to check for an update.
appUpdateManager = AppUpdateManagerFactory.create(this)
}
private val listener: InstallStateUpdatedListener =
InstallStateUpdatedListener { installState ->
if (installState.installStatus() == InstallStatus.DOWNLOADED) {
// After the update is downloaded, show a notification
// and request user confirmation to restart the app.
Log.d(TAG, "An update has been downloaded")
} else if (installState.installStatus() == InstallStatus.INSTALLED) {
Log.d(TAG, "An update has been installed")
}
}
override fun onStart() {
super.onStart()
checkAppVersionNew()
}
private fun checkAppVersionNew() {
val appUpdateInfoTask = appUpdateManager!!.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { result: AppUpdateInfo ->
if (result.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && result.isUpdateTypeAllowed(
AppUpdateType.IMMEDIATE
)
) {
try {
Log.d(TAG, "An update available")
appUpdateManager!!.startUpdateFlowForResult(
result,
AppUpdateType.IMMEDIATE,
this,
UPDATE_REQUEST_CODE
)
} catch (e: SendIntentException) {
Log.d(TAG, "SendIntentException $e")
e.printStackTrace()
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == UPDATE_REQUEST_CODE) {
when (resultCode) {
RESULT_OK -> {
Log.d(TAG, "Update success")
}
RESULT_CANCELED -> {
Log.d(TAG, "Update cancelled")
}
ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> {
Log.d(TAG, "Update failed")
}
}
}
}
}

I faced this issue and after 2 days I added
android:launchMode="singleTask"
to the launcher Activity And I used
ProcessPhoenix.triggerRebirth(this)
From ProcessPhoenix library And then the app restarted after updating.

Related

Determine the user is premium - Qonversion API

With the Qonversion API, I am adding a feature for users to purchase subscriptions. When the user comes back after the purchased subscription, I want to check the subscription status, but I get a "null" value from checkEntitlements as a return. At the same time, although the purchase is positive in the purchase function, it does not enter onSuccess at all. How can I check the user's subscription status? (When I try to subscribe again after the purchase is made, google says I am already subscribed, so the subscription process is successful.)
Fragment:
class PurchaseFragment : Fragment() {
private var _binding: FragmentPurchaseBinding? = null
private val binding get() = _binding!!
private var product: QProduct? = null
Displaying product:
private fun displayProducts() {
Qonversion.shared.offerings(object : QonversionOfferingsCallback {
override fun onSuccess(offerings: QOfferings) {
val mainOffering = offerings.main
if (mainOffering != null && mainOffering.products.isNotEmpty()) {
binding.monthlyPrice.text = mainOffering.products[0].prettyPrice
product = mainOffering.products[0]
}
}
override fun onError(error: QonversionError) {
Log.d(TAG, "Error: $error")
}
})
}
Making purchase:
private fun makePurchase(product: QProduct) {
Qonversion.shared.purchase(
requireActivity(),
product,
callback = object : QonversionEntitlementsCallback {
override fun onSuccess(entitlements: Map<String, QEntitlement>) {
val premiumEntitlement = entitlements["premium_permisson"]
if (premiumEntitlement != null && premiumEntitlement.isActive) {
Log.d(TAG, "Entitlements: $premiumEntitlement")
} else {
Log.d(TAG, "Entitlements else: $premiumEntitlement")
}
}
override fun onError(error: QonversionError) {
// Handle error here
if (error.code === QonversionErrorCode.CanceledPurchase) {
Log.d(TAG, "Canceled")
}
}
})
}
Check status of user:
private fun isItPremiumUser() {
Qonversion.shared.checkEntitlements(object : QonversionEntitlementsCallback {
override fun onSuccess(entitlements: Map<String, QEntitlement>) {
val premiumEntitlement = entitlements["premium_permisson"]
Log.d(TAG, "isItPremiumUser $premiumEntitlement") // it returns null
if (premiumEntitlement != null && premiumEntitlement.isActive) {
Log.d(TAG, "isItPremiumUser Entitlements: $premiumEntitlement")
// handle active entitlement here
// also you can check renew state if needed
// for example to check if user has canceled subscription and offer him a discount
when (premiumEntitlement.renewState) {
QEntitlementRenewState.NonRenewable -> {
Log.d(TAG, "NonRenewable Entitlements: $premiumEntitlement")
// NonRenewable is the state of a consumable or non-consumable in-app purchase
}
QEntitlementRenewState.WillRenew -> {
Log.d(TAG, "WillRenew Entitlements: $premiumEntitlement")
// WillRenew is the state of an auto-renewable subscription
}
QEntitlementRenewState.BillingIssue -> {
Log.d(TAG, "BillingIssue Entitlements: $premiumEntitlement")
// Prompt the user to update the payment method.
}
QEntitlementRenewState.Canceled -> {
Log.d(TAG, "Canceled Entitlements: $premiumEntitlement")
// The user has turned off auto-renewal for the subscription, but the subscription has not expired yet.
// Prompt the user to resubscribe with a special offer.
}
else -> {
Log.d(TAG, "else Entitlements: $premiumEntitlement")
}
}
}
}
override fun onError(error: QonversionError) {
// handle error here
Log.d(TAG, "onError: $error")
}
})
}

Why is my In-App Update availability check always returning update not available?

I'm having issues with in app updates. My procedure as to testing it is I release two versions of the app, with one that has a higher version code(in gradle). When I check my log statements, they say that there is no update available. However, I did install the app from the Play Store(using the internal testing track), and the play store does show an update button(so my version code, etc, should be right). I have also tried refreshing the cache on the PlayStore app and my own, and have tried rebuilding with several other version codes with an even wider gap, all to no help. Here is all of my code regarding in-app updates. I call the checkUpdate function in onCreate().
class MainActivity : AppCompatActivity() {
private lateinit var appUpdateManager: AppUpdateManager
private val MY_REQUEST_CODE = 17326
override fun onCreate(savedInstanceState: Bundle?) {
//Rest of Code
checkUpdate()
}
private fun checkUpdate() {
val listener =
InstallStateUpdatedListener { installState ->
if (installState.installStatus() == InstallStatus.DOWNLOADED) {
Timber.tag("InstallDownloaded").d("InstallStatus success")
Log.d("InstallDownloaded", "Install Successful")
notifyUser()
}
}
appUpdateManager = AppUpdateManagerFactory.create(this)
appUpdateManager.registerListener(listener)
val appUpdateInfoTask: Task<AppUpdateInfo> = appUpdateManager.getAppUpdateInfo()
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
val data = "packageName :" + appUpdateInfo.packageName() + ", " +
"availableVersionCode :" + appUpdateInfo.availableVersionCode() + ", " +
"updateAvailability :" + appUpdateInfo.updateAvailability() + ", " +
"installStatus :" + appUpdateInfo.installStatus() + ", "
Timber.tag("appUpdateInfo :").e("%s", data)
Log.d("UpdatesInfo", data)
Log.d("Updates", "UpdateAvailability: ${appUpdateInfo.updateAvailability()}")
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
requestUpdate(appUpdateInfo)
Timber.tag("UpdateAvailable").d("update is there ")
Log.d("Updates", "Update is there")
} else if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
Timber.tag("Update").d("3")
notifyUser()
} else {
Timber.tag("NoUpdateAvailable").e("update is not there ")
Log.d("Updates", "Update is not there")
}
}
}
private fun requestUpdate(appUpdateInfo: AppUpdateInfo) {
try {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this#MainActivity,
MY_REQUEST_CODE
)
resume()
} catch (e: IntentSender.SendIntentException) {
e.printStackTrace()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == MY_REQUEST_CODE) {
when (resultCode) {
RESULT_OK -> if (resultCode != RESULT_OK) {
Timber.tag("RESULT_OK :").d("%s", resultCode)
Log.d("RESULT_OK: ", "%s: $resultCode")
}
RESULT_CANCELED -> if (resultCode != RESULT_CANCELED) {
Timber.tag("RESULT_CANCELED :").d("%s", resultCode)
Log.d("RESULT_CANCELED: ", "%s: $resultCode")
}
ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> {}
}
}
}
override fun onDestroy() {
super.onDestroy()
try {
appUpdateManager.unregisterListener(this as InstallStateUpdatedListener)
} catch (e: RuntimeException) {
}
}
private fun notifyUser() {
val snackbar = Snackbar.make(
findViewById(R.id.parent),
"An update has just been downloaded.",
Snackbar.LENGTH_INDEFINITE
)
snackbar.setAction(
"RESTART"
) { appUpdateManager.completeUpdate() }
snackbar.setActionTextColor(
resources.getColor(R.color.main_red)
)
snackbar.show()
}
private fun resume() {
appUpdateManager.appUpdateInfo
.addOnSuccessListener(OnSuccessListener<AppUpdateInfo> { appUpdateInfo ->
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
notifyUser()
Log.d("Update", "Successful")
}
})
}
}
No matter the gap between the two build's version codes, the app still does not update. I have updated both the version codes in the manifest and the gradle files. Even clearing all Google Play and my app data, did not help. This is my 6th-8th try to no avail. Any help is appreciated. Thank You.

In-App Update Finish Downloading But The Apps Still The Old Version

so I'm working on a In-Apps Update for my application that already published in Play Store. I follow the documentation and try to test it by lowering my version code then run it, but after finish downloading the app, it supposed to re-open the new version app from but it still re-open my current app which is the lower version. So it's like download for nothing.
I search and got some solution to try like :
installing using apk-release (didn't work)
trying another code for in-app update (still didn't work)
Here's my code :
I put it in my Login page
private val appUpdateManager: AppUpdateManager by lazy { AppUpdateManagerFactory.create(this) }
private val appUpdatedListener: InstallStateUpdatedListener by lazy {
object : InstallStateUpdatedListener {
override fun onStateUpdate(installState: InstallState) {
when {
installState.installStatus() == InstallStatus.DOWNLOADED -> popupSnackbarForCompleteUpdate()
installState.installStatus() == InstallStatus.INSTALLED -> appUpdateManager.unregisterListener(this)
else -> Log.i("TAG", "onStateUpdate: InstallStateUpdatedListener: state: %s"+installState.installStatus())
}
}
}
}
private fun checkForAppUpdate() {
// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
// This example applies an immediate update. To apply a flexible update
// instead, pass in AppUpdateType.FLEXIBLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
// Request the update.
appUpdateManager.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// Or 'AppUpdateType.FLEXIBLE' for flexible updates.
AppUpdateType.IMMEDIATE,
// The current activity making the update request.
this,
// Include a request code to later monitor this update request.
APP_UPDATE_REQUEST_CODE)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == APP_UPDATE_REQUEST_CODE) {
if (resultCode != RESULT_OK) {
Toast.makeText(this,
"App Update failed, please try again on the next app launch.",
Toast.LENGTH_SHORT)
.show()
}
}
}
private fun popupSnackbarForCompleteUpdate() {
val snackbar = Snackbar.make(
findViewById(R.id.drawer_layout),
"An update has just been downloaded.",
Snackbar.LENGTH_INDEFINITE)
snackbar.setAction("RESTART") { appUpdateManager.completeUpdate() }
snackbar.setActionTextColor(ContextCompat.getColor(this, R.color.accent))
snackbar.show()
}
override fun onResume() {
super.onResume()
appUpdateManager
.appUpdateInfo
.addOnSuccessListener { appUpdateInfo ->
// If the update is downloaded but not installed,
// notify the user to complete the update.
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
}
//Check if Immediate update is required
try {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
APP_UPDATE_REQUEST_CODE)
}
} catch (e: IntentSender.SendIntentException) {
e.printStackTrace()
}
}
}
Please help me, I already tried to fixing it for 3 days but still have none of the solution are work for me.

Android app Play Core library in App update not working properly

I have implemented google play inApp update using play core Play Core library. When a new update is pushed app shows mandatory update notification using this library , downloads new update and shows update complete. But after i exit the app and relaunch it same circle continues. It shows update available please update again notification. I think after download the update is not actually taking place. Please help me with this issue.
fun initUpdate() {
val appUpdateManager = AppUpdateManagerFactory.create(this)
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
appUpdateManager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType. IMMEDIATE, this, MY_REQUEST_CODE
)}}}override fun initComponents() {
initUpdate()
//rest of the code//
}
I had the same issue, I got it working after signing the app(with the lower app version to be updated) with production-release KeyStore. I was using a debug keystore to sign it earlier.
Here is code to get the in-app update.
override fun onResume() {
super.onResume()
checkForVersionUpdate()
}
private fun checkForVersionUpdate() {
val appUpdateInfoTask = appUpdateManager.getAppUpdateInfo()
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
startUpdateFlow(appUpdateInfo)
} else if ((appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE))) {
startUpdateFlow(appUpdateInfo)
}
}
}
private fun startUpdateFlow(appUpdateInfo: AppUpdateInfo) {
try {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
REQUEST_IMMEDIATE)
} catch (e: IntentSender.SendIntentException) {
e.printStackTrace()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_IMMEDIATE && resultCode == RESULT_CANCELED) {
checkForVersionUpdate();
}
}

Suggest users to update Android app with Google Play (Google In-App Update Support)

I saw this dialog always appears when I open old version of Tokopedia app. Tokopedia suggests me to update the app.
The dialog gives me two methods to update the app:
Update now
Update when Wi-Fi available
If I select Lain Kali (Cancel), the dialog appears again on the next app open. But, if I select the second option, I open Play Store and see this behavior:
It really do update on background until my device is connected to Wi-Fi.
I want to mimic the same action like Tokopedia did, because some version of my app contains critical bug. I want to give users a better user experience.
Do you know how to show the dialog above?
This is possible using In-App Update provided by Google.
In-app updates works only with devices running Android 5.0 (API level 21) or higher, and requires you to use Play Core library 1.5.0 or higher. There are two types - 1.Flexible and 2. Immediate.
Follow this link and implement In-App Update as per your requirement.
https://developer.android.com/guide/app-bundle/in-app-updates
You can achieve this by using Support in-app updates
It only work from Android 5.0 (API level 21) or higher.
There are two types of Update Available with UX for in-app updates:
Flexible
Immediate
To Check for update availability
// Creates instance of the manager.
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);
// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
// For a flexible update, use AppUpdateType.FLEXIBLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
// Request the update.
}
});
Okay, here's the complete code as requested by #akshay_shahane.
Firstly, add this line on your app's build.gradle:
dependencies {
implementation 'com.google.android.play:core:1.6.1'
}
And inside your activity:
class MainActivity : AppCompatActivity(), InstallStateUpdatedListener {
private val appUpdateManager by lazy {
AppUpdateManagerFactory.create(this).also { it.registerListener(this) }
}
override fun onDestroy() {
if (Build.VERSION.SDK_INT >= 21) {
appUpdateManager.unregisterListener(this)
}
super.onDestroy()
}
override fun onResume() {
super.onResume()
if (Build.VERSION.SDK_INT >= 21) {
appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
// If the update is downloaded but not installed, notify the user to complete the update.
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
} else if (it.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
&& it.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
appUpdateManager.startUpdateFlowForResult(it, AppUpdateType.IMMEDIATE, this, REQUEST_CODE_UPDATE_APP)
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == ActivityResult.RESULT_IN_APP_UPDATE_FAILED && requestCode == REQUEST_CODE_UPDATE_APP) {
Toast.makeText(this, "Update failed", Toast.LENGTH_SHORT).show()
}
}
override fun onStateUpdate(state: InstallState) {
when (state.installStatus()) {
InstallStatus.DOWNLOADED -> popupSnackbarForCompleteUpdate()
InstallStatus.REQUIRES_UI_INTENT -> {
Snackbar.make(findViewById(R.id.activity_main_layout),
"To perform the installation, a Play Store UI flow needs to be started.",
Snackbar.LENGTH_LONG
).show()
}
else -> {
val stateString = when (state.installStatus()) {
InstallStatus.FAILED -> "failed"
InstallStatus.PENDING -> "pending"
InstallStatus.DOWNLOADING -> "downloading"
InstallStatus.INSTALLING -> "installing"
InstallStatus.INSTALLED -> "installed"
InstallStatus.CANCELED -> "canceled"
else -> null
}
if (stateString != null) {
Snackbar.make(findViewById(R.id.activity_main_layout),
"An update is $stateString.",
Snackbar.LENGTH_SHORT
).show()
}
}
}
}
private fun popupSnackbarForCompleteUpdate() {
Snackbar.make(findViewById(R.id.activity_main_layout),
"An update is ready to install.",
Snackbar.LENGTH_INDEFINITE
).apply {
setAction("INSTALL") { appUpdateManager.completeUpdate() }
show()
}
}
#RequiresApi(21)
fun checkUpdateViaGooglePlay() {
appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
when (appUpdateInfo.updateAvailability()) {
UpdateAvailability.UPDATE_AVAILABLE -> {
if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo, AppUpdateType.FLEXIBLE, this, REQUEST_CODE_UPDATE_APP)
} else if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo, AppUpdateType.IMMEDIATE, this, REQUEST_CODE_UPDATE_APP)
}
}
UpdateAvailability.UPDATE_NOT_AVAILABLE -> {
Toast.makeText(this, R.string.no_updates_found, Toast.LENGTH_SHORT).show()
}
}
}.addOnFailureListener {
Toast.makeText(this, R.string.error_check_update, Toast.LENGTH_SHORT).show()
}
}
companion object {
const val REQUEST_CODE_UPDATE_APP = 8
}
}

Categories

Resources