blob: 1f0c12f454e944fbe45129ec0ce981780e5a1122 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.omaha.inline;
import android.app.Activity;
import android.content.IntentSender.SendIntentException;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.Nullable;
import com.google.android.play.core.appupdate.AppUpdateInfo;
import com.google.android.play.core.appupdate.AppUpdateManager;
import com.google.android.play.core.install.InstallState;
import com.google.android.play.core.install.InstallStateUpdatedListener;
import com.google.android.play.core.install.model.AppUpdateType;
import com.google.android.play.core.install.model.InstallStatus;
import com.google.android.play.core.install.model.UpdateAvailability;
import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState;
/**
* Helper class for gluing interactions with the Play store's AppUpdateManager with Chrome. This
* involves hooking up to Play as a listener for install state changes, should only happen if we are
* in the foreground.
*/
public class InlineUpdateController implements InstallStateUpdatedListener {
private static final int RESULT_IN_APP_UPDATE_FAILED = 1;
private static final int REQUEST_CODE = 8123;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Runnable mCallback;
private final AppUpdateManager mAppUpdateManager;
private boolean mEnabled;
private @Nullable @UpdateState Integer mUpdateState;
private AppUpdateInfo mAppUpdateInfo;
private @Nullable @UpdateAvailability Integer mUpdateAvailability;
private @Nullable @InstallStatus Integer mInstallStatus;
/**
* Builds an instance of {@link InlineUpdateController}.
* @param callback The {@link Runnable} to notify when an inline update state change occurs.
*/
public InlineUpdateController(Runnable callback) {
mCallback = callback;
mAppUpdateManager = InlineAppUpdateManagerFactory.create();
setEnabled(true);
}
public void setEnabled(boolean enabled) {
if (mEnabled == enabled) return;
mEnabled = enabled;
if (mEnabled) {
mUpdateState = UpdateState.NONE;
mAppUpdateManager.registerListener(this);
pullCurrentState();
} else {
mAppUpdateManager.unregisterListener(this);
}
}
/**
* @return The current state of the inline update process. May be {@code null} if the state
* hasn't been determined yet.
*/
public @Nullable @UpdateState Integer getStatus() {
return mUpdateState;
}
/**
* Starts the update, if possible. This will send an {@link Intent} out to play, which may
* cause Chrome to move to the background.
* @param activity The {@link Activity} to use to interact with Play.
*/
public void startUpdate(Activity activity) {
try {
boolean success = mAppUpdateManager.startUpdateFlowForResult(
mAppUpdateInfo, AppUpdateType.FLEXIBLE, activity, REQUEST_CODE);
} catch (SendIntentException exception) {
mInstallStatus = InstallStatus.FAILED;
}
// TODO(dtrainor): Use success.
}
/**
* Completes the Play installation process, if possible. This may cause Chrome to restart.
*/
public void completeUpdate() {
mAppUpdateManager.completeUpdate()
.addOnSuccessListener(unused -> { pushStatus(); })
.addOnFailureListener(exception -> {
mInstallStatus = InstallStatus.FAILED;
pushStatus();
});
}
// InstallStateUpdatedListener implementation.
@Override
public void onStateUpdate(InstallState state) {
mInstallStatus = state.installStatus();
pushStatus();
}
private void pullCurrentState() {
mAppUpdateManager.getAppUpdateInfo()
.addOnSuccessListener(info -> {
mAppUpdateInfo = info;
mUpdateAvailability = info.updateAvailability();
mInstallStatus = info.installStatus();
pushStatus();
})
.addOnFailureListener(exception -> {
mAppUpdateInfo = null;
mUpdateAvailability = UpdateAvailability.UNKNOWN;
mInstallStatus = InstallStatus.UNKNOWN;
pushStatus();
});
}
private void pushStatus() {
if (!mEnabled || mUpdateAvailability == null || mInstallStatus == null) return;
@UpdateState
int newState = toUpdateState(mUpdateAvailability, mInstallStatus);
if (mUpdateState != null && mUpdateState == newState) return;
mUpdateState = newState;
mCallback.run();
}
private static @UpdateState int toUpdateState(
@UpdateAvailability int updateAvailability, @InstallStatus int installStatus) {
@UpdateState
int newStatus = UpdateState.NONE;
// Note, use InstallStatus first then UpdateAvailability if InstallStatus doesn't indicate
// a currently active install.
switch (installStatus) {
case InstallStatus.PENDING:
// Intentional fall through.
case InstallStatus.DOWNLOADING:
newStatus = UpdateState.INLINE_UPDATE_DOWNLOADING;
break;
case InstallStatus.DOWNLOADED:
newStatus = UpdateState.INLINE_UPDATE_READY;
break;
case InstallStatus.FAILED:
newStatus = UpdateState.INLINE_UPDATE_FAILED;
break;
}
if (newStatus == UpdateState.NONE) {
switch (updateAvailability) {
case UpdateAvailability.UPDATE_AVAILABLE:
newStatus = UpdateState.INLINE_UPDATE_AVAILABLE;
break;
}
}
return newStatus;
}
}