Merge "Add hover and pressed states for app chip and menu items" into main
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index 7b1ebee..d27a214 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -5,14 +5,14 @@
name: "enable_grid_only_overview"
namespace: "launcher_overview"
description: "Enable a grid-only overview without a focused task."
- bug: "257950105"
+ bug: "360204325"
}
flag {
name: "enable_overview_icon_menu"
namespace: "launcher_overview"
description: "Enable updated overview icon and menu within task."
- bug: "257950105"
+ bug: "360205084"
}
flag {
diff --git a/quickstep/res/layout/keyboard_quick_switch_view.xml b/quickstep/res/layout/keyboard_quick_switch_view.xml
index 885bdb9..2dea79c 100644
--- a/quickstep/res/layout/keyboard_quick_switch_view.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_view.xml
@@ -18,6 +18,8 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/keyboard_quick_switch_view"
+ android:contentDescription="@string/quick_switch_content_description"
+ android:accessibilityPaneTitle="@string/quick_switch_pane_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/keyboard_quick_switch_margin_top"
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 65f4b3c..7578bd5 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -325,6 +325,12 @@
<!-- Label for creating an application bubble (from the Taskbar only). -->
<string name="open_app_as_a_bubble">Open app as a bubble</string>
+ <!-- Accessibility pane title for quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
+ <string name="quick_switch_pane_title">Recent apps</string>
+
+ <!-- Content description for the quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
+ <string name="quick_switch_content_description">Recent app list</string>
+
<!-- Label for quick switch tile showing how many more apps are available. The number will be displayed above this text. [CHAR LIMIT=NONE] -->
<string name="quick_switch_overflow">{count, plural,
=1{more app}
@@ -336,6 +342,8 @@
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
+ <!-- Accessibility label for quick switch tiles that include information about the tile's position in the parent list [CHAR LIMIT=NONE] -->
+ <string name="quick_switch_task_with_position_in_parent"><xliff:g id="task_description" example="Chrome">%1$s</xliff:g>, item <xliff:g id="index_in_parent" example="1">%2$d</xliff:g> of <xliff:g id="total_tasks" example="5">%3$d</xliff:g></string>
<!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content left
TODO(b/397975686): Make these translatable when verified by UX. -->
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 7cf0605..aae8a56 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -352,7 +352,7 @@
new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay),
new RemoteTransition(runner.toRemoteTransition(),
mLauncher.getIApplicationThread(), "QuickstepLaunch"));
- IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback);
+ IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback, mLauncher);
options.setOnAnimationAbortListener(endCallback);
options.setOnAnimationFinishedListener(endCallback);
options.setLaunchCookie(StableViewInfo.toLaunchCookie(itemInfo));
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 40cfe92..a01846d 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -30,6 +30,7 @@
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TaskViewUtils
+import com.android.quickstep.util.DesksUtils.Companion.areMultiDesksFlagsEnabled
import com.android.quickstep.views.DesktopTaskView
import com.android.quickstep.views.TaskContainer
import com.android.quickstep.views.TaskView
@@ -60,7 +61,11 @@
callback,
)
val transition = RemoteTransition(animRunner, appThread, "RecentsToDesktop")
- systemUiProxy.showDesktopApps(desktopTaskView.displayId, transition)
+ if (areMultiDesksFlagsEnabled()) {
+ systemUiProxy.activateDesk(desktopTaskView.deskId, transition)
+ } else {
+ systemUiProxy.showDesktopApps(desktopTaskView.displayId, transition)
+ }
}
/** Launch desktop tasks from recents view */
diff --git a/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
index f9cec82..ada7301 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
@@ -16,7 +16,6 @@
package com.android.launcher3.model;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -202,19 +201,12 @@
* Converts the list of {@link WidgetItem}s to the list of {@link ItemInfo}s.
*/
private List<ItemInfo> mapWidgetItemsToItemInfo(List<WidgetItem> widgetItems) {
- List<ItemInfo> items;
- if (enableCategorizedWidgetSuggestions()) {
- WidgetRecommendationCategoryProvider categoryProvider =
- new WidgetRecommendationCategoryProvider();
- items = widgetItems.stream()
- .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
- categoryProvider.getWidgetRecommendationCategory(mContext, it)))
- .collect(Collectors.toList());
- } else {
- items = widgetItems.stream().map(it -> new PendingAddWidgetInfo(it.widgetInfo,
- CONTAINER_WIDGETS_PREDICTION)).collect(Collectors.toList());
- }
- return items;
+ WidgetRecommendationCategoryProvider categoryProvider =
+ new WidgetRecommendationCategoryProvider();
+ return widgetItems.stream()
+ .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
+ categoryProvider.getWidgetRecommendationCategory(mContext, it)))
+ .collect(Collectors.toList());
}
/** Cleans up any open prediction sessions. */
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index b732cba..0a4b7c8 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.model;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.model.ModelUtils.WIDGET_FILTER;
@@ -129,20 +128,12 @@
}
}
- List<ItemInfo> items;
- if (enableCategorizedWidgetSuggestions()) {
- WidgetRecommendationCategoryProvider categoryProvider =
- new WidgetRecommendationCategoryProvider();
- items = servicePredictedItems.stream()
- .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
- categoryProvider.getWidgetRecommendationCategory(context, it)))
- .collect(Collectors.toList());
- } else {
- items = servicePredictedItems.stream()
- .map(it -> new PendingAddWidgetInfo(it.widgetInfo,
- CONTAINER_WIDGETS_PREDICTION)).collect(
- Collectors.toList());
- }
+ WidgetRecommendationCategoryProvider categoryProvider =
+ new WidgetRecommendationCategoryProvider();
+ List<ItemInfo> items = servicePredictedItems.stream()
+ .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
+ categoryProvider.getWidgetRecommendationCategory(context, it)))
+ .collect(Collectors.toList());
FixedContainerItems fixedContainerItems =
new FixedContainerItems(mPredictorState.containerId, items);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
index eb24df1..2402a28 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -401,6 +401,39 @@
DisplayController.INSTANCE.get(context).notifyConfigChange()
}
+ private fun notifyOnDeskAdded(displayId: Int, deskId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyOnDeskAdded: displayId=$displayId, deskId=$deskId")
+ }
+
+ for (listener in desktopVisibilityListeners) {
+ listener.onDeskAdded(displayId, deskId)
+ }
+ }
+
+ private fun notifyOnDeskRemoved(displayId: Int, deskId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyOnDeskRemoved: displayId=$displayId, deskId=$deskId")
+ }
+
+ for (listener in desktopVisibilityListeners) {
+ listener.onDeskRemoved(displayId, deskId)
+ }
+ }
+
+ private fun notifyOnActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "notifyOnActiveDeskChanged: displayId=$displayId, newActiveDesk=$newActiveDesk, oldActiveDesk=$oldActiveDesk",
+ )
+ }
+
+ for (listener in desktopVisibilityListeners) {
+ listener.onActiveDeskChanged(displayId, newActiveDesk, oldActiveDesk)
+ }
+ }
+
/** TODO: b/333533253 - Remove after flag rollout */
private fun setBackgroundStateEnabled(backgroundStateEnabled: Boolean) {
if (DEBUG) {
@@ -511,6 +544,8 @@
"Found a duplicate desk Id: $deskId on display: $displayId"
}
}
+
+ notifyOnDeskAdded(displayId, deskId)
}
private fun onDeskRemoved(displayId: Int, deskId: Int) {
@@ -526,6 +561,8 @@
it.activeDeskId = INACTIVE_DESK_ID
}
}
+
+ notifyOnDeskRemoved(displayId, deskId)
}
private fun onActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {
@@ -539,12 +576,16 @@
check(oldActiveDesk == it.activeDeskId) {
"Mismatch between the Shell's oldActiveDesk: $oldActiveDesk, and Launcher's: ${it.activeDeskId}"
}
- check(it.deskIds.contains(newActiveDesk)) {
+ check(newActiveDesk == INACTIVE_DESK_ID || it.deskIds.contains(newActiveDesk)) {
"newActiveDesk: $newActiveDesk was never added to display: $displayId"
}
it.activeDeskId = newActiveDesk
}
+ if (newActiveDesk != oldActiveDesk) {
+ notifyOnActiveDeskChanged(displayId, newActiveDesk, oldActiveDesk)
+ }
+
if (wasInDesktopMode != isInDesktopModeAndNotInOverview(displayId)) {
notifyIsInDesktopModeChanged(displayId, !wasInDesktopMode)
}
@@ -718,6 +759,6 @@
private const val TAG = "DesktopVisController"
private const val DEBUG = false
- public const val INACTIVE_DESK_ID = -1
+ const val INACTIVE_DESK_ID = -1
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index 09a8670..aa3feb7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -23,6 +23,7 @@
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.BaseContext;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
import com.android.quickstep.SystemUiProxy;
@@ -32,10 +33,20 @@
implements SystemShortcut.BubbleActivityStarter {
protected final LayoutInflater mLayoutInflater;
+ private final boolean mIsPrimaryDisplay;
- public BaseTaskbarContext(Context windowContext) {
+ public BaseTaskbarContext(Context windowContext, boolean isPrimaryDisplay) {
super(windowContext, Themes.getActivityThemeRes(windowContext));
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
+ mIsPrimaryDisplay = isPrimaryDisplay;
+ }
+
+ public boolean isTransientTaskbar() {
+ return DisplayController.isTransientTaskbar(this) && mIsPrimaryDisplay;
+ }
+
+ public boolean isPrimaryDisplay() {
+ return mIsPrimaryDisplay;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index cc94824..1698050 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -170,7 +170,7 @@
mHasDesktopTask,
mWasDesktopTaskFilteredOut);
}, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
- : RecentsFilterState.getEmptyDesktopTaskFilter());
+ : RecentsFilterState.getDesktopTaskFilter());
}
mQuickSwitchViewController.updateLayoutForSurface(wasOpenedFromTaskbar,
@@ -232,7 +232,7 @@
mWasDesktopTaskFilteredOut,
wasOpenedFromTaskbar);
}, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
- : RecentsFilterState.getEmptyDesktopTaskFilter());
+ : RecentsFilterState.getDesktopTaskFilter());
}
private boolean shouldExcludeTask(GroupTask task, Set<Integer> taskIdsToExclude) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index f80dc90..15be03a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -40,6 +40,8 @@
import com.android.quickstep.util.BorderAnimator;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import kotlin.Unit;
@@ -64,6 +66,11 @@
@Nullable private ImageView mIcon2;
@Nullable private View mContent;
+ // Describe the task position in the parent container. Used to add information about the task's
+ // position in a task list to the task view's content description.
+ private int mIndexInParent = -1;
+ private int mTotalTasksInParent = -1;
+
public KeyboardQuickSwitchTaskView(@NonNull Context context) {
this(context, null);
}
@@ -108,11 +115,11 @@
TypefaceUtils.setTypeface(
mContent.findViewById(R.id.large_text),
- TypefaceUtils.FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED
+ FontFamily.GSF_HEADLINE_LARGE_EMPHASIZED
);
TypefaceUtils.setTypeface(
mContent.findViewById(R.id.small_text),
- TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE
+ FontFamily.GSF_LABEL_LARGE
);
Resources resources = mContext.getResources();
@@ -153,36 +160,51 @@
applyThumbnail(mThumbnailView1, task1, thumbnailUpdateFunction);
applyThumbnail(mThumbnailView2, task2, thumbnailUpdateFunction);
+ // Update content description, even in cases task icons, and content descriptions need to be
+ // loaded asynchronously to ensure that the task has non empty description (assuming task
+ // position information was set), as KeyboardQuickSwitch view may request accessibility
+ // focus to be moved to the task when the quick switch UI gets shown. The description will
+ // be updated once the task metadata has been loaded - the delay should be very short, and
+ // the content description when task titles are not available still gives some useful
+ // information to the user (the task's position in the list).
+ updateContentDesctiptionForTasks(task1, task2);
+
if (iconUpdateFunction == null) {
applyIcon(mIcon1, task1);
applyIcon(mIcon2, task2);
- setContentDescription(task2 == null
- ? task1.titleDescription
- : getContext().getString(
- R.string.quick_switch_split_task,
- task1.titleDescription,
- task2.titleDescription));
return;
}
+
iconUpdateFunction.updateIconInBackground(task1, t -> {
applyIcon(mIcon1, task1);
if (task2 != null) {
return;
}
- setContentDescription(task1.titleDescription);
+ updateContentDesctiptionForTasks(task1, null);
});
+
if (task2 == null) {
return;
}
iconUpdateFunction.updateIconInBackground(task2, t -> {
applyIcon(mIcon2, task2);
- setContentDescription(getContext().getString(
- R.string.quick_switch_split_task,
- task1.titleDescription,
- task2.titleDescription));
+ updateContentDesctiptionForTasks(task1, task2);
});
}
+ /**
+ * Initializes information about the task's position within the parent container context - used
+ * to add position information to the view's content description.
+ * Should be called before associating the view with tasks.
+ *
+ * @param index The view's 0-based index within the parent task container.
+ * @param totalTasks The total number of tasks in the parent task container.
+ */
+ protected void setPositionInformation(int index, int totalTasks) {
+ mIndexInParent = index;
+ mTotalTasksInParent = totalTasks;
+ }
+
protected void setThumbnailsForSplitTasks(
@NonNull Task task1,
@Nullable Task task2,
@@ -281,6 +303,28 @@
constantState.newDrawable(getResources(), getContext().getTheme()));
}
+ /**
+ * Updates the task view's content description to reflect tasks represented by the view.
+ */
+ private void updateContentDesctiptionForTasks(@NonNull Task task1, @Nullable Task task2) {
+ String tasksDescription = task1.titleDescription == null || task2 == null
+ ? task1.titleDescription
+ : getContext().getString(
+ R.string.quick_switch_split_task,
+ task1.titleDescription,
+ task2.titleDescription);
+ if (mIndexInParent < 0) {
+ setContentDescription(tasksDescription);
+ return;
+ }
+
+ setContentDescription(
+ getContext().getString(R.string.quick_switch_task_with_position_in_parent,
+ tasksDescription != null ? tasksDescription : "",
+ mIndexInParent + 1,
+ mTotalTasksInParent));
+ }
+
protected interface ThumbnailUpdateFunction {
void updateThumbnailInBackground(Task task, Consumer<ThumbnailData> callback);
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 336ef48..ab147bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -58,6 +58,8 @@
import com.android.quickstep.util.SplitTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import java.util.HashMap;
import java.util.List;
@@ -189,10 +191,9 @@
}
}
-
TypefaceUtils.setTypeface(
mNoRecentItemsPane.findViewById(R.id.no_recent_items_text),
- TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE);
+ FontFamily.GSF_LABEL_LARGE);
}
private void registerOnBackInvokedCallback() {
@@ -300,6 +301,7 @@
continue;
}
+ currentTaskView.setPositionInformation(i, tasksToDisplay);
currentTaskView.setThumbnailsForSplitTasks(
task1,
task2,
@@ -547,6 +549,9 @@
ViewOutlineProvider outlineProvider = getOutlineProvider();
+ int defaultFocusedTaskIndex = Math.min(
+ getTaskCount() - 1,
+ currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride);
mOpenAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -600,9 +605,7 @@
});
}
- animateFocusMove(-1, Math.min(
- getTaskCount() - 1,
- currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride));
+ animateFocusMove(-1, defaultFocusedTaskIndex);
displayedContent.setVisibility(VISIBLE);
setVisibility(VISIBLE);
requestFocus();
@@ -622,6 +625,11 @@
invalidateOutline();
mOpenAnimation = null;
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_OPEN);
+
+ View focusedTask = getTaskAt(defaultFocusedTaskIndex);
+ if (focusedTask != null) {
+ focusedTask.requestAccessibilityFocus();
+ }
}
});
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 5f7a026..b5f2532 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -40,7 +40,6 @@
import com.android.launcher3.desktop.DesktopAppLaunchTransition;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.GroupTask;
@@ -106,8 +105,7 @@
boolean hasDesktopTask,
boolean wasDesktopTaskFilteredOut,
boolean wasOpenedFromTaskbar) {
- final boolean isTransientTaskBar = DisplayController.isTransientTaskbar(
- mControllers.taskbarActivityContext);
+ final boolean isTransientTaskBar = mControllers.taskbarActivityContext.isTransientTaskbar();
positionView(wasOpenedFromTaskbar, isTransientTaskBar);
// Keep the taskbar unstashed if the KQS is opened.
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 2272d11..e998388 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -135,11 +135,11 @@
@Override
protected void onDestroy() {
onLauncherVisibilityChanged(false /* isVisible */, true /* fromInitOrDestroy */);
+ mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
super.onDestroy();
mTaskbarLauncherStateController.onDestroy();
mLauncher.setTaskbarUIController(null);
- mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
mHomeState.removeListener(mVisibilityChangeListener);
}
@@ -225,9 +225,8 @@
if (isVisible || isPinnedTaskbar) {
return getTaskbarToHomeDuration(shouldOverrideToFastAnimation, isPinnedTaskbar);
} else {
- return DisplayController.isTransientTaskbar(mLauncher)
- ? TRANSIENT_TASKBAR_TRANSITION_DURATION
- : TASKBAR_TO_APP_DURATION;
+ return mControllers.taskbarActivityContext.isTransientTaskbar()
+ ? TRANSIENT_TASKBAR_TRANSITION_DURATION : TASKBAR_TO_APP_DURATION;
}
}
@@ -279,7 +278,10 @@
private void postAdjustHotseatForBubbleBar() {
Hotseat hotseat = mLauncher.getHotseat();
if (hotseat == null || !isBubbleBarVisible()) return;
- hotseat.post(() -> adjustHotseatForBubbleBar(isBubbleBarVisible()));
+ hotseat.post(() -> {
+ if (mControllers == null) return;
+ adjustHotseatForBubbleBar(isBubbleBarVisible());
+ });
}
private boolean isBubbleBarVisible() {
@@ -334,7 +336,7 @@
}
// Persistent features EDU tooltip.
- if (!DisplayController.isTransientTaskbar(mLauncher)) {
+ if (!mControllers.taskbarActivityContext.isTransientTaskbar()) {
mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
return;
}
@@ -357,7 +359,7 @@
}
// Persistent features EDU tooltip.
- if (!DisplayController.isTransientTaskbar(mLauncher)) {
+ if (!mControllers.taskbarActivityContext.isTransientTaskbar()) {
return !OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP.hasReachedMax(mLauncher);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index ee5b8d1..27b38e5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -870,6 +870,9 @@
if (predictiveBackThreeButtonNav() && buttonType == BUTTON_BACK) {
// set up special touch listener for back button to support predictive back
setBackButtonTouchListener(buttonView, navButtonController);
+ // Set this View clickable, so that NearestTouchFrame.java forwards closeby touches to
+ // this View
+ buttonView.setClickable(true);
} else {
buttonView.setOnClickListener(view ->
navButtonController.onButtonClick(buttonType, view));
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 2e5bebc..6bd3d85 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -36,7 +36,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
@@ -217,7 +216,7 @@
.getTransientTaskbarIconLayoutBounds();
float startRadius = mStashedHandleRadius;
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
// Account for the full visual height of the transient taskbar.
int heightDiff = (mTaskbarSize - visualBounds.height()) / 2;
visualBounds.top -= heightDiff;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 39f80c2..e9d8209 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -99,6 +99,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.config.FeatureFlags;
@@ -255,7 +256,7 @@
TaskbarNavButtonController buttonController,
ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
boolean isPrimaryDisplay, SystemUiProxy sysUiProxy) {
- super(windowContext);
+ super(windowContext, isPrimaryDisplay);
mIsPrimaryDisplay = isPrimaryDisplay;
mNavigationBarPanelContext = navigationBarPanelContext;
mSysUiProxy = sysUiProxy;
@@ -386,13 +387,6 @@
onViewCreated();
}
- /**
- * Returns whether this is a primary display.
- */
- public boolean isPrimaryDisplay() {
- return mIsPrimaryDisplay;
- }
-
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
public void updateDeviceProfile(DeviceProfile launcherDp) {
applyDeviceProfile(launcherDp);
@@ -411,7 +405,7 @@
/** Returns whether current taskbar is transient. */
public boolean isTransientTaskbar() {
- return DisplayController.isTransientTaskbar(this) && !isPhoneMode();
+ return super.isTransientTaskbar() && !isPhoneMode();
}
/**
@@ -432,7 +426,7 @@
mDeviceProfile = originDeviceProfile.toBuilder(this)
.withDimensionsOverride(overrideProvider).build();
- if (DisplayController.isTransientTaskbar(this)) {
+ if (isTransientTaskbar()) {
mTransientTaskbarDeviceProfile = mDeviceProfile;
mPersistentTaskbarDeviceProfile = mDeviceProfile
.toBuilder(this)
@@ -450,7 +444,6 @@
mNavMode = (DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
&& !mIsPrimaryDisplay) ? NavigationMode.THREE_BUTTONS
: DisplayController.getNavigationMode(this);
-
}
/** Called when the visibility of the bubble bar changed. */
@@ -661,8 +654,7 @@
int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SLIPPERY
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
- boolean watchOutside = DisplayController.isTransientTaskbar(this)
- || isThreeButtonNav();
+ boolean watchOutside = isTransientTaskbar() || isThreeButtonNav();
if (watchOutside && !isRunningInTestHarness()) {
windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
@@ -926,7 +918,7 @@
options.setSplashScreenStyle(splashScreenStyle);
options.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- IRemoteCallback endCallback = completeRunnableListCallback(callbacks);
+ IRemoteCallback endCallback = completeRunnableListCallback(callbacks, this);
options.setOnAnimationAbortListener(endCallback);
options.setOnAnimationFinishedListener(endCallback);
@@ -1223,8 +1215,8 @@
bubbleControllers.bubbleBarViewController.getBubbleBarWithFlyoutMaximumHeight()
).orElse(0);
int taskbarWindowSize;
- boolean shouldTreatAsTransient = DisplayController.isTransientTaskbar(this)
- || (enableTaskbarPinning() && !isThreeButtonNav());
+ boolean shouldTreatAsTransient =
+ isTransientTaskbar() || (enableTaskbarPinning() && !isThreeButtonNav());
int extraHeightForTaskbarTooltips = enableCursorHoverStates()
? resources.getDimensionPixelSize(R.dimen.arrow_toast_arrow_height)
@@ -1297,7 +1289,7 @@
* Applies forcibly show flag to taskbar window iff transient taskbar is unstashed.
*/
public void applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow) {
- if (!DisplayController.isTransientTaskbar(this) || isPhoneMode()) {
+ if (!isTransientTaskbar() || isPhoneMode()) {
return;
}
if (shouldForceShow) {
@@ -1887,7 +1879,7 @@
*/
@VisibleForTesting
public void unstashTaskbarIfStashed() {
- if (DisplayController.isTransientTaskbar(this)) {
+ if (isTransientTaskbar()) {
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
}
}
@@ -1950,6 +1942,8 @@
// Override the alpha updates in the icon alignment animation.
allAppsButton.setAlpha(0);
});
+ alphaOverride.addListener(AnimatorListeners.forSuccessCallback(
+ () -> allAppsButton.setAlpha(1f)));
fullAnimation.play(alphaOverride);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 89cc991..abbcd6d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -30,7 +30,6 @@
import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_PERSISTENT
import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_TRANSIENT
-import com.android.launcher3.util.DisplayController
import kotlin.math.min
/** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
@@ -43,7 +42,7 @@
private val maxPersistentTaskbarHeight =
context.persistentTaskbarDeviceProfile.taskbarHeight.toFloat()
var backgroundProgress =
- if (DisplayController.isTransientTaskbar(context)) {
+ if (context.isTransientTaskbar) {
PINNING_TRANSIENT
} else {
PINNING_PERSISTENT
@@ -124,7 +123,7 @@
* @param cornerRoundness 0 has no round corner, 1 has complete round corner.
*/
fun setCornerRoundness(cornerRoundness: Float) {
- if (DisplayController.isTransientTaskbar(context) && !transientBackgroundBounds.isEmpty) {
+ if (context.isTransientTaskbar && !transientBackgroundBounds.isEmpty) {
return
}
@@ -146,7 +145,7 @@
/** Draws the background with the given paint and height, on the provided canvas. */
fun draw(canvas: Canvas) {
if (isInSetup) return
- val isTransientTaskbar = DisplayController.isTransientTaskbar(context)
+ val isTransientTaskbar = context.isTransientTaskbar
canvas.save()
if (!isTransientTaskbar || transientBackgroundBounds.isEmpty || isAnimatingPinning) {
drawPersistentBackground(canvas)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index 3f6ebe2..e3e7499 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -35,7 +35,6 @@
import com.android.launcher3.R
import com.android.launcher3.popup.ArrowPopup
import com.android.launcher3.popup.RoundedArrowDrawable
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
import kotlin.math.max
@@ -64,12 +63,17 @@
false,
) as TaskbarDividerPopupView<*>
- return taskMenuViewWithArrow.populateForView(view, horizontalPosition)
+ return taskMenuViewWithArrow.populateForView(
+ view,
+ horizontalPosition,
+ taskbarActivityContext,
+ )
}
}
private lateinit var dividerView: View
private var horizontalPosition = 0.0f
+ private lateinit var taskbarActivityContext: TaskbarActivityContext
private val popupCornerRadius = Themes.getDialogCornerRadius(context)
private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
@@ -78,7 +82,7 @@
private val minPaddingFromScreenEdge =
resources.getDimension(R.dimen.taskbar_pinning_popup_menu_min_padding_from_screen_edge)
- private var alwaysShowTaskbarOn = !DisplayController.isTransientTaskbar(context)
+ private var alwaysShowTaskbarOn = !taskbarActivityContext.isTransientTaskbar
private var didPreferenceChange = false
private var verticalOffsetForPopupView =
resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_vertical_margin)
@@ -175,8 +179,13 @@
return false
}
- private fun populateForView(view: View, horizontalPosition: Float): TaskbarDividerPopupView<*> {
+ private fun populateForView(
+ view: View,
+ horizontalPosition: Float,
+ taskbar: TaskbarActivityContext,
+ ): TaskbarDividerPopupView<*> {
dividerView = view
+ taskbarActivityContext = taskbar
this@TaskbarDividerPopupView.horizontalPosition = horizontalPosition
tryUpdateBackground()
return this
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 1b516be..c884d39 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -80,7 +80,6 @@
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.views.BubbleTextHolder;
@@ -463,7 +462,7 @@
com.android.launcher3.logging.InstanceId launcherInstanceId = instanceIds.second;
intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId);
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
// Tell WM Shell to ignore drag events in the provided transient taskbar region.
TaskbarDragLayer dragLayer = mControllers.taskbarActivityContext.getDragLayer();
int[] locationOnScreen = dragLayer.getLocationOnScreen();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 55ecc37..1e193f6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -34,7 +34,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.util.DimensionUtils;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.TouchController;
@@ -108,19 +107,18 @@
if (startAnimation != null) {
// set taskbar background render animation boolean
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
mTaskbarDragLayer.setIsAnimatingTransientTaskbarBackground(true);
} else {
mTaskbarDragLayer.setIsAnimatingPersistentTaskbarBackground(true);
}
- float desiredValue = DisplayController.isTransientTaskbar(mActivity)
+ float desiredValue = mActivity.isTransientTaskbar()
? PINNING_TRANSIENT
: PINNING_PERSISTENT;
- float nonDesiredvalue = !DisplayController.isTransientTaskbar(mActivity)
- ? PINNING_TRANSIENT
- : PINNING_PERSISTENT;
+ float nonDesiredvalue =
+ !mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT;
ObjectAnimator objectAnimator = mTaskbarBackgroundProgress.animateToValue(
nonDesiredvalue, desiredValue);
@@ -133,9 +131,8 @@
}));
} else {
- mTaskbarBackgroundProgress.updateValue(DisplayController.isTransientTaskbar(mActivity)
- ? PINNING_TRANSIENT
- : PINNING_PERSISTENT);
+ mTaskbarBackgroundProgress.updateValue(
+ mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT);
}
mBgTaskbar.value = 1;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index 5d1288c..7a23006 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -52,6 +52,8 @@
import com.android.launcher3.views.BaseDragLayer
import com.android.quickstep.util.ContextualSearchInvoker
import com.android.quickstep.util.LottieAnimationColorUtils
+import com.android.wm.shell.shared.TypefaceUtils
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily
import java.io.PrintWriter
/** First EDU step for swiping up to show transient Taskbar. */
@@ -153,7 +155,7 @@
fun maybeShowSwipeEdu() {
if (
!isTooltipEnabled ||
- !DisplayController.isTransientTaskbar(activityContext) ||
+ !activityContext.isTransientTaskbar ||
tooltipStep > TOOLTIP_STEP_SWIPE
) {
return
@@ -164,7 +166,7 @@
tooltip?.run {
TypefaceUtils.setTypeface(
requireViewById(R.id.taskbar_edu_title),
- TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+ FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
)
val swipeAnimation = requireViewById<LottieAnimationView>(R.id.swipe_animation)
swipeAnimation.supportLightTheme()
@@ -198,7 +200,7 @@
suggestionsAnim.supportLightTheme()
pinningAnim.supportLightTheme()
handleEduAnimations(listOf(splitscreenAnim, suggestionsAnim, pinningAnim))
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
splitscreenAnim.setAnimation(R.raw.taskbar_edu_splitscreen_transient)
suggestionsAnim.setAnimation(R.raw.taskbar_edu_suggestions_transient)
pinningEdu.visibility = if (enableTaskbarPinning()) VISIBLE else GONE
@@ -210,25 +212,25 @@
TypefaceUtils.setTypeface(
requireViewById(R.id.taskbar_edu_title),
- TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+ FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
)
TypefaceUtils.setTypeface(
requireViewById(R.id.splitscreen_text),
- TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+ FontFamily.GSF_BODY_MEDIUM,
)
TypefaceUtils.setTypeface(
requireViewById(R.id.suggestions_text),
- TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+ FontFamily.GSF_BODY_MEDIUM,
)
TypefaceUtils.setTypeface(
requireViewById(R.id.pinning_text),
- TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+ FontFamily.GSF_BODY_MEDIUM,
)
// Set up layout parameters.
content.updateLayoutParams { width = MATCH_PARENT }
updateLayoutParams<MarginLayoutParams> {
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
width =
resources.getDimensionPixelSize(
if (enableTaskbarPinning())
@@ -261,7 +263,7 @@
// for the original 2 edu steps) as a proxy to needing to show the separate pinning edu
if (
!enableTaskbarPinning() ||
- !DisplayController.isTransientTaskbar(activityContext) ||
+ !activityContext.isTransientTaskbar ||
!isTooltipEnabled ||
tooltipStep > TOOLTIP_STEP_PINNING ||
tooltipStep < TOOLTIP_STEP_FEATURES
@@ -275,11 +277,11 @@
allowTouchDismissal = true
TypefaceUtils.setTypeface(
requireViewById(R.id.taskbar_edu_title),
- TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+ FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
)
TypefaceUtils.setTypeface(
requireViewById(R.id.pinning_text),
- TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
+ FontFamily.GSF_BODY_MEDIUM,
)
val pinningAnim =
@@ -287,7 +289,7 @@
pinningAnim.supportLightTheme()
handleEduAnimations(listOf(pinningAnim))
updateLayoutParams<BaseDragLayer.LayoutParams> {
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
bottomMargin += activityContext.deviceProfile.taskbarHeight
}
// Unlike other tooltips, we want to align with taskbar divider rather than center.
@@ -336,13 +338,13 @@
TypefaceUtils.setTypeface(
requireViewById(R.id.taskbar_edu_title),
- TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
+ FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED,
)
- TypefaceUtils.setTypeface(eduSubtitle, TypefaceUtils.FONT_FAMILY_BODY_SMALL_BASELINE)
+ TypefaceUtils.setTypeface(eduSubtitle, FontFamily.GSF_BODY_SMALL)
showDisclosureText(eduSubtitle)
updateLayoutParams<BaseDragLayer.LayoutParams> {
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
bottomMargin += activityContext.deviceProfile.taskbarHeight
}
// Unlike other tooltips, we want to align with the all apps button rather than
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index a8ce10f..3af2ab6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -235,8 +235,7 @@
context.resources,
)
val isPinnedTaskbar =
- context.deviceProfile.isTaskbarPresent &&
- !context.deviceProfile.isTransientTaskbar
+ context.deviceProfile.isTaskbarPresent && !context.isTransientTaskbar
val mandatoryGestureHeight = if (isPinnedTaskbar) contentHeight else gestureHeight
provider.insetsSize =
getInsetsForGravityWithCutout(mandatoryGestureHeight, gravity, endRotation)
@@ -388,10 +387,7 @@
bubbleBarVisible
) {
// Taskbar has some touchable elements, take over the full taskbar area
- if (
- controllers.uiController.isInOverviewUi &&
- DisplayController.isTransientTaskbar(context)
- ) {
+ if (controllers.uiController.isInOverviewUi && context.isTransientTaskbar) {
val region =
controllers.taskbarActivityContext.dragLayer.lastDrawnTransientRect.toRegion()
val bubbleBarBounds =
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index cc340ce..e8e5b30 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -15,8 +15,8 @@
*/
package com.android.launcher3.taskbar;
-import static android.content.Context.RECEIVER_NOT_EXPORTED;
import static android.content.Context.RECEIVER_EXPORTED;
+import static android.content.Context.RECEIVER_NOT_EXPORTED;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -25,6 +25,7 @@
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
+import static com.android.launcher3.taskbar.growth.GrowthConstants.BROADCAST_SHOW_NUDGE;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
import static com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
@@ -32,7 +33,6 @@
import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
-import static com.android.launcher3.taskbar.growth.GrowthConstants.BROADCAST_SHOW_NUDGE;
import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR;
@@ -81,6 +81,7 @@
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.quickstep.util.GroupTask;
@@ -628,7 +629,7 @@
/** Creates a {@link TaskbarUIController} to use with non default displays. */
private TaskbarUIController createTaskbarUIControllerForNonDefaultDisplay(int displayId) {
debugPrimaryTaskbar("createTaskbarUIControllerForNonDefaultDisplay");
- if (RecentsDisplayModel.enableOverviewInWindow()) {
+ if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
RecentsViewContainer rvc = mRecentsDisplayModel.getRecentsWindowManager(displayId);
if (rvc != null) {
return createTaskbarUIControllerForRecentsViewContainer(rvc);
@@ -732,8 +733,7 @@
displayId);
taskbar.updateDeviceProfile(dp);
}
- mSharedState.startTaskbarVariantIsTransient =
- DisplayController.isTransientTaskbar(taskbar);
+ mSharedState.startTaskbarVariantIsTransient = taskbar.isTransientTaskbar();
mSharedState.allAppsVisible = mSharedState.allAppsVisible && isLargeScreenTaskbar;
taskbar.init(mSharedState, duration);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 4a7e4f0..bf73f02 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -31,7 +31,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
-import com.android.launcher3.util.DisplayController;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -93,7 +92,8 @@
// There is no scrim for the bar in the phone mode.
return;
}
- if (isBubbleBarEnabled() && DisplayController.isTransientTaskbar(mActivity)) {
+ boolean isTransient = mActivity.isTransientTaskbar();
+ if (isBubbleBarEnabled() && isTransient) {
// These scrims aren't used if bubble bar & transient taskbar are active.
return;
}
@@ -112,7 +112,7 @@
boolean showScrimForBubbles = bubblesExpanded
&& !mTaskbarVisible
&& isBubbleControllersPresented
- && !DisplayController.isTransientTaskbar(mActivity)
+ && !mActivity.isTransientTaskbar()
&& !bubbleControllers.bubbleStashController.isBubblesShowingOnHome();
return bubblesExpanded && !mControllers.navbarButtonsViewController.isImeVisible()
&& !isShadeVisible
@@ -122,8 +122,8 @@
}
private float computeScrimAlpha() {
- final boolean isPersistentTaskBarVisible =
- mTaskbarVisible && !DisplayController.isTransientTaskbar(mScrimView.getContext());
+ boolean isTransient = mActivity.isTransientTaskbar();
+ final boolean isPersistentTaskBarVisible = mTaskbarVisible && !isTransient;
final boolean manageMenuExpanded =
(mSysUiStateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
if (isPersistentTaskBarVisible && manageMenuExpanded) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
index f87c21e..fa35a03 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
@@ -27,7 +27,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController;
-import com.android.launcher3.util.DisplayController;
import java.io.PrintWriter;
@@ -47,7 +46,7 @@
public TaskbarSpringOnStashController(TaskbarActivityContext context) {
mContext = context;
- mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+ mIsTransientTaskbar = context.isTransientTaskbar();
mStartVelocityPxPerS = context.getResources()
.getDimension(R.dimen.transient_taskbar_stash_spring_velocity_dp_per_s);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 95724ad..5284edd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -346,7 +346,7 @@
StashedHandleViewController.ALPHA_INDEX_STASHED);
mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ boolean isTransientTaskbar = mActivity.isTransientTaskbar();
boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
boolean isStashedInAppAuto =
isTransientTaskbar && !mTaskbarSharedState.getTaskbarWasPinned();
@@ -367,9 +367,7 @@
// Hide the background while stashed so it doesn't show on fast swipes home
boolean shouldHideTaskbarBackground = mActivity.isPhoneMode() ||
- (enableScalingRevealHomeAnimation()
- && DisplayController.isTransientTaskbar(mActivity)
- && isStashed());
+ (enableScalingRevealHomeAnimation() && isTransientTaskbar && isStashed());
mTaskbarBackgroundAlphaForStash.setValue(shouldHideTaskbarBackground ? 0 : 1);
@@ -414,8 +412,7 @@
if (DisplayController.isPinnedTaskbar(mActivity)) {
return PINNED_TASKBAR_TRANSITION_DURATION;
}
- return DisplayController.isTransientTaskbar(mActivity)
- ? TRANSIENT_TASKBAR_STASH_DURATION
+ return mActivity.isTransientTaskbar() ? TRANSIENT_TASKBAR_STASH_DURATION
: TASKBAR_STASH_DURATION;
}
@@ -509,8 +506,8 @@
* @see android.view.WindowInsets.Type#systemBars()
*/
public int getContentHeightToReportToApps() {
- if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode()
- || DisplayController.isTransientTaskbar(mActivity))) {
+ boolean isTransient = mActivity.isTransientTaskbar();
+ if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode() || isTransient)) {
return getStashedHeight();
}
@@ -577,8 +574,7 @@
*/
public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow,
boolean delayTaskbarBackground) {
- if (!DisplayController.isTransientTaskbar(mActivity)
- || mActivity.isBubbleBarOnPhone()) {
+ if (!mActivity.isTransientTaskbar() || mActivity.isBubbleBarOnPhone()) {
return;
}
@@ -646,7 +642,7 @@
/** Toggles the Taskbar's stash state. */
public void toggleTaskbarStash() {
- if (!DisplayController.isTransientTaskbar(mActivity) || !hasAnyFlag(FLAGS_IN_APP)) return;
+ if (!mActivity.isTransientTaskbar() || !hasAnyFlag(FLAGS_IN_APP)) return;
updateAndAnimateTransientTaskbar(!hasAnyFlag(FLAG_STASHED_IN_APP_AUTO));
}
@@ -697,8 +693,7 @@
mAnimator = new AnimatorSet();
addJankMonitorListener(
mAnimator, /* expanding= */ !isStashed, /* tag= */ jankTag);
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
- final float stashTranslation = mActivity.isPhoneMode() || isTransientTaskbar
+ final float stashTranslation = mActivity.isPhoneMode() || mActivity.isTransientTaskbar()
? 0
: (mUnstashedHeight - mStashedHeight);
@@ -722,7 +717,7 @@
return;
}
- if (isTransientTaskbar) {
+ if (mActivity.isTransientTaskbar()) {
createTransientAnimToIsStashed(mAnimator, isStashed, duration,
shouldDelayBackground, animationType);
} else {
@@ -1144,7 +1139,7 @@
boolean stashForBubbles = hasAnyFlag(FLAG_IN_OVERVIEW)
&& hasAnyFlag(systemUiStateFlags, SYSUI_STATE_BUBBLES_EXPANDED)
- && DisplayController.isTransientTaskbar(mActivity);
+ && mActivity.isTransientTaskbar();
updateStateForFlag(FLAG_STASHED_SYSUI,
hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING) || stashForBubbles);
updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED,
@@ -1174,7 +1169,7 @@
* <p>Do not stash if a system gesture is started.
*/
private boolean shouldStashForIme() {
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
return false;
}
// Do not stash if in small screen, with 3 button nav, and in landscape.
@@ -1270,7 +1265,7 @@
*/
public void setUpTaskbarSystemAction(boolean visible) {
UI_HELPER_EXECUTOR.execute(() -> {
- if (!visible || !DisplayController.isTransientTaskbar(mActivity)
+ if (!visible || !mActivity.isTransientTaskbar()
|| mActivity.isPhoneMode()) {
mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_TASKBAR);
mIsTaskbarSystemActionRegistered = false;
@@ -1321,7 +1316,7 @@
* If false, attempts to re/start the timeout
*/
public void updateTaskbarTimeout(boolean isAutohideSuspended) {
- if (!DisplayController.isTransientTaskbar(mActivity)) {
+ if (!mActivity.isTransientTaskbar()) {
return;
}
if (isAutohideSuspended) {
@@ -1335,9 +1330,7 @@
* Attempts to start timer to auto hide the taskbar based on time.
*/
private void tryStartTaskbarTimeout() {
- if (!DisplayController.isTransientTaskbar(mActivity)
- || mIsStashed
- || mEnableBlockingTimeoutDuringTests) {
+ if (!mActivity.isTransientTaskbar() || mIsStashed || mEnableBlockingTimeoutDuringTests) {
return;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
index deaf024..df10d24 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
@@ -23,7 +23,6 @@
import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE
import com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.TouchController
import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer
@@ -39,7 +38,7 @@
class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : TouchController {
private val activity: TaskbarActivityContext = controllers.taskbarActivityContext
- private val enabled = DisplayController.isTransientTaskbar(activity)
+ private val enabled = activity.isTransientTaskbar
private val swipeDownDetector: SingleAxisSwipeDetector
private val translationCallback = controllers.taskbarTranslationController.transitionCallback
/** Interpolator to apply resistance as user swipes down to the bottom of the screen. */
@@ -67,7 +66,7 @@
val gestureHeight: Int =
ResourceUtils.getNavbarSize(
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
- activity.resources
+ activity.resources,
)
gestureHeightYThreshold = (activity.deviceProfile.heightPx - gestureHeight).toFloat()
}
@@ -89,7 +88,7 @@
maxTouchDisplacement,
0f,
maxVisualDisplacement,
- displacementInterpolator
+ displacementInterpolator,
)
)
return false
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 5a5d6d0..13fb296 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -29,7 +29,6 @@
import com.android.app.animation.Interpolators;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.util.DisplayController;
import java.io.PrintWriter;
@@ -63,7 +62,7 @@
public TaskbarTranslationController(TaskbarActivityContext context) {
mContext = context;
- mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+ mIsTransientTaskbar = mContext.isTransientTaskbar();
mCallback = new TransitionCallback();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index ea0b81e..061a5a1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -37,7 +37,6 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.taskbar.bubbles.BubbleBarController;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.SplitTask;
import com.android.quickstep.views.RecentsView;
@@ -116,9 +115,8 @@
*/
public void hideOverlayWindow() {
mControllers.keyboardQuickSwitchController.closeQuickSwitchView();
-
- if (!DisplayController.isTransientTaskbar(mControllers.taskbarActivityContext)
- || mControllers.taskbarAllAppsController.isOpen()) {
+ boolean isTransientTaskbar = mControllers.taskbarActivityContext.isTransientTaskbar();
+ if (!isTransientTaskbar || mControllers.taskbarAllAppsController.isOpen()) {
mControllers.taskbarOverlayController.hideWindow();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 07b77c9..3ff037e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -61,7 +61,6 @@
import com.android.launcher3.taskbar.customization.TaskbarAllAppsButtonContainer;
import com.android.launcher3.taskbar.customization.TaskbarDividerContainer;
import com.android.launcher3.uioverrides.PredictedAppIcon;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.util.GroupTask;
@@ -184,8 +183,9 @@
setWillNotDraw(false);
mAllAppsButtonContainer = new TaskbarAllAppsButtonContainer(context);
- mAllAppsButtonTranslationOffset = (int) getResources().getDimension(
- mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(isTransientTaskbar()));
+ mAllAppsButtonTranslationOffset = (int) getResources().getDimension(
+ mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
+ mActivityContext.isTransientTaskbar()));
if (enableTaskbarPinning() || enableRecentsInTaskbar()) {
mTaskbarDividerContainer = new TaskbarDividerContainer(context);
@@ -200,10 +200,7 @@
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
- mNumStaticViews =
- ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue() && !mActivityContext.isPhoneMode()
- ? addStaticViews()
- : 0;
+ mNumStaticViews = ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue() ? addStaticViews() : 0;
}
/**
@@ -243,7 +240,7 @@
enableTaskbarPinning() && !mActivityContext.isThreeButtonNav();
availableWidth -= iconSize - (int) getResources().getDimension(
mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
- forceTransientTaskbarSize || isTransientTaskbar()));
+ forceTransientTaskbarSize || mActivityContext.isTransientTaskbar()));
++additionalIcons;
return Math.floorDiv(availableWidth, iconSize) + additionalIcons;
@@ -1101,11 +1098,6 @@
// Ignore, we just implement Insettable to draw behind system insets.
}
- private boolean isTransientTaskbar() {
- return DisplayController.isTransientTaskbar(mActivityContext)
- && !mActivityContext.isPhoneMode();
- }
-
public boolean areIconsVisible() {
// Consider the overall visibility
return getVisibility() == VISIBLE;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index a80e2c4..605171a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -267,9 +267,8 @@
: mActivity.getDeviceProfile().taskbarHeight;
mTaskbarIconScaleForStash.updateValue(1f);
- float pinningValue = DisplayController.isTransientTaskbar(mActivity)
- ? PINNING_TRANSIENT
- : PINNING_PERSISTENT;
+ float pinningValue =
+ mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT;
mTaskbarIconScaleForPinning.updateValue(pinningValue);
mTaskbarIconTranslationYForPinning.updateValue(pinningValue);
mTaskbarIconTranslationXForPinning.updateValue(pinningValue);
@@ -936,7 +935,7 @@
mOnControllerPreCreateCallback.run();
DeviceProfile taskbarDp = mActivity.getDeviceProfile();
Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ boolean isTransientTaskbar = mActivity.isTransientTaskbar();
float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.taskbarIconSize;
int borderSpacing = launcherDp.hotseatBorderSpace;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt b/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
deleted file mode 100644
index e9c62d1..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2025 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.taskbar
-
-import android.graphics.Typeface
-import android.widget.TextView
-import com.android.launcher3.Flags
-
-/**
- * Helper util class to set pre-defined typefaces to textviews
- *
- * If the typeface font family is already defined here, you can just reuse it directly. Otherwise,
- * please define it here for future use. You do not need to define the font style. If you need
- * anything other than [Typeface.NORMAL], pass it inline when calling [setTypeface]
- */
-class TypefaceUtils {
-
- companion object {
- const val FONT_FAMILY_BODY_SMALL_BASELINE = "variable-body-small"
- const val FONT_FAMILY_BODY_MEDIUM_BASELINE = "variable-body-medium"
- const val FONT_FAMILY_BODY_LARGE_BASELINE = "variable-body-large"
- const val FONT_FAMILY_LABEL_LARGE_BASELINE = "variable-label-large"
- const val FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED = "variable-display-small-emphasized"
- const val FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED = "variable-display-medium-emphasized"
- const val FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED = "variable-headline-small-emphasized"
- const val FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED = "variable-headline-large-emphasized"
-
- @JvmStatic
- @JvmOverloads
- fun setTypeface(
- textView: TextView?,
- fontFamilyName: String,
- fontStyle: Int = Typeface.NORMAL,
- ) {
- if (!Flags.expressiveThemeInTaskbarAndNavigation()) return
- textView?.typeface = Typeface.create(fontFamilyName, fontStyle)
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index c380c8d..c7d42b1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -22,7 +22,6 @@
import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.views.BaseDragLayer
import com.android.systemui.animation.ViewRootSync
import java.io.PrintWriter
@@ -41,8 +40,7 @@
class VoiceInteractionWindowController(val context: TaskbarActivityContext) :
TaskbarControllers.LoggableTaskbarController, TaskbarControllers.BackgroundRendererController {
- private val isSeparateBackgroundEnabled =
- !DisplayController.isTransientTaskbar(context) && !context.isPhoneMode
+ private val isSeparateBackgroundEnabled = !context.isTransientTaskbar && !context.isPhoneMode
private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
private val nonTouchableInsetsComputer =
ViewTreeObserver.OnComputeInternalInsetsListener {
@@ -97,7 +95,7 @@
separateWindowLayoutParams =
context.createDefaultWindowLayoutParams(
TYPE_APPLICATION_OVERLAY,
- TEMP_BACKGROUND_WINDOW_TITLE
+ TEMP_BACKGROUND_WINDOW_TITLE,
)
separateWindowLayoutParams?.isSystemApplicationOverlay = true
}
@@ -163,7 +161,7 @@
// First add the temporary window, then hide the overlapping taskbar background.
context.addWindowView(
separateWindowForTaskbarBackground,
- separateWindowLayoutParams
+ separateWindowLayoutParams,
);
{ controllers.taskbarDragLayerController.setIsBackgroundDrawnElsewhere(true) }
} else {
@@ -179,7 +177,7 @@
ViewRootSync.synchronizeNextDraw(
separateWindowForTaskbarBackground!!,
context.dragLayer,
- onWindowsSynchronized
+ onWindowsSynchronized,
)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 52f7176..f1ccd39 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -31,7 +31,6 @@
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
-import com.android.launcher3.util.DisplayController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import java.util.Optional;
@@ -90,7 +89,7 @@
}
private void setUpTaskbarStashing() {
- if (DisplayController.isTransientTaskbar(mContext)) {
+ if (mContext.isTransientTaskbar()) {
mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
mTaskbarStashController.applyState();
}
@@ -103,7 +102,7 @@
AbstractFloatingView.closeOpenContainer(
mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
- if (DisplayController.isTransientTaskbar(mContext)) {
+ if (mContext.isTransientTaskbar()) {
mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
mTaskbarStashController.applyState();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 277dbbf..ddf9d51 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -273,7 +273,7 @@
mBoundsChangeListener.onBoundsChanged();
}
});
- float pinningValue = DisplayController.isTransientTaskbar(mActivity)
+ float pinningValue = mActivity.isTransientTaskbar()
? PINNING_TRANSIENT
: PINNING_PERSISTENT;
mBubbleBarPinning.updateValue(pinningValue);
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index 4932654..82b1295 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -32,7 +32,6 @@
import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarViewCallbacks
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
@@ -69,7 +68,7 @@
)
backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
setIconDrawable(drawable)
- if (!DisplayController.isTransientTaskbar(context)) {
+ if (activityContext.isTransientTaskbar) {
setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
}
setForegroundTint(activityContext.getColor(R.color.all_apps_button_color))
@@ -106,7 +105,7 @@
@DimenRes
fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int {
- return if (isTransientTaskbar) {
+ return if (isTransientTaskbar && activityContext.isTransientTaskbar) {
R.dimen.transient_taskbar_all_apps_button_translation_x_offset
} else {
R.dimen.taskbar_all_apps_search_button_translation_x_offset
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
index d5f72d5..060ce46 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
@@ -26,7 +26,6 @@
import com.android.launcher3.Utilities.dpToPx
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarViewCallbacks
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
@@ -52,7 +51,7 @@
backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
val drawable = resources.getDrawable(R.drawable.taskbar_divider_button)
setIconDrawable(drawable)
- if (!DisplayController.isTransientTaskbar(context)) {
+ if (!activityContext.isTransientTaskbar) {
setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
index f130d29..a14c5a4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
@@ -19,7 +19,6 @@
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.launcher3.util.DisplayController
/** Evaluates all the features taskbar can have. */
class TaskbarFeatureEvaluator
@@ -36,7 +35,7 @@
get() = enableTaskbarPinning() || isRecentsEnabled
val isTransient: Boolean
- get() = DisplayController.isTransientTaskbar(taskbarActivityContext)
+ get() = taskbarActivityContext.isTransientTaskbar
val isLandscape: Boolean
get() = taskbarActivityContext.deviceProfile.isLandscape
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 55bb0f9..fdb5315 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -55,7 +55,7 @@
Context windowContext,
TaskbarActivityContext taskbarContext,
TaskbarControllers controllers) {
- super(windowContext);
+ super(windowContext, taskbarContext.isPrimaryDisplay());
mTaskbarContext = taskbarContext;
mOverlayController = controllers.taskbarOverlayController;
mDragController = new TaskbarDragController(this);
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index 669850c..41694ec 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -34,7 +34,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -147,7 +146,7 @@
* 2) Sets tappableInsets bottom inset to 0.
*/
private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
- if (!DisplayController.isTransientTaskbar(mContainer)) {
+ if (!mContainer.isTransientTaskbar()) {
return oldInsets;
}
WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 1a42d21..cd0a4f3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -1274,7 +1274,7 @@
options.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- IRemoteCallback endCallback = completeRunnableListCallback(callbacks);
+ IRemoteCallback endCallback = completeRunnableListCallback(callbacks, this);
options.setOnAnimationAbortListener(endCallback);
options.setOnAnimationFinishedListener(endCallback);
return new ActivityOptionsWrapper(options, callbacks);
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index 914855b..4280baf 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -82,6 +82,7 @@
taskKey.numActivities,
taskKey.isTopActivityNoDisplay,
taskKey.isActivityStackTransparent,
+ taskKey.userId,
) -> null
!taskContainer.task.isDockable -> null
diff --git a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
index f97cf9c..3b823f5 100644
--- a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
@@ -80,6 +80,7 @@
taskKey.numActivities,
taskKey.isTopActivityNoDisplay,
taskKey.isActivityStackTransparent,
+ taskKey.userId,
) -> null
else -> {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 74aa8e2..c4ba2d5 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -38,10 +38,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.Flags;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulContainer;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -323,8 +323,7 @@
*/
public boolean useSyntheticRecentsTransition() {
return mRunningTask.isHomeTask()
- && (Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow());
+ && RecentsWindowFlags.Companion.getEnableOverviewInWindow();
}
/**
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 783ec2c..2ff42cd 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -220,7 +220,7 @@
mHandler.post(() -> {
LauncherBackAnimationController controller = mControllerRef.get();
if (controller != null) {
- controller.startBack(backEvent);
+ controller.initBackMotion(backEvent);
mProgressAnimator.onBackStarted(backEvent, event -> {
float backProgress = event.getProgress();
controller.mBackProgress =
@@ -270,6 +270,7 @@
}
}
controller.mAnimationFinishedCallback = finishedCallback;
+ controller.startBack();
}
@Override
@@ -294,34 +295,32 @@
mBackCallback = null;
}
- private void startBack(BackMotionEvent backEvent) {
+ private void initBackMotion(BackMotionEvent backEvent) {
// in case we're still animating an onBackCancelled event, let's remove the finish-
// callback from the progress animator to prevent calling finishAnimation() before
// restarting a new animation
- // Side note: startBack is never called during the post-commit phase if the back gesture
- // was committed (not cancelled). BackAnimationController prevents that. Therefore we
- // don't have to handle that case.
+ // Side note: initBackMotion is never called during the post-commit phase if the back
+ // gesture was committed (not cancelled). BackAnimationController prevents that. Therefore
+ // we don't have to handle that case.
mProgressAnimator.removeOnBackCancelledFinishCallback();
mBackInProgress = true;
- RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
-
- if (appTarget == null || appTarget.leash == null || !appTarget.leash.isValid()) {
+ mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
+ }
+ private void startBack() {
+ if (mBackTarget == null) {
return;
}
mTransaction
- .show(appTarget.leash)
+ .show(mBackTarget.leash)
.setAnimationTransaction();
- mBackTarget = appTarget;
- mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
-
- mStartRect.set(appTarget.windowConfiguration.getMaxBounds());
+ mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
// inset bottom in case of taskbar being present
if (!predictiveBackThreeButtonNav() || mLauncher.getDeviceProfile().isTaskbarPresent
|| DisplayController.getNavigationMode(mLauncher) == NavigationMode.NO_BUTTON) {
- mStartRect.inset(0, 0, 0, appTarget.contentInsets.bottom);
+ mStartRect.inset(0, 0, 0, mBackTarget.contentInsets.bottom);
}
mLauncherTargetView = mQuickstepTransitionManager.findLauncherView(
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index 984f390..bf87291 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -30,9 +30,7 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.Cuj
import com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays
-import com.android.launcher3.Flags.enableFallbackOverviewInWindow
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
-import com.android.launcher3.Flags.enableLauncherOverviewInWindow
import com.android.launcher3.Flags.enableOverviewCommandHelperTimeout
import com.android.launcher3.PagedView
import com.android.launcher3.logger.LauncherAtom
@@ -53,6 +51,7 @@
import com.android.quickstep.OverviewCommandHelper.CommandType.SHOW
import com.android.quickstep.OverviewCommandHelper.CommandType.TOGGLE
import com.android.quickstep.fallback.window.RecentsDisplayModel
+import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
import com.android.quickstep.util.ActiveGestureLog
import com.android.quickstep.util.ActiveGestureProtoLogProxy
import com.android.quickstep.views.RecentsView
@@ -299,7 +298,7 @@
val focusedDisplayId = focusState.focusedDisplayId
val focusedDisplayUIController: TaskbarUIController? =
- if (RecentsDisplayModel.enableOverviewInWindow()) {
+ if (enableOverviewInWindow) {
Log.d(
TAG,
"Querying RecentsDisplayModel for TaskbarUIController for display: $focusedDisplayId",
@@ -392,9 +391,7 @@
return false
}
- val recentsInWindowFlagSet =
- enableFallbackOverviewInWindow() || enableLauncherOverviewInWindow()
- if (!recentsInWindowFlagSet) {
+ if (!enableOverviewInWindow) {
containerInterface.getCreatedContainer()?.rootView?.let { view ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
}
@@ -425,7 +422,7 @@
transitionInfo: TransitionInfo?,
) {
Log.d(TAG, "recents animation started: $command")
- if (recentsInWindowFlagSet) {
+ if (enableOverviewInWindow) {
containerInterface.getCreatedContainer()?.rootView?.let { view ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 7eacef3..7d3a1da 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableLauncherOverviewInWindow;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
import android.content.ActivityNotFoundException;
@@ -41,7 +42,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppComponent;
@@ -50,6 +50,7 @@
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -180,7 +181,7 @@
mDefaultDisplayContainerInterface.onAssistantVisibilityChanged(0.f);
}
- if (SEPARATE_RECENTS_ACTIVITY.get() || Flags.enableLauncherOverviewInWindow()) {
+ if (SEPARATE_RECENTS_ACTIVITY.get() || enableLauncherOverviewInWindow.isTrue()) {
mIsDefaultHome = false;
if (defaultHome == null) {
defaultHome = mMyHomeIntent.getComponent();
@@ -203,7 +204,7 @@
unregisterOtherHomeAppUpdateReceiver();
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
+ if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
mDefaultDisplayContainerInterface =
mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
} else {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index ac88e5a..cc5b2da 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -38,8 +38,10 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.ExternalDisplaysKt;
import com.android.quickstep.util.GroupTask;
@@ -70,7 +72,10 @@
/**
* Manages the recent task list from the system, caching it as necessary.
*/
-public class RecentTasksList {
+// TODO: b/401602554 - Consider letting [DesktopTasksController] notify [RecentTasksController] of
+// desk changes to trigger [IRecentTasksListener.onRecentTasksChanged()], instead of implementing
+// [DesktopVisibilityListener].
+public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityListener {
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
@@ -78,6 +83,7 @@
private final KeyguardManager mKeyguardManager;
private final LooperExecutor mMainThreadExecutor;
private final SystemUiProxy mSysUiProxy;
+ private final DesktopVisibilityController mDesktopVisibilityController;
// The list change id, increments as the task list changes in the system
private int mChangeId;
@@ -95,13 +101,16 @@
public RecentTasksList(Context context, LooperExecutor mainThreadExecutor,
KeyguardManager keyguardManager, SystemUiProxy sysUiProxy,
- TopTaskTracker topTaskTracker) {
+ TopTaskTracker topTaskTracker,
+ DesktopVisibilityController desktopVisibilityController,
+ DaggerSingletonTracker tracker) {
mContext = context;
mMainThreadExecutor = mainThreadExecutor;
mKeyguardManager = keyguardManager;
mChangeId = 1;
mSysUiProxy = sysUiProxy;
- sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() {
+ mDesktopVisibilityController = desktopVisibilityController;
+ final IRecentTasksListener recentTasksListener = new IRecentTasksListener.Stub() {
@Override
public void onRecentTasksChanged() throws RemoteException {
mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
@@ -147,7 +156,19 @@
topTaskTracker.onVisibleTasksChanged(visibleTasks);
});
}
- });
+ };
+
+ mSysUiProxy.registerRecentTasksListener(recentTasksListener);
+ tracker.addCloseable(
+ () -> mSysUiProxy.unregisterRecentTasksListener(recentTasksListener));
+
+ if (DesktopModeStatus.enableMultipleDesktops(mContext)) {
+ mDesktopVisibilityController.registerDesktopVisibilityListener(
+ this);
+ tracker.addCloseable(
+ () -> mDesktopVisibilityController.unregisterDesktopVisibilityListener(this));
+ }
+
// We may receive onRunningTaskAppeared events later for tasks which have already been
// included in the list returned by mSysUiProxy.getRunningTasks(), or may receive
// onRunningTaskVanished for tasks not included in the returned list. These cases will be
@@ -286,6 +307,27 @@
return mRunningTasks;
}
+ @Override
+ public void onDeskAdded(int displayId, int deskId) {
+ onRecentTasksChanged();
+ }
+
+ @Override
+ public void onDeskRemoved(int displayId, int deskId) {
+ onRecentTasksChanged();
+ }
+
+ @Override
+ public void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {
+ // Should desk activation changes lead to the invalidation of the loaded tasks? The cases
+ // are:
+ // - Switching from one active desk to another.
+ // - Switching from out of a desk session into an active desk.
+ // - Switching from an active desk to a non-desk session.
+ // These changes don't affect the list of desks, nor their contents, so let's ignore them
+ // for now.
+ }
+
private void onRunningTaskAppeared(RunningTaskInfo taskInfo) {
// Make sure this task is not already in the list
for (RunningTaskInfo existingTask : mRunningTasks) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index d7152b5..ecde37b 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -34,9 +34,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
-import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -113,8 +113,8 @@
boolean isOpeningHome = Arrays.stream(appTargets).filter(app -> app.mode == MODE_OPENING
&& app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME)
.count() > 0;
- if (appCount == 0 && (!(Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow()) || isOpeningHome)) {
+ if (appCount == 0 && (!RecentsWindowFlags.Companion.getEnableOverviewInWindow()
+ || isOpeningHome)) {
ActiveGestureProtoLogProxy.logOnRecentsAnimationStartCancelled();
// Edge case, if there are no closing app targets, then Launcher has nothing to handle
notifyAnimationCanceled();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index cf7e499..0deb1ca 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -59,6 +59,10 @@
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return false;
}
+ // TODO: b/400866688 - Check if we need to update this such that for an empty desk, we
+ // receive a list of apps that contain only the Launcher and the `DesktopWallpaperActivity`
+ // and both are fullscreen windowing mode. A desk can also have transparent modals and
+ // immersive apps which may not have a "freeform" windowing mode.
for (RemoteAnimationTarget target : apps) {
if (target.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
return true;
diff --git a/quickstep/src/com/android/quickstep/RecentsFilterState.java b/quickstep/src/com/android/quickstep/RecentsFilterState.java
index c4b0f25..1808a97 100644
--- a/quickstep/src/com/android/quickstep/RecentsFilterState.java
+++ b/quickstep/src/com/android/quickstep/RecentsFilterState.java
@@ -18,6 +18,7 @@
import androidx.annotation.Nullable;
+import com.android.quickstep.util.DesksUtils;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.TaskViewType;
import com.android.systemui.shared.recents.model.Task;
@@ -117,37 +118,43 @@
* Returns a predicate for filtering out GroupTasks by package name.
*
* @param packageName package name to filter GroupTasks by
- * if null, Predicate filters out desktop tasks with no non-minimized tasks.
+ * if null, Predicate filters out desktop tasks with no non-minimized tasks,
+ * unless the multiple desks feature is enabled, which allows empty desks.
*/
public static Predicate<GroupTask> getFilter(@Nullable String packageName) {
if (packageName == null) {
- return getEmptyDesktopTaskFilter();
+ return getDesktopTaskFilter();
}
return (groupTask) -> (groupTask.containsPackage(packageName)
- && !isDestopTaskWithMinimizedTasksOnly(groupTask));
+ && shouldKeepGroupTask(groupTask));
}
/**
- * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks.
+ * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks,
+ * unless the multiple desks feature is enabled, which allows empty desks.
*/
- public static Predicate<GroupTask> getEmptyDesktopTaskFilter() {
- return (groupTask -> !isDestopTaskWithMinimizedTasksOnly(groupTask));
+ public static Predicate<GroupTask> getDesktopTaskFilter() {
+ return (groupTask -> shouldKeepGroupTask(groupTask));
}
/**
- * Whether the provided task is a desktop task with no non-minimized tasks - returns true if the
- * desktop task has no tasks at all.
+ * Returns true if the given `groupTask` should be kept, and false if it should be filtered out.
+ * Desks will be filtered out if they are empty unless the multiple desks feature is enabled.
*
* @param groupTask The group task to check.
*/
- static boolean isDestopTaskWithMinimizedTasksOnly(GroupTask groupTask) {
+ private static boolean shouldKeepGroupTask(GroupTask groupTask) {
if (groupTask.taskViewType != TaskViewType.DESKTOP) {
- return false;
+ return true;
}
+
+ if (DesksUtils.areMultiDesksFlagsEnabled()) {
+ return true;
+ }
+
return groupTask.getTasks().stream()
- .filter(task -> !task.isMinimized)
- .toList().isEmpty();
+ .anyMatch(task -> !task.isMinimized);
}
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 1d83d42..e1adf3d 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -43,6 +43,7 @@
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener;
import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.DisplayController;
@@ -61,6 +62,8 @@
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -72,8 +75,6 @@
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Singleton class to load and manage recents model.
*/
@@ -104,12 +105,14 @@
DisplayController displayController,
LockedUserState lockedUserState,
Lazy<ThemeManager> themeManagerLazy,
+ DesktopVisibilityController desktopVisibilityController,
DaggerSingletonTracker tracker
) {
// Lazily inject the ThemeManager and access themeManager once the device is
// unlocked. See b/393248495 for details.
this(context, new IconProvider(context), systemUiProxy, topTaskTracker,
- displayController, lockedUserState,themeManagerLazy, tracker);
+ displayController, lockedUserState, themeManagerLazy, desktopVisibilityController,
+ tracker);
}
@SuppressLint("VisibleForTests")
@@ -120,6 +123,7 @@
DisplayController displayController,
LockedUserState lockedUserState,
Lazy<ThemeManager> themeManagerLazy,
+ DesktopVisibilityController desktopVisibilityController,
DaggerSingletonTracker tracker) {
this(context,
new RecentTasksList(
@@ -127,7 +131,7 @@
MAIN_EXECUTOR,
context.getSystemService(KeyguardManager.class),
systemUiProxy,
- topTaskTracker),
+ topTaskTracker, desktopVisibilityController, tracker),
new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider, displayController),
new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR),
iconProvider,
@@ -205,7 +209,7 @@
@Override
public int getTasks(@Nullable Consumer<List<GroupTask>> callback) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback,
- RecentsFilterState.getEmptyDesktopTaskFilter());
+ RecentsFilterState.getDesktopTaskFilter());
}
/**
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
index d6f6540..506f85d 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -1096,18 +1096,26 @@
// Desktop Mode
//
/** Calls shell to create a new desk (if possible) on the display whose ID is `displayId`. */
- fun createDesktop(displayId: Int) =
+ fun createDesk(displayId: Int) =
executeWithErrorLog({ "Failed call createDesk" }) { desktopMode?.createDesk(displayId) }
/**
* Calls shell to activate the desk whose ID is `deskId` on whatever display it exists on. This
* will bring all tasks on this desk to the front.
*/
- fun activateDesktop(deskId: Int, transition: RemoteTransition?) =
+ fun activateDesk(deskId: Int, transition: RemoteTransition?) =
executeWithErrorLog({ "Failed call activateDesk" }) {
desktopMode?.activateDesk(deskId, transition)
}
+ /** Calls shell to remove the desk whose ID is `deskId`. */
+ fun removeDesk(deskId: Int) =
+ executeWithErrorLog({ "Failed call removeDesk" }) { desktopMode?.removeDesk(deskId) }
+
+ /** Calls shell to remove all the available desks on all displays. */
+ fun removeAllDesks() =
+ executeWithErrorLog({ "Failed call removeAllDesks" }) { desktopMode?.removeAllDesks() }
+
/** Call shell to show all apps active on the desktop */
fun showDesktopApps(displayId: Int, transition: RemoteTransition?) =
executeWithErrorLog({ "Failed call showDesktopApps" }) {
@@ -1159,9 +1167,9 @@
}
/** Call shell to remove the desktop that is on given `displayId` */
- fun removeDesktop(displayId: Int) =
- executeWithErrorLog({ "Failed call removeDesktop" }) {
- desktopMode?.removeDesktop(displayId)
+ fun removeDefaultDeskInDisplay(displayId: Int) =
+ executeWithErrorLog({ "Failed call removeDefaultDeskInDisplay" }) {
+ desktopMode?.removeDefaultDeskInDisplay(displayId)
}
/** Call shell to move a task with given `taskId` to external display. */
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 64a8c25..1c7f23c 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -42,12 +42,12 @@
import androidx.annotation.UiThread;
import com.android.internal.util.ArrayUtils;
-import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.SystemUiFlagUtils;
@@ -63,7 +63,6 @@
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
public static final boolean SHELL_TRANSITIONS_ROTATION =
SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
-
private final Context mCtx;
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
@@ -313,8 +312,7 @@
}
if(containerInterface.getCreatedContainer() instanceof RecentsWindowManager
- && (Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow())) {
+ && RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent, options,
mCallbacks, gestureState.useSyntheticRecentsTransition());
RecentsDisplayModel.getINSTANCE().get(mCtx)
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ba662c4..741ae7d 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -94,6 +94,7 @@
import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
@@ -1081,10 +1082,9 @@
}
public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
- boolean recentsInWindow =
- Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow();
return mOverviewComponentObserver.isHomeAndOverviewSame()
- ? mLauncherSwipeHandlerFactory : (recentsInWindow
+ ? mLauncherSwipeHandlerFactory
+ : (RecentsWindowFlags.Companion.getEnableOverviewInWindow()
? mRecentsWindowSwipeHandlerFactory : mFallbackSwipeHandlerFactory);
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index d8662f2..8ec97ed 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -30,7 +30,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Flags;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
@@ -46,6 +45,7 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SingleTask;
import com.android.quickstep.util.SplitSelectStateController;
@@ -80,9 +80,10 @@
@Override
public BaseContainerInterface<RecentsState, ?> getContainerInterface(int displayId) {
- return (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow())
+ return RecentsWindowFlags.Companion.getEnableOverviewInWindow()
? RecentsDisplayModel.getINSTANCE().get(mContext)
- .getFallbackWindowInterface(displayId) : FallbackActivityInterface.INSTANCE;
+ .getFallbackWindowInterface(displayId)
+ : FallbackActivityInterface.INSTANCE;
}
@Override
@@ -290,8 +291,7 @@
}
// disabling this so app icons aren't drawn on top of recent tasks.
- if (isOverlayEnabled && !(Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow())) {
+ if (isOverlayEnabled && !RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
index 58c6c50..12dc177 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.view.Display
import androidx.core.util.valueIterator
-import com.android.launcher3.Flags
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.DaggerSingletonObject
@@ -29,6 +28,7 @@
import com.android.quickstep.FallbackWindowInterface
import com.android.quickstep.dagger.QuickstepBaseAppComponent
import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource
+import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
import java.io.PrintWriter
import javax.inject.Inject
@@ -50,14 +50,10 @@
DaggerSingletonObject<RecentsDisplayModel>(
QuickstepBaseAppComponent::getRecentsDisplayModel
)
-
- @JvmStatic
- fun enableOverviewInWindow() =
- Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()
}
init {
- if (enableOverviewInWindow()) {
+ if (enableOverviewInWindow) {
registerDisplayListener()
tracker.addCloseable { destroy() }
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
index 333571c..d70d7eb 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.graphics.PixelFormat
-import android.view.Display.DEFAULT_DISPLAY
+import android.view.Display
import android.view.ViewGroup
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
@@ -53,7 +53,7 @@
fun initDeviceProfile() {
deviceProfile =
- if (displayId == DEFAULT_DISPLAY)
+ if (displayId == Display.DEFAULT_DISPLAY)
InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this)
else InvariantDeviceProfile.INSTANCE[this].createDeviceProfileForSecondaryDisplay(this)
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
new file mode 100644
index 0000000..9953154
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 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.fallback.window
+
+import android.window.DesktopModeFlags.DesktopModeFlag
+import com.android.launcher3.Flags
+
+class RecentsWindowFlags {
+ companion object {
+ @JvmField
+ val enableLauncherOverviewInWindow: DesktopModeFlag =
+ DesktopModeFlag(Flags::enableLauncherOverviewInWindow, false)
+
+ @JvmField
+ val enableFallbackOverviewInWindow: DesktopModeFlag =
+ DesktopModeFlag(Flags::enableFallbackOverviewInWindow, false)
+
+ @JvmStatic
+ val enableOverviewInWindow
+ get() = enableLauncherOverviewInWindow.isTrue || enableFallbackOverviewInWindow.isTrue
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 7cae5b8..c8cf58c 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -42,7 +42,6 @@
import androidx.annotation.UiThread;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
@@ -59,6 +58,7 @@
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
@@ -438,10 +438,8 @@
notifyGestureStarted(true /*isLikelyToStartNewTask*/);
} else {
// todo differentiate intent based on if we are on home or in app for overview in window
- boolean useHomeIntentForWindow = Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow();
- Intent intent = new Intent(useHomeIntentForWindow ? mInteractionHandler.getHomeIntent()
- : mInteractionHandler.getLaunchIntent());
+ Intent intent = new Intent(RecentsWindowFlags.Companion.getEnableOverviewInWindow()
+ ? mInteractionHandler.getHomeIntent() : mInteractionHandler.getLaunchIntent());
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
mInteractionHandler);
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 1e61967..5995ca2 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -56,11 +56,12 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.taskbar.TypefaceUtils;
import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieComposition;
@@ -409,7 +410,7 @@
if (mTutorialFragment.isAtFinalStep()) {
TypefaceUtils.setTypeface(
mDoneButton,
- TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE
+ FontFamily.GSF_LABEL_LARGE
);
showActionButton();
}
@@ -517,11 +518,11 @@
TypefaceUtils.setTypeface(
mFeedbackTitleView,
mTutorialFragment.isLargeScreen()
- ? TypefaceUtils.FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED
- : TypefaceUtils.FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED);
+ ? FontFamily.GSF_DISPLAY_MEDIUM_EMPHASIZED
+ : FontFamily.GSF_DISPLAY_SMALL_EMPHASIZED);
TypefaceUtils.setTypeface(
mFeedbackSubtitleView,
- TypefaceUtils.FONT_FAMILY_BODY_LARGE_BASELINE
+ FontFamily.GSF_BODY_LARGE
);
}
diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
index fda0c29..3492788 100644
--- a/quickstep/src/com/android/quickstep/util/AnimUtils.java
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -21,16 +21,22 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.animation.AnimatorSet;
-import android.annotation.NonNull;
+import android.os.BinderUtils;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.IRemoteCallback;
import android.view.animation.Interpolator;
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.views.RecentsViewContainer;
/**
@@ -95,14 +101,30 @@
}
/**
- * Returns a IRemoteCallback which completes the provided list as a result
+ * Returns a IRemoteCallback which completes the provided list as a result or when the owner
+ * is destroyed
*/
- public static IRemoteCallback completeRunnableListCallback(RunnableList list) {
+ public static IRemoteCallback completeRunnableListCallback(
+ RunnableList list, ActivityContext owner) {
+ DefaultLifecycleObserver destroyObserver = new DefaultLifecycleObserver() {
+ @Override
+ public void onDestroy(@NonNull LifecycleOwner owner) {
+ list.executeAllAndClear();
+ }
+ };
+ MAIN_EXECUTOR.execute(() -> owner.getLifecycle().addObserver(destroyObserver));
+ list.add(() -> owner.getLifecycle().removeObserver(destroyObserver));
+
return new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle bundle) {
MAIN_EXECUTOR.execute(list::executeAllAndDestroy);
}
+
+ @Override
+ public IBinder asBinder() {
+ return BinderUtils.wrapLifecycle(this, owner.getOwnerCleanupSet());
+ }
};
}
diff --git a/quickstep/src/com/android/quickstep/util/DesksUtils.kt b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
new file mode 100644
index 0000000..ccfdbb9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 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.content.Context
+import android.window.DesktopExperienceFlags
+import com.android.systemui.shared.recents.model.Task
+
+class DesksUtils {
+ companion object {
+ @JvmStatic
+ fun areMultiDesksFlagsEnabled() =
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue() &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_FRONTEND.isTrue()
+
+ /** Returns true if this [task] contains the [DesktopWallpaperActivity]. */
+ @JvmStatic
+ fun isDesktopWallpaperTask(context: Context, task: Task): Boolean {
+ val sysUiPackage =
+ context.getResources().getString(com.android.internal.R.string.config_systemUi)
+ val component = task.key.component
+ if (component != null) {
+ return component.className.contains("DesktopWallpaperActivity") &&
+ component.packageName.contains(sysUiPackage)
+ }
+ return false
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 8954d80..9cdde01 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
import static com.android.launcher3.LauncherPrefs.ALLOW_ROTATION;
+import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SettingsCache.ROTATION_SETTING_URI;
import static com.android.quickstep.BaseActivityInterface.getTaskDimension;
@@ -44,6 +45,7 @@
import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherPrefChangeListener;
import com.android.launcher3.LauncherPrefs;
@@ -107,9 +109,12 @@
// Ignore shared prefs for home rotation rotation, allowing it in if the activity supports it
private static final int FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF = 1 << 9;
+ // Shared prefs for fixed 90 degree rotation, activities should rotate if they support it
+ private static final int FLAG_HOME_FIXED_LANDSCAPE_PREFS = 1 << 10;
+
private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
- | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
+ | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
// State for which rotation watcher will be enabled. We skip it when home rotation or
// multi-window is enabled as in that case, activity itself rotates.
@@ -227,7 +232,7 @@
private boolean updateHandler() {
mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation);
- if (mRecentsActivityRotation == mTouchRotation || isRecentsActivityRotationAllowed()) {
+ if (mRecentsActivityRotation == mTouchRotation || shouldUseRealOrientation()) {
mOrientationHandler = RecentsPagedOrientationHandler.PORTRAIT;
} else if (mTouchRotation == ROTATION_90) {
mOrientationHandler = RecentsPagedOrientationHandler.LANDSCAPE;
@@ -249,9 +254,13 @@
return mStateId != oldStateId;
}
+ private boolean shouldUseRealOrientation() {
+ return isRecentsActivityRotationAllowed() || isLauncherFixedLandscape();
+ }
+
@SurfaceRotation
private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) {
- if (isRecentsActivityRotationAllowed()) {
+ if (shouldUseRealOrientation()) {
return mRecentsRotation < 0 ? displayRotation : mRecentsRotation;
} else {
return ROTATION_0;
@@ -288,6 +297,9 @@
if (LauncherPrefs.ALLOW_ROTATION.getSharedPrefKey().equals(s)) {
updateHomeRotationSetting();
}
+ if (LauncherPrefs.FIXED_LANDSCAPE_MODE.getSharedPrefKey().equals(s)) {
+ updateFixedLandscapeSetting();
+ }
}
private void updateAutoRotateSetting() {
@@ -295,6 +307,15 @@
mSettingsCache.getValue(ROTATION_SETTING_URI, 1));
}
+ private void updateFixedLandscapeSetting() {
+ if (Flags.oneGridSpecs()) {
+ setFlag(
+ FLAG_HOME_FIXED_LANDSCAPE_PREFS,
+ LauncherPrefs.get(mContext).get(FIXED_LANDSCAPE_MODE)
+ );
+ }
+ }
+
private void updateHomeRotationSetting() {
boolean homeRotationEnabled = LauncherPrefs.get(mContext).get(ALLOW_ROTATION);
setFlag(FLAG_HOME_ROTATION_ALLOWED_IN_PREFS, homeRotationEnabled);
@@ -307,6 +328,7 @@
// initialize external flags
updateAutoRotateSetting();
updateHomeRotationSetting();
+ updateFixedLandscapeSetting();
}
private void initMultipleOrientationListeners() {
@@ -383,15 +405,19 @@
setFlag(FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF, true);
}
+ public boolean isLauncherFixedLandscape() {
+ return (mFlags & FLAG_HOME_FIXED_LANDSCAPE_PREFS) == FLAG_HOME_FIXED_LANDSCAPE_PREFS;
+ }
+
public boolean isRecentsActivityRotationAllowed() {
// Activity rotation is allowed if the multi-simulated-rotation is not supported
// (fallback recents or tablets) or activity rotation is enabled by various settings.
return ((mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
!= MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
|| (mFlags & (FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF
- | FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
- | FLAG_MULTIWINDOW_ROTATION_ALLOWED
- | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
+ | FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
+ | FLAG_MULTIWINDOW_ROTATION_ALLOWED
+ | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
}
/**
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 1d035e9..27657b4 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -35,6 +35,7 @@
import com.android.launcher3.Flags.enableOverviewIconMenu
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
+import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.testing.TestLogging
import com.android.launcher3.testing.shared.TestProtocol
import com.android.launcher3.util.RunnableList
@@ -68,6 +69,8 @@
type = TaskViewType.DESKTOP,
thumbnailFullscreenParams = DesktopFullscreenDrawParams(context),
) {
+ var deskId = DesktopVisibilityController.INACTIVE_DESK_ID
+
private val contentViewFullscreenParams = FullscreenDrawParams(context)
private val taskContentViewPool =
@@ -281,6 +284,7 @@
orientedState: RecentsOrientedState,
taskOverlayFactory: TaskOverlayFactory,
) {
+ deskId = desktopTask.deskId
// TODO(b/370495260): Minimized tasks should not be filtered with desktop exploded view
// support.
// Minimized tasks should not be shown in Overview.
@@ -332,12 +336,18 @@
override fun onRecycle() {
super.onRecycle()
+ deskId = DesktopVisibilityController.INACTIVE_DESK_ID
explodeProgress = 0.0f
viewModel = null
visibility = VISIBLE
taskContainers.forEach { removeAndRecycleThumbnailView(it) }
}
+ override fun setOrientationState(orientationState: RecentsOrientedState) {
+ super.setOrientationState(orientationState)
+ iconView.setIconOrientation(orientationState, isGridTask)
+ }
+
@SuppressLint("RtlHardcoded")
override fun updateTaskSize(lastComputedTaskSize: Rect, lastComputedGridTaskSize: Rect) {
super.updateTaskSize(lastComputedTaskSize, lastComputedGridTaskSize)
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 4a2be2a..5f08209 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -40,6 +40,8 @@
import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
import com.android.quickstep.util.LayoutUtils;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -157,6 +159,7 @@
// Currently, the only grouped task action is "save app pairs".
mActionButtons = findViewById(R.id.action_buttons);
mSaveAppPairButton = findViewById(R.id.action_save_app_pair);
+ TypefaceUtils.setTypeface(mSaveAppPairButton, FontFamily.GSF_LABEL_LARGE);
// Initialize a list to hold alphas for mActionButtons and any group action buttons.
mMultiValueAlphas[ACTIONS_ALPHAS] = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
mMultiValueAlphas[GROUP_ACTIONS_ALPHAS] =
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index ee519b5..bb2aa75 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -200,6 +200,7 @@
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.ViewUtils;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.recents.data.RecentTasksRepository;
import com.android.quickstep.recents.data.RecentsDeviceProfileRepository;
@@ -211,6 +212,7 @@
import com.android.quickstep.recents.viewmodel.RecentsViewModel;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.AnimUtils;
+import com.android.quickstep.util.DesksUtils;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LayoutUtils;
@@ -253,11 +255,11 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-
/**
* A list of recent tasks.
*
@@ -1924,6 +1926,8 @@
return;
}
+ // TODO: b/400532675 - The use of `currentTaskIds`, `runningTaskIds`, and `focusedTaskIds`
+ // needs to be audited so that they can work with empty desks that have no tasks.
int[] currentTaskIds;
TaskView currentTaskView = getTaskViewAt(mCurrentPage);
if (currentTaskView != null) {
@@ -2826,9 +2830,13 @@
/**
* Called when a gesture from an app is starting.
*/
+ // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity` from being
+ // considered in Overview.
public void onGestureAnimationStart(Task[] runningTasks) {
Log.d(TAG, "onGestureAnimationStart - runningTasks: " + Arrays.toString(runningTasks));
mActiveGestureRunningTasks = runningTasks;
+
+
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
reapplyActiveRotation();
@@ -2908,10 +2916,10 @@
if (!shouldRotateMenuForFakeRotation) {
return;
}
- TaskMenuView tv = (TaskMenuView) getTopOpenViewWithType(mContainer, TYPE_TASK_MENU);
- if (tv != null) {
+ AbstractFloatingView floatingView = getTopOpenViewWithType(mContainer, TYPE_TASK_MENU);
+ if (floatingView instanceof TaskMenuView taskMenuView) {
// Rotation is supported on phone (details at b/254198019#comment4)
- tv.onRotationChanged();
+ taskMenuView.onRotationChanged();
}
}
@@ -3062,6 +3070,21 @@
}
/**
+ * Creates a `DesktopTaskView` for the currently active desk on this display, which contains the
+ * gievn `runningTasks`.
+ */
+ private DesktopTaskView createDesktopTaskViewForActiveDesk(Task[] runningTasks) {
+ final int activeDeskId = mUtils.getActiveDeskIdOnThisDisplay();
+ final var desktopTaskView = (DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
+
+ // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
+ desktopTaskView.bind(
+ new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
+ mOrientationState, mTaskOverlayFactory);
+ return desktopTaskView;
+ }
+
+ /**
* Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
*
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
@@ -3075,20 +3098,14 @@
}
int runningTaskViewId = -1;
- boolean needGroupTaskView = runningTasks.length > 1;
- boolean needDesktopTask = hasDesktopTask(runningTasks);
if (shouldAddStubTaskView(runningTasks)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView;
+ final boolean needGroupTaskView = runningTasks.length > 1;
+ final boolean needDesktopTask = hasDesktopTask(runningTasks);
if (needDesktopTask) {
- final int activeDeskId =
- DesktopVisibilityController.INSTANCE.get(mContext).getActiveDeskId(
- mContainer.getDisplay().getDisplayId());
- taskView = getTaskViewFromPool(TaskViewType.DESKTOP);
- ((DesktopTaskView) taskView).bind(
- new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
- mOrientationState, mTaskOverlayFactory);
+ taskView = createDesktopTaskViewForActiveDesk(runningTasks);
} else if (needGroupTaskView) {
taskView = getTaskViewFromPool(TaskViewType.GROUPED);
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
@@ -3114,8 +3131,11 @@
measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
- } else if (getTaskViewByTaskId(runningTasks[0].key.id) != null) {
- runningTaskViewId = getTaskViewByTaskId(runningTasks[0].key.id).getTaskViewId();
+ } else {
+ var runningTaskView = getTaskViewByTaskId(runningTasks[0].key.id);
+ if (runningTaskView != null) {
+ runningTaskViewId = runningTaskView.getTaskViewId();
+ }
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -3154,6 +3174,10 @@
return true;
}
}
+
+ // A running empty desk will have a single running app for the `DesktopWallpaperActivity`.
+ // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
+
return false;
}
@@ -4525,24 +4549,33 @@
return lastVisibleTaskView;
}
- private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
- UI_HELPER_EXECUTOR
- .getHandler()
- .post(
- () -> {
- if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
- && dismissedTaskView instanceof DesktopTaskView) {
- // TODO: b/362720497 - Use the api with desktop id instead.
- SystemUiProxy.INSTANCE
+ private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
+ UI_HELPER_EXECUTOR
+ .getHandler()
+ .post(
+ () -> {
+ if (dismissedTaskView instanceof DesktopTaskView desktopTaskView) {
+ removeDesktopTaskView(desktopTaskView);
+ } else {
+ for (int taskId : dismissedTaskView.getTaskIds()) {
+ ActivityManagerWrapper.getInstance().removeTask(taskId);
+ }
+ }
+ });
+ }
+
+ private void removeDesktopTaskView(DesktopTaskView desktopTaskView) {
+ if (DesksUtils.areMultiDesksFlagsEnabled()) {
+ SystemUiProxy.INSTANCE
.get(getContext())
- .removeDesktop(mContainer.getDisplay().getDisplayId());
- } else {
- for (int taskId : dismissedTaskView.getTaskIds()) {
- ActivityManagerWrapper.getInstance().removeTask(taskId);
- }
- }
- });
- }
+ .removeDesk(desktopTaskView.getDeskId());
+ } else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
+ SystemUiProxy.INSTANCE
+ .get(getContext())
+ .removeDefaultDeskInDisplay(
+ mContainer.getDisplay().getDisplayId());
+ }
+ }
protected void onDismissAnimationEnds() {
AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
@@ -4562,6 +4595,12 @@
mPendingAnimation = anim;
mPendingAnimation.addEndListener(isSuccess -> {
if (isSuccess) {
+ // Remove desktops first, since desks can be empty (so they have no recent tasks),
+ // and closing all tasks on a desk doesn't always necessarily mean that the desk
+ // will be removed. So, there are no guarantees that the below call to
+ // `ActivityManagerWrapper::removeAllRecentTasks()` will be enough.
+ SystemUiProxy.INSTANCE.get(getContext()).removeAllDesks();
+
// Remove all the task views now
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> {
UI_HELPER_EXECUTOR.getHandler().post(
@@ -4691,7 +4730,7 @@
private void createDesk(View view) {
SystemUiProxy.INSTANCE
.get(getContext())
- .createDesktop(mContainer.getDisplay().getDisplayId());
+ .createDesk(mContainer.getDisplay().getDisplayId());
}
@Override
@@ -6026,7 +6065,7 @@
// mSyncTransactionApplier doesn't get transferred over
runActionOnRemoteHandles(remoteTargetHandle -> {
final TransformParams params = remoteTargetHandle.getTransformParams();
- if (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()) {
+ if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
params.setHomeBuilderProxy((builder, app, transformParams) -> {
mTmpMatrix.setScale(
1f, 1f, app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
@@ -6924,6 +6963,58 @@
// TODO: b/389209338 - update the AddDesktopButton's visibility on this.
}
+ @Override
+ public void onDeskAdded(int displayId, int deskId) {
+ // Ignore desk changes that don't belong to this display.
+ if (displayId != mContainer.getDisplay().getDisplayId()) {
+ return;
+ }
+
+ if (mUtils.getDesktopTaskViewForDeskId(deskId) != null) {
+ Log.e(TAG, "A task view for this desk has already been added.");
+ return;
+ }
+
+ // We assume that a newly added desk is always empty and gets added to the left of the
+ // `AddNewDesktopButton`.
+ DesktopTaskView desktopTaskView =
+ (DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
+ desktopTaskView.bind(new DesktopTask(deskId, new ArrayList<>()),
+ mOrientationState, mTaskOverlayFactory);
+
+ Objects.requireNonNull(mAddDesktopButton);
+ final int insertionIndex = 1 + indexOfChild(mAddDesktopButton);
+ addView(desktopTaskView, insertionIndex);
+
+ updateTaskSize();
+ updateChildTaskOrientations();
+
+ // TODO: b/401002178 - Recalculate the new current page such that the addition of the new
+ // desk does not result in a change in the current scroll page.
+ }
+
+ @Override
+ public void onDeskRemoved(int displayId, int deskId) {
+ // Ignore desk changes that don't belong to this display.
+ if (displayId != mContainer.getDisplay().getDisplayId()) {
+ return;
+ }
+
+ // We need to distinguish between desk removals that are triggered from outside of overview
+ // vs. the ones that were initiated from overview by dismissing the corresponding desktop
+ // task view.
+ var taskView = mUtils.getDesktopTaskViewForDeskId(deskId);
+ if (taskView != null) {
+ dismissTaskView(taskView, true, true);
+ }
+ }
+
+ @Override
+ public void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {
+ // TODO: b/400870600 - We may need to add code here to special case when an empty desk gets
+ // activated, since `RemoteDesktopLaunchTransitionRunner` doesn't always get triggered.
+ }
+
/** Get the color used for foreground scrimming the RecentsView for sharing. */
public static int getForegroundScrimDimColor(Context context) {
return context.getColor(R.color.overview_foreground_scrim_color);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 1c37986..037bef6 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -22,7 +22,11 @@
import androidx.core.view.children
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks
+import com.android.launcher3.statehandlers.DesktopVisibilityController
+import com.android.launcher3.statehandlers.DesktopVisibilityController.Companion.INACTIVE_DESK_ID
import com.android.launcher3.util.IntArray
+import com.android.quickstep.util.DesksUtils
+import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
import com.android.quickstep.util.isExternalDisplay
import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
@@ -52,7 +56,12 @@
* @return Sorted list of GroupTasks to be used in the RecentsView.
*/
fun sortDesktopTasksToFront(tasks: List<GroupTask>): List<GroupTask> {
- val (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP }
+ var (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP }
+ if (DesksUtils.areMultiDesksFlagsEnabled()) {
+ // Desk IDs of newer desks are larger than those of older desks, hence we can use them
+ // to sort desks from old to new.
+ desktopTasks = desktopTasks.sortedBy { (it as DesktopTask).deskId }
+ }
return otherTasks + desktopTasks
}
@@ -114,6 +123,22 @@
it.isLargeTile && !(recentsView.isSplitSelectionActive && it is DesktopTaskView)
}
+ /**
+ * Returns the [DesktopTaskView] that matches the given [deskId], or null if it doesn't exist.
+ */
+ fun getDesktopTaskViewForDeskId(deskId: Int): DesktopTaskView? {
+ if (deskId == INACTIVE_DESK_ID) {
+ return null
+ }
+ return taskViews.firstOrNull { it is DesktopTaskView && it.deskId == deskId }
+ as? DesktopTaskView
+ }
+
+ /** Returns the active desk ID of the display that contains the [recentsView] instance. */
+ fun getActiveDeskIdOnThisDisplay(): Int =
+ DesktopVisibilityController.INSTANCE.get(recentsView.context)
+ .getActiveDeskId(recentsView.mContainer.display.displayId)
+
/** Returns the expected focus task. */
fun getFirstNonDesktopTaskView(): TaskView? =
if (enableLargeDesktopWindowingTile()) taskViews.firstOrNull { it !is DesktopTaskView }
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 0d80f6a..4cf1eae 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -39,10 +39,11 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager;
import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
/**
* A rounded rectangular component containing a single TextView.
@@ -129,6 +130,7 @@
cancelTextView.setVisibility(VISIBLE);
cancelTextView.setOnClickListener((v) -> exitSplitSelection());
instructionTextView.setText(R.string.toast_contextual_split_select_app);
+ TypefaceUtils.setTypeface(instructionTextView, FontFamily.GSF_BODY_MEDIUM);
// After layout, expand touch target of cancel button to meet minimum a11y measurements.
post(() -> {
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
index 95ff46b..6bc0666 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
@@ -376,13 +376,10 @@
deviceProfile = recentsViewContainer.deviceProfile,
taskMenuX = translationX,
taskMenuY =
- when {
- !enableOverviewIconMenu() -> translationY
- // Bottom menu can translate up to show more options. So we use the min
- // translation allowed to calculate its max height.
- taskView.isOnGridBottomRow() -> minMenuTop
- else -> menuTranslationYBeforeOpen
- },
+ // Bottom menu can translate up to show more options. So we use the min
+ // translation allowed to calculate its max height.
+ if (enableOverviewIconMenu() && taskView.isOnGridBottomRow()) minMenuTop
+ else translationY,
)
private fun setOnClosingStartCallback(onClosingStartCallback: Runnable?) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index b7f1d1d..55432b8 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -33,7 +33,6 @@
import android.view.Display
import android.view.MotionEvent
import android.view.View
-import android.view.View.OnClickListener
import android.view.ViewGroup
import android.view.ViewStub
import android.view.accessibility.AccessibilityNodeInfo
@@ -142,6 +141,7 @@
/** Returns whether the task is part of overview grid and not being focused. */
get() = container.deviceProfile.isTablet && !isLargeTile
+ // TODO: b/400532675 - This will not work for empty desks until b/400532675 is fixed.
val isRunningTask: Boolean
get() = this === recentsView?.runningTaskView
diff --git a/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java b/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
index c319cb1..cb7254f 100644
--- a/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
@@ -16,22 +16,25 @@
package com.android.launcher3.util;
-import static com.android.launcher3.Flags.enableStateManagerProtoLog;
import static com.android.quickstep.util.QuickstepProtoLogGroup.LAUNCHER_STATE_MANAGER;
import static com.android.quickstep.util.QuickstepProtoLogGroup.isProtoLogInitialized;
+import android.window.DesktopModeFlags.DesktopModeFlag;
+
import androidx.annotation.NonNull;
import com.android.internal.protolog.ProtoLog;
+import com.android.launcher3.Flags;
/**
* Proxy class used for StateManager ProtoLog support.
*/
public class StateManagerProtoLogProxy {
-
+ private static final DesktopModeFlag ENABLE_STATE_MANAGER_PROTO_LOG =
+ new DesktopModeFlag(Flags::enableStateManagerProtoLog, true);
public static void logGoToState(
@NonNull Object fromState, @NonNull Object toState, @NonNull String trace) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER,
"StateManager.goToState: fromState: %s, toState: %s, partial trace:\n%s",
fromState,
@@ -41,7 +44,7 @@
public static void logCreateAtomicAnimation(
@NonNull Object fromState, @NonNull Object toState, @NonNull String trace) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.createAtomicAnimation: "
+ "fromState: %s, toState: %s, partial trace:\n%s",
fromState,
@@ -50,17 +53,17 @@
}
public static void logOnStateTransitionStart(@NonNull Object state) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.onStateTransitionStart: state: %s", state);
}
public static void logOnStateTransitionEnd(@NonNull Object state) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.onStateTransitionEnd: state: %s", state);
}
public static void logCancelAnimation(boolean animationOngoing, @NonNull String trace) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER,
"StateManager.cancelAnimation: animation ongoing: %b, partial trace:\n%s",
animationOngoing,
diff --git a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
index 2c9ae33..99888fb 100644
--- a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
@@ -16,14 +16,16 @@
package com.android.quickstep.util;
-import static com.android.launcher3.Flags.enableRecentsWindowProtoLog;
import static com.android.quickstep.util.QuickstepProtoLogGroup.RECENTS_WINDOW;
import static com.android.quickstep.util.QuickstepProtoLogGroup.isProtoLogInitialized;
+import android.window.DesktopModeFlags;
+
import androidx.annotation.NonNull;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.launcher3.Flags;
/**
* Proxy class used for Recents Window ProtoLog support.
@@ -35,19 +37,20 @@
* method. Or, if an existing entry needs to be modified, simply update it here.
*/
public class RecentsWindowProtoLogProxy {
-
+ private static final DesktopModeFlags.DesktopModeFlag ENABLE_RECENTS_WINDOW_PROTO_LOG =
+ new DesktopModeFlags.DesktopModeFlag(Flags::enableRecentsWindowProtoLog, true);
public static void logOnStateSetStart(@NonNull String stateName) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW, "onStateSetStart: %s", stateName);
}
public static void logOnStateSetEnd(@NonNull String stateName) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW, "onStateSetEnd: %s", stateName);
}
public static void logStartRecentsWindow(boolean isShown, boolean windowViewIsNull) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW,
"Starting recents window: isShow= %b, windowViewIsNull=%b",
isShown,
@@ -55,7 +58,7 @@
}
public static void logCleanup(boolean isShown) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW, "Cleaning up recents window: isShow= %b", isShown);
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
index 70bf6b4..9722e9d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
@@ -49,6 +49,8 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.LooperExecutor;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.TaskViewType;
@@ -102,7 +104,9 @@
.thenReturn(true);
mRecentTasksList = new RecentTasksList(mContext, mockMainThreadExecutor,
- mockKeyguardManager, mSystemUiProxy, mTopTaskTracker);
+ mockKeyguardManager, mSystemUiProxy, mTopTaskTracker,
+ mock(DesktopVisibilityController.class),
+ mock(DaggerSingletonTracker.class));
}
@Test
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt
new file mode 100644
index 0000000..3cdf608
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2025 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.view.Surface
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_180
+import android.view.Surface.ROTATION_90
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.quickstep.FallbackActivityInterface
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler.Companion.PORTRAIT
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+/**
+ * Test all possible inputs to RecentsOrientedState.updateHandler. It tests all possible
+ * combinations of rotations and relevant methods (two methods that return boolean values) but it
+ * only provides the expected result when the final rotation is different from ROTATION_0 for
+ * simplicity. So any case not shown in resultMap you can assume results in ROTATION_0.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RecentOrientedStateHandlerTests {
+
+ data class TestCase(
+ val recentsRotation: Int,
+ val displayRotation: Int,
+ val touchRotation: Int,
+ val isRotationAllowed: Boolean,
+ val isFixedLandscape: Boolean,
+ ) {
+ override fun toString(): String {
+ return "TestCase(recentsRotation=${Surface.rotationToString(recentsRotation)}, " +
+ "displayRotation=${Surface.rotationToString(displayRotation)}, " +
+ "touchRotation=${Surface.rotationToString(touchRotation)}, " +
+ "isRotationAllowed=$isRotationAllowed, " +
+ "isFixedLandscape=$isFixedLandscape)"
+ }
+ }
+
+ private fun runTestCase(testCase: TestCase, expectedHandler: RecentsPagedOrientationHandler) {
+ val recentOrientedState =
+ spy(
+ RecentsOrientedState(
+ ApplicationProvider.getApplicationContext(),
+ FallbackActivityInterface.INSTANCE,
+ ) {}
+ )
+ whenever(recentOrientedState.isRecentsActivityRotationAllowed).thenAnswer {
+ testCase.isRotationAllowed
+ }
+ whenever(recentOrientedState.isLauncherFixedLandscape).thenAnswer {
+ testCase.isFixedLandscape
+ }
+
+ recentOrientedState.update(testCase.displayRotation, testCase.touchRotation)
+ val rotation = recentOrientedState.orientationHandler.rotation
+ assertWithMessage("$testCase to ${Surface.rotationToString(rotation)},")
+ .that(rotation)
+ .isEqualTo(expectedHandler.rotation)
+ }
+
+ @Test
+ fun `test fixed landscape when device is portrait`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_0,
+ displayRotation = -1,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is landscape`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_90,
+ displayRotation = -1,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is seascape`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_180,
+ displayRotation = -1,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is portrait and display rotation is portrait`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_0,
+ displayRotation = ROTATION_0,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is landscape and display rotation is landscape `() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_90,
+ displayRotation = ROTATION_90,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+
+ @Test
+ fun `test fixed landscape when device is seascape and display rotation is seascape`() {
+ runTestCase(
+ TestCase(
+ recentsRotation = ROTATION_180,
+ displayRotation = ROTATION_180,
+ touchRotation = ROTATION_0,
+ isRotationAllowed = false,
+ isFixedLandscape = true,
+ ),
+ PORTRAIT,
+ )
+ }
+}
diff --git a/src/android/os/BinderUtils.kt b/src/android/os/BinderUtils.kt
new file mode 100644
index 0000000..0536283
--- /dev/null
+++ b/src/android/os/BinderUtils.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 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 android.os
+
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.WeakCleanupSet
+import com.android.launcher3.util.WeakCleanupSet.OnOwnerDestroyedCallback
+
+/** Utility methods related to Binder */
+object BinderUtils {
+
+ /** Creates a binder wrapper which is tied to the [lifecycle] */
+ @JvmStatic
+ fun <T : Binder> T.wrapLifecycle(cleanupSet: WeakCleanupSet): Binder =
+ LifecycleBinderWrapper(this, cleanupSet)
+
+ private class LifecycleBinderWrapper<T : Binder>(
+ private var realBinder: T?,
+ cleanupSet: WeakCleanupSet,
+ ) : Binder(realBinder?.interfaceDescriptor), OnOwnerDestroyedCallback {
+
+ init {
+ MAIN_EXECUTOR.execute { cleanupSet.addOnOwnerDestroyedCallback(this) }
+ }
+
+ override fun queryLocalInterface(descriptor: String): IInterface? =
+ realBinder?.queryLocalInterface(descriptor)
+
+ override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean =
+ realBinder?.transact(code, data, reply, flags)
+ ?: throw RemoteException("Original binder cleaned up")
+
+ override fun onOwnerDestroyed() {
+ realBinder = null
+ }
+ }
+}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 3e6b4dd..2426a61 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -55,6 +55,7 @@
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.util.WeakCleanupSet;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.ScrimView;
@@ -105,6 +106,7 @@
private final SavedStateRegistryController mSavedStateRegistryController =
SavedStateRegistryController.create(this);
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
+ private final WeakCleanupSet mCleanupSet = new WeakCleanupSet(this);
protected DeviceProfile mDeviceProfile;
protected SystemUiController mSystemUiController;
@@ -504,6 +506,11 @@
return mLifecycleRegistry;
}
+ @Override
+ public WeakCleanupSet getOwnerCleanupSet() {
+ return mCleanupSet;
+ }
+
public static <T extends BaseActivity> T fromContext(Context context) {
if (context instanceof BaseActivity) {
return (T) context;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index cb3a0bc..9a9bc1d 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
+import static com.android.launcher3.graphics.ShapeDelegate.DEFAULT_PATH_SIZE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -39,6 +40,7 @@
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -632,7 +634,7 @@
Drawable badge = null;
if ((info instanceof ItemInfoWithIcon iiwi) && !iiwi.getMatchingLookupFlag().useLowRes()) {
- badge = iiwi.bitmap.getBadgeDrawable(context, useTheme);
+ badge = iiwi.bitmap.getBadgeDrawable(context, useTheme, getIconShapeOrNull(context));
}
if (info instanceof PendingAddShortcutInfo) {
@@ -659,8 +661,11 @@
// Only fetch badge if the icon is on workspace
if (info.id != ItemInfo.NO_ID && badge == null) {
badge = appState.getIconCache().getShortcutInfoBadge(si).newIcon(
- context, ThemeManager.INSTANCE.get(context).isIconThemeEnabled()
- ? FLAG_THEMED : 0);
+ context,
+ ThemeManager.INSTANCE.get(context).isIconThemeEnabled()
+ ? FLAG_THEMED : 0,
+ getIconShapeOrNull(context)
+ );
}
}
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
@@ -706,10 +711,11 @@
if (badge == null) {
badge = BitmapInfo.LOW_RES_INFO.withFlags(
- UserCache.INSTANCE.get(context)
- .getUserInfo(info.user)
- .applyBitmapInfoFlags(FlagOp.NO_OP))
- .getBadgeDrawable(context, useTheme);
+ UserCache.INSTANCE.get(context)
+ .getUserInfo(info.user)
+ .applyBitmapInfoFlags(FlagOp.NO_OP)
+ )
+ .getBadgeDrawable(context, useTheme, getIconShapeOrNull(context));
if (badge == null) {
badge = new ColorDrawable(Color.TRANSPARENT);
}
@@ -939,4 +945,18 @@
}
return null;
}
+
+ /**
+ * Returns current icon shape to use for badges if flag is on, otherwise null.
+ */
+ @Nullable
+ public static Path getIconShapeOrNull(Context context) {
+ if (Flags.enableLauncherIconShapes()) {
+ return ThemeManager.INSTANCE.get(context)
+ .getIconShape()
+ .getPath(DEFAULT_PATH_SIZE);
+ } else {
+ return null;
+ }
+ }
}
diff --git a/src/com/android/launcher3/graphics/ShapeDelegate.kt b/src/com/android/launcher3/graphics/ShapeDelegate.kt
index 9033eac..7c04292 100644
--- a/src/com/android/launcher3/graphics/ShapeDelegate.kt
+++ b/src/com/android/launcher3/graphics/ShapeDelegate.kt
@@ -203,7 +203,11 @@
start =
poly.transformed(
Matrix().apply {
- setRectToRect(RectF(0f, 0f, 100f, 100f), RectF(startRect), FILL)
+ setRectToRect(
+ RectF(0f, 0f, DEFAULT_PATH_SIZE, DEFAULT_PATH_SIZE),
+ RectF(startRect),
+ FILL,
+ )
}
),
end =
@@ -281,7 +285,10 @@
PathParser.createPathFromPathData(shapeStr).apply {
transform(
Matrix().apply {
- setScale(AREA_CALC_SIZE / 100f, AREA_CALC_SIZE / 100f)
+ setScale(
+ AREA_CALC_SIZE / DEFAULT_PATH_SIZE,
+ AREA_CALC_SIZE / DEFAULT_PATH_SIZE,
+ )
}
)
}
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index b60b8cc..f5e5e16 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -26,6 +26,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.Flags;
+import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags;
@@ -325,10 +326,12 @@
* Returns a FastBitmapDrawable with the icon and context theme applied
*/
public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) {
- if (!ThemeManager.INSTANCE.get(context).isIconThemeEnabled()) {
+ ThemeManager themeManager = ThemeManager.INSTANCE.get(context);
+ if (!themeManager.isIconThemeEnabled()) {
creationFlags &= ~FLAG_THEMED;
}
- FastBitmapDrawable drawable = bitmap.newIcon(context, creationFlags);
+ FastBitmapDrawable drawable = bitmap.newIcon(
+ context, creationFlags, Utilities.getIconShapeOrNull(context));
drawable.setIsDisabled(isDisabled());
return drawable;
}
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 20c0ecc..98a3882 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -219,6 +219,6 @@
public static UserBadgeDrawable getBadgeDrawable(Context context, UserHandle userHandle) {
return (UserBadgeDrawable) BitmapInfo.LOW_RES_INFO.withFlags(UserCache.getInstance(context)
.getUserInfo(userHandle).applyBitmapInfoFlags(FlagOp.NO_OP))
- .getBadgeDrawable(context, false /* isThemed */);
+ .getBadgeDrawable(context, false /* isThemed */, null);
}
}
diff --git a/src/com/android/launcher3/util/BaseContext.kt b/src/com/android/launcher3/util/BaseContext.kt
index 819470b..8aa10f3 100644
--- a/src/com/android/launcher3/util/BaseContext.kt
+++ b/src/com/android/launcher3/util/BaseContext.kt
@@ -49,6 +49,7 @@
private val savedStateRegistryController = SavedStateRegistryController.create(this)
private val lifecycleRegistry = LifecycleRegistry(this)
+ private val cleanupSet = WeakCleanupSet(this)
override val savedStateRegistry: SavedStateRegistry
get() = savedStateRegistryController.savedStateRegistry
@@ -110,6 +111,8 @@
override fun getViewCache() = viewCache
+ override fun getOwnerCleanupSet() = cleanupSet
+
private fun updateState() {
if (lifecycleRegistry.currentState.isAtLeast(CREATED)) {
lifecycleRegistry.currentState =
diff --git a/src/com/android/launcher3/util/WeakCleanupSet.kt b/src/com/android/launcher3/util/WeakCleanupSet.kt
new file mode 100644
index 0000000..7bf3289
--- /dev/null
+++ b/src/com/android/launcher3/util/WeakCleanupSet.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 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 androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import java.util.Collections
+import java.util.WeakHashMap
+
+/**
+ * Utility class which maintains a list of cleanup callbacks using weak-references. These callbacks
+ * are called when the [owner] is destroyed, but can also be cleared when the caller is GCed
+ */
+class WeakCleanupSet(owner: LifecycleOwner) {
+
+ private val callbacks = Collections.newSetFromMap<OnOwnerDestroyedCallback>(WeakHashMap())
+ private var destroyed = false
+
+ init {
+ MAIN_EXECUTOR.execute {
+ owner.lifecycle.addObserver(
+ object : DefaultLifecycleObserver {
+
+ override fun onDestroy(owner: LifecycleOwner) {
+ destroyed = true
+ callbacks.forEach { it.onOwnerDestroyed() }
+ }
+ }
+ )
+ }
+ }
+
+ fun addOnOwnerDestroyedCallback(callback: OnOwnerDestroyedCallback) {
+ if (destroyed) callback.onOwnerDestroyed() else callbacks.add(callback)
+ }
+
+ /** Callback when the owner is destroyed */
+ interface OnOwnerDestroyedCallback {
+ fun onOwnerDestroyed()
+ }
+}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 11f0bc2..68e0324 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -520,6 +520,33 @@
*/
default void onCanCreateDesksChanged(boolean canCreateDesks) {
}
+
+ /**
+ * Called when a new desk is added.
+ *
+ * @param displayId The ID of the display on which the desk was added.
+ * @param deskId The ID of the newly added desk.
+ */
+ default void onDeskAdded(int displayId, int deskId) {}
+
+ /**
+ * Called when an existing desk is removed.
+ *
+ * @param displayId The ID of the display on which the desk was removed.
+ * @param deskId The ID of the desk that was removed.
+ */
+ default void onDeskRemoved(int displayId, int deskId) {}
+
+ /**
+ * Called when the active desk changes.
+ *
+ * @param displayId The ID of the display on which the desk activation change is happening.
+ * @param newActiveDesk The ID of the new active desk or -1 if no desk is active anymore
+ * (i.e. exit desktop mode).
+ * @param oldActiveDesk The ID of the desk that was previously active, or -1 if no desk was
+ * active before.
+ */
+ default void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {}
}
}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index bcb9295..cbf5341 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -87,6 +87,7 @@
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.util.WeakCleanupSet;
import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import java.util.List;
@@ -520,6 +521,9 @@
return new CellPosMapper(dp.isVerticalBarLayout(), dp.numShownHotseatIcons);
}
+ /** Set to manage objects that can be cleaned up along with the context */
+ WeakCleanupSet getOwnerCleanupSet();
+
/** Whether bubbles are enabled. */
default boolean isBubbleBarEnabled() {
return false;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index fda5175..af31276 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -16,7 +16,6 @@
package com.android.launcher3.widget;
import static com.android.app.animation.Interpolators.EMPHASIZED;
-import static com.android.launcher3.Flags.enableWidgetTapToAdd;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP;
@@ -150,40 +149,36 @@
return;
}
- if (enableWidgetTapToAdd()) {
- scrollToWidgetCell(wc);
+ scrollToWidgetCell(wc);
- if (mWidgetCellWithAddButton != null) {
- if (mWidgetCellWithAddButton.isShowingAddButton()) {
- // If there is a add button currently showing, hide it.
- mWidgetCellWithAddButton.hideAddButton(/* animate= */ true);
- } else {
- // The last recorded widget cell to show an add button is no longer showing it,
- // likely because the widget cell has been recycled or lost focus. If this is
- // the cell that has been clicked, we will show it below.
- mWidgetCellWithAddButton = null;
- }
- }
-
- if (mWidgetCellWithAddButton != wc) {
- // If click is on a cell not showing an add button, show it now.
- final PendingAddItemInfo info = (PendingAddItemInfo) wc.getTag();
- if (mActivityContext instanceof Launcher) {
- wc.showAddButton((view) -> addWidget(info));
- } else {
- wc.showAddButton((view) -> mActivityContext.getItemOnClickListener()
- .onClick(wc));
- }
- }
-
- mWidgetCellWithAddButton = mWidgetCellWithAddButton != wc ? wc : null;
- if (mWidgetCellWithAddButton != null) {
- mLastSelectedWidgetItem = mWidgetCellWithAddButton.getWidgetItem();
+ if (mWidgetCellWithAddButton != null) {
+ if (mWidgetCellWithAddButton.isShowingAddButton()) {
+ // If there is a add button currently showing, hide it.
+ mWidgetCellWithAddButton.hideAddButton(/* animate= */ true);
} else {
- mLastSelectedWidgetItem = null;
+ // The last recorded widget cell to show an add button is no longer showing it,
+ // likely because the widget cell has been recycled or lost focus. If this is
+ // the cell that has been clicked, we will show it below.
+ mWidgetCellWithAddButton = null;
}
+ }
+
+ if (mWidgetCellWithAddButton != wc) {
+ // If click is on a cell not showing an add button, show it now.
+ final PendingAddItemInfo info = (PendingAddItemInfo) wc.getTag();
+ if (mActivityContext instanceof Launcher) {
+ wc.showAddButton((view) -> addWidget(info));
+ } else {
+ wc.showAddButton((view) -> mActivityContext.getItemOnClickListener()
+ .onClick(wc));
+ }
+ }
+
+ mWidgetCellWithAddButton = mWidgetCellWithAddButton != wc ? wc : null;
+ if (mWidgetCellWithAddButton != null) {
+ mLastSelectedWidgetItem = mWidgetCellWithAddButton.getWidgetItem();
} else {
- mActivityContext.getItemOnClickListener().onClick(wc);
+ mLastSelectedWidgetItem = null;
}
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 7a27bf4..130843b 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -18,7 +18,6 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
-import static com.android.launcher3.Flags.enableWidgetTapToAdd;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
import static com.android.launcher3.widget.util.WidgetSizes.getWidgetItemSizePx;
@@ -152,24 +151,22 @@
mWidgetTextContainer = findViewById(R.id.widget_text_container);
mWidgetAddButton = findViewById(R.id.widget_add_button);
- if (enableWidgetTapToAdd()) {
- setAccessibilityDelegate(new AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(View host,
- AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- if (hasOnClickListeners()) {
- String accessibilityLabel = getResources().getString(
- mWidgetAddButton.isShown()
- ? R.string.widget_cell_tap_to_hide_add_button_label
- : R.string.widget_cell_tap_to_show_add_button_label);
- info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK,
- accessibilityLabel));
- }
+ setAccessibilityDelegate(new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ if (hasOnClickListeners()) {
+ String accessibilityLabel = getResources().getString(
+ mWidgetAddButton.isShown()
+ ? R.string.widget_cell_tap_to_hide_add_button_label
+ : R.string.widget_cell_tap_to_show_add_button_label);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK,
+ accessibilityLabel));
}
- });
- mWidgetAddButton.setVisibility(INVISIBLE);
- }
+ }
+ });
+ mWidgetAddButton.setVisibility(INVISIBLE);
}
public void setRemoteViewsPreview(RemoteViews view) {
@@ -210,9 +207,7 @@
showDescription(true);
showDimensions(true);
- if (enableWidgetTapToAdd()) {
- hideAddButton(/* animate= */ false);
- }
+ hideAddButton(/* animate= */ false);
if (mActiveRequest != null) {
mActiveRequest.cancel();
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 4ccf16b..ec91622 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.widget.picker.WidgetRecommendationCategory.DEFAULT_WIDGET_RECOMMENDATION_CATEGORY;
import static com.android.launcher3.widget.util.WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering;
import android.content.ComponentName;
@@ -55,6 +56,7 @@
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
private @Px float mAvailableHeight = Float.MAX_VALUE;
private @Px float mAvailableWidth = 0;
+ private int mLastUiMode = -1;
private static final String INITIALLY_DISPLAYED_WIDGETS_STATE_KEY =
"widgetRecommendationsView:mDisplayedWidgets";
private static final int MAX_CATEGORIES = 3;
@@ -151,41 +153,7 @@
mPageSwitchListeners.add(pageChangeListener);
}
- /**
- * Displays all the provided recommendations in a single table if they fit.
- *
- * @param recommendedWidgets list of widgets to be displayed in recommendation section.
- * @param deviceProfile the current {@link DeviceProfile}
- * @param availableHeight height in px that can be used to display the recommendations;
- * recommendations that don't fit in this height won't be shown
- * @param availableWidth width in px that the recommendations should display in
- * @param cellPadding padding in px that should be applied to each widget in the
- * recommendations
- * @return number of recommendations that could fit in the available space.
- */
- public int setRecommendations(
- List<WidgetItem> recommendedWidgets, DeviceProfile deviceProfile,
- final @Px float availableHeight, final @Px int availableWidth,
- final @Px int cellPadding) {
- this.mAvailableHeight = availableHeight;
- this.mAvailableWidth = availableWidth;
- clear();
-
- Set<ComponentName> displayedWidgets = maybeDisplayInTable(recommendedWidgets,
- deviceProfile,
- availableWidth, cellPadding);
-
- if (mDisplayedWidgets.isEmpty()) {
- // Save the widgets shown for the first time user opened the picker; so that, they can
- // be maintained across orientation changes.
- mDisplayedWidgets = displayedWidgets;
- }
-
- updateTitleAndIndicator(/* requestedPage= */ 0);
- return displayedWidgets.size();
- }
-
- private boolean shouldShowFullPageView(
+ private boolean shouldShowSinglePageView(
Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations) {
if (mShowFullPageViewIfLowDensity) {
boolean hasLessCategories = recommendations.size() < MAX_CATEGORIES;
@@ -213,63 +181,82 @@
* @param cellPadding padding in px that should be applied to each widget in the
* recommendations
* @param requestedPage page number to display initially.
+ * @param forceUpdate whether to re-render even if available space didn't change
* @return number of recommendations that could fit in the available space.
*/
public int setRecommendations(
Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations,
DeviceProfile deviceProfile, final @Px float availableHeight,
- final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
- if (shouldShowFullPageView(recommendations)) {
- // Show all widgets in single page with unlimited available height.
- return setRecommendations(
- recommendations.values().stream().flatMap(Collection::stream)
- .collect(Collectors.toList()),
- deviceProfile, /*availableHeight=*/ Float.MAX_VALUE, availableWidth,
- cellPadding);
+ final @Px int availableWidth, final @Px int cellPadding, final int requestedPage,
+ final boolean forceUpdate) {
+ if (forceUpdate || shouldUpdate(availableWidth, availableHeight)) {
+ Context context = getContext();
+ this.mAvailableHeight = availableHeight;
+ this.mAvailableWidth = availableWidth;
+ this.mLastUiMode = context.getResources().getConfiguration().uiMode;
- }
- this.mAvailableHeight = availableHeight;
- this.mAvailableWidth = availableWidth;
- Context context = getContext();
- // For purpose of recommendations section, we don't want paging dots to be halved in two
- // pane display, so, we always provide isTwoPanels = "false".
- mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
- clear();
-
- int displayedCategories = 0;
- Set<ComponentName> allDisplayedWidgets = new HashSet<>();
-
- // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
- for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
- new TreeMap<>(recommendations).entrySet()) {
- // If none of the recommendations for the category could fit in the mAvailableHeight, we
- // don't want to add that category; and we look for the next one.
- Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(entry.getValue(),
- deviceProfile,
- availableWidth, cellPadding);
- if (!displayedWidgetsForCategory.isEmpty()) {
- mCategoryTitles.add(
- context.getResources().getString(entry.getKey().categoryTitleRes));
- displayedCategories++;
- allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+ final Map<WidgetRecommendationCategory, List<WidgetItem>> mappedRecommendations;
+ if (shouldShowSinglePageView(recommendations)) { // map to single category.
+ mappedRecommendations = Map.of(DEFAULT_WIDGET_RECOMMENDATION_CATEGORY,
+ recommendations.values().stream().flatMap(
+ Collection::stream).toList());
+ } else {
+ mappedRecommendations = recommendations;
}
- if (displayedCategories == MAX_CATEGORIES) {
- break;
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
+ clear();
+
+ int displayedCategories = 0;
+ Set<ComponentName> allDisplayedWidgets = new HashSet<>();
+
+ // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
+ for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
+ new TreeMap<>(mappedRecommendations).entrySet()) {
+ // If none of the recommendations for the category could fit in the
+ // mAvailableHeight, we don't want to add that category; and we look for the next
+ // one.
+ Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(
+ entry.getValue(),
+ deviceProfile,
+ availableWidth, cellPadding);
+ if (!displayedWidgetsForCategory.isEmpty()) {
+ mCategoryTitles.add(
+ context.getResources().getString(entry.getKey().categoryTitleRes));
+ displayedCategories++;
+ allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+ }
+
+ if (displayedCategories == MAX_CATEGORIES) {
+ break;
+ }
}
- }
- if (mDisplayedWidgets.isEmpty()) {
- // Save the widgets shown for the first time user opened the picker; so that, they can
- // be maintained across orientation changes.
- mDisplayedWidgets = allDisplayedWidgets;
- }
+ if (mDisplayedWidgets.isEmpty()) {
+ // Save the widgets shown for the first time user opened the picker; so that,
+ // they can
+ // be maintained across orientation changes.
+ mDisplayedWidgets = allDisplayedWidgets;
+ }
- updateTitleAndIndicator(requestedPage);
- // For purpose of recommendations section, we don't want paging dots to be halved in two
- // pane display, so, we always provide isTwoPanels = "false".
- mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
- return allDisplayedWidgets.size();
+ updateTitleAndIndicator(requestedPage);
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
+ return allDisplayedWidgets.size();
+ } else {
+ return mDisplayedWidgets.size();
+ }
+ }
+
+ /**
+ * Returns if we should re-render the views.
+ */
+ private boolean shouldUpdate(int availableWidth, float availableHeight) {
+ return this.mAvailableWidth != availableWidth || this.mAvailableHeight != availableHeight
+ || getContext().getResources().getConfiguration().uiMode != this.mLastUiMode;
}
private void clear() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 7a218ae..44c0ebd 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -15,9 +15,7 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
-import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_EXPAND_PRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
@@ -82,12 +80,10 @@
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
@@ -632,35 +628,22 @@
if (mIsInSearchMode) {
return;
}
- if (enableCategorizedWidgetSuggestions()) {
- // We avoid applying new recommendations when some are already displayed.
- if (mRecommendedWidgetsMap.isEmpty()) {
- mRecommendedWidgetsMap =
- mActivityContext.getWidgetPickerDataProvider().get().getRecommendations();
- }
- mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
- mRecommendedWidgetsMap,
- mDeviceProfile,
- /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
- /* availableWidth= */ mMaxSpanPerRow,
- /* cellPadding= */ mWidgetCellHorizontalPadding,
- /* requestedPage= */ mRecommendationsCurrentPage
- );
- } else {
- if (mRecommendedWidgets.isEmpty()) {
- mRecommendedWidgets = mActivityContext.getWidgetPickerDataProvider().get()
- .getRecommendations()
- .values().stream()
- .flatMap(Collection::stream).collect(Collectors.toList());
- mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
- mRecommendedWidgets,
- mDeviceProfile,
- /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
- /* availableWidth= */ mMaxSpanPerRow,
- /* cellPadding= */ mWidgetCellHorizontalPadding
- );
- }
+ boolean forceUpdate = false;
+ // We avoid applying new recommendations when some are already displayed.
+ if (mRecommendedWidgetsMap.isEmpty()) {
+ mRecommendedWidgetsMap =
+ mActivityContext.getWidgetPickerDataProvider().get().getRecommendations();
+ forceUpdate = true;
}
+ mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
+ mRecommendedWidgetsMap,
+ mDeviceProfile,
+ /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
+ /* availableWidth= */ mMaxSpanPerRow,
+ /* cellPadding= */ mWidgetCellHorizontalPadding,
+ /* requestedPage= */ mRecommendationsCurrentPage,
+ /* forceUpdate= */ forceUpdate
+ );
mWidgetRecommendationsContainer.setVisibility(
mRecommendedWidgetsCount > 0 ? VISIBLE : GONE);
@@ -792,13 +775,7 @@
}
private static int getWidgetSheetId(BaseActivity activity) {
- boolean isTwoPane = (activity.getDeviceProfile().isTablet
- // Enables two pane picker for tablets in all orientations when the
- // enableCategorizedWidgetSuggestions flag is on.
- && (activity.getDeviceProfile().isLandscape || enableCategorizedWidgetSuggestions())
- && !activity.getDeviceProfile().isTwoPanels)
- // Enables two pane picker for unfolded foldables if the flag is on.
- || (activity.getDeviceProfile().isTwoPanels && enableUnfoldedTwoPanePicker());
+ boolean isTwoPane = activity.getDeviceProfile().isTablet;
return isTwoPane ? R.layout.widgets_two_pane_sheet : R.layout.widgets_full_sheet;
}
@@ -945,16 +922,7 @@
private static boolean shouldRecreateLayout(DeviceProfile oldDp, DeviceProfile newDp) {
// When folding/unfolding the foldables, we need to switch between the regular widget picker
// and the two pane picker, so we rebuild the picker with the correct layout.
- boolean isFoldUnFold =
- oldDp.isTwoPanels != newDp.isTwoPanels && enableUnfoldedTwoPanePicker();
- // In tablets, on orientation change we switch between single and two pane picker unless the
- // categorized suggestions flag was on. With the categorized suggestions feature, we use a
- // two pane picker across all orientations.
- boolean useDifferentLayoutOnOrientationChange =
- (!enableCategorizedWidgetSuggestions() && (newDp.isTablet && !newDp.isTwoPanels
- && oldDp.isLandscape != newDp.isLandscape));
-
- return isFoldUnFold || useDifferentLayoutOnOrientationChange;
+ return oldDp.isTwoPanels != newDp.isTwoPanels;
}
/**
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 679b0f5..fc99fcc 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -112,21 +112,48 @@
// Bind the widget items.
for (int i = 0; i < widgetItemsTable.size(); i++) {
List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
- for (int j = 0; j < widgetItemsPerRow.size(); j++) {
- WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
- row.setVisibility(View.VISIBLE);
- WidgetCell widget = (WidgetCell) row.getChildAt(j);
- widget.clear();
- widget.addPreviewReadyListener(row);
- WidgetItem widgetItem = widgetItemsPerRow.get(j);
- widget.setVisibility(View.VISIBLE);
+ WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
- widget.applyFromCellItem(widgetItem);
- widget.requestLayout();
+ if (areRowItemsUnchanged(row, widgetItemsPerRow)) { // Just show widgets in row as is
+ row.setVisibility(View.VISIBLE);
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ WidgetCell widget = (WidgetCell) row.getChildAt(j);
+ widget.setVisibility(View.VISIBLE);
+ }
+ } else {
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ row.setVisibility(View.VISIBLE);
+ WidgetCell widget = (WidgetCell) row.getChildAt(j);
+ widget.clear();
+ WidgetItem widgetItem = widgetItemsPerRow.get(j);
+ widget.addPreviewReadyListener(row);
+ widget.setVisibility(View.VISIBLE);
+
+ widget.applyFromCellItem(widgetItem);
+ widget.requestLayout();
+ }
}
}
}
+ private boolean areRowItemsUnchanged(WidgetTableRow row, List<WidgetItem> widgetItemsPerRow) {
+ // NOTE: on rotation or fold / unfold, we bind different view holders
+ // so, we don't any special handling for that case.
+ if (row.getChildCount() != widgetItemsPerRow.size()) { // Items not equal
+ return false;
+ }
+
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ WidgetCell widgetCell = (WidgetCell) row.getChildAt(j);
+ WidgetItem widgetItem = widgetItemsPerRow.get(j);
+ if (widgetCell.getWidgetItem() == null
+ || !widgetCell.getWidgetItem().equals(widgetItem)) {
+ return false; // Items at given position in row aren't same.
+ }
+ }
+ return true;
+ }
+
/**
* Adds and hides table rows and columns from {@code table} to ensure there is sufficient room
* to display {@code widgetItemsTable}.
@@ -151,26 +178,31 @@
tableRow.setGravity(Gravity.TOP);
table.addView(tableRow);
}
- // Pass resize delay to let the "move" and "change" animations run before resizing the
- // row.
- tableRow.setupRow(widgetItems.size(),
- /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
- if (tableRow.getChildCount() > widgetItems.size()) {
- for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
- tableRow.getChildAt(j).setVisibility(View.GONE);
- }
- } else {
- for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
- WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
- R.layout.widget_cell, tableRow, false);
- // set up touch.
- widget.setOnClickListener(mIconClickListener);
- widget.addPreviewReadyListener(tableRow);
- View preview = widget.findViewById(R.id.widget_preview_container);
- preview.setOnClickListener(mIconClickListener);
- preview.setOnLongClickListener(mIconLongClickListener);
- widget.setAnimatePreview(false);
- tableRow.addView(widget);
+
+ // If the row items are unchanged, we don't need to re-setup the row or the items;
+ // we can just show the row as is.
+ if (!areRowItemsUnchanged(tableRow, widgetItems)) {
+ // Pass resize delay to let the "move" and "change" animations run before resizing
+ // the row.
+ tableRow.setupRow(widgetItems.size(),
+ /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
+ if (tableRow.getChildCount() > widgetItems.size()) {
+ for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
+ tableRow.getChildAt(j).setVisibility(View.GONE);
+ }
+ } else {
+ for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
+ WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
+ R.layout.widget_cell, tableRow, false);
+ // set up touch.
+ widget.setOnClickListener(mIconClickListener);
+ widget.addPreviewReadyListener(tableRow);
+ View preview = widget.findViewById(R.id.widget_preview_container);
+ preview.setOnClickListener(mIconClickListener);
+ preview.setOnLongClickListener(mIconLongClickListener);
+ widget.setAnimatePreview(false);
+ tableRow.addView(widget);
+ }
}
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 216f4d4..9ee9150 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.widget.util.WidgetSizes.getWidgetSizePx;
import static com.android.launcher3.widget.util.WidgetsTableUtils.WIDGETS_TABLE_ROW_COUNT_COMPARATOR;
@@ -112,10 +111,8 @@
WidgetCell widgetCell = addItemCell(tableRow);
widgetCell.applyFromCellItem(widgetItem);
widgetCell.showAppIconInWidgetTitle(true);
- if (enableCategorizedWidgetSuggestions()) {
- widgetCell.showDescription(false);
- widgetCell.showDimensions(false);
- }
+ widgetCell.showDescription(false);
+ widgetCell.showDimensions(false);
}
addView(tableRow);
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index df76400..9ffaf51 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -15,9 +15,7 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
-import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER;
import static com.android.launcher3.UtilitiesKt.CLIP_TO_PADDING_FALSE_MODIFIER;
import static com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree;
@@ -215,22 +213,9 @@
@Override
protected int getTabletHorizontalMargin(DeviceProfile deviceProfile) {
- if (enableCategorizedWidgetSuggestions()) {
- // two pane picker is full width for fold as well as tablet.
- return getResources().getDimensionPixelSize(
- R.dimen.widget_picker_two_panels_left_right_margin);
- }
- if (deviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
- // enableUnfoldedTwoPanePicker made two pane picker full-width for fold only.
- return getResources().getDimensionPixelSize(
- R.dimen.widget_picker_two_panels_left_right_margin);
- }
- if (deviceProfile.isLandscape && !deviceProfile.isTwoPanels) {
- // non-fold tablet landscape margins (ag/22163531)
- return getResources().getDimensionPixelSize(
- R.dimen.widget_picker_landscape_tablet_left_right_margin);
- }
- return deviceProfile.allAppsLeftRightMargin;
+ // two pane picker is full width for fold as well as tablet.
+ return getResources().getDimensionPixelSize(
+ R.dimen.widget_picker_two_panels_left_right_margin);
}
@Override
@@ -257,7 +242,7 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- if (changed && mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
+ if (changed && mDeviceProfile.isTwoPanels) {
LinearLayout layout = mContent.findViewById(R.id.linear_layout_container);
FrameLayout leftPane = layout.findViewById(R.id.recycler_view_container);
LinearLayout.LayoutParams layoutParams = (LayoutParams) leftPane.getLayoutParams();
@@ -427,7 +412,7 @@
protected int getAvailableWidthForSuggestions(int pickerAvailableWidth) {
int rightPaneWidth = (int) Math.ceil(0.67 * pickerAvailableWidth);
- if (mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
+ if (mDeviceProfile.isTwoPanels) {
// See onLayout
int leftPaneWidth = (int) (0.33 * pickerAvailableWidth);
@Px int minLeftPaneWidthPx = Utilities.dpToPx(MINIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP);
@@ -552,13 +537,9 @@
}
WidgetsListContentEntry contentEntryToBind;
- if (enableCategorizedWidgetSuggestions()) {
- // Setting max span size enables row to understand how to fit more than one item
- // in a row.
- contentEntryToBind = contentEntry.withMaxSpanSize(mMaxSpanPerRow);
- } else {
- contentEntryToBind = contentEntry;
- }
+ // Setting max span size enables row to understand how to fit more than one item
+ // in a row.
+ contentEntryToBind = contentEntry.withMaxSpanSize(mMaxSpanPerRow);
WidgetsRowViewHolder widgetsRowViewHolder =
mWidgetsListTableViewHolderBinder.newViewHolder(mRightPane);
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
index d611ae8..91ba628 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
@@ -34,19 +34,20 @@
private val context = InstrumentationRegistry.getInstrumentation().targetContext
private val canvas = mock<Canvas>()
private val systemUnderTest =
- UserBadgeDrawable(context, R.drawable.ic_work_app_badge, R.color.badge_tint_work, false)
+ UserBadgeDrawable(
+ context,
+ R.drawable.ic_work_app_badge,
+ R.color.badge_tint_work,
+ false /* isThemed */,
+ null, /* shape */
+ )
@Test
fun draw_opaque() {
val colorList = mutableListOf<Int>()
- whenever(
- canvas.drawCircle(
- any(),
- any(),
- any(),
- any()
- )
- ).then { colorList.add(it.getArgument<Paint>(3).color) }
+ whenever(canvas.drawCircle(any(), any(), any(), any())).then {
+ colorList.add(it.getArgument<Paint>(3).color)
+ }
systemUnderTest.alpha = 255
systemUnderTest.draw(canvas)
@@ -57,14 +58,9 @@
@Test
fun draw_transparent() {
val colorList = mutableListOf<Int>()
- whenever(
- canvas.drawCircle(
- any(),
- any(),
- any(),
- any()
- )
- ).then { colorList.add(it.getArgument<Paint>(3).color) }
+ whenever(canvas.drawCircle(any(), any(), any(), any())).then {
+ colorList.add(it.getArgument<Paint>(3).color)
+ }
systemUnderTest.alpha = 0
systemUnderTest.draw(canvas)
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 95d5076..f490bd6 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -36,7 +36,6 @@
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.Test;
@@ -127,7 +126,6 @@
* Adds three icons to the workspace and removes one of them by dragging to uninstall.
*/
@Test
- @ScreenRecordRule.ScreenRecord // b/399756302
@PlatinumTest(focusArea = "launcher")
public void uninstallWorkspaceIcon() throws IOException {
Point[] gridPositions = TestUtil.getCornersAndCenterPositions(mLauncher);