Merge "Allows Launcher to recover gracefully into OverviewSplitSelect state" into tm-qpr-dev

This commit is contained in:
Jeremy Sim
2022-07-01 21:40:34 +00:00
committed by Android (Google) Code Review
10 changed files with 208 additions and 22 deletions
@@ -20,6 +20,8 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
@@ -70,6 +72,7 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.NavigationMode;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.UiThreadHelper;
@@ -91,10 +94,10 @@ import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -130,9 +133,19 @@ public abstract class BaseQuickstepLauncher extends Launcher {
private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
/**
* If Launcher restarted while in the middle of an Overview split select, it needs this data to
* recover. In all other cases this will remain null.
*/
private PendingSplitSelectInfo mPendingSplitSelectInfo = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mPendingSplitSelectInfo = ObjectWrapper.unwrap(
savedInstanceState.getIBinder(PENDING_SPLIT_SELECT_INFO));
}
addMultiWindowModeChangedListener(mDepthController);
initUnfoldTransitionProgressProvider();
}
@@ -643,4 +656,53 @@ public abstract class BaseQuickstepLauncher extends Launcher {
mDepthController.dump(prefix, writer);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// If Launcher shuts downs during split select, we save some extra data in the recovery
// bundle to allow graceful recovery. The normal LauncherState restore mechanism doesn't
// work in this case because restoring straight to OverviewSplitSelect without staging data,
// or before the tasks themselves have loaded into Overview, causes a crash. So we tell
// Launcher to first restore into Overview state, wait for the relevant tasks and icons to
// load in, and then proceed to OverviewSplitSelect.
if (isInState(OVERVIEW_SPLIT_SELECT)) {
SplitSelectStateController splitSelectStateController =
((RecentsView) getOverviewPanel()).getSplitPlaceholder();
// Launcher will restart in Overview and then transition to OverviewSplitSelect.
outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap(
new PendingSplitSelectInfo(
splitSelectStateController.getInitialTaskId(),
splitSelectStateController.getActiveSplitStagePosition()
)
));
outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal);
}
}
/**
* When Launcher restarts, it sometimes needs to recover to a split selection state.
* This function checks if such a recovery is needed.
* @return a boolean representing whether the launcher is waiting to recover to
* OverviewSplitSelect state.
*/
public boolean hasPendingSplitSelectInfo() {
return mPendingSplitSelectInfo != null;
}
/**
* See {@link #hasPendingSplitSelectInfo()}
*/
public @Nullable PendingSplitSelectInfo getPendingSplitSelectInfo() {
return mPendingSplitSelectInfo;
}
/**
* When the launcher has successfully recovered to OverviewSplitSelect state, this function
* deletes the recovery data, returning it to a null state.
*/
public void finishSplitSelectRecovery() {
mPendingSplitSelectInfo = null;
}
}
@@ -36,6 +36,7 @@ import com.android.launcher3.icons.IconProvider.IconChangeListener;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.TaskVisualsChangeListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -54,7 +55,8 @@ import java.util.function.Consumer;
* Singleton class to load and manage recents model.
*/
@TargetApi(Build.VERSION_CODES.O)
public class RecentsModel implements IconChangeListener, TaskStackChangeListener {
public class RecentsModel implements IconChangeListener, TaskStackChangeListener,
TaskVisualsChangeListener {
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
@@ -77,6 +79,7 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener
IconProvider iconProvider = new IconProvider(context);
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
mIconCache.registerTaskVisualsChangeListener(this);
mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
@@ -203,6 +206,13 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener
}
}
@Override
public void onTaskIconChanged(int taskId) {
for (TaskVisualsChangeListener listener : mThumbnailChangeListeners) {
listener.onTaskIconChanged(taskId);
}
}
@Override
public void onSystemIconStateChanged(String iconState) {
mIconCache.clearCache();
@@ -226,20 +236,4 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener
writer.println(prefix + "RecentsModel:");
mTaskList.dump(" ", writer);
}
/**
* Listener for receiving various task properties changes
*/
public interface TaskVisualsChangeListener {
/**
* Called whn the task thumbnail changes
*/
Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
/**
* Called when the icon for a task changes
*/
void onTaskIconChanged(String pkg, UserHandle user);
}
}
@@ -18,6 +18,7 @@ package com.android.quickstep;
import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.content.Context;
@@ -46,6 +47,7 @@ import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache;
import com.android.quickstep.util.TaskVisualsChangeListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -70,6 +72,9 @@ public class TaskIconCache implements DisplayInfoChangeListener {
private BaseIconFactory mIconFactory;
@Nullable
public TaskVisualsChangeListener mTaskVisualsChangeListener = null;
public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) {
mContext = context;
mBgExecutor = bgExecutor;
@@ -116,6 +121,7 @@ public class TaskIconCache implements DisplayInfoChangeListener {
task.icon = result.icon;
task.titleDescription = result.contentDescription;
callback.accept(task);
dispatchIconUpdate(task.key.id);
}
};
mBgExecutor.execute(request);
@@ -272,4 +278,18 @@ public class TaskIconCache implements DisplayInfoChangeListener {
public Drawable icon;
public String contentDescription = "";
}
void registerTaskVisualsChangeListener(TaskVisualsChangeListener newListener) {
mTaskVisualsChangeListener = newListener;
}
void removeTaskVisualsChangeListener() {
mTaskVisualsChangeListener = null;
}
void dispatchIconUpdate(int taskId) {
if (mTaskVisualsChangeListener != null) {
mTaskVisualsChangeListener.onTaskIconChanged(taskId);
}
}
}
@@ -330,4 +330,8 @@ public class SplitSelectStateController {
private boolean isInitialTaskIntentSet() {
return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskIntent != null);
}
public int getInitialTaskId() {
return mInitialTaskId;
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.util;
import android.os.UserHandle;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
/**
* Listener for receiving various task properties changes
*/
public interface TaskVisualsChangeListener {
/**
* Called when the task thumbnail changes
*/
default Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
return null;
}
/**
* Called when the icon for a task changes
*/
default void onTaskIconChanged(String pkg, UserHandle user) {}
/**
* Called when the icon for a task changes
*/
default void onTaskIconChanged(int taskId) {}
}
@@ -39,6 +39,7 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.util.SplitSelectStateController;
@@ -88,6 +89,21 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun
super.onTaskLaunchAnimationEnd(success);
}
@Override
public void onTaskIconChanged(int taskId) {
// If Launcher needs to return to split select state, do it now, after the icon has updated.
if (mActivity.hasPendingSplitSelectInfo()) {
PendingSplitSelectInfo recoveryData = mActivity.getPendingSplitSelectInfo();
if (recoveryData.getStagedTaskId() == taskId) {
initiateSplitSelect(
getTaskViewByTaskId(recoveryData.getStagedTaskId()),
recoveryData.getStagePosition()
);
mActivity.finishSplitSelectRecovery();
}
}
}
@Override
public void reset() {
super.reset();
@@ -152,7 +152,6 @@ import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.RemoteTargetGluer;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
@@ -170,6 +169,7 @@ import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TaskVisualsChangeListener;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.VibratorWrapper;
import com.android.systemui.plugins.ResourceProvider;
@@ -87,7 +87,6 @@ import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
+4 -2
View File
@@ -33,7 +33,6 @@ import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -266,7 +265,7 @@ public class Launcher extends StatefulActivity<LauncherState>
protected static final int REQUEST_LAST = 100;
// Type: int
private static final String RUNTIME_STATE = "launcher.state";
protected static final String RUNTIME_STATE = "launcher.state";
// Type: PendingRequestArgs
private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
// Type: int
@@ -278,6 +277,9 @@ public class Launcher extends StatefulActivity<LauncherState>
// Type int[]
private static final String RUNTIME_STATE_CURRENT_SCREEN_IDS = "launcher.current_screen_ids";
// Type PendingSplitSelectInfo<Parcelable>
protected static final String PENDING_SPLIT_SELECT_INFO = "launcher.pending_split_select_info";
public static final String ON_CREATE_EVT = "Launcher.onCreate";
public static final String ON_START_EVT = "Launcher.onStart";
public static final String ON_RESUME_EVT = "Launcher.onResume";
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.util;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
/**
* Utility class to store information regarding a split select request. This includes the taskId of
* the originating task, plus the stage position.
* This information is intended to be saved across launcher instances, e.g. when Launcher needs to
* recover straight into a split select state.
*/
public class PendingSplitSelectInfo {
private final int mStagedTaskId;
private final int mStagePosition;
public PendingSplitSelectInfo(int stagedTaskId, int stagePosition) {
this.mStagedTaskId = stagedTaskId;
this.mStagePosition = stagePosition;
}
public int getStagedTaskId() {
return mStagedTaskId;
}
public @StagePosition int getStagePosition() {
return mStagePosition;
}
}