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.
Related
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.
I am using in-app updates for android and as per the documentation, they are using onActivityResult to handle app behaviour incase the update is interrupted.
This is my function that is called from my fragment:
private fun startImmediateUpdate(appUpdateInfo: AppUpdateInfo) {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
requireActivity(),
Constants.CODES.APP_UPDATE_REQUEST_CODE
)
}
This is how i am handling results in parent activity
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
Constants.CODES.APP_UPDATE_REQUEST_CODE -> {
if (resultCode != RESULT_OK || resultCode == RESULT_CANCELED || resultCode == ActivityResult.RESULT_IN_APP_UPDATE_FAILED) {
//Do whatever i want to
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
Now super.onActivityResult(requestCode, resultCode, data) is deprecated. Things are working fine for now but i am worried the app will crash if its wiped out completely
What can i do to replace onActivityResult()? I have looked into registerForActivityResult() but could not find anything that suits my usecase.
As Ian Lake mentioned there is a solution that uses IntentSenderForResultStarter.
First of all in your Activity, create a ActivityResultLauncher:
private val updateFlowResultLauncher =
registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult(),
) { result ->
if (result.resultCode == RESULT_OK) {
// Handle successful app update
}
}
Now start the update flow as follows:
fun startUpdate(
appUpdateInfo: AppUpdateInfo,
requestCode: Int,
) {
val starter =
IntentSenderForResultStarter { intent, _, fillInIntent, flagsMask, flagsValues, _, _ ->
val request = IntentSenderRequest.Builder(intent)
.setFillInIntent(fillInIntent)
.setFlags(flagsValues, flagsMask)
.build()
updateFlowResultLauncher.launch(request)
}
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.FLEXIBLE,
starter,
requestCode,
)
}
I have the same issue and I see that one possibility is to rewrite to use the startUpdateFlow call instead.
Personally, I think this is not worth doing, so I will ignore this specific deprecation warning instead and wait/hope for Android/Google to make a method where we can pass an ActivityResultLauncher instead.
The New Way
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& (appUpdateInfo.clientVersionStalenessDays() ?: -1) >= 2
&& appUpdateInfo.updatePriority() >= updatePriority
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
updateCode
)
openActivityForResult()
}
}
}
private fun openActivityForResult(){
resultUpdate.launch(Intent(this, MainActivity::class.java))
}
var resultUpdate = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result->
if (result.resultCode != Activity.RESULT_OK){
Toast.makeText(
this,
"App Update failed, please try again on the next app launch.",
Toast.LENGTH_SHORT
).show()
}
}
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();
}
}
I'm implementing AppUpdateType.IMMEDIATE in-app-updates from Play Market using this guide: https://developer.android.com/guide/app-bundle/in-app-updates
When I test I can't finish update process - it just throws me back to old app where I get prompted to update again and again.
What am I doing wrong?
SplashActivity.kt
private fun checkAppUpdate() {
val appUpdateManager = getUpdateManager()
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
showError("Update available. You should update")
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
UPDATE_REQUEST_CODE
)
} else {
viewModel.loadData()
}
}
}
Maybe I should listen to apk download and start installing manually?
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
}
}