Merge "DisplayController refactoring for multiple displays" into main
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 0fec4e6..f7014ff 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -28,7 +28,6 @@
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_SHOW_LOCKED_TASKBAR;
import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
-import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
@@ -103,9 +102,6 @@
public class TaskbarManager {
private static final String TAG = "TaskbarManager";
private static final boolean DEBUG = false;
- // TODO(b/382378283) remove all logs with this tag
- public static final String NULL_TASKBAR_ROOT_LAYOUT_TAG = "b/382378283";
- public static final String ILLEGAL_ARGUMENT_WM_ADD_VIEW = "b/391653300";
private static final int TASKBAR_DESTROY_DURATION = 100;
/**
@@ -131,10 +127,10 @@
Settings.Secure.NAV_BAR_KIDS_MODE);
private final Context mBaseContext;
- private TaskbarNavButtonCallbacks mNavCallbacks;
+ private final TaskbarNavButtonCallbacks mNavCallbacks;
// TODO: Remove this during the connected displays lifecycle refactor.
private final Context mPrimaryWindowContext;
- private WindowManager mPrimaryWindowManager;
+ private final WindowManager mPrimaryWindowManager;
private TaskbarNavButtonController mPrimaryNavButtonController;
private ComponentCallbacks mPrimaryComponentCallbacks;
@@ -180,48 +176,47 @@
@Override
public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
if ((flags & CHANGE_DENSITY) != 0) {
- debugTaskbarManager("onDisplayInfoChanged - Display density changed",
+ debugTaskbarManager("onDisplayInfoChanged: Display density changed",
context.getDisplayId());
}
if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
- debugTaskbarManager("onDisplayInfoChanged - Navigation mode changed",
+ debugTaskbarManager("onDisplayInfoChanged: Navigation mode changed",
context.getDisplayId());
}
if ((flags & CHANGE_DESKTOP_MODE) != 0) {
- debugTaskbarManager("onDisplayInfoChanged - Desktop mode changed",
+ debugTaskbarManager("onDisplayInfoChanged: Desktop mode changed",
context.getDisplayId());
}
if ((flags & CHANGE_TASKBAR_PINNING) != 0) {
- debugTaskbarManager("onDisplayInfoChanged - Taskbar pinning changed",
+ debugTaskbarManager("onDisplayInfoChanged: Taskbar pinning changed",
context.getDisplayId());
}
if ((flags & (CHANGE_DENSITY | CHANGE_NAVIGATION_MODE | CHANGE_DESKTOP_MODE
| CHANGE_TASKBAR_PINNING | CHANGE_SHOW_LOCKED_TASKBAR)) != 0) {
- debugTaskbarManager("onDisplayInfoChanged - Recreating Taskbar!",
+ debugTaskbarManager("onDisplayInfoChanged: Recreating Taskbar!",
context.getDisplayId());
TaskbarActivityContext taskbarActivityContext = getCurrentActivityContext();
if ((flags & CHANGE_SHOW_LOCKED_TASKBAR) != 0) {
- recreateTaskbar();
+ recreateTaskbars();
} else if ((flags & CHANGE_DESKTOP_MODE) != 0) {
// Only Handles Special Exit Cases for Desktop Mode Taskbar Recreation.
if (taskbarActivityContext != null
&& !DesktopVisibilityController.INSTANCE.get(taskbarActivityContext)
.isInDesktopMode()
&& !DisplayController.showLockedTaskbarOnHome(context)) {
- recreateTaskbar();
+ recreateTaskbars();
}
} else {
- recreateTaskbar();
+ recreateTaskbars();
}
-
}
}
}
private final SettingsCache.OnChangeListener mOnSettingsChangeListener = c -> {
- debugTaskbarManager("Settings changed! Recreating Taskbar!");
- recreateTaskbar();
+ debugPrimaryTaskbar("Settings changed! Recreating Taskbar!");
+ recreateTaskbars();
};
private final PerceptibleTaskListener mTaskStackListener;
@@ -283,8 +278,6 @@
}
}
- ;
-
private final DesktopVisibilityController.TaskbarDesktopModeListener
mTaskbarDesktopModeListener =
new DesktopVisibilityController.TaskbarDesktopModeListener() {
@@ -339,14 +332,12 @@
@Override
public void run() {
int displayId = getDefaultDisplayId();
- debugTaskbarManager("mActivityOnDestroyCallback running!", displayId);
+ debugTaskbarManager("onActivityDestroyed:", displayId);
if (mActivity != null) {
displayId = mActivity.getDisplayId();
mActivity.removeOnDeviceProfileChangeListener(
mDebugActivityDeviceProfileChanged);
- Log.d(TASKBAR_NOT_DESTROYED_TAG,
- "unregistering activity lifecycle callbacks from "
- + "onActivityDestroyed.");
+ debugTaskbarManager("onActivityDestroyed: unregistering callbacks", displayId);
mActivity.removeEventCallback(EVENT_DESTROYED, this);
}
if (mActivity == mRecentsViewContainer) {
@@ -355,7 +346,10 @@
mActivity = null;
TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
if (taskbar != null) {
+ debugTaskbarManager("onActivityDestroyed: setting taskbarUIController", displayId);
taskbar.setUIController(TaskbarUIController.DEFAULT);
+ } else {
+ debugTaskbarManager("onActivityDestroyed: taskbar is null!", displayId);
}
mUnfoldProgressProvider.setSourceProvider(null);
}
@@ -365,26 +359,26 @@
new UnfoldTransitionProgressProvider.TransitionProgressListener() {
@Override
public void onTransitionStarted() {
- debugTaskbarManager("fold/unfold transition started getting called.");
+ debugPrimaryTaskbar("fold/unfold transition started getting called.");
}
@Override
public void onTransitionProgress(float progress) {
- debugTaskbarManager(
+ debugPrimaryTaskbar(
"fold/unfold transition progress getting called. | progress="
+ progress);
}
@Override
public void onTransitionFinishing() {
- debugTaskbarManager(
+ debugPrimaryTaskbar(
"fold/unfold transition finishing getting called.");
}
@Override
public void onTransitionFinished() {
- debugTaskbarManager(
+ debugPrimaryTaskbar(
"fold/unfold transition finished getting called.");
}
};
@@ -402,7 +396,7 @@
// Set up primary display.
int primaryDisplayId = getDefaultDisplayId();
- debugTaskbarManager("TaskbarManager constructor", primaryDisplayId);
+ debugPrimaryTaskbar("TaskbarManager constructor");
mPrimaryWindowContext = createWindowContext(primaryDisplayId);
mPrimaryWindowManager = mPrimaryWindowContext.getSystemService(WindowManager.class);
DesktopVisibilityController.INSTANCE.get(
@@ -439,26 +433,32 @@
} else {
mTaskStackListener = null;
}
- debugTaskbarManager("TaskbarManager created");
- recreateTaskbar();
+ recreateTaskbars();
+ debugPrimaryTaskbar("TaskbarManager created");
}
private void destroyAllTaskbars() {
+ debugPrimaryTaskbar("destroyAllTaskbars");
for (int i = 0; i < mTaskbars.size(); i++) {
int displayId = mTaskbars.keyAt(i);
+ debugTaskbarManager("destroyAllTaskbars: call destroyTaskbarForDisplay", displayId);
destroyTaskbarForDisplay(displayId);
+
+ debugTaskbarManager("destroyAllTaskbars: call removeTaskbarRootViewFromWindow",
+ displayId);
removeTaskbarRootViewFromWindow(displayId);
}
}
private void destroyTaskbarForDisplay(int displayId) {
- Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "destroyTaskbarForDisplay: " + displayId);
+ debugTaskbarManager("destroyTaskbarForDisplay", displayId);
TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
- debugTaskbarManager("destroyTaskbarForDisplay: " + taskbar, displayId);
if (taskbar != null) {
taskbar.onDestroy();
// remove all defaults that we store
removeTaskbarFromMap(displayId);
+ } else {
+ debugTaskbarManager("destroyTaskbarForDisplay: taskbar is NULL!", displayId);
}
DeviceProfile dp = getDeviceProfile(displayId);
@@ -471,6 +471,7 @@
* Show Taskbar upon receiving broadcast
*/
private void showTaskbarFromBroadcast(Intent intent) {
+ debugPrimaryTaskbar("destroyTaskbarForDisplay");
// TODO: make this code displayId specific
TaskbarActivityContext taskbar = getTaskbarForDisplay(getDefaultDisplayId());
if (ACTION_SHOW_TASKBAR.equals(intent.getAction()) && taskbar != null) {
@@ -507,12 +508,15 @@
* Called when the user is unlocked
*/
public void onUserUnlocked() {
+ debugPrimaryTaskbar("onUserUnlocked");
mUserUnlocked = true;
DisplayController.INSTANCE.get(mPrimaryWindowContext).addChangeListener(
mRecreationListener);
- recreateTaskbar();
+ debugPrimaryTaskbar("onUserUnlocked: recreating all taskbars!");
+ recreateTaskbars();
for (int i = 0; i < mTaskbars.size(); i++) {
int displayId = mTaskbars.keyAt(i);
+ debugTaskbarManager("onUserUnlocked: addTaskbarRootViewToWindow()", displayId);
addTaskbarRootViewToWindow(displayId);
}
}
@@ -521,15 +525,15 @@
* Sets a {@link StatefulActivity} to act as taskbar callback
*/
public void setActivity(@NonNull StatefulActivity activity) {
+ debugPrimaryTaskbar("setActivity: mActivity=" + mActivity);
if (mActivity == activity) {
+ debugPrimaryTaskbar("setActivity: No need to set activity!");
return;
}
removeActivityCallbacksAndListeners();
mActivity = activity;
- debugTaskbarManager("Set mActivity=" + mActivity);
mActivity.addOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
- Log.d(TASKBAR_NOT_DESTROYED_TAG,
- "registering activity lifecycle callbacks from setActivity().");
+ debugPrimaryTaskbar("setActivity: registering activity lifecycle callbacks.");
mActivity.addEventCallback(EVENT_DESTROYED, mActivityOnDestroyCallback);
UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
getUnfoldTransitionProgressProviderForActivity(activity);
@@ -547,6 +551,7 @@
* Sets the current RecentsViewContainer, from which we create a TaskbarUIController.
*/
public void setRecentsViewContainer(@NonNull RecentsViewContainer recentsViewContainer) {
+ debugPrimaryTaskbar("setRecentsViewContainer");
if (mRecentsViewContainer == recentsViewContainer) {
return;
}
@@ -570,6 +575,7 @@
*/
private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity(
StatefulActivity activity) {
+ debugPrimaryTaskbar("getUnfoldTransitionProgressProviderForActivity");
if (!enableUnfoldStateAnimation()) {
if (activity instanceof QuickstepLauncher ql) {
return ql.getUnfoldTransitionProgressProvider();
@@ -582,6 +588,7 @@
/** Creates a {@link TaskbarUIController} to use with non default displays. */
private TaskbarUIController createTaskbarUIControllerForNonDefaultDisplay(int displayId) {
+ debugPrimaryTaskbar("createTaskbarUIControllerForNonDefaultDisplay");
if (RecentsDisplayModel.enableOverviewInWindow()) {
RecentsViewContainer rvc = mRecentsDisplayModel.getRecentsWindowManager(displayId);
if (rvc != null) {
@@ -597,6 +604,7 @@
*/
private TaskbarUIController createTaskbarUIControllerForRecentsViewContainer(
RecentsViewContainer container) {
+ debugPrimaryTaskbar("createTaskbarUIControllerForRecentsViewContainer");
if (container instanceof QuickstepLauncher quickstepLauncher) {
return new LauncherTaskbarUIController(quickstepLauncher);
}
@@ -617,15 +625,18 @@
* In other case (folding/unfolding) we don't need to remove and add window.
*/
@VisibleForTesting
- public synchronized void recreateTaskbar() {
+ public synchronized void recreateTaskbars() {
+ debugPrimaryTaskbar("recreateTaskbars");
// Handles initial creation case.
if (mTaskbars.size() == 0) {
+ debugTaskbarManager("recreateTaskbars: create primary taskbar", getDefaultDisplayId());
recreateTaskbarForDisplay(getDefaultDisplayId(), 0);
return;
}
for (int i = 0; i < mTaskbars.size(); i++) {
int displayId = mTaskbars.keyAt(i);
+ debugTaskbarManager("recreateTaskbars: create external taskbar", displayId);
recreateTaskbarForDisplay(displayId, 0);
}
}
@@ -636,16 +647,17 @@
* In other case (folding/unfolding) we don't need to remove and add window.
*/
private void recreateTaskbarForDisplay(int displayId, int duration) {
+ debugTaskbarManager("recreateTaskbarForDisplay: ", displayId);
Trace.beginSection("recreateTaskbarForDisplay");
try {
- Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "recreateTaskbarForDisplay: " + displayId);
+ debugTaskbarManager("recreateTaskbarForDisplay: getting device profile", displayId);
// TODO (b/381113004): make this display-specific via getWindowContext()
DeviceProfile dp = getDeviceProfile(displayId);
// All Apps action is unrelated to navbar unification, so we only need to check DP.
final boolean isLargeScreenTaskbar = dp != null && dp.isTaskbarPresent;
mAllAppsActionManager.setTaskbarPresent(isLargeScreenTaskbar);
-
+ debugTaskbarManager("recreateTaskbarForDisplay: destroying taskbar", displayId);
destroyTaskbarForDisplay(displayId);
boolean displayExists = getDisplay(displayId) != null;
@@ -654,17 +666,22 @@
+ " [dp != null (i.e. mUserUnlocked)]=" + (dp != null)
+ " FLAG_HIDE_NAVBAR_WINDOW=" + ENABLE_TASKBAR_NAVBAR_UNIFICATION
+ " dp.isTaskbarPresent=" + (dp == null ? "null" : dp.isTaskbarPresent)
- + " displayExists=" + displayExists);
+ + " displayExists=" + displayExists, displayId);
if (!isTaskbarEnabled || !isLargeScreenTaskbar || !displayExists) {
SystemUiProxy.INSTANCE.get(mBaseContext)
.notifyTaskbarStatus(/* visible */ false, /* stashed */ false);
if (!isTaskbarEnabled || !displayExists) {
+ debugTaskbarManager(
+ "recreateTaskbarForDisplay: exiting bc (!isTaskbarEnabled || "
+ + "!displayExists)",
+ displayId);
return;
}
}
TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
if (enableTaskbarNoRecreate() || taskbar == null) {
+ debugTaskbarManager("recreateTaskbarForDisplay: creating taskbar", displayId);
taskbar = createTaskbarActivityContext(dp, displayId);
if (taskbar == null) {
debugTaskbarManager(
@@ -672,6 +689,8 @@
return;
}
} else {
+ debugTaskbarManager("recreateTaskbarForDisplay: updating taskbar device profile",
+ displayId);
taskbar.updateDeviceProfile(dp);
}
mSharedState.startTaskbarVariantIsTransient =
@@ -690,15 +709,17 @@
}
if (enableTaskbarNoRecreate()) {
+ debugTaskbarManager("recreateTaskbarForDisplay: adding rootView", displayId);
addTaskbarRootViewToWindow(displayId);
FrameLayout taskbarRootLayout = getTaskbarRootLayoutForDisplay(displayId);
if (taskbarRootLayout != null) {
+ debugTaskbarManager("recreateTaskbarForDisplay: adding root layout", displayId);
taskbarRootLayout.removeAllViews();
taskbarRootLayout.addView(taskbar.getDragLayer());
taskbar.notifyUpdateLayoutParams();
} else {
- Log.e(NULL_TASKBAR_ROOT_LAYOUT_TAG,
- "taskbarRootLayout is null for displayId=" + displayId);
+ debugTaskbarManager("recreateTaskbarForDisplay: taskbarRootLayout is null!",
+ displayId);
}
}
} finally {
@@ -843,27 +864,62 @@
* primary device or a previously mirroring display is switched to extended mode.
*/
public void onDisplayAddSystemDecorations(int displayId) {
+ debugTaskbarManager("onDisplayAddSystemDecorations: ", displayId);
Display display = getDisplay(displayId);
- if (!DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue() || isDefaultDisplay(
- displayId) || display == null) {
- debugTaskbarManager("onDisplayAddSystemDecorations: not adding display");
+ if (display == null) {
+ debugTaskbarManager("onDisplayAddSystemDecorations: can't find display!", displayId);
return;
}
+ if (!DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue() || isDefaultDisplay(
+ displayId)) {
+ debugTaskbarManager(
+ "onDisplayAddSystemDecorations: not an external display! | "
+ + "ENABLE_TASKBAR_CONNECTED_DISPLAYS="
+ + DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
+ + " isDefaultDisplay=" + isDefaultDisplay(displayId), displayId);
+ return;
+ }
+ debugTaskbarManager("onDisplayAddSystemDecorations: creating new windowContext!",
+ displayId);
Context newWindowContext = createWindowContext(displayId);
if (newWindowContext != null) {
+ debugTaskbarManager("onDisplayAddSystemDecorations: add new windowContext to map!",
+ displayId);
addWindowContextToMap(displayId, newWindowContext);
- // TODO (b/391965805): remove once onDisplayAddSystemDecorations is working.
WindowManager wm = getWindowManager(displayId);
if (wm == null || !wm.shouldShowSystemDecors(displayId)) {
+ String wmStatus = wm == null ? "WindowManager is null!" : "WindowManager exists";
+ boolean showDecor = wm != null && wm.shouldShowSystemDecors(displayId);
+ debugTaskbarManager(
+ "onDisplayAddSystemDecorations:\n\t" + wmStatus + "\n\tshowSystemDecors="
+ + showDecor, displayId);
return;
}
+ debugTaskbarManager("onDisplayAddSystemDecorations: creating RootLayout!", displayId);
+
createExternalDeviceProfile(displayId);
+
+ debugTaskbarManager("onDisplayAddSystemDecorations: creating RootLayout!", displayId);
createTaskbarRootLayout(displayId);
+
+ debugTaskbarManager("onDisplayAddSystemDecorations: creating NavButtonController!",
+ displayId);
createNavButtonController(displayId);
+
+ debugTaskbarManager(
+ "onDisplayAddSystemDecorations: createAndRegisterComponentCallbacks!",
+ displayId);
createAndRegisterComponentCallbacks(displayId);
+ debugTaskbarManager("onDisplayAddSystemDecorations: recreateTaskbarForDisplay!",
+ displayId);
recreateTaskbarForDisplay(displayId, 0);
+ } else {
+ debugTaskbarManager("onDisplayAddSystemDecorations: newWindowContext is NULL!",
+ displayId);
}
+
+ debugTaskbarManager("onDisplayAddSystemDecorations: finished!", displayId);
}
/**
@@ -871,18 +927,38 @@
* removed from the primary device.
*/
public void onDisplayRemoved(int displayId) {
+ debugTaskbarManager("onDisplayRemoved: ", displayId);
if (!DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue() || isDefaultDisplay(
displayId)) {
+ debugTaskbarManager(
+ "onDisplayRemoved: not an external display! | "
+ + "ENABLE_TASKBAR_CONNECTED_DISPLAYS="
+ + DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
+ + " isDefaultDisplay=" + isDefaultDisplay(displayId), displayId);
return;
}
Context windowContext = getWindowContext(displayId);
if (windowContext != null) {
+ debugTaskbarManager("onDisplayRemoved: removing NavButtonController!", displayId);
removeNavButtonController(displayId);
+
+ debugTaskbarManager("onDisplayRemoved: removeAndUnregisterComponentCallbacks!",
+ displayId);
removeAndUnregisterComponentCallbacks(displayId);
+
+ debugTaskbarManager("onDisplayRemoved: destroying Taskbar!", displayId);
destroyTaskbarForDisplay(displayId);
+
+ debugTaskbarManager("onDisplayRemoved: removing DeviceProfile from map!", displayId);
removeDeviceProfileFromMap(displayId);
+
+ debugTaskbarManager("onDisplayRemoved: removing WindowContext from map!", displayId);
removeWindowContextFromMap(displayId);
+
+ debugTaskbarManager("onDisplayRemoved: finished!", displayId);
+ } else {
+ debugTaskbarManager("onDisplayRemoved: removing NavButtonController!", displayId);
}
}
@@ -898,9 +974,7 @@
private void removeActivityCallbacksAndListeners() {
if (mActivity != null) {
mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
- Log.d(TASKBAR_NOT_DESTROYED_TAG,
- "unregistering activity lifecycle callbacks from "
- + "removeActivityCallbackAndListeners().");
+ debugPrimaryTaskbar("unregistering activity lifecycle callbacks");
mActivity.removeEventCallback(EVENT_DESTROYED, mActivityOnDestroyCallback);
UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
getUnfoldTransitionProgressProviderForActivity(mActivity);
@@ -914,8 +988,9 @@
* Called when the manager is no longer needed
*/
public void destroy() {
+ debugPrimaryTaskbar("TaskbarManager#destroy()");
mRecentsViewContainer = null;
- debugTaskbarManager("TaskbarManager#destroy()");
+ debugPrimaryTaskbar("destroy: removing activity callbacks");
DesktopVisibilityController.INSTANCE.get(
mPrimaryWindowContext).unregisterTaskbarDesktopModeListener(
mTaskbarDesktopModeListener);
@@ -930,7 +1005,7 @@
.unregister(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
SettingsCache.INSTANCE.get(mPrimaryWindowContext)
.unregister(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
- Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy().");
+ debugPrimaryTaskbar("destroy: unregistering component callbacks");
removeAndUnregisterComponentCallbacks(getDefaultDisplayId());
mShutdownReceiver.unregisterReceiverSafely();
if (ActivityManagerWrapper.usePerceptibleTasks(getPrimaryWindowContext())) {
@@ -939,7 +1014,9 @@
}
}
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ debugPrimaryTaskbar("destroy: destroying all taskbars!");
destroyAllTaskbars();
+ debugPrimaryTaskbar("destroy: finished!");
}
public @Nullable TaskbarActivityContext getCurrentActivityContext() {
@@ -962,14 +1039,15 @@
}
private void addTaskbarRootViewToWindow(int displayId) {
+ debugTaskbarManager("addTaskbarRootViewToWindow:", displayId);
TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
if (!enableTaskbarNoRecreate() || taskbar == null) {
- debugTaskbarManager("addTaskbarRootViewToWindow - taskbar null", displayId);
+ debugTaskbarManager("addTaskbarRootViewToWindow: taskbar null", displayId);
return;
}
if (getDisplay(displayId) == null) {
- debugTaskbarManager("addTaskbarRootViewToWindow - display null", displayId);
+ debugTaskbarManager("addTaskbarRootViewToWindow: display null", displayId);
return;
}
@@ -980,18 +1058,21 @@
windowManager.addView(rootLayout, taskbar.getWindowLayoutParams());
mAddedRootLayouts.put(displayId, true);
} else {
- Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW,
- "addTaskbarRootViewToWindow - root layout null | displayId=" + displayId);
+ String rootLayoutStatus =
+ (rootLayout == null) ? "rootLayout is NULL!" : "rootLayout exists!";
+ String wmStatus = (windowManager == null) ? "windowManager is NULL!"
+ : "windowManager exists!";
+ debugTaskbarManager(
+ "addTaskbarRootViewToWindow: \n\t" + rootLayoutStatus + "\n\t" + wmStatus,
+ displayId);
}
} else {
- Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG,
- "addTaskbarRootViewToWindow - root layout already added | displayId="
- + displayId);
+ debugTaskbarManager("addTaskbarRootViewToWindow: rootLayout already added!", displayId);
}
}
private void removeTaskbarRootViewFromWindow(int displayId) {
- Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "removeTaskbarRootViewFromWindow: " + displayId);
+ debugTaskbarManager("removeTaskbarRootViewFromWindow", displayId);
FrameLayout rootLayout = getTaskbarRootLayoutForDisplay(displayId);
if (!enableTaskbarNoRecreate() || rootLayout == null) {
return;
@@ -1003,7 +1084,7 @@
mAddedRootLayouts.put(displayId, false);
removeTaskbarRootLayoutFromMap(displayId);
} else {
- debugTaskbarManager("removeTaskbarRootViewFromWindow - WindowManager is null",
+ debugTaskbarManager("removeTaskbarRootViewFromWindow: WindowManager is null",
displayId);
}
}
@@ -1142,6 +1223,7 @@
* @param displayId The ID of the display.
*/
private void createAndRegisterComponentCallbacks(int displayId) {
+ debugTaskbarManager("createAndRegisterComponentCallbacks", displayId);
ComponentCallbacks callbacks = new ComponentCallbacks() {
private Configuration mOldConfig =
getWindowContext(displayId).getResources().getConfiguration();
@@ -1150,14 +1232,13 @@
public void onConfigurationChanged(Configuration newConfig) {
Trace.instantForTrack(Trace.TRACE_TAG_APP, "TaskbarManager",
"onConfigurationChanged: " + newConfig);
- debugTaskbarManager(
- "TaskbarManager#mComponentCallbacks.onConfigurationChanged: " + newConfig);
+ debugTaskbarManager("onConfigurationChanged: " + newConfig, displayId);
DeviceProfile dp = getDeviceProfile(displayId);
int configDiff = mOldConfig.diff(newConfig) & ~SKIP_RECREATE_CONFIG_CHANGES;
if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0) {
- Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "onConfigurationChanged: theme changed");
+ debugTaskbarManager("onConfigurationChanged: theme changed", displayId);
// Only recreate for theme changes, not other UI mode changes such as docking.
int oldUiNightMode = (mOldConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
int newUiNightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
@@ -1166,27 +1247,36 @@
}
}
- debugTaskbarManager("ComponentCallbacks#onConfigurationChanged() "
- + "configDiff=" + Configuration.configurationDiffToString(configDiff));
+ debugTaskbarManager("onConfigurationChanged: | configDiff="
+ + Configuration.configurationDiffToString(configDiff), displayId);
if (configDiff != 0 || getCurrentActivityContext() == null) {
- recreateTaskbar();
- } else {
+ debugTaskbarManager("onConfigurationChanged: call recreateTaskbars", displayId);
+ recreateTaskbars();
+ } else if (dp != null) {
// Config change might be handled without re-creating the taskbar
- if (dp != null && !isTaskbarEnabled(dp)) {
+ if (!isTaskbarEnabled(dp)) {
+ debugPrimaryTaskbar(
+ "onConfigurationChanged: isTaskbarEnabled(dp)=False | "
+ + "destroyTaskbarForDisplay");
destroyTaskbarForDisplay(getDefaultDisplayId());
} else {
- if (dp != null && isTaskbarEnabled(dp)) {
- if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
- // Re-initialize for screen size change? Should this be done
- // by looking at screen-size change flag in configDiff in the
- // block above?
- recreateTaskbar();
- } else {
- getCurrentActivityContext().updateDeviceProfile(dp);
- }
+ debugPrimaryTaskbar("onConfigurationChanged: isTaskbarEnabled(dp)=True");
+ if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
+ // Re-initialize for screen size change? Should this be done
+ // by looking at screen-size change flag in configDiff in the
+ // block above?
+ debugPrimaryTaskbar("onConfigurationChanged: call recreateTaskbars");
+ recreateTaskbars();
+ } else {
+ debugPrimaryTaskbar(
+ "onConfigurationChanged: updateDeviceProfile for current "
+ + "taskbar.");
+ getCurrentActivityContext().updateDeviceProfile(dp);
}
- getCurrentActivityContext().onConfigurationChanged(configDiff);
}
+ } else {
+
+ getCurrentActivityContext().onConfigurationChanged(configDiff);
}
mOldConfig = new Configuration(newConfig);
// reset taskbar was pinned value, so we don't automatically unstash taskbar upon
@@ -1294,7 +1384,7 @@
* @param displayId The ID of the display for which to create the taskbar root layout.
*/
private void createTaskbarRootLayout(int displayId) {
- Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "createTaskbarRootLayout: " + displayId);
+ debugTaskbarManager("createTaskbarRootLayout: ", displayId);
if (!enableTaskbarNoRecreate()) {
return;
}
@@ -1302,6 +1392,7 @@
FrameLayout newTaskbarRootLayout = new FrameLayout(getWindowContext(displayId)) {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ debugTaskbarManager("dispatchTouchEvent: ", displayId);
// The motion events can be outside the view bounds of task bar, and hence
// manually dispatching them to the drag layer here.
TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
@@ -1311,8 +1402,9 @@
return super.dispatchTouchEvent(ev);
}
};
+
+ debugTaskbarManager("createTaskbarRootLayout: adding to map", displayId);
addTaskbarRootLayoutToMap(displayId, newTaskbarRootLayout);
- Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "created new root layout - displayId=" + displayId);
}
private boolean isDefaultDisplay(int displayId) {
@@ -1326,13 +1418,12 @@
* @return The taskbar root layout {@link FrameLayout} for a given display or {@code null}.
*/
private FrameLayout getTaskbarRootLayoutForDisplay(int displayId) {
- Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "getTaskbarRootLayoutForDisplay: " + displayId);
+ debugTaskbarManager("getTaskbarRootLayoutForDisplay:", displayId);
FrameLayout frameLayout = mRootLayouts.get(displayId);
if (frameLayout != null) {
return frameLayout;
} else {
- Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG,
- "getTaskbarRootLayoutForDisplay == null | displayId=" + displayId);
+ debugTaskbarManager("getTaskbarRootLayoutForDisplay: rootLayout is null!", displayId);
return null;
}
}
@@ -1344,11 +1435,14 @@
* @param rootLayout The taskbar root layout {@link FrameLayout} to add to the map.
*/
private void addTaskbarRootLayoutToMap(int displayId, FrameLayout rootLayout) {
+ debugTaskbarManager("addTaskbarRootLayoutToMap: ", displayId);
if (!mRootLayouts.contains(displayId) && rootLayout != null) {
mRootLayouts.put(displayId, rootLayout);
}
- Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "mRootLayouts.size()=" + mRootLayouts.size());
+ debugTaskbarManager(
+ "addTaskbarRootLayoutToMap: finished! mRootLayouts.size()=" + mRootLayouts.size(),
+ displayId);
}
/**
@@ -1357,12 +1451,14 @@
* @param displayId The ID of the display for which to remove the taskbar root layout.
*/
private void removeTaskbarRootLayoutFromMap(int displayId) {
+ debugTaskbarManager("removeTaskbarRootLayoutFromMap:", displayId);
if (mRootLayouts.contains(displayId)) {
mAddedRootLayouts.delete(displayId);
mRootLayouts.delete(displayId);
}
- Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "mRootLayouts.size()=" + mRootLayouts.size());
+ debugTaskbarManager("removeTaskbarRootLayoutFromMap: finished! mRootLayouts.size="
+ + mRootLayouts.size(), displayId);
}
/**
@@ -1371,10 +1467,10 @@
* @param displayId The ID of the display for which to create the window context.
*/
private @Nullable Context createWindowContext(int displayId) {
- debugTaskbarManager("createWindowContext: " + displayId);
+ debugTaskbarManager("createWindowContext: ", displayId);
Display display = getDisplay(displayId);
if (display == null) {
- debugTaskbarManager("createWindowContext: display null", displayId);
+ debugTaskbarManager("createWindowContext: display null!", displayId);
return null;
}
@@ -1471,23 +1567,62 @@
return mBaseContext.getDisplayId();
}
- /** Temp logs for b/254119092. */
- public void debugTaskbarManager(String debugReason) {
- debugTaskbarManager(debugReason, getDefaultDisplayId());
- }
-
- /** Temp logs for b/254119092. */
+ /**
+ * Logs debug information about the TaskbarManager for primary display.
+ * @param debugReason A string describing the reason for the debug log.
+ * @param displayId The ID of the display for which to log debug information.
+ */
public void debugTaskbarManager(String debugReason, int displayId) {
StringJoiner log = new StringJoiner("\n");
log.add(debugReason + " displayId=" + displayId + " isDefaultDisplay=" + isDefaultDisplay(
displayId));
+ Log.d(TAG, log.toString());
+ }
+ /**
+ * Logs verbose debug information about the TaskbarManager for primary display.
+ * @param debugReason A string describing the reason for the debug log.
+ * @param displayId The ID of the display for which to log debug information.
+ * @param verbose Indicates whether or not to debug with detail.
+ */
+ public void debugTaskbarManager(String debugReason, int displayId, boolean verbose) {
+ StringJoiner log = new StringJoiner("\n");
+ log.add(debugReason + " displayId=" + displayId + " isDefaultDisplay=" + isDefaultDisplay(
+ displayId));
+ if (verbose) {
+ generateVerboseLogs(log, displayId);
+ }
+ Log.d(TAG, log.toString());
+ }
+
+ /**
+ * Logs debug information about the TaskbarManager for primary display.
+ * @param debugReason A string describing the reason for the debug log.
+ *
+ */
+ public void debugPrimaryTaskbar(String debugReason) {
+ debugTaskbarManager(debugReason, getDefaultDisplayId(), false);
+ }
+
+ /**
+ * Logs debug information about the TaskbarManager for primary display.
+ * @param debugReason A string describing the reason for the debug log.
+ *
+ */
+ public void debugPrimaryTaskbar(String debugReason, boolean verbose) {
+ debugTaskbarManager(debugReason, getDefaultDisplayId(), verbose);
+ }
+
+ /**
+ * Logs verbose debug information about the TaskbarManager for a specific display.
+ */
+ private void generateVerboseLogs(StringJoiner log, int displayId) {
boolean activityTaskbarPresent = mActivity != null
&& mActivity.getDeviceProfile().isTaskbarPresent;
// TODO (b/381113004): make this display-specific via getWindowContext()
Context windowContext = mPrimaryWindowContext;
if (windowContext == null) {
- log.add("window context for displayId" + displayId);
+ log.add("windowContext is null!");
return;
}
@@ -1498,7 +1633,7 @@
}
if (activityTaskbarPresent == contextTaskbarPresent) {
log.add("mActivity and mWindowContext agree taskbarIsPresent=" + contextTaskbarPresent);
- Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString());
+ Log.d(TAG, log.toString());
return;
}
@@ -1522,11 +1657,9 @@
} else {
log.add("\t\tCouldn't get DeviceProfile because !mUserUnlocked");
}
-
- Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString());
}
private final DeviceProfile.OnDeviceProfileChangeListener mDebugActivityDeviceProfileChanged =
- dp -> debugTaskbarManager("mActivity onDeviceProfileChanged");
+ dp -> debugPrimaryTaskbar("mActivity onDeviceProfileChanged", true);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 019e746..aab8ad1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -1380,7 +1380,8 @@
SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx);
TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
if (taskbarManager != null) {
- taskbarManager.debugTaskbarManager("QuickstepLauncher#onDeviceProfileChanged");
+ taskbarManager.debugPrimaryTaskbar("QuickstepLauncher#onDeviceProfileChanged",
+ true);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
index cca8bf8..04e1905 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
@@ -27,17 +27,16 @@
import com.android.launcher3.anim.AnimatorListeners.forSuccessCallback
import com.android.launcher3.anim.PendingAnimation
import com.android.launcher3.anim.PropertySetter
-import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.statemanager.StateManager.StateHandler
import com.android.launcher3.states.StateAnimationConfig
import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE
import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE
import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL
import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE
-import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE
import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X
import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y
import com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW
+import com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE
import com.android.quickstep.util.AnimUtils
import com.android.quickstep.views.ClearAllButton
import com.android.quickstep.views.RecentsView
@@ -226,8 +225,8 @@
builder: PendingAnimation,
animate: Boolean,
) {
- val goingToOverviewFromWorkspaceContextual = toState == LauncherState.OVERVIEW &&
- launcher.isSplitSelectionActive
+ val goingToOverviewFromWorkspaceContextual =
+ toState == LauncherState.OVERVIEW && launcher.isSplitSelectionActive
if (
toState != LauncherState.OVERVIEW_SPLIT_SELECT &&
!goingToOverviewFromWorkspaceContextual
@@ -302,6 +301,14 @@
overviewButtonAlpha,
config.getInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, LINEAR),
)
+ recentsView.addDeskButton?.let {
+ propertySetter.setFloat(
+ it.visibilityAlphaProperty,
+ MULTI_PROPERTY_VALUE,
+ if (state.areElementsVisible(launcher, LauncherState.ADD_DESK_BUTTON)) 1f else 0f,
+ LINEAR,
+ )
+ }
}
private fun getOverviewInterpolator(fromState: LauncherState, toState: LauncherState) =
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index ca388c6..b1196af 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -77,7 +77,8 @@
return super.getVisibleElements(launcher)
& ~OVERVIEW_ACTIONS
& ~CLEAR_ALL_BUTTON
- & ~VERTICAL_SWIPE_INDICATOR;
+ & ~VERTICAL_SWIPE_INDICATOR
+ & ~ADD_DESK_BUTTON;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 80fc5fa..0c0b4fd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -45,7 +45,7 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return OVERVIEW_ACTIONS | CLEAR_ALL_BUTTON;
+ return OVERVIEW_ACTIONS;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 15216fe..5fdedcc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -110,7 +110,7 @@
@Override
public int getVisibleElements(Launcher launcher) {
- int elements = CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
+ int elements = CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS | ADD_DESK_BUTTON;
DeviceProfile dp = launcher.getDeviceProfile();
boolean showFloatingSearch;
if (dp.isPhone) {
@@ -124,7 +124,7 @@
elements |= FLOATING_SEARCH_BAR;
}
if (launcher.isSplitSelectionActive()) {
- elements &= ~CLEAR_ALL_BUTTON;
+ elements &= ~CLEAR_ALL_BUTTON & ~ADD_DESK_BUTTON;
}
return elements;
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 9b0e75c..f47937c 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -177,7 +177,7 @@
case TestProtocol.REQUEST_RECREATE_TASKBAR:
// Allow null-pointer to catch illegal states.
- runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
+ runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbars());
return response;
case TestProtocol.REQUEST_TASKBAR_IME_DOCKED:
return getTISBinderUIProperty(Bundle::putBoolean, tisBinder ->
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 56dd696..537092f 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.DESKTOP_CAROUSEL_DETACH_PROGRESS;
@@ -99,6 +100,11 @@
float clearAllButtonAlpha = state.hasClearAllButton() ? 1 : 0;
setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
clearAllButtonAlpha, LINEAR);
+ if (mRecentsView.getAddDeskButton() != null) {
+ float addDeskButtonAlpha = state.hasAddDeskButton() ? 1 : 0;
+ setter.setFloat(mRecentsView.getAddDeskButton().getVisibilityAlphaProperty(),
+ MULTI_PROPERTY_VALUE, addDeskButtonAlpha, LINEAR);
+ }
float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
setter.setFloat(mRecentsViewContainer.getActionsView().getVisibilityAlpha(),
AnimatedFloat.VALUE, overviewButtonAlpha, LINEAR);
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index f27b60c..f722c5d 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -45,14 +45,16 @@
private static final int FLAG_RECENTS_VIEW_VISIBLE = BaseState.getFlag(7);
private static final int FLAG_TASK_THUMBNAIL_SPLASH = BaseState.getFlag(8);
private static final int FLAG_DETACH_DESKTOP_CAROUSEL = BaseState.getFlag(9);
+ private static final int FLAG_ADD_DESK_BUTTON = BaseState.getFlag(10);
private static final RecentsState[] sAllStates = new RecentsState[6];
public static final RecentsState DEFAULT = new RecentsState(0,
FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID
- | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE);
+ | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE
+ | FLAG_ADD_DESK_BUTTON);
public static final RecentsState MODAL_TASK = new ModalState(1,
- FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
+ FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
| FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE);
public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN
@@ -122,6 +124,13 @@
}
/**
+ * For this state, whether add desk button should be shown.
+ */
+ public boolean hasAddDeskButton() {
+ return hasFlag(FLAG_ADD_DESK_BUTTON);
+ }
+
+ /**
* For this state, whether overview actions should be shown.
*/
public boolean hasOverviewActions() {
diff --git a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
index 9943770..244c3b2 100644
--- a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
+++ b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
@@ -20,10 +20,12 @@
import android.graphics.Canvas
import android.graphics.Rect
import android.util.AttributeSet
+import android.view.View
import android.widget.ImageButton
import com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X
import com.android.launcher3.R
import com.android.launcher3.util.MultiPropertyFactory
+import com.android.launcher3.util.MultiValueAlpha
import com.android.quickstep.util.BorderAnimator
import com.android.quickstep.util.BorderAnimator.Companion.createSimpleBorderAnimator
@@ -34,6 +36,22 @@
class AddDesktopButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
ImageButton(context, attrs) {
+ private enum class Alpha {
+ CONTENT,
+ VISIBILITY,
+ }
+
+ private val addDeskButtonAlpha = MultiValueAlpha(this, Alpha.entries.size)
+
+ var contentAlpha
+ set(value) {
+ addDeskButtonAlpha.get(Alpha.CONTENT.ordinal).value = value
+ }
+ get() = addDeskButtonAlpha.get(Alpha.CONTENT.ordinal).value
+
+ val visibilityAlphaProperty: MultiPropertyFactory<View>.MultiProperty
+ get() = addDeskButtonAlpha.get(Alpha.VISIBILITY.ordinal)
+
private enum class TranslationX {
GRID,
OFFSET,
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index d17dfb8..6b91df1 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -4762,9 +4762,8 @@
}
mClearAllButton.setContentAlpha(mContentAlpha);
- // TODO(b/389209338): Handle the visibility of the `mAddDesktopButton`.
if (mAddDesktopButton != null) {
- mAddDesktopButton.setAlpha(mContentAlpha);
+ mAddDesktopButton.setContentAlpha(mContentAlpha);
}
int alphaInt = Math.round(alpha * 255);
mEmptyMessagePaint.setAlpha(alphaInt);
@@ -6329,6 +6328,11 @@
return mClearAllButton;
}
+ @Nullable
+ public AddDesktopButton getAddDeskButton() {
+ return mAddDesktopButton;
+ }
+
/**
* @return How many pixels the running task is offset on the currently laid out dominant axis.
*/
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
deleted file mode 100644
index 0b3eb75..0000000
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright (C) 2018 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.views;
-
-import static com.android.app.animation.Interpolators.EMPHASIZED;
-import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
-import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.quickstep.views.TaskThumbnailViewDeprecated.DIM_ALPHA;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.RectShape;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.app.animation.Interpolators;
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
-import com.android.quickstep.util.TaskCornerRadius;
-
-/**
- * Contains options for a recent task when long-pressing its icon.
- */
-public class TaskMenuView extends AbstractFloatingView {
-
- private static final Rect sTempRect = new Rect();
-
- private static final int REVEAL_OPEN_DURATION = enableOverviewIconMenu() ? 417 : 150;
- private static final int REVEAL_CLOSE_DURATION = enableOverviewIconMenu() ? 333 : 100;
-
- private RecentsViewContainer mContainer;
- private TextView mTaskName;
- @Nullable
- private AnimatorSet mOpenCloseAnimator;
- @Nullable
- private ValueAnimator mRevealAnimator;
- @Nullable private Runnable mOnClosingStartCallback;
- private TaskView mTaskView;
- private TaskContainer mTaskContainer;
- private LinearLayout mOptionLayout;
- private float mMenuTranslationYBeforeOpen;
- private float mMenuTranslationXBeforeOpen;
-
- public TaskMenuView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- mContainer = RecentsViewContainer.containerFromContext(context);
- setClipToOutline(true);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mTaskName = findViewById(R.id.task_name);
- mOptionLayout = findViewById(R.id.menu_option_layout);
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- BaseDragLayer dl = mContainer.getDragLayer();
- if (!dl.isEventOverView(this, ev)) {
- // TODO: log this once we have a new container type for it?
- close(true);
- return true;
- }
- }
- return false;
- }
-
- @Override
- protected void handleClose(boolean animate) {
- animateClose();
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_TASK_MENU) != 0;
- }
-
- @Override
- public ViewOutlineProvider getOutlineProvider() {
- return new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(),
- TaskCornerRadius.get(view.getContext()));
- }
- };
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (!(enableOverviewIconMenu()
- && ((RecentsView) mContainer.getOverviewPanel()).isOnGridBottomRow(mTaskView))) {
- // TODO(b/326952853): Cap menu height for grid bottom row in a way that doesn't break
- // additionalTranslationY.
- int maxMenuHeight = calculateMaxHeight();
- if (MeasureSpec.getSize(heightMeasureSpec) > maxMenuHeight) {
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST);
- }
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- public void onRotationChanged() {
- if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
- mOpenCloseAnimator.end();
- }
- if (mIsOpen) {
- mOptionLayout.removeAllViews();
- if (enableOverviewIconMenu() || !populateAndLayoutMenu()) {
- close(false);
- }
- }
- }
-
- /**
- * Show a task menu for the given taskContainer.
- */
- public static boolean showForTask(TaskContainer taskContainer,
- @Nullable Runnable onClosingStartCallback) {
- RecentsViewContainer container = RecentsViewContainer.containerFromContext(
- taskContainer.getTaskView().getContext());
- final TaskMenuView taskMenuView = (TaskMenuView) container.getLayoutInflater().inflate(
- R.layout.task_menu, container.getDragLayer(), false);
- taskMenuView.setOnClosingStartCallback(onClosingStartCallback);
- return taskMenuView.populateAndShowForTask(taskContainer);
- }
-
- /**
- * Show a task menu for the given taskContainer.
- */
- public static boolean showForTask(TaskContainer taskContainer) {
- return showForTask(taskContainer, null);
- }
-
- private boolean populateAndShowForTask(TaskContainer taskContainer) {
- if (isAttachedToWindow()) {
- return false;
- }
- mContainer.getDragLayer().addView(this);
- mTaskView = taskContainer.getTaskView();
- mTaskContainer = taskContainer;
- if (!populateAndLayoutMenu()) {
- return false;
- }
- post(this::animateOpen);
- return true;
- }
-
- /** @return true if successfully able to populate task view menu, false otherwise */
- private boolean populateAndLayoutMenu() {
- addMenuOptions(mTaskContainer);
- orientAroundTaskView(mTaskContainer);
- return true;
- }
-
- private void addMenuOptions(TaskContainer taskContainer) {
- if (enableOverviewIconMenu()) {
- removeView(mTaskName);
- } else {
- mTaskName.setText(TaskUtils.getTitle(getContext(), taskContainer.getTask()));
- mTaskName.setOnClickListener(v -> close(true));
- }
- TaskOverlayFactory.getEnabledShortcuts(mTaskView, taskContainer)
- .forEach(this::addMenuOption);
- }
-
- private void addMenuOption(SystemShortcut menuOption) {
- LinearLayout menuOptionView = (LinearLayout) mContainer.getLayoutInflater().inflate(
- R.layout.task_view_menu_option, this, false);
- if (enableOverviewIconMenu()) {
- ((GradientDrawable) menuOptionView.getBackground()).setCornerRadius(0);
- }
- menuOption.setIconAndLabelFor(
- menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
- LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
- mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp,
- menuOptionView, mContainer.getDeviceProfile());
- // Set an onClick listener on each menu option. The onClick method is responsible for
- // ending LiveTile mode on the thumbnail if needed.
- menuOptionView.setOnClickListener(menuOption::onClick);
- mOptionLayout.addView(menuOptionView);
- }
-
- private void orientAroundTaskView(TaskContainer taskContainer) {
- RecentsView recentsView = mContainer.getOverviewPanel();
- RecentsPagedOrientationHandler orientationHandler =
- recentsView.getPagedOrientationHandler();
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-
- // Get Position
- DeviceProfile deviceProfile = mContainer.getDeviceProfile();
- mContainer.getDragLayer().getDescendantRectRelativeToSelf(
- enableOverviewIconMenu()
- ? getIconView().findViewById(R.id.icon_view_menu_anchor)
- : taskContainer.getSnapshotView(),
- sTempRect);
- Rect insets = mContainer.getDragLayer().getInsets();
- BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
- params.width = orientationHandler.getTaskMenuWidth(
- taskContainer.getSnapshotView(), deviceProfile,
- taskContainer.getStagePosition());
- // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
- params.gravity = Gravity.LEFT;
- setLayoutParams(params);
- setScaleX(mTaskView.getScaleX());
- setScaleY(mTaskView.getScaleY());
-
- // Set divider spacing
- ShapeDrawable divider = new ShapeDrawable(new RectShape());
- divider.getPaint().setColor(getResources().getColor(android.R.color.transparent));
- int dividerSpacing = (int) getResources().getDimension(R.dimen.task_menu_spacing);
- mOptionLayout.setShowDividers(
- enableOverviewIconMenu() ? SHOW_DIVIDER_NONE : SHOW_DIVIDER_MIDDLE);
-
- orientationHandler.setTaskOptionsMenuLayoutOrientation(
- deviceProfile, mOptionLayout, dividerSpacing, divider);
- float thumbnailAlignedX = sTempRect.left - insets.left;
- float thumbnailAlignedY = sTempRect.top - insets.top;
-
- // Changing pivot to make computations easier
- // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
- // which would render the X and Y position set here incorrect
- setPivotX(0);
- setPivotY(0);
- setRotation(orientationHandler.getDegreesRotated());
-
- if (enableOverviewIconMenu()) {
- setTranslationX(thumbnailAlignedX);
- setTranslationY(thumbnailAlignedY);
- } else {
- // Margin that insets the menuView inside the taskView
- float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
- setTranslationX(orientationHandler.getTaskMenuX(thumbnailAlignedX,
- mTaskContainer.getSnapshotView(), deviceProfile, taskInsetMargin,
- getIconView()));
- setTranslationY(orientationHandler.getTaskMenuY(
- thumbnailAlignedY, mTaskContainer.getSnapshotView(),
- mTaskContainer.getStagePosition(), this, taskInsetMargin,
- getIconView()));
- }
- }
-
- private void animateOpen() {
- mMenuTranslationYBeforeOpen = getTranslationY();
- mMenuTranslationXBeforeOpen = getTranslationX();
- animateOpenOrClosed(false);
- mIsOpen = true;
- }
-
- private View getIconView() {
- return mTaskContainer.getIconView().asView();
- }
-
- private void animateClose() {
- animateOpenOrClosed(true);
- }
-
- private void animateOpenOrClosed(boolean closing) {
- if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
- mOpenCloseAnimator.cancel();
- }
- mOpenCloseAnimator = new AnimatorSet();
- // If we're opening, we just start from the beginning as a new `TaskMenuView` is created
- // each time we do the open animation so there will never be a partial value here.
- float revealAnimationStartProgress = 0f;
- if (closing && mRevealAnimator != null) {
- revealAnimationStartProgress = 1f - mRevealAnimator.getAnimatedFraction();
- }
- mRevealAnimator = createOpenCloseOutlineProvider()
- .createRevealAnimator(this, closing, revealAnimationStartProgress);
- mRevealAnimator.setInterpolator(enableOverviewIconMenu() ? Interpolators.EMPHASIZED
- : Interpolators.DECELERATE);
- AnimatorSet.Builder openCloseAnimatorBuilder = mOpenCloseAnimator.play(mRevealAnimator);
- if (enableOverviewIconMenu()) {
- IconAppChipView iconAppChip = (IconAppChipView) mTaskContainer.getIconView().asView();
-
- float additionalTranslationY = 0;
- if (((RecentsView) mContainer.getOverviewPanel()).isOnGridBottomRow(mTaskView)) {
- // Animate menu up for enough room to display full menu when task on bottom row.
- float menuBottom = getHeight() + mMenuTranslationYBeforeOpen;
- float taskBottom = mTaskView.getHeight() + mTaskView.getPersistentTranslationY();
- float taskbarTop = mContainer.getDeviceProfile().heightPx
- - mContainer.getDeviceProfile().getOverviewActionsClaimedSpaceBelow();
- float midpoint = (taskBottom + taskbarTop) / 2f;
- additionalTranslationY = -Math.max(menuBottom - midpoint, 0);
- }
- ObjectAnimator translationYAnim = ObjectAnimator.ofFloat(this, TRANSLATION_Y,
- closing ? mMenuTranslationYBeforeOpen
- : mMenuTranslationYBeforeOpen + additionalTranslationY);
- translationYAnim.setInterpolator(EMPHASIZED);
- openCloseAnimatorBuilder.with(translationYAnim);
-
- ObjectAnimator menuTranslationYAnim = ObjectAnimator.ofFloat(
- iconAppChip.getMenuTranslationY(),
- MULTI_PROPERTY_VALUE, closing ? 0 : additionalTranslationY);
- menuTranslationYAnim.setInterpolator(EMPHASIZED);
- openCloseAnimatorBuilder.with(menuTranslationYAnim);
-
- float additionalTranslationX = 0;
- if (mContainer.getDeviceProfile().isLandscape
- && mTaskContainer.getStagePosition() == STAGE_POSITION_BOTTOM_OR_RIGHT) {
- // Animate menu and icon when split task would display off the side of the screen.
- additionalTranslationX = Math.max(
- getTranslationX() + getWidth() - (mContainer.getDeviceProfile().widthPx
- - getResources().getDimensionPixelSize(
- R.dimen.task_menu_edge_padding) * 2), 0);
- }
-
- ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(this, TRANSLATION_X,
- closing ? mMenuTranslationXBeforeOpen
- : mMenuTranslationXBeforeOpen - additionalTranslationX);
- translationXAnim.setInterpolator(EMPHASIZED);
- openCloseAnimatorBuilder.with(translationXAnim);
-
- ObjectAnimator menuTranslationXAnim = ObjectAnimator.ofFloat(
- iconAppChip.getMenuTranslationX(),
- MULTI_PROPERTY_VALUE, closing ? 0 : -additionalTranslationX);
- menuTranslationXAnim.setInterpolator(EMPHASIZED);
- openCloseAnimatorBuilder.with(menuTranslationXAnim);
- }
- openCloseAnimatorBuilder.with(ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
- if (enableRefactorTaskThumbnail()) {
- mRevealAnimator.addUpdateListener(animation -> {
- float animatedFraction = animation.getAnimatedFraction();
- float openProgress = closing ? (1 - animatedFraction) : animatedFraction;
- mTaskContainer.updateMenuOpenProgress(openProgress);
- });
- } else {
- openCloseAnimatorBuilder.with(ObjectAnimator.ofFloat(
- mTaskContainer.getThumbnailViewDeprecated(), DIM_ALPHA,
- closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA));
- }
- mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- setVisibility(VISIBLE);
- if (closing && mOnClosingStartCallback != null) {
- mOnClosingStartCallback.run();
- }
- }
-
- @Override
- public void onAnimationSuccess(Animator animator) {
- if (closing) {
- closeComplete();
- }
- }
- });
- mOpenCloseAnimator.setDuration(closing ? REVEAL_CLOSE_DURATION: REVEAL_OPEN_DURATION);
- mOpenCloseAnimator.start();
- }
-
- private void closeComplete() {
- mIsOpen = false;
- mContainer.getDragLayer().removeView(this);
- mRevealAnimator = null;
- }
-
- private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
- float radius = TaskCornerRadius.get(mContext);
- Rect fromRect = new Rect(
- enableOverviewIconMenu() && isLayoutRtl() ? getWidth() : 0,
- 0,
- enableOverviewIconMenu() && !isLayoutRtl() ? 0 : getWidth(),
- 0);
- Rect toRect = new Rect(0, 0, getWidth(), getHeight());
- return new RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect);
- }
-
- /**
- * Calculates max height based on how much space we have available.
- * If not enough space then the view will scroll. The maximum menu size will sit inside the task
- * with a margin on the top and bottom.
- */
- private int calculateMaxHeight() {
- float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
- return mTaskView.getPagedOrientationHandler().getTaskMenuHeight(taskInsetMargin,
- mContainer.getDeviceProfile(), getTranslationX(), getTranslationY());
- }
-
- private void setOnClosingStartCallback(Runnable onClosingStartCallback) {
- mOnClosingStartCallback = onClosingStartCallback;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
new file mode 100644
index 0000000..95336cf
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2018 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.views
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Outline
+import android.graphics.Rect
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.RectShape
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewOutlineProvider
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.app.animation.Interpolators
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.Flags.enableOverviewIconMenu
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
+import com.android.launcher3.R
+import com.android.launcher3.anim.AnimationSuccessListener
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider
+import com.android.launcher3.popup.SystemShortcut
+import com.android.launcher3.util.MultiPropertyFactory
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.views.BaseDragLayer
+import com.android.quickstep.TaskOverlayFactory
+import com.android.quickstep.TaskUtils
+import com.android.quickstep.util.TaskCornerRadius
+import java.util.function.Consumer
+import kotlin.math.max
+
+/** Contains options for a recent task when long-pressing its icon. */
+class TaskMenuView
+@JvmOverloads
+constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int = 0) :
+ AbstractFloatingView(context, attrs, defStyleAttr) {
+ private val recentsViewContainer: RecentsViewContainer =
+ RecentsViewContainer.containerFromContext(context)
+ private val tempRect = Rect()
+ private val taskName: TextView by lazy { findViewById(R.id.task_name) }
+ private val optionLayout: LinearLayout by lazy { findViewById(R.id.menu_option_layout) }
+ private var openCloseAnimator: AnimatorSet? = null
+ private var revealAnimator: ValueAnimator? = null
+ private var onClosingStartCallback: Runnable? = null
+ private lateinit var taskView: TaskView
+ private lateinit var taskContainer: TaskContainer
+ private var menuTranslationXBeforeOpen = 0f
+ private var menuTranslationYBeforeOpen = 0f
+
+ init {
+ clipToOutline = true
+ }
+
+ override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
+ if (ev.action == MotionEvent.ACTION_DOWN) {
+ if (!recentsViewContainer.dragLayer.isEventOverView(this, ev)) {
+ // TODO: log this once we have a new container type for it?
+ close(true)
+ return true
+ }
+ }
+ return false
+ }
+
+ override fun handleClose(animate: Boolean) {
+ animateClose()
+ }
+
+ override fun isOfType(type: Int): Boolean = (type and TYPE_TASK_MENU) != 0
+
+ override fun getOutlineProvider(): ViewOutlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ outline.setRoundRect(
+ 0,
+ 0,
+ view.width,
+ view.height,
+ TaskCornerRadius.get(view.context),
+ )
+ }
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ var heightMeasure = heightMeasureSpec
+ if (!(enableOverviewIconMenu() && taskView.isOnGridBottomRow())) {
+ // TODO(b/326952853): Cap menu height for grid bottom row in a way that doesn't break
+ // additionalTranslationY.
+ val maxMenuHeight = calculateMaxHeight()
+ if (MeasureSpec.getSize(heightMeasure) > maxMenuHeight) {
+ heightMeasure = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST)
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasure)
+ }
+
+ fun onRotationChanged() {
+ openCloseAnimator?.let { if (it.isRunning) it.end() }
+ if (mIsOpen) {
+ optionLayout.removeAllViews()
+ if (enableOverviewIconMenu() || !populateAndLayoutMenu()) {
+ close(false)
+ }
+ }
+ }
+
+ private fun populateAndShowForTask(taskContainer: TaskContainer): Boolean {
+ if (isAttachedToWindow) return false
+ recentsViewContainer.dragLayer.addView(this)
+ taskView = taskContainer.taskView
+ this.taskContainer = taskContainer
+ if (!populateAndLayoutMenu()) return false
+ post { this.animateOpen() }
+ return true
+ }
+
+ /** @return true if successfully able to populate task view menu, false otherwise */
+ private fun populateAndLayoutMenu(): Boolean {
+ addMenuOptions(taskContainer)
+ orientAroundTaskView(taskContainer)
+ return true
+ }
+
+ private fun addMenuOptions(taskContainer: TaskContainer) {
+ if (enableOverviewIconMenu()) {
+ removeView(taskName)
+ } else {
+ taskName.text = TaskUtils.getTitle(context, taskContainer.task)
+ taskName.setOnClickListener { close(true) }
+ }
+ TaskOverlayFactory.getEnabledShortcuts(taskView, taskContainer)
+ .forEach(Consumer { menuOption: SystemShortcut<*> -> this.addMenuOption(menuOption) })
+ }
+
+ private fun addMenuOption(menuOption: SystemShortcut<*>) {
+ val menuOptionView =
+ recentsViewContainer.layoutInflater.inflate(R.layout.task_view_menu_option, this, false)
+ as LinearLayout
+ if (enableOverviewIconMenu()) {
+ (menuOptionView.background as GradientDrawable).cornerRadius = 0f
+ }
+ menuOption.setIconAndLabelFor(
+ menuOptionView.findViewById(R.id.icon),
+ menuOptionView.findViewById(R.id.text),
+ )
+ val lp = menuOptionView.layoutParams as LayoutParams
+ taskView.pagedOrientationHandler.setLayoutParamsForTaskMenuOptionItem(
+ lp,
+ menuOptionView,
+ recentsViewContainer.deviceProfile,
+ )
+ // Set an onClick listener on each menu option. The onClick method is responsible for
+ // ending LiveTile mode on the thumbnail if needed.
+ menuOptionView.setOnClickListener { v: View? -> menuOption.onClick(v) }
+ optionLayout.addView(menuOptionView)
+ }
+
+ private fun orientAroundTaskView(taskContainer: TaskContainer) {
+ val recentsView = recentsViewContainer.getOverviewPanel<RecentsView<*, *>>()
+ val orientationHandler = recentsView.pagedOrientationHandler
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
+
+ // Get Position
+ val deviceProfile = recentsViewContainer.deviceProfile
+ recentsViewContainer.dragLayer.getDescendantRectRelativeToSelf(
+ if (enableOverviewIconMenu()) iconView.findViewById(R.id.icon_view_menu_anchor)
+ else taskContainer.snapshotView,
+ tempRect,
+ )
+ val insets = recentsViewContainer.dragLayer.getInsets()
+ val params = layoutParams as BaseDragLayer.LayoutParams
+ params.width =
+ orientationHandler.getTaskMenuWidth(
+ taskContainer.snapshotView,
+ deviceProfile,
+ taskContainer.stagePosition,
+ )
+ // Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
+ params.gravity = Gravity.START
+ layoutParams = params
+ scaleX = taskView.scaleX
+ scaleY = taskView.scaleY
+
+ // Set divider spacing
+ val divider = ShapeDrawable(RectShape())
+ divider.paint.color = resources.getColor(android.R.color.transparent)
+ val dividerSpacing = resources.getDimension(R.dimen.task_menu_spacing).toInt()
+ optionLayout.showDividers =
+ if (enableOverviewIconMenu()) SHOW_DIVIDER_NONE else SHOW_DIVIDER_MIDDLE
+
+ orientationHandler.setTaskOptionsMenuLayoutOrientation(
+ deviceProfile,
+ optionLayout,
+ dividerSpacing,
+ divider,
+ )
+ val thumbnailAlignedX = (tempRect.left - insets.left).toFloat()
+ val thumbnailAlignedY = (tempRect.top - insets.top).toFloat()
+
+ // Changing pivot to make computations easier
+ // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
+ // which would render the X and Y position set here incorrect
+ pivotX = 0f
+ pivotY = 0f
+ rotation = orientationHandler.degreesRotated
+
+ if (enableOverviewIconMenu()) {
+ translationX = thumbnailAlignedX
+ translationY = thumbnailAlignedY
+ } else {
+ // Margin that insets the menuView inside the taskView
+ val taskInsetMargin = resources.getDimension(R.dimen.task_card_margin)
+ translationX =
+ orientationHandler.getTaskMenuX(
+ thumbnailAlignedX,
+ this.taskContainer.snapshotView,
+ deviceProfile,
+ taskInsetMargin,
+ iconView,
+ )
+ translationY =
+ orientationHandler.getTaskMenuY(
+ thumbnailAlignedY,
+ this.taskContainer.snapshotView,
+ this.taskContainer.stagePosition,
+ this,
+ taskInsetMargin,
+ iconView,
+ )
+ }
+ }
+
+ private fun animateOpen() {
+ menuTranslationYBeforeOpen = translationY
+ menuTranslationXBeforeOpen = translationX
+ animateOpenOrClosed(false)
+ mIsOpen = true
+ }
+
+ private val iconView: View
+ get() = taskContainer.iconView.asView()
+
+ private fun animateClose() {
+ animateOpenOrClosed(true)
+ }
+
+ private fun animateOpenOrClosed(closing: Boolean) {
+ openCloseAnimator?.let { if (it.isRunning) it.cancel() }
+ openCloseAnimator = AnimatorSet()
+ // If we're opening, we just start from the beginning as a new `TaskMenuView` is created
+ // each time we do the open animation so there will never be a partial value here.
+ var revealAnimationStartProgress = 0f
+ if (closing && revealAnimator != null) {
+ revealAnimationStartProgress = 1f - revealAnimator!!.animatedFraction
+ }
+ revealAnimator =
+ createOpenCloseOutlineProvider()
+ .createRevealAnimator(this, closing, revealAnimationStartProgress)
+ revealAnimator!!.interpolator =
+ if (enableOverviewIconMenu()) Interpolators.EMPHASIZED else Interpolators.DECELERATE
+ val openCloseAnimatorBuilder = openCloseAnimator!!.play(revealAnimator)
+ if (enableOverviewIconMenu()) {
+ animateOpenOrCloseAppChip(closing, openCloseAnimatorBuilder)
+ }
+ openCloseAnimatorBuilder.with(
+ ObjectAnimator.ofFloat(this, ALPHA, (if (closing) 0 else 1).toFloat())
+ )
+ if (enableRefactorTaskThumbnail()) {
+ revealAnimator?.addUpdateListener { animation: ValueAnimator ->
+ val animatedFraction = animation.animatedFraction
+ val openProgress = if (closing) (1 - animatedFraction) else animatedFraction
+ taskContainer.updateMenuOpenProgress(openProgress)
+ }
+ } else {
+ openCloseAnimatorBuilder.with(
+ ObjectAnimator.ofFloat(
+ taskContainer.thumbnailViewDeprecated,
+ TaskThumbnailViewDeprecated.DIM_ALPHA,
+ if (closing) 0f else TaskView.MAX_PAGE_SCRIM_ALPHA,
+ )
+ )
+ }
+ openCloseAnimator!!.addListener(
+ object : AnimationSuccessListener() {
+ override fun onAnimationStart(animation: Animator) {
+ visibility = VISIBLE
+ if (closing) onClosingStartCallback?.run()
+ }
+
+ override fun onAnimationSuccess(animator: Animator) {
+ if (closing) closeComplete()
+ }
+ }
+ )
+ val animationDuration = if (closing) REVEAL_CLOSE_DURATION else REVEAL_OPEN_DURATION
+ openCloseAnimator!!.setDuration(animationDuration)
+ openCloseAnimator!!.start()
+ }
+
+ private fun TaskView.isOnGridBottomRow(): Boolean =
+ (recentsViewContainer.getOverviewPanel<View>() as RecentsView<*, *>).isOnGridBottomRow(this)
+
+ private fun closeComplete() {
+ mIsOpen = false
+ recentsViewContainer.dragLayer.removeView(this)
+ revealAnimator = null
+ }
+
+ private fun createOpenCloseOutlineProvider(): RoundedRectRevealOutlineProvider {
+ val radius = TaskCornerRadius.get(mContext)
+ val fromRect =
+ Rect(
+ if (enableOverviewIconMenu() && isLayoutRtl) width else 0,
+ 0,
+ if (enableOverviewIconMenu() && !isLayoutRtl) 0 else width,
+ 0,
+ )
+ val toRect = Rect(0, 0, width, height)
+ return RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect)
+ }
+
+ /**
+ * Calculates max height based on how much space we have available. If not enough space then the
+ * view will scroll. The maximum menu size will sit inside the task with a margin on the top and
+ * bottom.
+ */
+ private fun calculateMaxHeight(): Int {
+ val taskInsetMargin = resources.getDimension(R.dimen.task_card_margin)
+ return taskView.pagedOrientationHandler.getTaskMenuHeight(
+ taskInsetMargin,
+ recentsViewContainer.deviceProfile,
+ translationX,
+ translationY,
+ )
+ }
+
+ private fun setOnClosingStartCallback(onClosingStartCallback: Runnable?) {
+ this.onClosingStartCallback = onClosingStartCallback
+ }
+
+ private fun animateOpenOrCloseAppChip(closing: Boolean, animatorBuilder: AnimatorSet.Builder) {
+ val iconAppChip = taskContainer.iconView.asView() as IconAppChipView
+
+ var additionalTranslationY = 0f
+ if (taskView.isOnGridBottomRow()) {
+ // Animate menu up for enough room to display full menu when task on bottom row.
+ val menuBottom = height + menuTranslationYBeforeOpen
+ val taskBottom = taskView.height + taskView.persistentTranslationY
+ val taskbarTop =
+ (recentsViewContainer.deviceProfile.heightPx -
+ recentsViewContainer.deviceProfile.overviewActionsClaimedSpaceBelow)
+ .toFloat()
+ val midpoint = (taskBottom + taskbarTop) / 2f
+ additionalTranslationY = (-max((menuBottom - midpoint).toDouble(), 0.0)).toFloat()
+ }
+ val translationYAnim =
+ ObjectAnimator.ofFloat(
+ this,
+ TRANSLATION_Y,
+ if (closing) menuTranslationYBeforeOpen
+ else menuTranslationYBeforeOpen + additionalTranslationY,
+ )
+ translationYAnim.interpolator = Interpolators.EMPHASIZED
+ animatorBuilder.with(translationYAnim)
+
+ val menuTranslationYAnim: ObjectAnimator =
+ ObjectAnimator.ofFloat(
+ iconAppChip.getMenuTranslationY(),
+ MultiPropertyFactory.MULTI_PROPERTY_VALUE,
+ if (closing) 0f else additionalTranslationY,
+ )
+ menuTranslationYAnim.interpolator = Interpolators.EMPHASIZED
+ animatorBuilder.with(menuTranslationYAnim)
+
+ var additionalTranslationX = 0f
+ if (
+ recentsViewContainer.deviceProfile.isLandscape &&
+ taskContainer.stagePosition ==
+ SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+ ) {
+ // Animate menu and icon when split task would display off the side of the screen.
+ additionalTranslationX =
+ max(
+ (translationX + width -
+ (recentsViewContainer.deviceProfile.widthPx -
+ resources.getDimensionPixelSize(
+ R.dimen.task_menu_edge_padding
+ ) * 2))
+ .toDouble(),
+ 0.0,
+ )
+ .toFloat()
+ }
+
+ val translationXAnim =
+ ObjectAnimator.ofFloat(
+ this,
+ TRANSLATION_X,
+ if (closing) menuTranslationXBeforeOpen
+ else menuTranslationXBeforeOpen - additionalTranslationX,
+ )
+ translationXAnim.interpolator = Interpolators.EMPHASIZED
+ animatorBuilder.with(translationXAnim)
+
+ val menuTranslationXAnim: ObjectAnimator =
+ ObjectAnimator.ofFloat(
+ iconAppChip.getMenuTranslationX(),
+ MultiPropertyFactory.MULTI_PROPERTY_VALUE,
+ if (closing) 0f else -additionalTranslationX,
+ )
+ menuTranslationXAnim.interpolator = Interpolators.EMPHASIZED
+ animatorBuilder.with(menuTranslationXAnim)
+ }
+
+ companion object {
+ private val REVEAL_OPEN_DURATION = if (enableOverviewIconMenu()) 417L else 150L
+ private val REVEAL_CLOSE_DURATION = if (enableOverviewIconMenu()) 333L else 100L
+
+ /** Show a task menu for the given taskContainer. */
+ /** Show a task menu for the given taskContainer. */
+ @JvmOverloads
+ fun showForTask(
+ taskContainer: TaskContainer,
+ onClosingStartCallback: Runnable? = null,
+ ): Boolean {
+ val container: RecentsViewContainer =
+ RecentsViewContainer.containerFromContext(taskContainer.taskView.context)
+ val taskMenuView =
+ container.layoutInflater.inflate(R.layout.task_menu, container.dragLayer, false)
+ as TaskMenuView
+ taskMenuView.setOnClosingStartCallback(onClosingStartCallback)
+ return taskMenuView.populateAndShowForTask(taskContainer)
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 0e5382a..27db6d6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -199,7 +199,7 @@
*/
get() = (getNonGridTrans(nonGridTranslationX) + getGridTrans(this.gridTranslationX))
- protected val persistentTranslationY: Float
+ val persistentTranslationY: Float
/**
* Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does
* not change according to a temporary state (e.g. task offset).
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index 0204b2d..2dacf69 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -113,8 +113,8 @@
object : TaskbarNavButtonCallbacks {},
RecentsDisplayModel.INSTANCE.get(context),
) {
- override fun recreateTaskbar() {
- super.recreateTaskbar()
+ override fun recreateTaskbars() {
+ super.recreateTaskbars()
if (currentActivityContext != null) {
injectControllers()
controllerInjectionCallback.invoke()
@@ -146,7 +146,7 @@
}
/** Simulates Taskbar recreation lifecycle. */
- fun recreateTaskbar() = instrumentation.runOnMainSync { taskbarManager.recreateTaskbar() }
+ fun recreateTaskbar() = instrumentation.runOnMainSync { taskbarManager.recreateTaskbars() }
private fun injectControllers() {
val bubbleControllerTypes =
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 8c6555e..78ad04b 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -70,6 +70,7 @@
public static final int WORKSPACE_PAGE_INDICATOR = 1 << 5;
public static final int SPLIT_PLACHOLDER_VIEW = 1 << 6;
public static final int FLOATING_SEARCH_BAR = 1 << 7;
+ public static final int ADD_DESK_BUTTON = 1 << 8;
// Flag indicating workspace has multiple pages visible.
public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);