Merge "Include AndroidX test rules in Robolectric tests" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 634caa7..8274bd6 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -90,9 +90,6 @@
namespace: "launcher"
description: "Enables full width two pane widget picker for tablets in landscape and portrait"
bug: "315055849"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
}
flag {
@@ -185,6 +182,9 @@
namespace: "launcher"
description: "When adding app widget through config activity, directly add it to workspace to reduce flicker"
bug: "284236964"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index a290e84..f14cebd 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -52,5 +52,6 @@
"tests/src/com/android/quickstep/TaplOverviewIconTest.java",
"tests/src/com/android/quickstep/TaplTestsQuickstep.java",
"tests/src/com/android/quickstep/TaplTestsSplitscreen.java",
+ "tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java"
],
}
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index fcc2eff..93ef735 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -326,6 +326,7 @@
<dimen name="taskbar_nav_buttons_size">44dp</dimen>
<dimen name="taskbar_split_instructions_margin">48dp</dimen>
<dimen name="taskbar_contextual_button_margin">120dp</dimen>
+ <dimen name="taskbar_ime_switcher_button_margin_start">40dp</dimen>
<dimen name="taskbar_suw_insets">48dp</dimen>
<dimen name="taskbar_suw_frame">48dp</dimen>
<dimen name="taskbar_hotseat_nav_spacing">24dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 4741ddd..f9a8c99 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -19,7 +19,7 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.os.Debug;
import android.os.SystemProperties;
@@ -136,7 +136,7 @@
Log.d(TAG, "setVisibleFreeformTasksCount: visibleTasksCount=" + visibleTasksCount
+ " currentValue=" + mVisibleFreeformTasksCount);
}
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
@@ -180,7 +180,7 @@
Log.d(TAG, "setOverviewStateEnabled: enabled=" + overviewStateEnabled
+ " currentValue=" + mInOverviewState);
}
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
if (overviewStateEnabled != mInOverviewState) {
@@ -202,7 +202,7 @@
Log.d(TAG, "setBackgroundStateEnabled: enabled=" + backgroundStateEnabled
+ " currentValue=" + mBackgroundStateEnabled);
}
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
if (backgroundStateEnabled != mBackgroundStateEnabled) {
@@ -229,7 +229,7 @@
* Notify controller that recents gesture has started.
*/
public void setRecentsGestureStart() {
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
if (DEBUG) {
@@ -243,7 +243,7 @@
* {@link com.android.quickstep.GestureState.GestureEndTarget}
*/
public void setRecentsGestureEnd(@Nullable GestureState.GestureEndTarget endTarget) {
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
if (DEBUG) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 8566e20..0bcf2d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
@@ -113,7 +113,7 @@
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
final boolean onDesktop =
- isDesktopModeSupported()
+ enableDesktopWindowingMode()
&& desktopController != null
&& desktopController.areFreeformTasksVisible();
@@ -154,7 +154,7 @@
// Hide all desktop tasks and show them on the hidden tile
int hiddenDesktopTasks = 0;
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopTask desktopTask = findDesktopTask(tasks);
if (desktopTask != null) {
hiddenDesktopTasks = desktopTask.tasks.size();
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 2710bd9..a59aead 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -21,7 +21,7 @@
import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -209,7 +209,7 @@
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
final boolean onDesktop =
- isDesktopModeSupported()
+ enableDesktopWindowingMode()
&& desktopController != null
&& desktopController.areFreeformTasksVisible();
if (onDesktop) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 9625789..aedbe6c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -1254,7 +1254,7 @@
if (findExactPairMatch) {
// We did not find the app pair we were looking for, so launch one.
recents.getSplitSelectController().getAppPairsController().launchAppPair(
- (AppPairIcon) launchingIconView);
+ (AppPairIcon) launchingIconView, -1 /*cuj*/);
} else {
startItemInfoActivity(itemInfos.get(0), foundTask);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index a14e3fd..8d48154 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -205,13 +205,7 @@
mLauncherState = finalState;
updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, false);
applyState();
- boolean disallowLongClick =
- FeatureFlags.enableSplitContextually()
- ? mLauncher.isSplitSelectionActive()
- : finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
- com.android.launcher3.taskbar.Utilities.setOverviewDragState(
- mControllers, finalState.disallowTaskbarGlobalDrag(),
- disallowLongClick, finalState.allowTaskbarInitialSplitSelection());
+ updateOverviewDragState(finalState);
}
};
@@ -256,6 +250,7 @@
mCanSyncViews = true;
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+ updateOverviewDragState(mLauncherState);
}
public void onDestroy() {
@@ -328,7 +323,7 @@
updateStateForSysuiFlags(systemUiStateFlags, /* applyState */ true);
}
- private void updateStateForSysuiFlags(int systemUiStateFlags, boolean applyState) {
+ private void updateStateForSysuiFlags(int systemUiStateFlags, boolean applyState) {
final boolean prevIsAwake = hasAnyFlag(FLAG_AWAKE);
final boolean currIsAwake = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_AWAKE);
@@ -358,6 +353,21 @@
}
/**
+ * Updates overview drag state on various controllers based on {@link #mLauncherState}.
+ *
+ * @param launcherState The current state launcher is in
+ */
+ private void updateOverviewDragState(LauncherState launcherState) {
+ boolean disallowLongClick =
+ FeatureFlags.enableSplitContextually()
+ ? mLauncher.isSplitSelectionActive()
+ : launcherState == LauncherState.OVERVIEW_SPLIT_SELECT;
+ com.android.launcher3.taskbar.Utilities.setOverviewDragState(
+ mControllers, launcherState.disallowTaskbarGlobalDrag(),
+ disallowLongClick, launcherState.allowTaskbarInitialSplitSelection());
+ }
+
+ /**
* Updates the proper flag to change the state of the task bar.
*
* Note that this only updates the flag. {@link #applyState()} needs to be called separately.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 19293b5..e293ad4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -27,7 +27,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -281,7 +281,7 @@
private void navigateHome() {
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopVisibilityController desktopVisibilityController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
if (desktopVisibilityController != null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index 5c57a01..9840791 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -105,8 +105,16 @@
contextualMargin, Gravity.START)
if (imeSwitcher != null) {
+ val imeStartMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_ime_switcher_button_margin_start)
startContextualContainer.addView(imeSwitcher)
- imeSwitcher.layoutParams = getParamsToCenterView()
+ val imeSwitcherButtonParams = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ imeSwitcherButtonParams.apply {
+ marginStart = imeStartMargin
+ gravity = Gravity.CENTER_VERTICAL
+ }
+ imeSwitcher.layoutParams = imeSwitcherButtonParams
}
if (a11yButton != null) {
endContextualContainer.addView(a11yButton)
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 2b10bfd..b49c752 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -21,6 +21,7 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE;
import static com.android.launcher3.Flags.enablePredictiveBackGesture;
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
@@ -61,7 +62,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
@@ -258,7 +259,7 @@
getDepthController(), getStatsLogManager(),
systemUiProxy, RecentsModel.INSTANCE.get(this),
() -> onStateBack());
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
getDepthController());
@@ -284,7 +285,7 @@
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
mDesktopVisibilityController = new DesktopVisibilityController(this);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
mDesktopVisibilityController.registerSystemUiListener();
mSplitSelectStateController.initSplitFromDesktopController(this);
}
@@ -947,7 +948,7 @@
@Override
public void setResumed() {
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopVisibilityController controller = mDesktopVisibilityController;
if (controller != null && controller.areFreeformTasksVisible()
&& !controller.isRecentsGestureInProgress()) {
@@ -1347,7 +1348,8 @@
* Launches two apps as an app pair.
*/
public void launchAppPair(AppPairIcon appPairIcon) {
- mSplitSelectStateController.getAppPairsController().launchAppPair(appPairIcon);
+ mSplitSelectStateController.getAppPairsController().launchAppPair(appPairIcon,
+ CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE);
}
public boolean canStartHomeSafely() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 6a25c21..a443c00 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -18,7 +18,7 @@
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.content.Context;
import android.graphics.Color;
@@ -92,7 +92,8 @@
@Override
protected float getDepthUnchecked(Context context) {
- if (isDesktopModeSupported() && Launcher.getLauncher(context).areFreeformTasksVisible()) {
+ if (enableDesktopWindowingMode()
+ && Launcher.getLauncher(context).areFreeformTasksVisible()) {
// Don't blur the background while freeform tasks are visible
return BaseDepthController.DEPTH_0_PERCENT;
} else if (enableScalingRevealHomeAnimation()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index ba44d6a..2587395 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -16,7 +16,7 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.graphics.Color;
@@ -46,7 +46,7 @@
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
if (launcher.areFreeformTasksVisible()) {
// No scrim while freeform tasks are visible
return Color.TRANSPARENT;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 3a1c42d..8ef35c0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -49,7 +49,6 @@
import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
@@ -178,10 +177,6 @@
if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
return false;
}
- if (isDesktopModeSupported()) {
- // TODO(b/268075592): add support for quickswitch to/from desktop
- return false;
- }
if (isTrackpadMultiFingerSwipe(ev)) {
return isTrackpadFourFingerSwipe(ev);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index ff142fe..de73630 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -30,7 +30,6 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -79,10 +78,6 @@
if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
return false;
}
- if (isDesktopModeSupported()) {
- // TODO(b/268075592): add support for quickswitch to/from desktop
- return false;
- }
return true;
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 4752225..0320f50 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -59,9 +59,9 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.INVALID_VELOCITY_ON_SWIPE_UP;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.LAUNCHER_DESTROYED;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -947,7 +947,7 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
- if (isDesktopModeSupported() && targets.hasDesktopTasks()) {
+ if (enableDesktopWindowingMode() && targets.hasDesktopTasks()) {
mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
} else {
int untrimmedAppCount = mRemoteTargetHandles.length;
@@ -1170,7 +1170,7 @@
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify the SysUI to use fade-in animation when entering PiP
SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
// Notify the SysUI to stash desktop apps if they are visible
DesktopVisibilityController desktopVisibilityController =
mActivityInterface.getDesktopVisibilityController();
@@ -1255,7 +1255,11 @@
return LAST_TASK;
}
- if (isDesktopModeSupported() && endTarget == NEW_TASK) {
+ if (((mRecentsView.getNextPageTaskView() != null
+ && mRecentsView.getNextPageTaskView().isDesktopTask())
+ || (mRecentsView.getCurrentPageTaskView() != null
+ && mRecentsView.getCurrentPageTaskView().isDesktopTask()))
+ && endTarget == NEW_TASK) {
// TODO(b/268075592): add support for quickswitch to/from desktop
return LAST_TASK;
}
@@ -1416,9 +1420,11 @@
mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
setClampScrollOffset(false);
};
- if (mRecentsView != null) {
+ if (mRecentsView != null && (mRecentsView.getCurrentPageTaskView() != null
+ && !mRecentsView.getCurrentPageTaskView().isDesktopTask())) {
ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
.SET_ON_PAGE_TRANSITION_END_CALLBACK);
+ // TODO(b/268075592): add support for quickswitch to/from desktop
mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
} else {
onPageTransitionEnd.run();
@@ -2232,6 +2238,15 @@
mRecentsAnimationController, mRecentsAnimationTargets);
});
+ if ((mRecentsView.getNextPageTaskView() != null
+ && mRecentsView.getNextPageTaskView().isDesktopTask())
+ || (mRecentsView.getCurrentPageTaskView() != null
+ && mRecentsView.getCurrentPageTaskView().isDesktopTask())) {
+ // TODO(b/268075592): add support for quickswitch to/from desktop
+ mRecentsViewScrollLinked = false;
+ return;
+ }
+
// Disable scrolling in RecentsView for trackpad 3-finger swipe up gesture.
if (!mGestureState.isThreeFingerTrackpadGesture()) {
mRecentsViewScrollLinked = true;
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 59302b7..24c99e3 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -25,7 +25,7 @@
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
@@ -109,7 +109,7 @@
if (endTarget != null) {
// We were on our way to this state when we got canceled, end there instead.
startState = stateFromGestureEndTarget(endTarget);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopVisibilityController controller = getDesktopVisibilityController();
if (controller != null && controller.areFreeformTasksVisible()
&& endTarget == LAST_TASK) {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index f3704e09..4c1b1e0 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -20,7 +20,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.app.ActivityManager;
@@ -270,7 +270,7 @@
int numVisibleTasks = 0;
for (GroupedRecentTaskInfo rawTask : rawTasks) {
- if (isDesktopModeSupported() && rawTask.getType() == TYPE_FREEFORM) {
+ if (enableDesktopWindowingMode() && rawTask.getType() == TYPE_FREEFORM) {
GroupTask desktopTask = createDesktopTask(rawTask);
allTasks.add(desktopTask);
continue;
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 02f9a69..81ab6be 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -27,7 +27,7 @@
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -144,7 +144,7 @@
systemUiProxy, RecentsModel.INSTANCE.get(this),
null /*activityBackCallback*/);
mDragLayer.recreateControllers();
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
null /* depthController */
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 556dd7e..f936882 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -17,7 +17,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.app.WindowConfiguration;
import android.graphics.Rect;
@@ -52,7 +53,7 @@
* @return {@code true} if at least one target app is a desktop task
*/
public boolean hasDesktopTasks() {
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return false;
}
for (RemoteAnimationTarget target : apps) {
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 6a9caf7..ffbb064 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -17,7 +17,7 @@
package com.android.quickstep;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
import android.app.WindowConfiguration;
@@ -68,7 +68,7 @@
* running tasks
*/
public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) {
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopVisibilityController desktopVisibilityController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
if (desktopVisibilityController != null) {
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 62ce341..c97e62a 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -20,7 +20,7 @@
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -372,7 +372,7 @@
return Settings.Global.getInt(
activity.getContentResolver(),
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0
- && !isDesktopModeSupported();
+ && !enableDesktopWindowingMode();
}
};
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 8d4255c..450e960 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -38,7 +38,6 @@
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.quickstep.util.AnimUtils.clampToDuration;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -183,7 +182,7 @@
// Re-use existing handles
remoteTargetHandles = recentsViewHandles;
} else {
- boolean forDesktop = isDesktopModeSupported() && v instanceof DesktopTaskView;
+ boolean forDesktop = v instanceof DesktopTaskView;
RemoteTargetGluer gluer = new RemoteTargetGluer(v.getContext(),
recentsView.getSizeStrategy(), targets, forDesktop);
if (forDesktop) {
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index 757f1f8..ecb6118 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -19,6 +19,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_TASKBAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_LAUNCH;
import static com.android.launcher3.model.data.AppInfo.PACKAGE_KEY_COMPARATOR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -40,6 +41,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
@@ -62,6 +64,7 @@
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
import java.util.Arrays;
@@ -112,6 +115,7 @@
* well on trampoline apps).
*/
public void saveAppPair(GroupedTaskView gtv) {
+ InteractionJankMonitorWrapper.begin(gtv, Cuj.CUJ_LAUNCHER_SAVE_APP_PAIR);
TaskView.TaskIdAttributeContainer[] attributes = gtv.getTaskIdAttributeContainers();
WorkspaceItemInfo recentsInfo1 = attributes[0].getItemInfo();
WorkspaceItemInfo recentsInfo2 = attributes[1].getItemInfo();
@@ -168,7 +172,13 @@
LauncherAccessibilityDelegate delegate =
Launcher.getLauncher(mContext).getAccessibilityDelegate();
if (delegate != null) {
- delegate.addToWorkspace(newAppPair, true);
+ delegate.addToWorkspace(newAppPair, true, (success) -> {
+ if (success) {
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_SAVE_APP_PAIR);
+ } else {
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_SAVE_APP_PAIR);
+ }
+ });
mStatsLogManager.logger().withItemInfo(newAppPair)
.log(StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_SAVE);
}
@@ -179,12 +189,18 @@
/**
* Launches an app pair by searching the RecentsModel for running instances of each app, and
* staging either those running instances or launching the apps as new Intents.
+ *
+ * @param cuj Should be an integer from {@link Cuj} or -1 if no CUJ needs to be logged for jank
+ * monitoring
*/
- public void launchAppPair(AppPairIcon appPairIcon) {
+ public void launchAppPair(AppPairIcon appPairIcon, int cuj) {
WorkspaceItemInfo app1 = appPairIcon.getInfo().contents.get(0);
WorkspaceItemInfo app2 = appPairIcon.getInfo().contents.get(1);
ComponentKey app1Key = new ComponentKey(app1.getTargetComponent(), app1.user);
ComponentKey app2Key = new ComponentKey(app2.getTargetComponent(), app2.user);
+ mSplitSelectStateController.setLaunchingCuj(cuj);
+ InteractionJankMonitorWrapper.begin(appPairIcon, cuj);
+
mSplitSelectStateController.findLastActiveTasksAndRunCallback(
Arrays.asList(app1Key, app2Key),
false /* findExactPairMatch */,
@@ -343,7 +359,8 @@
&& !lastActiveTasksOfAppPair.contains(runningTaskId2)) {
// Neither A nor B are on screen, so just launch a new app pair
// normally.
- launchAppPair(launchingIconView);
+ launchAppPair(launchingIconView,
+ CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_TASKBAR);
} else {
// Exactly one app (A or B) is on-screen, so we have to launch the other
// on the appropriate side.
@@ -388,7 +405,8 @@
if (!task1IsOnScreen && !task2IsOnScreen) {
// Neither App A nor App B are on-screen, launch the app pair normally.
- launchAppPair(launchingIconView);
+ launchAppPair(launchingIconView,
+ CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_TASKBAR);
} else {
// Either A or B is on-screen, so launch the other on the appropriate
// side.
diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
index 660fc22..a854656 100644
--- a/quickstep/src/com/android/quickstep/util/AssistStateManager.java
+++ b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
@@ -53,12 +53,7 @@
}
/** Return {@code true} if the Settings toggle is enabled. */
- public boolean isSettingsNavHandleEnabled() {
- return false;
- }
-
- /** Return {@code true} if the Settings toggle is enabled. */
- public boolean isSettingsHomeButtonEnabled() {
+ public boolean isSettingsAllEntrypointsEnabled() {
return false;
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 8e2520e..34a75d0 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -35,7 +35,7 @@
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_PENDINGINTENT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
@@ -104,6 +104,7 @@
import com.android.systemui.animation.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
import com.android.wm.shell.splitscreen.ISplitSelectListener;
@@ -151,6 +152,12 @@
/** True when the first selected split app is being launched in fullscreen. */
private boolean mLaunchingFirstAppFullscreen;
+ /**
+ * Should be a constant from {@link com.android.internal.jank.Cuj} or -1, does not need to be
+ * set for all launches.
+ */
+ private int mLaunchCuj = -1;
+
private FloatingTaskView mFirstFloatingTaskView;
private SplitInstructionsView mSplitInstructionsView;
@@ -707,6 +714,10 @@
return mSplitAnimationController;
}
+ public void setLaunchingCuj(int launchCuj) {
+ mLaunchCuj = launchCuj;
+ }
+
/**
* Requires Shell Transitions
*/
@@ -850,6 +861,11 @@
mSplitInstructionsView = null;
mLaunchingFirstAppFullscreen = false;
+ if (mLaunchCuj != -1) {
+ InteractionJankMonitorWrapper.end(mLaunchCuj);
+ }
+ mLaunchCuj = -1;
+
if (mSessionInstanceIds != null) {
mStatsLogManager.logger()
.withInstanceId(mSessionInstanceIds.second)
@@ -950,7 +966,7 @@
@Override
public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
int splitPosition, Rect taskBounds) {
- if (!isDesktopModeSupported()) return false;
+ if (!enableDesktopWindowingMode()) return false;
MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo, splitPosition,
taskBounds));
return true;
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index 445a540..87be091 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -18,7 +18,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -199,7 +199,7 @@
private boolean shouldIgnoreSecondSplitLaunch() {
return (!FeatureFlags.enableSplitContextually()
- && !isDesktopModeSupported())
+ && !enableDesktopWindowingMode())
|| !mController.isSplitSelectActive();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index f2c9f27..10b4168 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -51,7 +51,6 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.window.flags.Flags;
import kotlin.Unit;
@@ -88,11 +87,6 @@
private int mChildCountAtInflation;
- /** Check whether desktop windowing is enabled */
- public static boolean isDesktopModeSupported() {
- return Flags.enableDesktopWindowingMode();
- }
-
public DesktopTaskView(Context context) {
this(context, null);
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 97f3d81..cd4fab6 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -26,7 +26,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.annotation.TargetApi;
import android.content.Context;
@@ -268,7 +268,7 @@
DesktopVisibilityController desktopVisibilityController = null;
boolean showDesktopApps = false;
GestureState.GestureEndTarget endTarget = null;
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
desktopVisibilityController = mActivity.getDesktopVisibilityController();
endTarget = mCurrentGestureEndTarget;
if (endTarget == GestureState.GestureEndTarget.LAST_TASK
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index af1dd3a..2aa16a9 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -61,7 +61,7 @@
import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_TAB;
import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_UP;
import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_ACTIONS_IN_MENU;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_DESKTOP;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
@@ -2791,7 +2791,7 @@
}
private boolean hasDesktopTask(Task[] runningTasks) {
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return false;
}
for (Task task : runningTasks) {
@@ -3967,7 +3967,7 @@
// Update flags for 1p/3p launchers
mActionsView.updateFor3pLauncher(!supportsAppPairs());
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
}
@@ -4692,7 +4692,7 @@
mSplitSelectStateController.setAnimateCurrentTaskDismissal(
true /*animateCurrentTaskDismissal*/);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
updateDesktopTaskVisibility(false /* visible */);
}
}
@@ -4716,7 +4716,7 @@
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
updateDesktopTaskVisibility(false /* visible */);
}
}
@@ -4922,7 +4922,7 @@
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE, INVALID_TASK_ID);
mSplitHiddenTaskView = null;
}
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
updateDesktopTaskVisibility(true /* visible */);
}
}
@@ -5312,7 +5312,7 @@
}
RemoteTargetGluer gluer;
- if (isDesktopModeSupported() && recentsAnimationTargets.hasDesktopTasks()) {
+ if (recentsAnimationTargets.hasDesktopTasks()) {
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy(), recentsAnimationTargets,
true /* forDesktop */);
mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 5123364..23fc315 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -40,7 +40,6 @@
import static com.android.quickstep.TaskOverlayFactory.getEnabledShortcuts;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -426,8 +425,7 @@
mCurrentFullscreenParams = new FullscreenDrawParams(context);
mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
- boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
- || isDesktopModeSupported();
+ boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get();
boolean cursorHoverStatesEnabled = enableCursorHoverStates();
setWillNotDraw(!keyboardFocusHighlightEnabled && !cursorHoverStatesEnabled);
diff --git a/quickstep/tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java b/quickstep/tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java
new file mode 100644
index 0000000..68ac3d5
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.testcomponent;
+
+/**
+ * Extension of BaseTestingActivity to help test excludeFromRecents="true".
+ */
+public class ExcludeFromRecentsTestActivity extends BaseTestingActivity {}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index a53bb4e..e37e5cc 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -21,6 +21,7 @@
import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
@@ -37,6 +38,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.tapl.BaseOverview;
import com.android.launcher3.tapl.LaunchedAppState;
import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
import com.android.launcher3.tapl.Overview;
@@ -54,6 +56,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -432,6 +435,7 @@
@PortraitLandscape
@TaskbarModeSwitch()
@TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/309820115
+ @Ignore("b/315376057")
@ScreenRecord // b/309820115
public void testOverviewForTablet() throws Exception {
assumeTrue(mLauncher.isTablet());
@@ -581,4 +585,25 @@
mLauncher.getDevice().setOrientationNatural();
}
}
+
+ @Test
+ public void testExcludeFromRecents() throws Exception {
+ startExcludeFromRecentsTestActivity();
+ OverviewTask currentTask = getAndAssertLaunchedApp().switchToOverview().getCurrentTask();
+ // TODO(b/326565120): the expected content description shouldn't be null but for now there
+ // is a bug that causes it to sometimes be for excludeForRecents tasks.
+ assertTrue("Can't find ExcludeFromRecentsTestActivity after entering Overview from it",
+ currentTask.containsContentDescription("ExcludeFromRecents")
+ || currentTask.containsContentDescription(null));
+ // Going home should clear out the excludeFromRecents task.
+ BaseOverview overview = mLauncher.goHome().switchToOverview();
+ if (overview.hasTasks()) {
+ currentTask = overview.getCurrentTask();
+ assertFalse("Found ExcludeFromRecentsTestActivity after entering Overview from Home",
+ currentTask.containsContentDescription("ExcludeFromRecents")
+ || currentTask.containsContentDescription(null));
+ } else {
+ // Presumably the test started with 0 tasks and remains that way after going home.
+ }
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java
index 6093816..208920a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java
@@ -190,16 +190,14 @@
info.spanX = 2;
info.spanY = 2;
AtomicInteger widgetId = new AtomicInteger();
- new FavoriteItemsTransaction(mTargetContext)
+
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext)
.addItem(() -> {
LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, true);
item.screenId = FIRST_SCREEN_ID;
widgetId.set(item.appWidgetId);
return item;
- })
- .commitAndLoadHome(mLauncher);
-
-
+ }));
assertTrue("Widget is not present",
mLauncher.goHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
diff --git a/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt
index 510faf6..adaf7ff 100644
--- a/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt
@@ -105,7 +105,7 @@
whenever(mockTopTaskTracker.getCachedTopTask(any())).thenReturn(mockCachedTaskInfo)
whenever(mockTask1.getKey()).thenReturn(mockTaskKey1)
whenever(mockTask2.getKey()).thenReturn(mockTaskKey2)
- doNothing().whenever(spyAppPairsController).launchAppPair(any())
+ doNothing().whenever(spyAppPairsController).launchAppPair(any(), any())
doNothing()
.whenever(spyAppPairsController)
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
@@ -210,7 +210,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchAppPair and launchToSide were never called
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, never())
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
}
@@ -234,7 +234,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_BOTTOM_OR_RIGHT))
}
@@ -258,7 +258,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_TOP_OR_LEFT))
}
@@ -282,7 +282,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_BOTTOM_OR_RIGHT))
}
@@ -306,7 +306,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_TOP_OR_LEFT))
}
@@ -330,7 +330,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchAppPair was called
- verify(spyAppPairsController, times(1)).launchAppPair(any())
+ verify(spyAppPairsController, times(1)).launchAppPair(any(), any())
verify(spyAppPairsController, never())
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
}
@@ -354,7 +354,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_BOTTOM_OR_RIGHT))
}
@@ -378,7 +378,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_TOP_OR_LEFT))
}
@@ -402,7 +402,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchAppPair was called
- verify(spyAppPairsController, times(1)).launchAppPair(any())
+ verify(spyAppPairsController, times(1)).launchAppPair(any(), any())
verify(spyAppPairsController, never())
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
}
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 31f4870..43a8aac 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -18,6 +18,7 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:defaultFocusHighlightEnabled="false"
android:orientation="vertical" >
<com.android.launcher3.folder.FolderPagedView
diff --git a/res/layout/widget_recommendations.xml b/res/layout/widget_recommendations.xml
index 89821ac..531db2e 100644
--- a/res/layout/widget_recommendations.xml
+++ b/res/layout/widget_recommendations.xml
@@ -28,6 +28,7 @@
android:layout_marginTop="16dp"
android:accessibilityLiveRegion="polite"
android:gravity="center_horizontal"
+ android:layout_gravity="top"
android:lineHeight="20sp"
android:textColor="?attr/widgetPickerTitleColor"
android:textFontWeight="500"
@@ -38,7 +39,7 @@
android:id="@+id/widget_recommendations_page_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
+ android:layout_gravity="center_horizontal|top"
android:elevation="1dp"
android:visibility="gone" />
<!--
@@ -50,8 +51,9 @@
<com.android.launcher3.widget.picker.WidgetRecommendationsView
android:id="@+id/widget_recommendations_view"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="0dp"
android:layout_gravity="center"
+ android:layout_weight="1"
android:background="@drawable/widgets_surface_background"
android:importantForAccessibility="yes"
launcher:pageIndicator="@+id/widget_recommendations_page_indicator" />
diff --git a/res/layout/widgets_two_pane_sheet.xml b/res/layout/widgets_two_pane_sheet.xml
index 8e45740f..6c4810c 100644
--- a/res/layout/widgets_two_pane_sheet.xml
+++ b/res/layout/widgets_two_pane_sheet.xml
@@ -122,7 +122,7 @@
<LinearLayout
android:id="@+id/widget_recommendations_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:background="@drawable/widgets_surface_background"
android:orientation="vertical"
android:visibility="gone">
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index a1edbb9..ffc8bd0 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -43,6 +43,7 @@
<attr name="popupNotificationDotColor" format="color" />
<attr name="notificationDotColor" format="color" />
<attr name="focusOutlineColor" format="color" />
+ <attr name="focusInnerOutlineColor" format="color" />
<attr name="pageIndicatorDotColor" format="color" />
<attr name="folderPreviewColor" format="color" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d265790..9b4460a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -451,7 +451,11 @@
<dimen name="split_instructions_start_margin_cancel">8dp</dimen>
<dimen name="focus_outline_radius">16dp</dimen>
- <dimen name="focus_outline_stroke_width">3dp</dimen>
+ <dimen name="focus_inner_outline_radius">14dp</dimen>
+ <dimen name="focus_outline_stroke_width">2dp</dimen>
+ <!-- -4dp for double stroke focus outlines, -2dp for padding between outline and widget,
+ make it negative to outset the rect and leave space for drawing focus outline and padding -->
+ <dimen name="focus_rect_widget_outsets">-6dp</dimen>
<!-- Workspace grid visualization parameters -->
<dimen name="grid_visualization_rounding_radius">16dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c2875d9..e35d241 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -53,7 +53,8 @@
<item name="workspaceKeyShadowColor">#89000000</item>
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
<item name="pageIndicatorDotColor">@color/page_indicator_dot_color_light</item>
- <item name="focusOutlineColor">@color/material_color_on_secondary_container</item>
+ <item name="focusOutlineColor">@color/material_color_secondary_fixed</item>
+ <item name="focusInnerOutlineColor">@color/material_color_on_secondary_fixed_variant</item>
<item name="folderPreviewColor">@color/folder_preview_light</item>
<item name="folderBackgroundColor">@color/folder_background_light</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 72977ee..020b34e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2730,7 +2730,7 @@
private void updateDisallowBack() {
if (Flags.enableDesktopWindowingMode()) {
- // Do not disable back in launcher when prototype behavior is enabled
+ // TODO(b/330183377) disable back in launcher when when we productionize
return;
}
LauncherRootView rv = getRootView();
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index d3019c5..d44438f 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -68,8 +68,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewParent;
import android.view.animation.Interpolator;
import androidx.annotation.ChecksSdkIntAtLeast;
@@ -775,136 +773,6 @@
}
/**
- * Recursively call {@link ViewGroup#setClipChildren(boolean)} from {@link View} to ts parent
- * (direct or indirect) inclusive. This method will also save the old clipChildren value on each
- * view with {@link View#setTag(int, Object)}, which can be restored in
- * {@link #restoreClipChildrenOnViewTree(View, ViewParent)}.
- *
- * Note that if parent is null or not a parent of the view, this method will be applied all the
- * way to root view.
- *
- * @param v child view
- * @param parent direct or indirect parent of child view
- * @param clipChildren whether we should clip children
- */
- public static void setClipChildrenOnViewTree(
- @Nullable View v,
- @Nullable ViewParent parent,
- boolean clipChildren) {
- if (v == null) {
- return;
- }
-
- if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- boolean oldClipChildren = viewGroup.getClipChildren();
- if (oldClipChildren != clipChildren) {
- v.setTag(R.id.saved_clip_children_tag_id, oldClipChildren);
- viewGroup.setClipChildren(clipChildren);
- }
- }
-
- if (v == parent) {
- return;
- }
-
- if (v.getParent() instanceof View) {
- setClipChildrenOnViewTree((View) v.getParent(), parent, clipChildren);
- }
- }
-
- /**
- * Recursively call {@link ViewGroup#setClipChildren(boolean)} to restore clip children value
- * set in {@link #setClipChildrenOnViewTree(View, ViewParent, boolean)} on view to its parent
- * (direct or indirect) inclusive.
- *
- * Note that if parent is null or not a parent of the view, this method will be applied all the
- * way to root view.
- *
- * @param v child view
- * @param parent direct or indirect parent of child view
- */
- public static void restoreClipChildrenOnViewTree(
- @Nullable View v, @Nullable ViewParent parent) {
- if (v == null) {
- return;
- }
- if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- Object viewTag = viewGroup.getTag(R.id.saved_clip_children_tag_id);
- if (viewTag instanceof Boolean) {
- viewGroup.setClipChildren((boolean) viewTag);
- viewGroup.setTag(R.id.saved_clip_children_tag_id, null);
- }
- }
-
- if (v == parent) {
- return;
- }
-
- if (v.getParent() instanceof View) {
- restoreClipChildrenOnViewTree((View) v.getParent(), parent);
- }
- }
-
- /**
- * Similar to {@link #setClipChildrenOnViewTree(View, ViewParent, boolean)} but is calling
- * {@link ViewGroup#setClipToPadding}.
- */
- public static void setClipToPaddingOnViewTree(
- @Nullable View v,
- @Nullable ViewParent parent,
- boolean clipToPadding) {
- if (v == null) {
- return;
- }
-
- if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- boolean oldClipToPadding = viewGroup.getClipToPadding();
- if (oldClipToPadding != clipToPadding) {
- v.setTag(R.id.saved_clip_to_padding_tag_id, oldClipToPadding);
- viewGroup.setClipToPadding(clipToPadding);
- }
- }
-
- if (v == parent) {
- return;
- }
-
- if (v.getParent() instanceof View) {
- setClipToPaddingOnViewTree((View) v.getParent(), parent, clipToPadding);
- }
- }
-
- /**
- * Similar to {@link #restoreClipChildrenOnViewTree(View, ViewParent)} but is calling
- * {@link ViewGroup#setClipToPadding}.
- */
- public static void restoreClipToPaddingOnViewTree(
- @Nullable View v, @Nullable ViewParent parent) {
- if (v == null) {
- return;
- }
- if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- Object viewTag = viewGroup.getTag(R.id.saved_clip_to_padding_tag_id);
- if (viewTag instanceof Boolean) {
- viewGroup.setClipToPadding((boolean) viewTag);
- viewGroup.setTag(R.id.saved_clip_to_padding_tag_id, null);
- }
- }
-
- if (v == parent) {
- return;
- }
-
- if (v.getParent() instanceof View) {
- restoreClipToPaddingOnViewTree((View) v.getParent(), parent);
- }
- }
-
- /**
* Translates the {@code targetView} so that it overlaps with {@code exclusionBounds} as little
* as possible, while remaining within {@code inclusionBounds}.
* <p>
diff --git a/src/com/android/launcher3/UtilitiesKt.kt b/src/com/android/launcher3/UtilitiesKt.kt
new file mode 100644
index 0000000..a207d57
--- /dev/null
+++ b/src/com/android/launcher3/UtilitiesKt.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2024 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
+
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewParent
+
+object UtilitiesKt {
+
+ /**
+ * Modify [ViewGroup]'s attribute with type [T]. The overridden attribute is saved by calling
+ * [View.setTag] and can be later restored by [View.getTag].
+ *
+ * @param <T> type of [ViewGroup] attribute. For example, [T] is [Boolean] if modifying
+ * [ViewGroup.setClipChildren]
+ */
+ abstract class ViewGroupAttrModifier<T>(
+ private val targetAttrValue: T,
+ private val tagKey: Int
+ ) {
+ /**
+ * If [targetAttrValue] is different from existing view attribute returned from
+ * [getAttribute], this method will save existing attribute by calling [ViewGroup.setTag].
+ * Then call [setAttribute] to set attribute with [targetAttrValue].
+ */
+ fun saveAndChangeAttribute(viewGroup: ViewGroup) {
+ val oldAttrValue = getAttribute(viewGroup)
+ if (oldAttrValue !== targetAttrValue) {
+ viewGroup.setTag(tagKey, oldAttrValue)
+ setAttribute(viewGroup, targetAttrValue)
+ }
+ }
+
+ /** Restore saved attribute in [saveAndChangeAttribute] by calling [ViewGroup.getTag]. */
+ @Suppress("UNCHECKED_CAST")
+ fun restoreAttribute(viewGroup: ViewGroup) {
+ val oldAttrValue: T = viewGroup.getTag(tagKey) as T ?: return
+ setAttribute(viewGroup, oldAttrValue)
+ viewGroup.setTag(tagKey, null)
+ }
+
+ /** Subclass will override this method to decide how to get [ViewGroup] attribute. */
+ abstract fun getAttribute(viewGroup: ViewGroup): T
+
+ /** Subclass will override this method to decide how to set [ViewGroup] attribute. */
+ abstract fun setAttribute(viewGroup: ViewGroup, attr: T)
+ }
+
+ /** [ViewGroupAttrModifier] to call [ViewGroup.setClipChildren] to false. */
+ @JvmField
+ val CLIP_CHILDREN_FALSE_MODIFIER: ViewGroupAttrModifier<Boolean> =
+ object : ViewGroupAttrModifier<Boolean>(false, R.id.saved_clip_children_tag_id) {
+ override fun getAttribute(viewGroup: ViewGroup): Boolean {
+ return viewGroup.clipChildren
+ }
+
+ override fun setAttribute(viewGroup: ViewGroup, clipChildren: Boolean) {
+ viewGroup.clipChildren = clipChildren
+ }
+ }
+
+ /** [ViewGroupAttrModifier] to call [ViewGroup.setClipToPadding] to false. */
+ @JvmField
+ val CLIP_TO_PADDING_FALSE_MODIFIER: ViewGroupAttrModifier<Boolean> =
+ object : ViewGroupAttrModifier<Boolean>(false, R.id.saved_clip_to_padding_tag_id) {
+ override fun getAttribute(viewGroup: ViewGroup): Boolean {
+ return viewGroup.clipToPadding
+ }
+
+ override fun setAttribute(viewGroup: ViewGroup, clipToPadding: Boolean) {
+ viewGroup.clipToPadding = clipToPadding
+ }
+ }
+
+ /**
+ * Recursively call [ViewGroupAttrModifier.saveAndChangeAttribute] from [View] to its parent
+ * (direct or indirect) inclusive.
+ *
+ * [ViewGroupAttrModifier.saveAndChangeAttribute] will save the existing attribute value on each
+ * view with [View.setTag], which can be restored in [restoreAttributesOnViewTree].
+ *
+ * Note that if parent is null or not a parent of the view, this method will be applied all the
+ * way to root view.
+ *
+ * @param v child view
+ * @param parent direct or indirect parent of child view
+ * @param modifiers list of [ViewGroupAttrModifier] to modify view attribute
+ */
+ @JvmStatic
+ fun modifyAttributesOnViewTree(
+ v: View?,
+ parent: ViewParent?,
+ vararg modifiers: ViewGroupAttrModifier<*>
+ ) {
+ if (v == null) {
+ return
+ }
+ if (v is ViewGroup) {
+ for (modifier in modifiers) {
+ modifier.saveAndChangeAttribute(v)
+ }
+ }
+ if (v === parent) {
+ return
+ }
+ if (v.parent is View) {
+ modifyAttributesOnViewTree(v.parent as View, parent, *modifiers)
+ }
+ }
+
+ /**
+ * Recursively call [ViewGroupAttrModifier.restoreAttribute]} to restore view attributes
+ * previously saved in [ViewGroupAttrModifier.saveAndChangeAttribute] on view to its parent
+ * (direct or indirect) inclusive.
+ *
+ * Note that if parent is null or not a parent of the view, this method will be applied all the
+ * way to root view.
+ *
+ * @param v child view
+ * @param parent direct or indirect parent of child view
+ * @param modifiers list of [ViewGroupAttrModifier] to restore view attributes
+ */
+ @JvmStatic
+ fun restoreAttributesOnViewTree(
+ v: View?,
+ parent: ViewParent?,
+ vararg modifiers: ViewGroupAttrModifier<*>
+ ) {
+ if (v == null) {
+ return
+ }
+ if (v is ViewGroup) {
+ for (modifier in modifiers) {
+ modifier.restoreAttribute(v)
+ }
+ }
+ if (v === parent) {
+ return
+ }
+ if (v.parent is View) {
+ restoreAttributesOnViewTree(v.parent as View, parent, *modifiers)
+ }
+ }
+}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index f130b89..66b8216 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -22,6 +22,8 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
@@ -53,11 +55,13 @@
import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.views.OptionsPopupView.OptionItem;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.util.WidgetSizes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.function.Consumer;
public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Launcher> {
@@ -173,7 +177,7 @@
} else if (action == MOVE) {
return beginAccessibleDrag(host, item, fromKeyboard);
} else if (action == ADD_TO_WORKSPACE) {
- return addToWorkspace(item, true);
+ return addToWorkspace(item, true /*accessibility*/, null /*finishCallback*/);
} else if (action == MOVE_TO_WORKSPACE) {
return moveToWorkspace(item);
} else if (action == RESIZE) {
@@ -384,13 +388,19 @@
* @param item item to be added
* @param accessibility true if the first item to be added to the workspace
* should be focused for accessibility.
+ * @param finishCallback Callback which will be run after this item has been added
+ * and the view has been transitioned to the workspace, or on failure.
*
* @return true if the item could be successfully added
*/
- public boolean addToWorkspace(ItemInfo item, boolean accessibility) {
+ public boolean addToWorkspace(ItemInfo item, boolean accessibility,
+ @Nullable Consumer<Boolean> finishCallback) {
final int[] coordinates = new int[2];
final int screenId = findSpaceOnWorkspace(item, coordinates);
if (screenId == -1) {
+ if (finishCallback != null) {
+ finishCallback.accept(false /*success*/);
+ }
return false;
}
mContext.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
@@ -400,10 +410,14 @@
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
- bindItem(item, accessibility);
+ bindItem(item, accessibility, finishCallback);
announceConfirmation(R.string.item_added_to_workspace);
} else if (item instanceof PendingAddItemInfo) {
PendingAddItemInfo info = (PendingAddItemInfo) item;
+ if (info instanceof PendingAddWidgetInfo widgetInfo
+ && widgetInfo.bindOptions == null) {
+ widgetInfo.bindOptions = widgetInfo.getDefaultSizeOptions(mContext);
+ }
Workspace<?> workspace = mContext.getWorkspace();
workspace.post(
() -> workspace.snapToPage(workspace.getPageIndexForScreenId(screenId))
@@ -415,7 +429,7 @@
mContext.getModelWriter().addItemToDatabase(info,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
- bindItem(info, accessibility);
+ bindItem(info, accessibility, finishCallback);
} else if (item instanceof FolderInfo fi) {
Workspace<?> workspace = mContext.getWorkspace();
workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
@@ -425,23 +439,30 @@
fi.contents.forEach(member -> {
mContext.getModelWriter().addItemToDatabase(member, fi.id, -1, -1, -1);
});
- bindItem(fi, accessibility);
+ bindItem(fi, accessibility, finishCallback);
}
}));
return true;
}
- private void bindItem(ItemInfo item, boolean focusForAccessibility) {
+ private void bindItem(ItemInfo item, boolean focusForAccessibility,
+ @Nullable Consumer<Boolean> finishCallback) {
View view = mContext.getItemInflater().inflateItem(item, mContext.getModelWriter());
if (view == null) {
+ if (finishCallback != null) {
+ finishCallback.accept(false /*success*/);
+ }
return;
}
- AnimatorSet anim = null;
- if (focusForAccessibility) {
- anim = new AnimatorSet();
- anim.addListener(forEndCallback(
- () -> view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)));
- }
+ AnimatorSet anim = new AnimatorSet();
+ anim.addListener(forEndCallback((success) -> {
+ if (focusForAccessibility) {
+ view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+ if (finishCallback != null) {
+ finishCallback.accept(success);
+ }
+ }));
mContext.bindInflatedItems(Collections.singletonList(Pair.create(item, view)), anim);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index aeef5b8..3678109 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -24,8 +24,9 @@
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.Utilities.restoreClipChildrenOnViewTree;
-import static com.android.launcher3.Utilities.setClipChildrenOnViewTree;
+import static com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER;
+import static com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree;
+import static com.android.launcher3.UtilitiesKt.restoreAttributesOnViewTree;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
@@ -305,9 +306,11 @@
if (hasScaleEffect != mHasScaleEffect) {
mHasScaleEffect = hasScaleEffect;
if (mHasScaleEffect) {
- setClipChildrenOnViewTree(rv, mLauncher.getAppsView(), false);
+ modifyAttributesOnViewTree(rv, mLauncher.getAppsView(),
+ CLIP_CHILDREN_FALSE_MODIFIER);
} else {
- restoreClipChildrenOnViewTree(rv, mLauncher.getAppsView());
+ restoreAttributesOnViewTree(rv, mLauncher.getAppsView(),
+ CLIP_CHILDREN_FALSE_MODIFIER);
}
}
}
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
index 3e320bd..72dc63e 100644
--- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -31,9 +31,11 @@
implements OnFocusChangeListener {
public FocusIndicatorHelper(View container) {
- super(container, Flags.enableFocusOutline() ? Themes.getAttrColor(container.getContext(),
- R.attr.focusOutlineColor)
- : container.getResources().getColor(R.color.focused_background));
+ super(container,
+ Flags.enableFocusOutline() ? new int[]{Themes.getAttrColor(container.getContext(),
+ R.attr.focusOutlineColor), Themes.getAttrColor(container.getContext(),
+ R.attr.focusInnerOutlineColor)}
+ : new int[]{container.getResources().getColor(R.color.focused_background)});
}
@Override
diff --git a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
index a8cd03b..456cde8 100644
--- a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
@@ -74,7 +74,8 @@
private static final Rect sTempRect2 = new Rect();
private final View mContainer;
- protected final Paint mPaint;
+ protected final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint mInnerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final int mMaxAlpha;
private final Rect mDirtyRect = new Rect();
@@ -93,24 +94,31 @@
private ObjectAnimator mCurrentAnimation;
private float mAlpha;
private float mRadius;
+ private float mInnerRadius;
- public ItemFocusIndicatorHelper(View container, int color) {
+ public ItemFocusIndicatorHelper(View container, int... colors) {
mContainer = container;
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setColor(0xFF000000 | color);
- if (Flags.enableFocusOutline()) {
+ mPaint.setColor(0xFF000000 | colors[0]);
+ if (Flags.enableFocusOutline() && colors.length > 1) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(container.getResources().getDimensionPixelSize(
R.dimen.focus_outline_stroke_width));
mRadius = container.getResources().getDimensionPixelSize(
R.dimen.focus_outline_radius);
+
+ mInnerPaint.setStyle(Paint.Style.STROKE);
+ mInnerPaint.setColor(0xFF000000 | colors[1]);
+ mInnerPaint.setStrokeWidth(container.getResources().getDimensionPixelSize(
+ R.dimen.focus_outline_stroke_width));
+ mInnerRadius = container.getResources().getDimensionPixelSize(
+ R.dimen.focus_inner_outline_radius);
} else {
mPaint.setStyle(Paint.Style.FILL);
mRadius = container.getResources().getDimensionPixelSize(
R.dimen.grid_visualization_rounding_radius);
}
- mMaxAlpha = Color.alpha(color);
+ mMaxAlpha = Color.alpha(colors[0]);
setAlpha(0);
mShift = 0;
@@ -119,6 +127,7 @@
protected void setAlpha(float alpha) {
mAlpha = alpha;
mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
+ mInnerPaint.setAlpha((int) (mAlpha * mMaxAlpha));
}
@Override
@@ -147,11 +156,18 @@
Rect newRect = getDrawRect();
if (newRect != null) {
if (Flags.enableFocusOutline()) {
- // Stroke is drawn with half outside and half inside the view. Inset by half
- // stroke width to move the whole stroke inside the view and avoid other views
- // occluding it
- int halfStrokeWidth = (int) mPaint.getStrokeWidth() / 2;
- newRect.inset(halfStrokeWidth, halfStrokeWidth);
+ int strokeWidth = (int) mPaint.getStrokeWidth();
+ // Inset for inner outline. Stroke is drawn with half outside and half inside
+ // the view. Inset by half stroke width to move the whole stroke inside the view
+ // and avoid other views occluding it. Inset one more stroke width to leave space
+ // for outer outline.
+ newRect.inset((int) (strokeWidth * 1.5), (int) (strokeWidth * 1.5));
+ c.drawRoundRect((float) newRect.left, (float) newRect.top,
+ (float) newRect.right, (float) newRect.bottom,
+ mInnerRadius, mInnerRadius, mInnerPaint);
+
+ // Inset outward for drawing outer outline
+ newRect.inset(-strokeWidth, -strokeWidth);
}
mDirtyRect.set(newRect);
c.drawRoundRect((float) mDirtyRect.left, (float) mDirtyRect.top,
diff --git a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
index f9bd343..4653bf1 100644
--- a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
+++ b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
@@ -27,6 +27,7 @@
public class ViewGroupFocusHelper extends FocusIndicatorHelper {
private final View mContainer;
+ private static final Rect sTempRect = new Rect();
public ViewGroupFocusHelper(View container) {
super(container);
@@ -35,18 +36,22 @@
@Override
public void viewToRect(View v, Rect outRect) {
- outRect.left = 0;
- outRect.top = 0;
+ // Using FocusedRect here allows views to provide their custom rect for drawing outline,
+ // e.g. making the Rect bigger than the content to leave some padding between view and
+ // outline
+ v.getFocusedRect(sTempRect);
+ outRect.left = sTempRect.left;
+ outRect.top = sTempRect.top;
computeLocationRelativeToContainer(v, outRect);
// If a view is scaled, its position will also shift accordingly. For optimization, only
// consider this for the last node.
- outRect.left += (1 - v.getScaleX()) * v.getWidth() / 2;
- outRect.top += (1 - v.getScaleY()) * v.getHeight() / 2;
+ outRect.left = (int) (outRect.left + (1 - v.getScaleX()) * sTempRect.width() / 2);
+ outRect.top = (int) (outRect.top + (1 - v.getScaleY()) * sTempRect.height() / 2);
- outRect.right = outRect.left + (int) (v.getScaleX() * v.getWidth());
- outRect.bottom = outRect.top + (int) (v.getScaleY() * v.getHeight());
+ outRect.right = outRect.left + (int) (v.getScaleX() * sTempRect.width());
+ outRect.bottom = outRect.top + (int) (v.getScaleY() * sTempRect.height());
}
private void computeLocationRelativeToContainer(View child, Rect outRect) {
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index fa2a1b0..966b6a6 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -29,6 +29,8 @@
import android.util.Pair;
import android.view.View;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
@@ -309,9 +311,16 @@
// Bind workspace screens
executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);
+ ItemInflater inflater = mCallbacks.getItemInflater();
+
// Load items on the current page.
- bindItemsInChunks(currentWorkspaceItems, ITEMS_CHUNK, mUiExecutor);
- bindItemsInChunks(currentAppWidgets, 1, mUiExecutor);
+ if (enableWorkspaceInflation() && inflater != null) {
+ inflateAsyncAndBind(currentWorkspaceItems, inflater, mUiExecutor);
+ inflateAsyncAndBind(currentAppWidgets, inflater, mUiExecutor);
+ } else {
+ bindItemsInChunks(currentWorkspaceItems, ITEMS_CHUNK, mUiExecutor);
+ bindItemsInChunks(currentAppWidgets, 1, mUiExecutor);
+ }
if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
mExtraItems.forEach(item ->
executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));
@@ -322,18 +331,20 @@
RunnableList onCompleteSignal = new RunnableList();
- if (enableWorkspaceInflation()) {
+ if (enableWorkspaceInflation() && inflater != null) {
MODEL_EXECUTOR.execute(() -> {
- setupPendingBind(otherWorkspaceItems, otherAppWidgets, currentScreenIds,
- pendingExecutor);
+ inflateAsyncAndBind(otherWorkspaceItems, inflater, pendingExecutor);
+ inflateAsyncAndBind(otherAppWidgets, inflater, pendingExecutor);
+ setupPendingBind(currentScreenIds, pendingExecutor);
// Wait for the async inflation to complete and then notify the completion
// signal on UI thread.
MAIN_EXECUTOR.execute(onCompleteSignal::executeAllAndDestroy);
});
} else {
- setupPendingBind(
- otherWorkspaceItems, otherAppWidgets, currentScreenIds, pendingExecutor);
+ bindItemsInChunks(otherWorkspaceItems, ITEMS_CHUNK, pendingExecutor);
+ bindItemsInChunks(otherAppWidgets, 1, pendingExecutor);
+ setupPendingBind(currentScreenIds, pendingExecutor);
onCompleteSignal.executeAllAndDestroy();
}
@@ -348,13 +359,8 @@
}
private void setupPendingBind(
- List<ItemInfo> otherWorkspaceItems,
- List<ItemInfo> otherAppWidgets,
IntSet currentScreenIds,
Executor pendingExecutor) {
- bindItemsInChunks(otherWorkspaceItems, ITEMS_CHUNK, pendingExecutor);
- bindItemsInChunks(otherAppWidgets, 1, pendingExecutor);
-
StringCache cacheClone = mBgDataModel.stringCache.clone();
executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
@@ -371,18 +377,11 @@
* Tries to inflate the items asynchronously and bind. Returns true on success or false if
* async-binding is not supported in this case.
*/
- private boolean inflateAsyncAndBind(List<ItemInfo> items, Executor executor) {
- if (!enableWorkspaceInflation()) {
- return false;
- }
- ItemInflater inflater = mCallbacks.getItemInflater();
- if (inflater == null) {
- return false;
- }
-
+ private void inflateAsyncAndBind(
+ List<ItemInfo> items, @NonNull ItemInflater inflater, Executor executor) {
if (mMyBindingId != mBgDataModel.lastBindId) {
Log.d(TAG, "Too many consecutive reloads, skipping obsolete view inflation");
- return true;
+ return;
}
ModelWriter writer = mApp.getModel()
@@ -390,15 +389,10 @@
List<Pair<ItemInfo, View>> bindItems = items.stream().map(i ->
Pair.create(i, inflater.inflateItem(i, writer, null))).toList();
executeCallbacksTask(c -> c.bindInflatedItems(bindItems), executor);
- return true;
}
- private void bindItemsInChunks(List<ItemInfo> workspaceItems, int chunkCount,
- Executor executor) {
- if (inflateAsyncAndBind(workspaceItems, executor)) {
- return;
- }
-
+ private void bindItemsInChunks(
+ List<ItemInfo> workspaceItems, int chunkCount, Executor executor) {
// Bind the workspace items
int count = workspaceItems.size();
for (int i = 0; i < count; i += chunkCount) {
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 2ec994e..f3769d5 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -215,11 +215,15 @@
&& SessionCommitReceiver.isEnabled(mAppContext, getUserHandle(sessionInfo))
&& verifySessionInfo(sessionInfo)
&& !promiseIconAddedForId(sessionInfo.getSessionId())) {
- FileLog.d(LOG, "Adding package name to install queue: "
- + sessionInfo.getAppPackageName());
+ // In case of unarchival, we do not want to add a workspace promise icon if one is
+ // not already present. For general app installations however, we do support it.
+ if (!Utilities.enableSupportForArchiving() || !sessionInfo.isUnarchival()) {
+ FileLog.d(LOG, "Adding package name to install queue: "
+ + sessionInfo.getAppPackageName());
- ItemInstallQueue.INSTANCE.get(mAppContext)
- .queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
+ ItemInstallQueue.INSTANCE.get(mAppContext)
+ .queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
+ }
getPromiseIconIds().add(sessionInfo.getSessionId());
updatePromiseIconPrefs();
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 0a5127b..5266448 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -168,7 +168,7 @@
StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP);
handleClose(true);
Launcher.getLauncher(mActivityContext).getAccessibilityDelegate()
- .addToWorkspace(info, /*accessibility=*/ false);
+ .addToWorkspace(info, /*accessibility=*/ false, /*finishCallback=*/ null);
}
@Override
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 2259e3c..c3e9ad6 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -92,6 +92,8 @@
private boolean mTrackingWidgetUpdate = false;
+ private int mFocusRectOutsets = 0;
+
public LauncherAppWidgetHostView(Context context) {
super(context);
mActivityContext = ActivityContext.lookupContext(context);
@@ -100,6 +102,8 @@
setBackgroundResource(R.drawable.widget_internal_focus_bg);
if (Flags.enableFocusOutline()) {
setDefaultFocusHighlightEnabled(false);
+ mFocusRectOutsets = context.getResources().getDimensionPixelSize(
+ R.dimen.focus_rect_widget_outsets);
}
if (Themes.getAttrBoolean(context, R.attr.isWorkspaceDarkText)) {
@@ -269,6 +273,13 @@
}
@Override
+ public void getFocusedRect(Rect r) {
+ super.getFocusedRect(r);
+ // Outset to a larger rect for drawing a padding between focus outline and widget
+ r.inset(mFocusRectOutsets, mFocusRectOutsets);
+ }
+
+ @Override
public void onTouchComplete() {
if (!mLongPressHelper.hasPerformedLongPress()) {
// If a long press has been performed, we don't want to clear the record of that since
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 3b661d0..255a6d2 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -38,6 +38,7 @@
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.function.Consumer;
/**
* A {@link PagedView} that displays widget recommendations in categories with dots as paged
@@ -45,11 +46,13 @@
*/
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
private @Px float mAvailableHeight = Float.MAX_VALUE;
-
private static final int MAX_CATEGORIES = 3;
private TextView mRecommendationPageTitle;
private final List<String> mCategoryTitles = new ArrayList<>();
+ /** Callbacks to run when page changes */
+ private final List<Consumer<Integer>> mPageSwitchListeners = new ArrayList<>();
+
@Nullable
private OnLongClickListener mWidgetCellOnLongClickListener;
@Nullable
@@ -84,6 +87,13 @@
}
/**
+ * Add a callback to run when the current displayed page changes.
+ */
+ public void addPageSwitchListener(Consumer<Integer> pageChangeListener) {
+ 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.
@@ -104,7 +114,7 @@
int displayedWidgets = maybeDisplayInTable(recommendedWidgets, deviceProfile,
availableWidth, cellPadding);
- updateTitleAndIndicator();
+ updateTitleAndIndicator(/* requestedPage= */ 0);
return displayedWidgets;
}
@@ -119,16 +129,18 @@
* @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
+ * @param requestedPage page number to display initially.
* @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) {
+ DeviceProfile deviceProfile, final @Px float availableHeight,
+ final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
this.mAvailableHeight = availableHeight;
Context context = getContext();
- mPageIndicator.setPauseScroll(true, deviceProfile.isTwoPanels);
+ // 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;
@@ -153,26 +165,33 @@
}
}
- updateTitleAndIndicator();
- mPageIndicator.setPauseScroll(false, deviceProfile.isTwoPanels);
+ 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 totalDisplayedWidgets;
}
private void clear() {
mCategoryTitles.clear();
removeAllViews();
+ setCurrentPage(0);
+ mPageIndicator.setActiveMarker(0);
}
/** Displays the page title and paging indicator if there are multiple pages. */
- private void updateTitleAndIndicator() {
+ private void updateTitleAndIndicator(int requestedPage) {
boolean showPaginatedView = getPageCount() > 1;
int titleAndIndicatorVisibility = showPaginatedView ? View.VISIBLE : View.GONE;
mRecommendationPageTitle.setVisibility(titleAndIndicatorVisibility);
mPageIndicator.setVisibility(titleAndIndicatorVisibility);
if (showPaginatedView) {
- mPageIndicator.setActiveMarker(0);
- setCurrentPage(0);
- mRecommendationPageTitle.setText(mCategoryTitles.get(0));
+ if (requestedPage <= 0 || requestedPage >= getPageCount()) {
+ requestedPage = 0;
+ }
+ setCurrentPage(requestedPage);
+ mPageIndicator.setActiveMarker(requestedPage);
+ mRecommendationPageTitle.setText(mCategoryTitles.get(requestedPage));
}
}
@@ -180,7 +199,9 @@
protected void notifyPageSwitchListener(int prevPage) {
if (getPageCount() > 1) {
// Since the title is outside the paging scroll, we update the title on page switch.
- mRecommendationPageTitle.setText(mCategoryTitles.get(getNextPage()));
+ int nextPage = getNextPage();
+ mRecommendationPageTitle.setText(mCategoryTitles.get(nextPage));
+ mPageSwitchListeners.forEach(listener -> listener.accept(nextPage));
super.notifyPageSwitchListener(prevPage);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 848f6fa..ba6c4cf 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.widget.picker;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -30,6 +28,7 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Build;
+import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
import android.os.UserHandle;
@@ -67,6 +66,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.UserManagerState;
+import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -81,7 +81,9 @@
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.IntStream;
@@ -98,8 +100,11 @@
// The widget recommendation table can easily take over the entire screen on devices with small
// resolution or landscape on phone. This ratio defines the max percentage of content area that
- // the table can display.
- private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f;
+ // the table can display with respect to bottom sheet's height.
+ private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.45f;
+ private static final String RECOMMENDATIONS_SAVED_STATE_KEY =
+ "widgetsFullSheet:mRecommendationsCurrentPage";
+ private static final String SUPER_SAVED_STATE_KEY = "widgetsFullSheet:superHierarchyState";
private final UserCache mUserCache;
private final UserManagerState mUserManagerState = new UserManagerState();
private final UserHandle mCurrentUser = Process.myUserHandle();
@@ -109,8 +114,13 @@
protected final boolean mHasWorkProfile;
// Number of recommendations displayed
protected int mRecommendedWidgetsCount;
+ private List<WidgetItem> mRecommendedWidgets = new ArrayList<>();
+ private Map<WidgetRecommendationCategory, List<WidgetItem>> mRecommendedWidgetsMap =
+ new HashMap<>();
+ protected int mRecommendationsCurrentPage = 0;
protected final SparseArray<AdapterHolder> mAdapters = new SparseArray();
- @Nullable private ArrowTipView mLatestEducationalTip;
+ @Nullable
+ private ArrowTipView mLatestEducationalTip;
private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
new OnLayoutChangeListener() {
@Override
@@ -156,14 +166,19 @@
}
};
- @Px private final int mTabsHeight;
+ @Px
+ private final int mTabsHeight;
- @Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
- @Nullable private WidgetsRecyclerView mCurrentTouchEventRecyclerView;
- @Nullable PersonalWorkPagedView mViewPager;
+ @Nullable
+ private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
+ @Nullable
+ private WidgetsRecyclerView mCurrentTouchEventRecyclerView;
+ @Nullable
+ PersonalWorkPagedView mViewPager;
private boolean mIsInSearchMode;
private boolean mIsNoWidgetsViewNeeded;
- @Px protected int mMaxSpanPerRow;
+ @Px
+ protected int mMaxSpanPerRow;
protected DeviceProfile mDeviceProfile;
protected TextView mNoWidgetsView;
@@ -227,13 +242,16 @@
R.id.widget_recommendations_container);
mWidgetRecommendationsView = mSearchScrollView.findViewById(
R.id.widget_recommendations_view);
+ // To save the currently displayed page, so that, it can be requested when rebinding
+ // recommendations with different size constraints.
+ mWidgetRecommendationsView.addPageSwitchListener(
+ newPage -> mRecommendationsCurrentPage = newPage);
mWidgetRecommendationsView.initParentViews(mWidgetRecommendationsContainer);
mWidgetRecommendationsView.setWidgetCellLongClickListener(this);
mWidgetRecommendationsView.setWidgetCellOnClickListener(this);
mHeaderTitle = mSearchScrollView.findViewById(R.id.title);
- onRecommendedWidgetsBound();
onWidgetsBound();
setUpEducationViewsIfNeeded();
}
@@ -354,7 +372,6 @@
super.onAttachedToWindow();
LauncherAppState.getInstance(mActivityContext).getModel()
.refreshAndBindWidgetsAndShortcuts(null);
- onRecommendedWidgetsBound();
}
@Override
@@ -582,16 +599,26 @@
}
if (enableCategorizedWidgetSuggestions()) {
+ // We avoid applying new recommendations when some are already displayed.
+ if (mRecommendedWidgetsMap.isEmpty()) {
+ mRecommendedWidgetsMap =
+ mActivityContext.getPopupDataProvider().getCategorizedRecommendedWidgets();
+ }
mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
- mActivityContext.getPopupDataProvider().getCategorizedRecommendedWidgets(),
+ mRecommendedWidgetsMap,
mDeviceProfile,
/* availableHeight= */ getMaxAvailableHeightForRecommendations(),
/* availableWidth= */ mMaxSpanPerRow,
- /* cellPadding= */ mWidgetCellHorizontalPadding
+ /* cellPadding= */ mWidgetCellHorizontalPadding,
+ /* requestedPage= */ mRecommendationsCurrentPage
);
} else {
+ if (mRecommendedWidgets.isEmpty()) {
+ mRecommendedWidgets =
+ mActivityContext.getPopupDataProvider().getRecommendedWidgets();
+ }
mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
- mActivityContext.getPopupDataProvider().getRecommendedWidgets(),
+ mRecommendedWidgets,
mDeviceProfile,
/* availableHeight= */ getMaxAvailableHeightForRecommendations(),
/* availableWidth= */ mMaxSpanPerRow,
@@ -604,23 +631,14 @@
@Px
private float getMaxAvailableHeightForRecommendations() {
- float noWidgetsViewHeight = 0;
- if (mIsNoWidgetsViewNeeded) {
- // Make sure recommended section leaves enough space for noWidgetsView.
- Rect noWidgetsViewTextBounds = new Rect();
- mNoWidgetsView.getPaint()
- .getTextBounds(mNoWidgetsView.getText().toString(), /* start= */ 0,
- mNoWidgetsView.getText().length(), noWidgetsViewTextBounds);
- noWidgetsViewHeight = noWidgetsViewTextBounds.height();
+ // There isn't enough space to show recommendations in landscape orientation on phones with
+ // a full sheet design. Tablets use a two pane picker.
+ if (!isTwoPane() && mDeviceProfile.isLandscape) {
+ return 0f;
}
- if (!isTwoPane()) {
- doMeasure(
- makeMeasureSpec(mActivityContext.getDeviceProfile().availableWidthPx,
- MeasureSpec.EXACTLY),
- makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
- MeasureSpec.EXACTLY));
- }
- return getMaxTableHeight(noWidgetsViewHeight);
+
+ return (mDeviceProfile.heightPx - mDeviceProfile.bottomSheetTopPadding)
+ * getRecommendationSectionHeightRatio();
}
/** b/209579563: "Widgets" header should be focused first. */
@@ -629,12 +647,12 @@
return mHeaderTitle;
}
+ /**
+ * Ratio of recommendations section with respect to bottom sheet's height on scale of 0 to 1.
+ */
@Px
- protected float getMaxTableHeight(@Px float noWidgetsViewHeight) {
- return (mContent.getMeasuredHeight()
- - mTabsHeight - getHeaderViewHeight()
- - noWidgetsViewHeight)
- * RECOMMENDATION_TABLE_HEIGHT_RATIO;
+ protected float getRecommendationSectionHeightRatio() {
+ return RECOMMENDATION_TABLE_HEIGHT_RATIO;
}
private void open(boolean animate) {
@@ -705,6 +723,27 @@
return sheet;
}
+ @Override
+ public void saveHierarchyState(SparseArray<Parcelable> sparseArray) {
+ Bundle bundle = new Bundle();
+ // With widget picker open, when we open shade to switch theme, Launcher re-creates the
+ // picker and calls save/restore hierarchy state. We save the state of recommendations
+ // across those updates.
+ bundle.putInt(RECOMMENDATIONS_SAVED_STATE_KEY, mRecommendationsCurrentPage);
+ SparseArray<Parcelable> superState = new SparseArray<>();
+ super.saveHierarchyState(superState);
+ bundle.putSparseParcelableArray(SUPER_SAVED_STATE_KEY, superState);
+ sparseArray.put(0, bundle);
+ }
+
+ @Override
+ public void restoreHierarchyState(SparseArray<Parcelable> sparseArray) {
+ Bundle state = (Bundle) sparseArray.get(0);
+ mRecommendationsCurrentPage = state.getInt(
+ RECOMMENDATIONS_SAVED_STATE_KEY, /*defaultValue=*/0);
+ super.restoreHierarchyState(state.getSparseParcelableArray(SUPER_SAVED_STATE_KEY));
+ }
+
private static int getWidgetSheetId(BaseActivity activity) {
boolean isTwoPane = (activity.getDeviceProfile().isTablet
// Enables two pane picker for tablets in all orientations when the
@@ -793,7 +832,7 @@
/** private the height, in pixel, + the vertical margins of a given view. */
protected static int measureHeightWithVerticalMargins(View view) {
- if (view.getVisibility() != VISIBLE) {
+ if (view == null || view.getVisibility() != VISIBLE) {
return 0;
}
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
@@ -828,6 +867,7 @@
saveHierarchyState(widgetsState);
handleClose(false);
WidgetsFullSheet sheet = show(BaseActivity.fromContext(getContext()), false);
+ sheet.restoreRecommendations(mRecommendedWidgets, mRecommendedWidgetsMap);
sheet.restoreHierarchyState(widgetsState);
sheet.restorePreviousAdapterHolderType(getCurrentAdapterHolderType());
} else if (!isTwoPane()) {
@@ -838,6 +878,12 @@
mDeviceProfile = dp;
}
+ private void restoreRecommendations(List<WidgetItem> recommendedWidgets,
+ Map<WidgetRecommendationCategory, List<WidgetItem>> recommendedWidgetsMap) {
+ mRecommendedWidgets = recommendedWidgets;
+ mRecommendedWidgetsMap = recommendedWidgetsMap;
+ }
+
/**
* Indicates if layout should be re-created on device profile change - so that a different
* layout can be displayed.
@@ -887,7 +933,8 @@
}
}
- @Nullable private View getViewToShowEducationTip() {
+ @Nullable
+ private View getViewToShowEducationTip() {
if (mWidgetRecommendationsContainer.getVisibility() == VISIBLE) {
return mWidgetRecommendationsView.getViewForEducationTip();
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 1e17252..c60bca0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -17,10 +17,10 @@
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
-import static com.android.launcher3.Utilities.restoreClipChildrenOnViewTree;
-import static com.android.launcher3.Utilities.restoreClipToPaddingOnViewTree;
-import static com.android.launcher3.Utilities.setClipChildrenOnViewTree;
-import static com.android.launcher3.Utilities.setClipToPaddingOnViewTree;
+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;
+import static com.android.launcher3.UtilitiesKt.restoreAttributesOnViewTree;
import android.content.Context;
import android.graphics.Outline;
@@ -62,6 +62,9 @@
private static final int MAXIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP = 395;
private static final String SUGGESTIONS_PACKAGE_NAME = "widgets_list_suggestions_entry";
+ // This ratio defines the max percentage of content area that the recommendations can display
+ // with respect to the bottom sheet's height.
+ private static final float RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE = 0.75f;
private FrameLayout mSuggestedWidgetsContainer;
private WidgetsListHeader mSuggestedWidgetsHeader;
private PackageUserKey mSuggestedWidgetsPackageUserKey;
@@ -126,6 +129,10 @@
mWidgetRecommendationsView.initParentViews(mWidgetRecommendationsContainer);
mWidgetRecommendationsView.setWidgetCellLongClickListener(this);
mWidgetRecommendationsView.setWidgetCellOnClickListener(this);
+ // To save the currently displayed page, so that, it can be requested when rebinding
+ // recommendations with different size constraints.
+ mWidgetRecommendationsView.addPageSwitchListener(
+ newPage -> mRecommendationsCurrentPage = newPage);
mHeaderTitle = mContent.findViewById(R.id.title);
mRightPane = mContent.findViewById(R.id.right_pane);
@@ -139,7 +146,6 @@
mPrimaryWidgetListView.setOutlineProvider(mViewOutlineProvider);
mPrimaryWidgetListView.setClipToOutline(true);
- onRecommendedWidgetsBound();
onWidgetsBound();
setUpEducationViewsIfNeeded();
@@ -156,13 +162,15 @@
}
mOldIsBackSwipeProgressing = isBackSwipeProgressing;
if (isBackSwipeProgressing) {
- setClipChildrenOnViewTree(mPrimaryWidgetListView, (ViewParent) mContent, false);
- setClipChildrenOnViewTree(mRightPaneScrollView, (ViewParent) mContent, false);
- setClipToPaddingOnViewTree(mRightPaneScrollView, (ViewParent) mContent, false);
+ modifyAttributesOnViewTree(mPrimaryWidgetListView, (ViewParent) mContent,
+ CLIP_CHILDREN_FALSE_MODIFIER);
+ modifyAttributesOnViewTree(mRightPaneScrollView, (ViewParent) mContent,
+ CLIP_CHILDREN_FALSE_MODIFIER, CLIP_TO_PADDING_FALSE_MODIFIER);
} else {
- restoreClipChildrenOnViewTree(mPrimaryWidgetListView, mContent);
- restoreClipChildrenOnViewTree(mRightPaneScrollView, mContent);
- restoreClipToPaddingOnViewTree(mRightPaneScrollView, mContent);
+ restoreAttributesOnViewTree(mPrimaryWidgetListView, mContent,
+ CLIP_CHILDREN_FALSE_MODIFIER);
+ restoreAttributesOnViewTree(mRightPaneScrollView, mContent,
+ CLIP_CHILDREN_FALSE_MODIFIER, CLIP_TO_PADDING_FALSE_MODIFIER);
}
}
@@ -240,11 +248,13 @@
String suggestionsRightPaneTitle = getContext().getString(
R.string.widget_picker_right_pane_accessibility_title, suggestionsHeaderTitle);
packageItemInfo.title = suggestionsHeaderTitle;
+ // Suggestions may update at run time. The widgets count on suggestions doesn't add any
+ // value, so, we don't show the count.
WidgetsListHeaderEntry widgetsListHeaderEntry = WidgetsListHeaderEntry.create(
packageItemInfo,
/*titleSectionName=*/ suggestionsHeaderTitle,
/*items=*/ mActivityContext.getPopupDataProvider().getRecommendedWidgets(),
- /*visibleWidgetsCount=*/ mRecommendedWidgetsCount)
+ /*visibleWidgetsCount=*/ 0)
.withWidgetListShown();
mSuggestedWidgetsHeader.applyFromItemInfoWithIcon(widgetsListHeaderEntry);
@@ -266,8 +276,8 @@
@Override
@Px
- protected float getMaxTableHeight(@Px float noWidgetsViewHeight) {
- return mContent.getMeasuredHeight() - measureHeightWithVerticalMargins(mHeaderTitle);
+ protected float getRecommendationSectionHeightRatio() {
+ return RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE;
}
@Override
diff --git a/tests/Android.bp b/tests/Android.bp
index 01e8224..eeafdba 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -23,37 +23,20 @@
srcs: [
"src/**/*.java",
"src/**/*.kt",
- "multivalentTests/src/**/*.java",
- "multivalentTests/src/**/*.kt",
+ ":launcher3-robo-src",
],
exclude_srcs: [
":launcher-non-quickstep-tests-src",
],
}
-// Source code used for screenshot tests
filegroup {
- name: "launcher-image-tests-helpers",
+ name: "launcher3-robo-src",
+ // multivalentTests directory is a shared folder for not only robolectric converted test
+ // classes but also shared helper classes.
srcs: [
- "src/com/android/launcher3/celllayout/board/*.java",
- "src/com/android/launcher3/celllayout/board/*.kt",
- "src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java",
- "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
- "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
- "src/com/android/launcher3/ui/TestViewHelpers.java",
- "multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java",
- "src/com/android/launcher3/util/ModelTestExtensions.kt",
- "src/com/android/launcher3/util/TestConstants.java",
- "multivalentTests/src/com/android/launcher3/util/TestUtil.java",
- "src/com/android/launcher3/util/Wait.java",
- "multivalentTests/src/com/android/launcher3/util/WidgetUtils.java",
- "src/com/android/launcher3/util/rule/*.java",
- "src/com/android/launcher3/util/rule/*.kt",
- "multivalentTests/src/com/android/launcher3/util/rule/*.java",
- "multivalentTests/src/com/android/launcher3/util/rule/*.kt",
- "src/com/android/launcher3/util/viewcapture_analysis/*.java",
- "src/com/android/launcher3/testcomponent/*.java",
- "src/com/android/launcher3/testcomponent/*.kt",
+ "multivalentTests/src/**/*.java",
+ "multivalentTests/src/**/*.kt",
],
}
@@ -70,35 +53,15 @@
filegroup {
name: "launcher-oop-tests-src",
srcs: [
+ ":launcher-testing-helpers",
"src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java",
"src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java",
"src/com/android/launcher3/appiconmenu/TaplAppIconMenuTest.java",
"src/com/android/launcher3/dragging/TaplDragTest.java",
"src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java",
- "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
- "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
"src/com/android/launcher3/ui/TaplTestsLauncher3Test.java",
"src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java",
"src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java",
- "multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java",
- "src/com/android/launcher3/util/TestConstants.java",
- "multivalentTests/src/com/android/launcher3/util/TestUtil.java",
- "src/com/android/launcher3/util/Wait.java",
- "multivalentTests/src/com/android/launcher3/util/WidgetUtils.java",
- "src/com/android/launcher3/util/rule/FailureWatcher.java",
- "src/com/android/launcher3/util/rule/ViewCaptureRule.kt",
- "src/com/android/launcher3/util/rule/SamplerRule.java",
- "src/com/android/launcher3/util/rule/ScreenRecordRule.java",
- "src/com/android/launcher3/util/rule/ShellCommandRule.java",
- "src/com/android/launcher3/util/rule/TestIsolationRule.java",
- "multivalentTests/src/com/android/launcher3/util/rule/TestStabilityRule.java",
- "src/com/android/launcher3/util/viewcapture_analysis/*.java",
- "src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
- "src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java",
- "src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
- "src/com/android/launcher3/testcomponent/TestCommandReceiver.java",
- "src/com/android/launcher3/testcomponent/TestLauncherActivity.java",
- "src/com/android/launcher3/testcomponent/ImeTestActivity.java",
],
}
@@ -222,11 +185,8 @@
android_robolectric_test {
enabled: true,
name: "Launcher3RoboTests",
- // multivalentTests directory is a shared folder for not only robolectric converted test
- // classes but also shared helper classes.
srcs: [
- "multivalentTests/src/**/*.java",
- "multivalentTests/src/**/*.kt",
+ ":launcher3-robo-src",
// Test util classes
":launcher-testing-helpers",
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 7cb7964..27dd2a9 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -388,6 +388,15 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
+ <activity android:name="com.android.launcher3.testcomponent.ExcludeFromRecentsTestActivity"
+ android:label="ExcludeFromRecentsTestActivity"
+ android:exported="true"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
<!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
<provider
diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
similarity index 89%
rename from tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
index dbb2715..b86333c 100644
--- a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.celllayout;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -24,8 +22,6 @@
import android.content.Context;
-import androidx.test.uiautomator.UiDevice;
-
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
@@ -33,7 +29,6 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
-import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.ModelTestExtensions;
@@ -106,15 +101,4 @@
runOnExecutorSync(MAIN_EXECUTOR, model::forceReload);
ModelTestExtensions.INSTANCE.loadModelSync(model);
}
-
- /**
- * Commits the transaction and waits for home load
- */
- public void commitAndLoadHome(LauncherInstrumentation inst) {
- commit();
-
- // Launch the home activity
- UiDevice.getInstance(getInstrumentation()).pressHome();
- inst.waitForLauncherInitialized();
- }
}
diff --git a/tests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
diff --git a/tests/src/com/android/launcher3/celllayout/board/CellType.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellType.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/CellType.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellType.java
diff --git a/tests/src/com/android/launcher3/celllayout/board/FolderPoint.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/FolderPoint.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/FolderPoint.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/FolderPoint.java
diff --git a/tests/src/com/android/launcher3/celllayout/board/IconPoint.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/IconPoint.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/IconPoint.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/IconPoint.java
diff --git a/tests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt
diff --git a/tests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt
diff --git a/tests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
similarity index 94%
rename from tests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
index 06a7db2..8a427dd 100644
--- a/tests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Rect;
import android.os.Process;
@@ -32,11 +31,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.celllayout.FavoriteItemsTransaction;
-import com.android.launcher3.celllayout.board.CellLayoutBoard;
-import com.android.launcher3.celllayout.board.CellType;
-import com.android.launcher3.celllayout.board.FolderPoint;
-import com.android.launcher3.celllayout.board.IconPoint;
-import com.android.launcher3.celllayout.board.WidgetRect;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -56,12 +50,10 @@
private UserHandle mMyUser;
private Context mContext;
- private ContentResolver mResolver;
public TestWorkspaceBuilder(Context context) {
mMyUser = Process.myUserHandle();
mContext = context;
- mResolver = mContext.getContentResolver();
}
/**
diff --git a/tests/src/com/android/launcher3/celllayout/board/WidgetRect.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/WidgetRect.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/WidgetRect.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/WidgetRect.java
diff --git a/tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java b/tests/multivalentTests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java
rename to tests/multivalentTests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java b/tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
similarity index 100%
rename from tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
rename to tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java b/tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java
similarity index 100%
rename from tests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java
rename to tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetNoConfig.java b/tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetNoConfig.java
similarity index 100%
rename from tests/src/com/android/launcher3/testcomponent/AppWidgetNoConfig.java
rename to tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetNoConfig.java
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetWithConfig.java b/tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetWithConfig.java
similarity index 100%
rename from tests/src/com/android/launcher3/testcomponent/AppWidgetWithConfig.java
rename to tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetWithConfig.java
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/multivalentTests/src/com/android/launcher3/ui/TestViewHelpers.java
similarity index 100%
rename from tests/src/com/android/launcher3/ui/TestViewHelpers.java
rename to tests/multivalentTests/src/com/android/launcher3/ui/TestViewHelpers.java
diff --git a/tests/src/com/android/launcher3/util/ModelTestExtensions.kt b/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
similarity index 100%
rename from tests/src/com/android/launcher3/util/ModelTestExtensions.kt
rename to tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
diff --git a/tests/src/com/android/launcher3/UtilitiesKtTest.kt b/tests/src/com/android/launcher3/UtilitiesKtTest.kt
new file mode 100644
index 0000000..dd83871
--- /dev/null
+++ b/tests/src/com/android/launcher3/UtilitiesKtTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 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
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER
+import com.android.launcher3.UtilitiesKt.CLIP_TO_PADDING_FALSE_MODIFIER
+import com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree
+import com.android.launcher3.UtilitiesKt.restoreAttributesOnViewTree
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.views.WidgetsEduView
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UtilitiesKtTest {
+
+ val context: Context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+
+ private lateinit var rootView: WidgetsEduView
+ private lateinit var midView: ViewGroup
+ private lateinit var childView: View
+ @Before
+ fun setup() {
+ rootView =
+ LayoutInflater.from(context).inflate(R.layout.widgets_edu, null) as WidgetsEduView
+ midView = rootView.requireViewById(R.id.edu_view)
+ childView = rootView.requireViewById(R.id.edu_header)
+ }
+
+ @Test
+ fun set_clipChildren_false() {
+ assertThat(rootView.clipChildren).isTrue()
+ assertThat(midView.clipChildren).isTrue()
+
+ modifyAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+
+ assertThat(rootView.clipChildren).isFalse()
+ assertThat(midView.clipChildren).isFalse()
+ }
+
+ @Test
+ fun restore_clipChildren_true() {
+ assertThat(rootView.clipChildren).isTrue()
+ assertThat(midView.clipChildren).isTrue()
+ modifyAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+ assertThat(rootView.clipChildren).isFalse()
+ assertThat(midView.clipChildren).isFalse()
+
+ restoreAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+
+ assertThat(rootView.clipChildren).isTrue()
+ assertThat(midView.clipChildren).isTrue()
+ }
+
+ @Test
+ fun restore_clipChildren_skipRestoreMidView() {
+ assertThat(rootView.clipChildren).isTrue()
+ assertThat(midView.clipChildren).isTrue()
+ rootView.clipChildren = false
+ modifyAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+ assertThat(rootView.clipChildren).isFalse()
+ assertThat(midView.clipChildren).isFalse()
+
+ restoreAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+
+ assertThat(rootView.clipChildren).isFalse()
+ assertThat(midView.clipChildren).isTrue()
+ }
+
+ @Test
+ fun set_clipToPadding_false() {
+ assertThat(rootView.clipToPadding).isTrue()
+ assertThat(midView.clipToPadding).isTrue()
+
+ modifyAttributesOnViewTree(childView, rootView, CLIP_TO_PADDING_FALSE_MODIFIER)
+
+ assertThat(rootView.clipToPadding).isFalse()
+ assertThat(midView.clipToPadding).isFalse()
+ }
+
+ @Test
+ fun restore_clipToPadding_true() {
+ assertThat(rootView.clipToPadding).isTrue()
+ assertThat(midView.clipToPadding).isTrue()
+ modifyAttributesOnViewTree(childView, rootView, CLIP_TO_PADDING_FALSE_MODIFIER)
+ assertThat(rootView.clipToPadding).isFalse()
+ assertThat(midView.clipToPadding).isFalse()
+
+ restoreAttributesOnViewTree(childView, rootView, CLIP_TO_PADDING_FALSE_MODIFIER)
+
+ assertThat(rootView.clipToPadding).isTrue()
+ assertThat(midView.clipToPadding).isTrue()
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java b/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java
index aca9765..1b03645 100644
--- a/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java
+++ b/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java
@@ -39,6 +39,7 @@
import com.android.launcher3.tapl.WidgetResizeFrame;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.util.ModelTestExtensions;
+import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.After;
@@ -236,6 +237,7 @@
}
@Test
+ @ScreenRecordRule.ScreenRecord // b/330019521
public void simpleReorder() throws Exception {
runTestCaseMap(getTestMap("ReorderWidgets/simple_reorder_case"),
"push_reorder_case");
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index a1ff346..972be80 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -56,6 +56,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
import com.android.launcher3.tapl.HomeAllApps;
import com.android.launcher3.tapl.HomeAppIcon;
import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -589,6 +590,17 @@
false /* newTask */);
}
+ /** Starts ExcludeFromRecentsTestActivity, which has excludeFromRecents="true". */
+ public static void startExcludeFromRecentsTestActivity() {
+ final String packageName = getAppPackageName();
+ final Intent intent = getInstrumentation().getContext().getPackageManager()
+ .getLaunchIntentForPackage(packageName);
+ intent.setComponent(new ComponentName(packageName,
+ "com.android.launcher3.testcomponent.ExcludeFromRecentsTestActivity"));
+ startIntent(intent, By.pkg(packageName).text("ExcludeFromRecentsTestActivity"),
+ false /* newTask */);
+ }
+
private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
if (newTask) {
@@ -674,4 +686,12 @@
}
return homeAppIcon;
}
+
+ protected void commitTransactionAndLoadHome(FavoriteItemsTransaction transaction) {
+ transaction.commit();
+
+ // Launch the home activity
+ UiDevice.getInstance(getInstrumentation()).pressHome();
+ mLauncher.waitForLauncherInitialized();
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
index 7aa26a1..1db6014 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
@@ -87,7 +87,7 @@
* @param acceptConfig accept the config activity
*/
private void runTest(boolean acceptConfig) throws Throwable {
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
// Drag widget to homescreen
WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 5ca5ba5..2c065f9 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -56,7 +56,7 @@
@ScreenRecord // b/316910614
public void testDragIcon() throws Throwable {
mLauncher.enableDebugTracing(); // b/289161193
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
@@ -92,7 +92,7 @@
// TODO(b/322820039): Enable test for tablets - the picker UI has changed and test needs to
// be updated to look for appropriate UI elements.
Assume.assumeFalse(mLauncher.isTablet());
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
mLauncher.getWorkspace().openAllWidgets()
.getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
@@ -108,7 +108,7 @@
@Test
@ScreenRecord // b/316910614
public void testResizeWidget() throws Throwable {
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
index 6aa746d..e69cb4c 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
@@ -251,22 +251,20 @@
private void addPendingItemToScreen(LauncherAppWidgetInfo item, int restoreStatus) {
item.restoreStatus = restoreStatus;
item.screenId = FIRST_SCREEN_ID;
- new FavoriteItemsTransaction(mTargetContext)
- .addItem(() -> item)
- .commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(
+ new FavoriteItemsTransaction(mTargetContext).addItem(() -> item));
}
private LauncherAppWidgetProviderInfo addWidgetToScreen(boolean hasConfigureScreen,
boolean bindWidget, Consumer<LauncherAppWidgetInfo> itemOverride) {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(hasConfigureScreen);
- new FavoriteItemsTransaction(mTargetContext)
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext)
.addItem(() -> {
LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, bindWidget);
item.screenId = FIRST_SCREEN_ID;
itemOverride.accept(item);
return item;
- })
- .commitAndLoadHome(mLauncher);
+ }));
return info;
}
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
index 7dba728..90a6d22 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
@@ -128,7 +128,7 @@
private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
Intent... commandIntents) throws Throwable {
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
// Open Pin item activity
BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index bfff541..0e523c3 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -2452,7 +2452,7 @@
int bottomBound = Math.min(
containerBounds.bottom,
getRealDisplaySize().y - systemGestureRegion.bottom);
- int y = (bottomBound - containerBounds.top) / 2;
+ int y = (bottomBound + containerBounds.top) / 2;
// Do not tap in the status bar.
y = Math.max(y, systemGestureRegion.top);
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 1cfbf09..99da5c3 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -25,6 +25,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
@@ -259,6 +260,23 @@
}
/**
+ * Returns whether the given String is contained in this Task's contentDescription. Also returns
+ * true if both Strings are null.
+ *
+ * TODO(b/326565120): remove Nullable support once the bug causing it to be null is fixed.
+ */
+ public boolean containsContentDescription(@Nullable String expected) {
+ String actual = mTask.getContentDescription();
+ if (actual == null && expected == null) {
+ return true;
+ }
+ if (actual == null || expected == null) {
+ return false;
+ }
+ return actual.contains(expected);
+ }
+
+ /**
* Enum used to specify which task is retrieved when it is a split task.
*/
public enum OverviewSplitTask {