Google Immediate app update, as far as I understand, shouldn't be cancelable, but the dialog as an X icon that cancels the update process.
Is there a way to remove the cancel option?
I'm using the latest play core version, 1.10.2
Thanks
At the beginning, I had the same understanding, but looking up in documentation, I wasn't able to find this information.
Both InApp update types are cancelable, and the only way to prevent the user to update before uses your app will be finish the app if onActivityResult() returns something diffrent than OK
The difference between Flexible and Immediate update is:
In Flexible update, if the user decide to update, it can uses the app during the download and when the download is finished, you need show some U.I to call the completeUpdate().
In Immediate update, if the user decide to update, it can't use uses the app, because a fullscreen progress dialog will be show. After the download is completed, the Android will install the new version even if your app is in background.
InAppUpdate Doc
You can handle the cancel button and back button click listener.
If the user clicks cancel or back button then
onActivityResult
callback is executed and you can reopen the app update screen if
resultcode == RESULT_CANCELED
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == APP_UPDATE_REQ_CODE) {
if (resultCode == RESULT_CANCELED) {
// App update is canceled by the user so re-open the in-app update screen.
// This block of code is executed if user clicks cancel button from the in-app update screen or press the hardware back button.
checkForAppUpdate();
} else if (resultCode == RESULT_OK) {
// App successfully updated to the latest version.
finish();
} else {
// Update fails then re-open the in-app update screen.
checkForAppUpdate();
}
}
}
private void checkForAppUpdate(){
if (this instanceof SplashActivity){
return;
}
if (appUpdateManager == null){
appUpdateManager = AppUpdateManagerFactory.create(this);
}
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
startUpdateFlow(appUpdateInfo);
}
});
Official android document:
Related
I have an app published in the play store with versionCode 3, in a new version, I want to implement the in app update functionality, so in build.gradle I add:
implementation 'com.google.android.play:core:1.8.2'
And in the main activity:
public class MainActivity extends BaseActivity {
//...
private AppUpdateManager mAppUpdateManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
//....
checkForUpdate();
}
/**
* check for update
*/
private void checkForUpdate() {
// Creates instance of the manager.
mAppUpdateManager = AppUpdateManagerFactory.create(this);
mAppUpdateManager.registerListener(state -> {
if (state.installStatus() == InstallStatus.DOWNLOADED) {
// After the update is downloaded, show a notification
// and request user confirmation to restart the app.
popupSnackbarForCompleteUpdate();
}
});
// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfoTask = mAppUpdateManager.getAppUpdateInfo();
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
//&& appUpdateInfo.clientVersionStalenessDays() != null
//&& appUpdateInfo.clientVersionStalenessDays() >= DAYS_FOR_FLEXIBLE_UPDATE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
// Request the update.
try {
mAppUpdateManager.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// Or 'AppUpdateType.IMMEDIATE' for flexible updates.
AppUpdateType.FLEXIBLE,
// The current activity making the update request.
this,
// Include a request code to later monitor this update request.
MY_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
Toast.makeText(this, R.string.update_failed, Toast.LENGTH_SHORT).show();
}
}
});
}
/**
* Displays the snackbar notification and call to action.
*/
private void popupSnackbarForCompleteUpdate() {
final Snackbar snackbar =
Snackbar.make(
findViewById(R.id.activity_main_layout),
R.string.an_update_has_been_just_downloaded,
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(R.string.install, view -> mAppUpdateManager.completeUpdate());
snackbar.setActionTextColor(
ContextCompat.getColor(this, R.color.color_snackbar_action_text_color));
snackbar.show();
}
}
To be able to test this functionality, I change the versionCode to a lower number than the one published in the Playstore (2 in this case and the app published with 3)When I run my app, it shows me the dialog to update the app and when I click the update button the update starts and installs without any errors, but when the app restarts, it shows me the update dialog again and the app seems to be not updated, I don't know if this is a bug in my app or i should upload the app to play store to be working correctly.
I am using a app bundle (.aab)
You need a Signed .apk for In App Update. Apps in debug mode will show you update dialog & even download new version, but app wont be installed/updated.
I am getting the following error:
java.lang.IllegalStateException: Cannot use sign-in mode: SIGN_IN_MODE_OPTIONAL. Mode was already set to SIGN_IN_MODE_NONE
What does this mean? How to prevent it?
The suggestion in other similar SO question was
mGoogleApiClient.connect(GoogleApiClient.SIGN_IN_MODE_OPTIONAL);
But I have been using this itself.
Here is the onActivityResult
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_OAUTH) {
authInProgress = false;
if (resultCode == RESULT_OK) {
progressDialog.dismiss();
if (!googleApiClient.isConnecting() && !googleApiClient.isConnected()) {
googleApiClient.connect(GoogleApiClient.SIGN_IN_MODE_OPTIONAL);
} else {
onConnected(null);
}
} else if (resultCode == RESULT_CANCELED) {
progressDialog.dismiss();
startApiClientConnect();
}
} else {
progressDialog.dismiss();
startApiClientConnect();
}
}
After the initial connection attempt if it was not successful then the googleApiClient.connect is reattempted in the onActivityResult. I believe this is reattempt is causing the problem. After initial connection failure / some user action makes the SIGN_IN_MODE_NONE.
How to handle this?
It looks like you're not allowed to provide a sign in mode to the connect(int) method more than once. The documentation says "It is an error to make multiple calls to this method passing different modes. Once a mode is selected, all future connect calls must use the same mode." So it might be that you're calling connect() with differing arguments. SIGN_IN_MODE_NONE doesn't seems to be an option for the parameter, so I think you're calling connect() in one case and connect(int) in another (with SIGN_IN_MODE_OPTIONAL). Check that out and see if you can make them consistent.
Also, I believe callbacks for GoogleApiClient are onConnected(Bundle) and onConnectionFailed(ConnectionResult)
When the leader board is shown in the screen there is a option called "setting". Inside that there is a option "Signout". When I clicked signout the leaderboard is closed,
Issue.
If I checked the sign in status the the below function always returns true. Means that the mGoogleApiClient is connected. and hence when I tried to click the icon which shows the leaderboard it always has the responseCode RESULT_RECONNECT_REQUIRED.
This issue goes away if i restart my App
public boolean isSignedIn() {
return mGoogleApiClient != null && mGoogleApiClient.isConnected();
}
Question.
How do the program knows that the user has signed-out in the leaderboard screen.
You have to catch the signout in onActivityResult and call GoogleApiClient.disconnect() yourself since the connection is in an inconsistent state (source).
So, when you open the leaderboard using the following code:
activity.startActivityForResult(Games.Leaderboards.getLeaderboardIntent(googleApiClient, leaderboardId), MY_CUSTOM_LEADERBOARD_RESULT_CODE);
You should handle the signout event as follows:
public void onActivityResult(int requestCode, int responseCode, Intent intent) {
boolean userLoggedOut = (responseCode == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED) && (requestCode == MY_CUSTOM_LEADERBOARD_RESULT_CODE);
if (userLoggedOut) {
googleApiClient.disconnect();
}
}
You should handle the RESULT_RECONNECT_REQUIRED by calling reconnect().
If there was a transient error with the connection, this will silently reconnect the player. If they did actually signout, onConnectionFailed() will be called, and you can reset the UI/game to be appropriate for the not logged in state.
if (resultCode == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED) {
mGoogleApiClient.reconnect();
}
Signing out or disconnecting the GamesClient is straightforward when it is from your own UI, such as a button on the main menu.
However, users can also sign out from the game from the Google Play UI in the acheivements and leaderboard views displayed by the intents such as getAllLeaderboardsIntent(). (It's a bit hidden, but if you tap the menu in the upper right, it lets you sign out.)
There are a few promising listener interfaces like OnSignOutCompleteListener but they don't seem to work with a sign out via the google UI, only from your own UI calling GamesClient.signOut().
How can I detect that the user has signed out from the leaderboards or achievement intents? Is it possible to have a callback for this?
I want to be able to update my in-game UI to reflect the logged-in status.
Unfortunately GameHelper doesn't detect when you logout from Google play games.
What you need to do is to put this in your onActivityResult() method in your activity.
I encounter a crash error when I tried using aHelper.signOut() when res == RESULT_RECONNECT_REQUIRED is true.
Instead I created a resetAllValues() method which resets back all values to its default in GameHelper.
In my MainActivity.java
protected void onActivityResult(int req, int res, Intent data) {
super.onActivityResult(req, res, data);
if (res == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED) {
aHelper.resetAllValues();
} else {
aHelper.onActivityResult(req, res, data);
}
}
My method in GameHelper.java
public void resetAllValues()
{
mProgressDialog = null;
mAutoSignIn = true;
mUserInitiatedSignIn = false;
mConnectionResult = null;
mSignInError = false;
mExpectingActivityResult = false;
mSignedIn = false;
mDebugLog = false;
}
Duplicate from:
How can i check if user sign's out from games services default view?
As I see it, there is no elegant solution to that. You can check the response_code in onActivityResult for INCONSISTENT_STATE and cut off the GamesClient, but I'm not sure, if you can potetially get to an inconsistent state in any other manner...
I am using timer(triggers every 2 hours) to check for any update in my app. If there is any update available, it will automatically download the latest .apk and pro grammatically starts the installation activity. (window with OK and CANCEL button)
Now the problem is :
If the user doesn't click "OK" or "CANCEL". Then the popup still exists. So, in the next timer hit (after 2 hours). It is downloading latest .apk, popups another installation activity.so now 2 activity window is displayed. Then for the next timer hit, it will 3 popup activity window (if the user still doesn't chooses OK or CANCEL)
Is there any better way to avoid displaying multiple window.
Please find my code below :
public void onActivityResult(int requestCode, int resultCode, Intent intent)
{
if (requestCode == 3)
{
isUpdating = false;
}
}
private void fnCheckforUpdate()
{
........
.......
if(false==isUpdating)
{
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-archive");
isUpdating = true;
startActivityForResult(intent, 3);
}
}
You can check showing dialog:
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}