Let me remind you what the initial issue was. We don’t get a confirmation window after user grants install permission. It’s not guaranteed that this issue presents itself only on Android TV, we should think of it as a general possibility. So let’s somehow check whether the confirmation was or wasn’t present.
How do we approach this? Well, if user didn’t confirm installation due to confirmation not appearing, that means install session’s progress didn’t change!
val isSessionStuck = sessionInfo != null && sessionInfo.progress < getProgressThresholdValue()
If our app doesn’t have install permission, we can just cancel the session here.
if (!canInstallPackages) abortSession("Install permission denied")
We also need to distinguish whether install permission request took place or not, because confirmation doesn’t appear only if user went to permission settings. We can check whether the install permission status changed after returning. If it changed, that means this request actually took place. It doesn’t matter if permission was denied, because we handled it earlier.
// canInstallPackages is a field in our Activity
val previousCanInstallPackagesValue = canInstallPackages
canInstallPackages = checkCanInstallPackages()
val isInstallPermissionStatusChanged = previousCanInstallPackagesValue != canInstallPackages
And with this, we can finally relaunch confirmation:
if (isSessionStuck && isInstallPermissionStatusChanged) {
launchInstallActivity()
return
}
Nice, now it works correctly in this case!
However, while investigating the issue, I actually opened a whole can of worms. It turns out Android’s confirmation window is more bugged than I thought!
Cursed dialog
If we dismiss the confirmation via clicking outside a dialog instead of clicking Cancel, install session becomes stuck, and BroadcastReceiver doesn’t receive failure status!
This bug got fixed only on Android 14, so we have to work around it as well.
This is quite easy. If the previous check didn’t succeed (which is when we got install permission and showed confirmation window), we check if the session was not stuck. If it progressed, we just finish our Activity:
val isSessionAlive = sessionInfo != null
if (isSessionAlive && !isSessionStuck) {
finish()
return
}
But if it got stuck, it means that confirmation was dismissed.
// Though confirmation Activity usually always returns RESULT_CANCELED
// value, on some Android versions resultCode is not equal to RESULT_CANCELED
// if Activity was finished normally via Cancel or Install buttons.
val isActivityCancelled = resultCode == RESULT_CANCELED
if (isSessionAlive && isActivityCancelled) {
abortSession()
return
}
There are two other issues related to this.
The first one is the most interesting. Remember that on some devices confirmation didn’t appear after we grant permission? Our fix for that issue turns into a problem on devices where it does appear, because in this case we will show confirmation twice! (Again, only if we dismiss the dialog by clicking outside of it).
That happens because our initial fix doesn’t check if there is no confirmation Activity actually showing, but only if we possibly need to launch it. Improving our solution is a bit tricky, because we also have to account for process death, as on Android 11 changing install permission state always kills the process.
Let’s remember Activity’s lifecycle. onStart() is called when Activity becomes visible, and onResume() is called when Activity gets focus to interact with user. Now we’ll see some logs with this in mind.
First, let’s look at lifecycle callbacks order in cases when confirmation Activity does reappear after permission request.