Preload overview activity in background

To reduce the jank when swiping-up the overview which is not
ready yet, we restart the overview activity if the process
was died and update its configuration if there is change.

Bug: 127350205
Test: manual - Enable 2-button or gesture navigation.
      Swipe-up overview after:
      Case 1: Other app is in foreground and kill the process
              of launcher.
      Case 2: Change configuration, e.g. font size, language.

Change-Id: Ia6e365cc0faf3765781484d040bdddd4e10a2650
This commit is contained in:
Riddle Hsu
2019-06-27 00:37:38 -06:00
parent bd0003da99
commit 70697e48a8
2 changed files with 116 additions and 28 deletions
@@ -43,6 +43,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
@@ -74,6 +76,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.logging.EventLogArray;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.UiThreadHelper;
@@ -92,6 +95,7 @@ import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -153,6 +157,7 @@ public class TouchInteractionService extends Service implements
.asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor);
MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet);
MAIN_THREAD_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */));
}
@Override
@@ -688,6 +693,54 @@ public class TouchInteractionService extends Service implements
}
}
private void preloadOverview(boolean fromInit) {
if (!mIsUserUnlocked) {
return;
}
final ActivityControlHelper<BaseDraggingActivity> activityControl =
mOverviewComponentObserver.getActivityControlHelper();
if (activityControl.getCreatedActivity() == null) {
// Make sure that UI states will be initialized.
activityControl.createActivityInitListener((activity, wasVisible) -> {
AppLaunchTracker.INSTANCE.get(activity);
return false;
}).register();
} else if (fromInit) {
// The activity has been created before the initialization of overview service. It is
// usually happens when booting or launcher is the top activity, so we should already
// have the latest state.
return;
}
// Pass null animation handler to indicate this start is preload.
BackgroundExecutor.get().submit(
() -> ActivityManagerWrapper.getInstance().startRecentsActivity(
mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(),
null /* assistDataReceiver */, null /* animationHandler */,
null /* resultCallback */, null /* resultCallbackHandler */));
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (!mIsUserUnlocked) {
return;
}
final ActivityControlHelper activityControl =
mOverviewComponentObserver.getActivityControlHelper();
final BaseDraggingActivity activity = activityControl.getCreatedActivity();
if (activity == null || activity.isStarted()) {
// We only care about the existing background activity.
return;
}
if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(),
activity.getResources().getConfiguration().diff(newConfig))) {
return;
}
preloadOverview(false /* fromInit */);
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) {
@@ -29,11 +29,15 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.util.SparseIntArray;
import com.android.systemui.shared.system.PackageManagerWrapper;
import java.util.ArrayList;
import java.util.Objects;
/**
* Class to keep track of the current overview component based off user preferences and app updates
@@ -53,22 +57,41 @@ public final class OverviewComponentObserver {
}
};
private final Context mContext;
private final ComponentName mMyHomeComponent;
private final Intent mCurrentHomeIntent;
private final Intent mMyHomeIntent;
private final Intent mFallbackIntent;
private final SparseIntArray mConfigChangesMap = new SparseIntArray();
private String mUpdateRegisteredPackage;
private ActivityControlHelper mActivityControlHelper;
private Intent mOverviewIntent;
private Intent mHomeIntent;
private int mSystemUiStateFlags;
private boolean mIsHomeAndOverviewSame;
private boolean mIsDefaultHome;
public OverviewComponentObserver(Context context) {
mContext = context;
Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setPackage(mContext.getPackageName());
ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
ComponentName myHomeComponent =
new ComponentName(context.getPackageName(), info.activityInfo.name);
mMyHomeIntent.setComponent(myHomeComponent);
mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
mFallbackIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_DEFAULT)
.setComponent(fallbackComponent)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
ActivityInfo fallbackInfo = context.getPackageManager().getActivityInfo(
mFallbackIntent.getComponent(), 0 /* flags */);
mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges);
} catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
mContext.registerReceiver(mUserPreferenceChangeReceiver,
new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
@@ -92,17 +115,14 @@ public final class OverviewComponentObserver {
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
final String overviewIntentCategory;
ComponentName overviewComponent;
mHomeIntent = null;
if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 &&
(defaultHome == null || mMyHomeComponent.equals(defaultHome))) {
mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
&& (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
overviewComponent = mMyHomeComponent;
mActivityControlHelper = new LauncherActivityControllerHelper();
mIsHomeAndOverviewSame = true;
overviewIntentCategory = Intent.CATEGORY_HOME;
mOverviewIntent = mMyHomeIntent;
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
if (mUpdateRegisteredPackage != null) {
// Remove any update listener as we don't care about other packages.
@@ -111,14 +131,11 @@ public final class OverviewComponentObserver {
}
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
overviewComponent = new ComponentName(mContext, RecentsActivity.class);
mActivityControlHelper = new FallbackActivityControllerHelper();
mIsHomeAndOverviewSame = false;
overviewIntentCategory = Intent.CATEGORY_DEFAULT;
mOverviewIntent = mFallbackIntent;
mCurrentHomeIntent.setComponent(defaultHome);
mHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setComponent(defaultHome);
// User's default home app can change as a result of package updates of this app (such
// as uninstalling the app or removing the "Launcher" feature in an update).
// Listen for package updates of this app (and remove any previously attached
@@ -138,14 +155,6 @@ public final class OverviewComponentObserver {
ACTION_PACKAGE_REMOVED));
}
}
mOverviewIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(overviewIntentCategory)
.setComponent(overviewComponent)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (mHomeIntent == null) {
mHomeIntent = mOverviewIntent;
}
}
/**
@@ -160,6 +169,32 @@ public final class OverviewComponentObserver {
}
}
/**
* @return {@code true} if the overview component is able to handle the configuration changes.
*/
boolean canHandleConfigChanges(ComponentName component, int changes) {
final int orientationChange =
ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE;
if ((changes & orientationChange) == orientationChange) {
// This is just an approximate guess for simple orientation change because the changes
// may contain non-public bits (e.g. window configuration).
return true;
}
int configMask = mConfigChangesMap.get(component.hashCode());
return configMask != 0 && (~configMask & changes) == 0;
}
/**
* Get the intent for overview activity. It is used when lockscreen is shown and home was died
* in background, we still want to restart the one that will be used after unlock.
*
* @return the overview intent
*/
Intent getOverviewIntentIgnoreSysUiState() {
return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent;
}
/**
* Get the current intent for going to the overview activity.
*
@@ -173,7 +208,7 @@ public final class OverviewComponentObserver {
* Get the current intent for going to the home activity.
*/
public Intent getHomeIntent() {
return mHomeIntent;
return mCurrentHomeIntent;
}
/**