Merge "Remove unnecesary AbstractLauncherUiTest.initialize whit two arguments" into main
diff --git a/Android.bp b/Android.bp
index eca0778..61042f6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -108,6 +108,7 @@
],
srcs: [
"tests/multivalentTests/tapl/**/*.java",
+ "tests/multivalentTests/tapl/**/*.kt",
],
resource_dirs: [],
manifest: "tests/multivalentTests/tapl/AndroidManifest.xml",
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index c13c68c..82ae4cb 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -92,6 +92,13 @@
}
flag {
+ name: "enable_predictive_back_gesture"
+ namespace: "launcher"
+ description: "Enable predictive back gesture on Launcher (including all apps and widget picker)."
+ bug: "238475505"
+}
+
+flag {
name: "enable_shortcut_dont_suggest_app"
namespace: "launcher"
description: "Enables don't suggest app shortcut for suggested apps"
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 823a86e..9d599c9 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -19,6 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/task"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
diff --git a/quickstep/res/layout/task_desktop.xml b/quickstep/res/layout/task_desktop.xml
index 60827cd..3cafcfd 100644
--- a/quickstep/res/layout/task_desktop.xml
+++ b/quickstep/res/layout/task_desktop.xml
@@ -19,6 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/task"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="true"
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index d20afd3..e91e773 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -24,6 +24,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/task"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 8a3ffb5..e45d9fd 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -53,5 +53,5 @@
<string name="setup_wizard_pkg" translatable="false" />
<!-- This is a float because it is converted to dp later in DeviceProfile -->
- <item name="taskbar_icon_size" type="dimen" format="float">48.4</item>
+ <item name="taskbar_icon_size" type="dimen" format="float">44</item>
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index b23c283..325c255 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -267,7 +267,7 @@
<string name="taskbar_edu_features">Do more with the Taskbar</string>
<!-- Title in dialog that shows a user how to pin the Taskbar. [CHAR_LIMIT 60] -->
<string name="taskbar_edu_pinning_title">Always show the Taskbar</string>
- <!-- Text in dialog that shows a user how to pin the Taskbar. [CHAR_LIMIT 60] -->
+ <!-- Text in dialog that shows a user how to pin the Taskbar. [CHAR_LIMIT 150] -->
<string name="taskbar_edu_pinning_standalone">To always show the Taskbar on the bottom of your screen, touch & hold the divider</string>
<!-- Text on button to exit a tutorial [CHAR_LIMIT=16] -->
<string name="taskbar_edu_close">Close</string>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index e8a3d3e..851f2b3 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -157,8 +157,8 @@
import com.android.quickstep.util.WorkspaceRevealAnim;
import com.android.quickstep.views.FloatingWidgetView;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.DelegateLaunchAnimatorController;
+import com.android.systemui.animation.ActivityTransitionAnimator;
+import com.android.systemui.animation.DelegateTransitionAnimatorController;
import com.android.systemui.animation.LaunchableView;
import com.android.systemui.animation.RemoteAnimationDelegate;
import com.android.systemui.shared.system.BlurUtils;
@@ -1677,7 +1677,7 @@
}
};
- if (fromPredictiveBack) {
+ if (fromPredictiveBack && rectFSpringAnim != null) {
rectFSpringAnim.addAnimatorListener(endListener);
} else {
anim.addListener(endListener);
@@ -1838,8 +1838,8 @@
// The CUJ is logged by the click handler, so we don't log it inside the animation
// library.
- ActivityLaunchAnimator.Controller controllerDelegate =
- ActivityLaunchAnimator.Controller.fromView(viewToUse, null /* cujType */);
+ ActivityTransitionAnimator.Controller controllerDelegate =
+ ActivityTransitionAnimator.Controller.fromView(viewToUse, null /* cujType */);
if (controllerDelegate == null) {
return null;
@@ -1847,15 +1847,15 @@
// This wrapper allows us to override the default value, telling the controller that the
// current window is below the animating window.
- ActivityLaunchAnimator.Controller controller =
- new DelegateLaunchAnimatorController(controllerDelegate) {
+ ActivityTransitionAnimator.Controller controller =
+ new DelegateTransitionAnimatorController(controllerDelegate) {
@Override
public boolean isBelowAnimatingWindow() {
return true;
}
};
- ActivityLaunchAnimator.Callback callback = task -> {
+ ActivityTransitionAnimator.Callback callback = task -> {
final int backgroundColor =
startingWindowListener.mBackgroundColor == Color.TRANSPARENT
? launcher.getScrimView().getBackgroundColor()
@@ -1863,15 +1863,17 @@
return ColorUtils.setAlphaComponent(backgroundColor, 255);
};
- ActivityLaunchAnimator.Listener listener = new ActivityLaunchAnimator.Listener() {
- @Override
- public void onLaunchAnimationEnd() {
- onEndCallback.executeAllAndDestroy();
- }
- };
+ ActivityTransitionAnimator.Listener listener =
+ new ActivityTransitionAnimator.Listener() {
+ @Override
+ public void onTransitionAnimationEnd() {
+ onEndCallback.executeAllAndDestroy();
+ }
+ };
return new ContainerAnimationRunner(
- new ActivityLaunchAnimator.AnimationDelegate(controller, callback, listener));
+ new ActivityTransitionAnimator.AnimationDelegate(
+ controller, callback, listener));
}
/**
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 6160378..f4cbf17 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -15,9 +15,11 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import android.app.prediction.AppTarget;
+import android.content.Context;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@@ -29,6 +31,7 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.picker.WidgetRecommendationCategoryProvider;
import java.util.ArrayList;
import java.util.List;
@@ -93,9 +96,21 @@
servicePredictedItems.addAll(localFilteredWidgets);
}
- List<ItemInfo> items = servicePredictedItems.stream()
- .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION))
- .collect(Collectors.toList());
+ List<ItemInfo> items;
+ if (enableCategorizedWidgetSuggestions()) {
+ Context context = appState.getContext();
+ WidgetRecommendationCategoryProvider categoryProvider =
+ WidgetRecommendationCategoryProvider.newInstance(context);
+ items = servicePredictedItems.stream()
+ .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
+ categoryProvider.getWidgetRecommendationCategory(context, it)))
+ .collect(Collectors.toList());
+ } else {
+ items = servicePredictedItems.stream()
+ .map(it -> new PendingAddWidgetInfo(it.widgetInfo,
+ CONTAINER_WIDGETS_PREDICTION)).collect(
+ Collectors.toList());
+ }
FixedContainerItems fixedContainerItems =
new FixedContainerItems(mPredictorState.containerId, items);
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index e1b6494..37e5309 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -253,6 +253,11 @@
return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
}
+ public void updateTaskbarLauncherStateGoingHome() {
+ mTaskbarLauncherStateController.updateStateForFlag(FLAG_VISIBLE, true);
+ mTaskbarLauncherStateController.applyState();
+ }
+
public boolean isDraggingItem() {
return mControllers.taskbarDragController.isDragging();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 9b8ab33..2f11fd7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -780,9 +780,11 @@
handleSetupUi();
// Hide back button in SUW if keyboard is showing (IME draws its own back).
- mPropertyHolders.add(new StatePropertyHolder(
- mBackButtonAlpha.get(ALPHA_INDEX_SUW),
- flags -> (flags & FLAG_IME_VISIBLE) == 0));
+ if (mIsImeRenderingNavButtons) {
+ mPropertyHolders.add(new StatePropertyHolder(
+ mBackButtonAlpha.get(ALPHA_INDEX_SUW),
+ flags -> (flags & FLAG_IME_VISIBLE) == 0));
+ }
} else if (isInKidsMode) {
int iconSize = res.getDimensionPixelSize(
R.dimen.taskbar_icon_size_kids);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 8e4a78f..9f6994a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -304,6 +304,10 @@
callbacks.addListener(mTaskBarRecentsAnimationListener);
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(() ->
mTaskBarRecentsAnimationListener.endGestureStateOverride(true));
+
+ ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchCancelledRunnable(() -> {
+ updateStateForUserFinishedToApp(false /* finishedToApp */);
+ });
return animatorSet;
}
@@ -770,21 +774,29 @@
mTaskBarRecentsAnimationListener = null;
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(null);
- // Update the visible state immediately to ensure a seamless handoff
- boolean launcherVisible = !finishedToApp;
- updateStateForFlag(FLAG_TRANSITION_TO_VISIBLE, false);
- updateStateForFlag(FLAG_VISIBLE, launcherVisible);
- applyState();
-
- TaskbarStashController controller = mControllers.taskbarStashController;
- if (DEBUG) {
- Log.d(TAG, "endGestureStateOverride - FLAG_IN_APP: " + finishedToApp);
- }
- controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
- controller.applyState();
+ updateStateForUserFinishedToApp(finishedToApp);
}
}
+ /**
+ * Updates the visible state immediately to ensure a seamless handoff.
+ * @param finishedToApp True iff user is in an app.
+ */
+ private void updateStateForUserFinishedToApp(boolean finishedToApp) {
+ // Update the visible state immediately to ensure a seamless handoff
+ boolean launcherVisible = !finishedToApp;
+ updateStateForFlag(FLAG_TRANSITION_TO_VISIBLE, false);
+ updateStateForFlag(FLAG_VISIBLE, launcherVisible);
+ applyState();
+
+ TaskbarStashController controller = mControllers.taskbarStashController;
+ if (DEBUG) {
+ Log.d(TAG, "endGestureStateOverride - FLAG_IN_APP: " + finishedToApp);
+ }
+ controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
+ controller.applyState();
+ }
+
private static String getStateString(int flags) {
StringJoiner result = new StringJoiner("|");
appendFlag(result, flags, FLAG_VISIBLE, "flag_visible");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index c883759..db7d0eb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -60,8 +60,10 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
@@ -951,6 +953,15 @@
if (mActivity.isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)) {
return false;
}
+
+ // Do not stash if hardware keyboard is attached, in 3 button nav and desktop windowing mode
+ DesktopVisibilityController visibilityController =
+ LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+ if (visibilityController != null && mActivity.isHardwareKeyboard()
+ && mActivity.isThreeButtonNav() && visibilityController.areFreeformTasksVisible()) {
+ return false;
+ }
+
return mIsImeShowing || mIsImeSwitcherShowing;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 964d329..5424fcf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar.allapps;
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.launcher3.Flags.enablePredictiveBackGesture;
import android.animation.Animator;
import android.content.Context;
@@ -168,7 +169,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mActivityContext.addOnDeviceProfileChangeListener(this);
- if (FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()) {
+ if (enablePredictiveBackGesture()) {
mAppsView.getAppsRecyclerViewContainer().setOutlineProvider(mViewOutlineProvider);
mAppsView.getAppsRecyclerViewContainer().setClipToOutline(true);
OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
@@ -183,7 +184,7 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mActivityContext.removeOnDeviceProfileChangeListener(this);
- if (FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()) {
+ if (enablePredictiveBackGesture()) {
mAppsView.getAppsRecyclerViewContainer().setOutlineProvider(null);
mAppsView.getAppsRecyclerViewContainer().setClipToOutline(false);
OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 5819bb3..6dc7db7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -71,6 +71,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.quickstep.SystemUiProxy;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.IBubblesListener;
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.common.bubbles.BubbleInfo;
@@ -101,8 +102,8 @@
*
* @see #onTaskbarRecreated()
*/
- private static boolean sBubbleBarEnabled =
- SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
+ private static boolean sBubbleBarEnabled = Flags.enableBubbleBar()
+ || SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
/** Whether showing bubbles in the launcher bubble bar is enabled. */
public static boolean isBubbleBarEnabled() {
@@ -111,8 +112,10 @@
/** Re-reads the value of the flag from SystemProperties when taskbar is recreated. */
public static void onTaskbarRecreated() {
- sBubbleBarEnabled = SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
+ sBubbleBarEnabled = Flags.enableBubbleBar()
+ || SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
}
+
private static final int MASK_HIDE_BUBBLE_BAR = SYSUI_STATE_BOUNCER_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 22f24f1..039c0a0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -28,10 +28,8 @@
import android.util.Pair;
import android.view.View;
import android.widget.RemoteViews;
-import android.widget.Toast;
import android.window.SplashScreen;
-import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
@@ -63,8 +61,7 @@
// Log metric
StatsLogManager.StatsLogger logger = mLauncher.getStatsLogManager().logger();
logger.log(LAUNCHER_SPLIT_WIDGET_ATTEMPT);
- Toast.makeText(hostView.getContext(), R.string.split_widgets_not_supported,
- Toast.LENGTH_SHORT).show();
+ mLauncher.handleIncorrectSplitTargetSelection();
return true;
}
Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(view);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 32d10b0..c2a248d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -36,6 +36,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
@@ -60,6 +61,7 @@
import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
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;
+import static com.android.launcher3.Flags.enablePredictiveBackGesture;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -644,7 +646,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (Utilities.ATLEAST_U && FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()) {
+ if (Utilities.ATLEAST_U && enablePredictiveBackGesture()) {
getApplicationInfo().setEnableOnBackInvokedCallback(true);
}
if (savedInstanceState != null) {
@@ -676,7 +678,7 @@
splitSelectSource.alreadyRunningTaskId = taskWasFound
? foundTask.key.id
: INVALID_TASK_ID;
- if (FeatureFlags.enableSplitContextually()) {
+ if (enableSplitContextually()) {
startSplitToHome(splitSelectSource);
} else {
recentsView.initiateSplitSelect(splitSelectSource);
@@ -761,7 +763,7 @@
super.onPause();
- if (FeatureFlags.enableSplitContextually()) {
+ if (enableSplitContextually()) {
// If Launcher pauses before both split apps are selected, exit split screen.
if (!mSplitSelectStateController.isBothSplitAppsConfirmed() &&
!mSplitSelectStateController.isLaunchingFirstAppFullscreen()) {
@@ -832,7 +834,7 @@
@Override
protected void registerBackDispatcher() {
- if (!FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()) {
+ if (!enablePredictiveBackGesture()) {
super.registerBackDispatcher();
return;
}
@@ -1352,6 +1354,15 @@
return (mTaskbarUIController != null && mTaskbarUIController.hasBubbles());
}
+ @Override
+ public boolean handleIncorrectSplitTargetSelection() {
+ if (!enableSplitContextually() || !mSplitSelectStateController.isSplitSelectActive()) {
+ return false;
+ }
+ mSplitSelectStateController.getSplitInstructionsView().goBoing();
+ return true;
+ }
+
private static final class LauncherTaskViewController extends
TaskViewTouchController<Launcher> {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index c961302..e6a115a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -69,6 +69,7 @@
super.setState(state);
if (state.overviewUi) {
mRecentsView.updateEmptyMessage();
+ } else {
mRecentsView.resetTaskVisuals();
}
setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig(), state);
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index e77a450..b42eb06 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -371,8 +371,8 @@
if (mSpringAnim != null) {
mSpringAnim.onTargetPositionChanged();
}
- mOnFinishCallback = data.getParcelable(EXTRA_ON_FINISH_CALLBACK);
}
+ mOnFinishCallback = data.getParcelable(EXTRA_ON_FINISH_CALLBACK);
maybeSendEndMessage();
} catch (Exception e) {
// Ignore
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index dffb882..8535a33 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -16,12 +16,15 @@
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
+import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.RecentsView;
+import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -37,6 +40,30 @@
public Bundle call(String method, String arg, @Nullable Bundle extras) {
final Bundle response = new Bundle();
switch (method) {
+ case TestProtocol.REQUEST_RECENT_TASKS_LIST: {
+ ArrayList<String> taskBaseIntentComponents = new ArrayList<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ RecentsModel.INSTANCE.get(mContext).getTasks((taskGroups) -> {
+ for (GroupTask group : taskGroups) {
+ taskBaseIntentComponents.add(
+ group.task1.key.baseIntent.getComponent().flattenToString());
+ if (group.task2 != null) {
+ taskBaseIntentComponents.add(
+ group.task2.key.baseIntent.getComponent().flattenToString());
+ }
+ }
+ latch.countDown();
+ });
+ try {
+ latch.await(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ response.putStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ taskBaseIntentComponents);
+ return response;
+ }
+
case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
final float swipeHeight =
LayoutUtils.getDefaultSwipeHeight(mContext, mDeviceProfile);
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 8ff43f0..c2cd11c 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -646,6 +646,18 @@
});
});
}
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ recentsView.onTaskLaunchedInLiveTileModeCancelled();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ recentsView.setTaskLaunchCancelledRunnable(null);
+ }
};
} else {
AnimatorPlaybackController controller =
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 5228420..b6b7d58 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -491,6 +491,7 @@
mDeviceState = new RecentsAnimationDeviceState(this, true);
mTaskbarManager = new TaskbarManager(this);
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
+ mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
BootAwarePreloader.start(this);
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
@@ -538,13 +539,13 @@
@UiThread
public void onUserUnlocked() {
+ Log.d(TAG, "onUserUnlocked: userId=" + getUserId());
mTaskAnimationManager = new TaskAnimationManager(this);
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
mOverviewCommandHelper = new OverviewCommandHelper(this,
mOverviewComponentObserver, mTaskAnimationManager);
mResetGestureInputConsumer = new ResetGestureInputConsumer(
mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
- mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
mInputConsumer.registerInputConsumer();
onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
onAssistantVisibilityChanged();
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index c8c7dc2..0f8ceba 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -154,6 +154,7 @@
mSquaredTouchSlop = mDeviceState.getSquaredTouchSlop();
mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
+ mStartDisplacement = continuingPreviousGesture ? 0 : -mTouchSlop;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
}
@@ -280,7 +281,7 @@
if (mGestureState.isTrackpadGesture() || Math.abs(displacement)
> mTouchSlop) {
mPassedWindowMoveSlop = true;
- mStartDisplacement = Math.min(displacement, -mTouchSlop);
+ mStartDisplacement = -mTouchSlop;
}
}
}
@@ -336,7 +337,7 @@
}
if (!mPassedWindowMoveSlop) {
mPassedWindowMoveSlop = true;
- mStartDisplacement = Math.min(displacement, -mTouchSlop);
+ mStartDisplacement = -mTouchSlop;
}
notifyGestureStarted(isLikelyToStartNewTask);
}
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.java
index e41832b..a7ca515 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.java
@@ -36,6 +36,7 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -533,34 +534,52 @@
int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
? splitBoundsConfig.dividerHeightPercent
: splitBoundsConfig.dividerWidthPercent));
- int primarySnapshotHeight;
- int primarySnapshotWidth;
- int secondarySnapshotHeight;
- int secondarySnapshotWidth;
- float taskPercent = splitBoundsConfig.appsStackedVertically ?
- splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
- primarySnapshotWidth = parentWidth;
- primarySnapshotHeight = (int) (totalThumbnailHeight * (taskPercent));
+ Pair<Point, Point> taskViewSizes =
+ getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight);
- secondarySnapshotWidth = parentWidth;
- secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar;
-
- int translationY = primarySnapshotHeight + spaceAboveSnapshot + dividerBar;
+ int translationY = taskViewSizes.first.y + spaceAboveSnapshot + dividerBar;
primarySnapshot.setTranslationY(spaceAboveSnapshot);
secondarySnapshot.setTranslationY(translationY - spaceAboveSnapshot);
primarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(primarySnapshotWidth, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(primarySnapshotHeight, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.x, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.y, View.MeasureSpec.EXACTLY)
);
secondarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(secondarySnapshotWidth, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(secondarySnapshotHeight, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.x, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.y, View.MeasureSpec.EXACTLY)
);
}
@Override
+ public Pair<Point, Point> getGroupedTaskViewSizes(
+ DeviceProfile dp,
+ SplitBounds splitBoundsConfig,
+ int parentWidth,
+ int parentHeight) {
+ int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
+ int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
+ int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.dividerHeightPercent
+ : splitBoundsConfig.dividerWidthPercent));
+ float taskPercent = splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.topTaskPercent
+ : splitBoundsConfig.leftTaskPercent;
+
+ Point firstTaskViewSize = new Point(
+ parentWidth,
+ (int) (totalThumbnailHeight * taskPercent)
+ );
+ Point secondTaskViewSize = new Point(
+ parentWidth,
+ totalThumbnailHeight - firstTaskViewSize.y - dividerBar
+ );
+
+ return new Pair<>(firstTaskViewSize, secondTaskViewSize);
+ }
+
+ @Override
public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
if (enableOverviewIconMenu()) {
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index 3d7065b..5d9a668 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -34,6 +34,7 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -530,25 +531,16 @@
float dividerScale = splitBoundsConfig.appsStackedVertically
? splitBoundsConfig.dividerHeightPercent
: splitBoundsConfig.dividerWidthPercent;
- int primarySnapshotHeight;
- int primarySnapshotWidth;
- int secondarySnapshotHeight;
- int secondarySnapshotWidth;
- float taskPercent = splitBoundsConfig.appsStackedVertically ?
- splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
+ Pair<Point, Point> taskViewSizes =
+ getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight);
if (dp.isLeftRightSplit) {
int scaledDividerBar = Math.round(parentWidth * dividerScale);
- primarySnapshotHeight = totalThumbnailHeight;
- primarySnapshotWidth = Math.round(parentWidth * taskPercent);
-
- secondarySnapshotHeight = totalThumbnailHeight;
- secondarySnapshotWidth = parentWidth - primarySnapshotWidth - scaledDividerBar;
if (isRtl) {
- int translationX = secondarySnapshotWidth + scaledDividerBar;
+ int translationX = taskViewSizes.second.x + scaledDividerBar;
primarySnapshot.setTranslationX(-translationX);
secondarySnapshot.setTranslationX(0);
} else {
- int translationX = primarySnapshotWidth + scaledDividerBar;
+ int translationX = taskViewSizes.first.x + scaledDividerBar;
secondarySnapshot.setTranslationX(translationX);
primarySnapshot.setTranslationX(0);
}
@@ -557,18 +549,8 @@
// Reset unused translations
primarySnapshot.setTranslationY(0);
} else {
- int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight;
- float scale = (float) totalThumbnailHeight / (dp.availableHeightPx - taskbarHeight);
- float topTaskHeight = dp.availableHeightPx * taskPercent;
float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale);
- float scaledTopTaskHeight = topTaskHeight * scale;
- primarySnapshotWidth = parentWidth;
- primarySnapshotHeight = Math.round(scaledTopTaskHeight);
-
- secondarySnapshotWidth = parentWidth;
- secondarySnapshotHeight = Math.round(totalThumbnailHeight - primarySnapshotHeight
- - finalDividerHeight);
- float translationY = primarySnapshotHeight + spaceAboveSnapshot + finalDividerHeight;
+ float translationY = taskViewSizes.first.y + spaceAboveSnapshot + finalDividerHeight;
secondarySnapshot.setTranslationY(translationY);
FrameLayout.LayoutParams primaryParams =
@@ -584,11 +566,11 @@
primarySnapshot.setTranslationX(0);
}
primarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(primarySnapshotWidth, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(primarySnapshotHeight, View.MeasureSpec.EXACTLY));
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.x, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.y, View.MeasureSpec.EXACTLY));
secondarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(secondarySnapshotWidth, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(secondarySnapshotHeight,
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.x, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.y,
View.MeasureSpec.EXACTLY));
primarySnapshot.setScaleX(1);
secondarySnapshot.setScaleX(1);
@@ -597,6 +579,48 @@
}
@Override
+ public Pair<Point, Point> getGroupedTaskViewSizes(
+ DeviceProfile dp,
+ SplitBounds splitBoundsConfig,
+ int parentWidth,
+ int parentHeight) {
+ int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
+ int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
+ float dividerScale = splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.dividerHeightPercent
+ : splitBoundsConfig.dividerWidthPercent;
+ float taskPercent = splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.topTaskPercent
+ : splitBoundsConfig.leftTaskPercent;
+
+ Point firstTaskViewSize = new Point();
+ Point secondTaskViewSize = new Point();
+
+ if (dp.isLeftRightSplit) {
+ int scaledDividerBar = Math.round(parentWidth * dividerScale);
+ firstTaskViewSize.x = Math.round(parentWidth * taskPercent);
+ firstTaskViewSize.y = totalThumbnailHeight;
+
+ secondTaskViewSize.x = parentWidth - firstTaskViewSize.x - scaledDividerBar;
+ secondTaskViewSize.y = totalThumbnailHeight;
+ } else {
+ int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight;
+ float scale = (float) totalThumbnailHeight / (dp.availableHeightPx - taskbarHeight);
+ float topTaskHeight = dp.availableHeightPx * taskPercent;
+ float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale);
+ float scaledTopTaskHeight = topTaskHeight * scale;
+ firstTaskViewSize.x = parentWidth;
+ firstTaskViewSize.y = Math.round(scaledTopTaskHeight);
+
+ secondTaskViewSize.x = parentWidth;
+ secondTaskViewSize.y = Math.round(totalThumbnailHeight - firstTaskViewSize.y
+ - finalDividerHeight);
+ }
+
+ return new Pair<>(firstTaskViewSize, secondTaskViewSize);
+ }
+
+ @Override
public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
if (enableOverviewIconMenu()) {
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.java b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.java
index 34756b4..9084297 100644
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.java
@@ -17,6 +17,7 @@
package com.android.quickstep.orientation;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -135,6 +136,15 @@
int parentWidth, int parentHeight,
SplitBounds splitBoundsConfig, DeviceProfile dp, boolean isRtl);
+ /**
+ * Creates two Points representing the dimensions of the two tasks in a GroupedTaskView
+ *
+ * @return first -> primary task snapshot, second -> secondary task snapshot.
+ * x -> width, y -> height
+ */
+ Pair<Point, Point> getGroupedTaskViewSizes(DeviceProfile dp, SplitBounds splitBoundsConfig,
+ int parentWidth, int parentHeight);
+
// Overview TaskMenuView methods
void setTaskIconParams(FrameLayout.LayoutParams iconParams,
int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl);
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.java
index 7e53cf1..f3001fc 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.java
@@ -31,6 +31,7 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.Pair;
@@ -342,31 +343,52 @@
int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
? splitBoundsConfig.dividerHeightPercent
: splitBoundsConfig.dividerWidthPercent));
- int primarySnapshotHeight;
- int primarySnapshotWidth;
- int secondarySnapshotHeight;
- int secondarySnapshotWidth;
- float taskPercent = splitBoundsConfig.appsStackedVertically ?
- splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
- primarySnapshotWidth = parentWidth;
- primarySnapshotHeight = (int) (totalThumbnailHeight * (taskPercent));
+ Pair<Point, Point> taskViewSizes =
+ getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight);
- secondarySnapshotWidth = parentWidth;
- secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar;
secondarySnapshot.setTranslationY(0);
- primarySnapshot.setTranslationY(secondarySnapshotHeight + spaceAboveSnapshot + dividerBar);
+ primarySnapshot.setTranslationY(taskViewSizes.second.y + spaceAboveSnapshot + dividerBar);
primarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(primarySnapshotWidth, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(primarySnapshotHeight, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.x, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.y, View.MeasureSpec.EXACTLY)
);
secondarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(secondarySnapshotWidth, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(secondarySnapshotHeight, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.x, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.y, View.MeasureSpec.EXACTLY)
);
}
+ @Override
+ public Pair<Point, Point> getGroupedTaskViewSizes(
+ DeviceProfile dp,
+ SplitBounds splitBoundsConfig,
+ int parentWidth,
+ int parentHeight) {
+ // Measure and layout the thumbnails bottom up, since the primary is on the visual left
+ // (portrait bottom) and secondary is on the right (portrait top)
+ int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
+ int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
+ int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.dividerHeightPercent
+ : splitBoundsConfig.dividerWidthPercent));
+ float taskPercent = splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.topTaskPercent
+ : splitBoundsConfig.leftTaskPercent;
+
+ Point firstTaskViewSize = new Point(
+ parentWidth,
+ (int) (totalThumbnailHeight * taskPercent)
+ );
+ Point secondTaskViewSize = new Point(
+ parentWidth,
+ totalThumbnailHeight - firstTaskViewSize.y - dividerBar
+ );
+
+ return new Pair<>(firstTaskViewSize, secondTaskViewSize);
+ }
+
/* ---------- The following are only used by TaskViewTouchHandler. ---------- */
@Override
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 1abedb7..555bf21 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -35,6 +35,7 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsAnimationCallbacks;
@@ -150,10 +151,29 @@
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- controller.finish(true /* toRecents */, null /* onFinishComplete */,
+ controller.finish(
+ true /* toRecents */,
+ () -> {
+ LauncherTaskbarUIController controller =
+ mLauncher.getTaskbarUIController();
+ if (controller != null) {
+ controller.updateTaskbarLauncherStateGoingHome();
+ }
+
+ },
false /* sendUserLeaveHint */);
}
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mLauncher.getDragLayer().removeView(floatingTaskView);
+ mController.getSplitAnimationController()
+ .removeSplitInstructionsView(mLauncher);
+ mController.resetState();
+ }
});
+ anim.add(mController.getSplitAnimationController()
+ .getShowSplitInstructionsAnim(mLauncher).buildAnim());
anim.buildAnim().start();
}
};
diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
index 348e4dc..9268511 100644
--- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.util.ArrayMap;
+import android.view.DisplayCutout;
import android.view.Surface;
import android.view.WindowManager;
import android.view.WindowMetrics;
@@ -74,4 +75,10 @@
}
return result;
}
+
+ @Override
+ protected DisplayCutout rotateCutout(DisplayCutout original, int startWidth, int startHeight,
+ int fromRotation, int toRotation) {
+ return original.getRotated(startWidth, startHeight, fromRotation, toRotation);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt b/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt
index 3a5873b..1c1e167 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt
+++ b/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt
@@ -115,7 +115,7 @@
}
override fun draw(canvas: Canvas) {
- if (launcher.deviceProfile.isLandscape) {
+ if (launcher.deviceProfile.isLeftRightSplit) {
drawLeftRightSplit(canvas)
} else {
drawTopBottomSplit(canvas)
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 2ae64ff..66c67e7 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -7,11 +7,14 @@
import static com.android.quickstep.util.SplitScreenUtils.convertLauncherSplitBoundsToShell;
import android.content.Context;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewStub;
import androidx.annotation.NonNull;
@@ -37,10 +40,11 @@
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
+import kotlin.Unit;
+
import java.util.HashMap;
import java.util.function.Consumer;
-import kotlin.Unit;
/**
* TaskView that contains and shows thumbnails for not one, BUT TWO(!!) tasks
@@ -374,18 +378,7 @@
}
if (!enableOverviewIconMenu()) {
updateIconPlacement();
- return;
}
-
- if (getRecentsView() == null) {
- return;
- }
-
- int iconMargins = getResources().getDimensionPixelSize(
- R.dimen.task_thumbnail_icon_menu_start_margin) * 2;
- ((IconAppChipView) mIconView).setMaxWidth(mSnapshotView.getMeasuredWidth() - iconMargins);
- ((IconAppChipView) mIconView2).setMaxWidth(mSnapshotView2.getMeasuredWidth() - iconMargins);
- setOrientationState(getRecentsView().getPagedViewOrientedState());
}
@Override
@@ -395,8 +388,25 @@
@Override
public void setOrientationState(RecentsOrientedState orientationState) {
- super.setOrientationState(orientationState);
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ if (enableOverviewIconMenu() && mSplitBoundsConfig != null) {
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ Pair<Point, Point> groupedTaskViewSizes =
+ orientationState.getOrientationHandler().getGroupedTaskViewSizes(
+ deviceProfile,
+ mSplitBoundsConfig,
+ layoutParams.width,
+ layoutParams.height
+ );
+ int iconMargins = getResources().getDimensionPixelSize(
+ R.dimen.task_thumbnail_icon_menu_start_margin) * 2;
+ ((IconAppChipView) mIconView).setMaxWidth(groupedTaskViewSizes.first.x - iconMargins);
+ ((IconAppChipView) mIconView2).setMaxWidth(groupedTaskViewSizes.second.x - iconMargins);
+ }
+ // setMaxWidth() needs to be called before mIconView.setIconOrientation which is called in
+ // the super below.
+ super.setOrientationState(orientationState);
+
boolean isGridTask = deviceProfile.isTablet && !isFocusedTask();
mIconView2.setIconOrientation(orientationState, isGridTask);
updateIconPlacement();
@@ -412,10 +422,27 @@
int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- getPagedOrientationHandler().setSplitIconParams(mIconView.asView(), mIconView2.asView(),
- taskIconHeight, mSnapshotView.getMeasuredWidth(), mSnapshotView.getMeasuredHeight(),
- getMeasuredHeight(), getMeasuredWidth(), isRtl, deviceProfile,
- mSplitBoundsConfig);
+ if (enableOverviewIconMenu()) {
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ Pair<Point, Point> groupedTaskViewSizes =
+ getPagedOrientationHandler()
+ .getGroupedTaskViewSizes(
+ deviceProfile,
+ mSplitBoundsConfig,
+ layoutParams.width,
+ layoutParams.height
+ );
+
+ getPagedOrientationHandler().setSplitIconParams(mIconView.asView(), mIconView2.asView(),
+ taskIconHeight, groupedTaskViewSizes.first.x, groupedTaskViewSizes.first.y,
+ getLayoutParams().height, getLayoutParams().width, isRtl, deviceProfile,
+ mSplitBoundsConfig);
+ } else {
+ getPagedOrientationHandler().setSplitIconParams(mIconView.asView(), mIconView2.asView(),
+ taskIconHeight, mSnapshotView.getMeasuredWidth(),
+ mSnapshotView.getMeasuredHeight(), getMeasuredHeight(), getMeasuredWidth(),
+ isRtl, deviceProfile, mSplitBoundsConfig);
+ }
}
private void updateSecondaryDwbPlacement() {
diff --git a/quickstep/src/com/android/quickstep/views/IconAppChipView.java b/quickstep/src/com/android/quickstep/views/IconAppChipView.java
index 0a261ef..3347665 100644
--- a/quickstep/src/com/android/quickstep/views/IconAppChipView.java
+++ b/quickstep/src/com/android/quickstep/views/IconAppChipView.java
@@ -52,9 +52,10 @@
private static final int MENU_BACKGROUND_REVEAL_DURATION = 417;
private static final int MENU_BACKGROUND_HIDE_DURATION = 333;
- private static final int NUM_ALPHA_CHANNELS = 2;
+ private static final int NUM_ALPHA_CHANNELS = 3;
private static final int INDEX_CONTENT_ALPHA = 0;
private static final int INDEX_COLOR_FILTER_ALPHA = 1;
+ private static final int INDEX_MODAL_ALPHA = 2;
private final MultiValueAlpha mMultiValueAlpha;
@@ -307,6 +308,11 @@
}
@Override
+ public void setModalAlpha(float alpha) {
+ mMultiValueAlpha.get(INDEX_MODAL_ALPHA).setValue(alpha);
+ }
+
+ @Override
public int getDrawableWidth() {
return mIconView == null ? 0 : mIconView.getDrawableWidth();
}
diff --git a/quickstep/src/com/android/quickstep/views/IconView.java b/quickstep/src/com/android/quickstep/views/IconView.java
index 042f581..4d33fda 100644
--- a/quickstep/src/com/android/quickstep/views/IconView.java
+++ b/quickstep/src/com/android/quickstep/views/IconView.java
@@ -149,6 +149,11 @@
}
@Override
+ public void setModalAlpha(float alpha) {
+ setAlpha(alpha);
+ }
+
+ @Override
public void setAlpha(float alpha) {
super.setAlpha(alpha);
if (alpha > 0) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index d10541a..9884d8d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -759,6 +759,9 @@
private RunnableList mSideTaskLaunchCallback;
@Nullable
private TaskLaunchListener mTaskLaunchListener;
+ @Nullable
+ private Runnable mOnTaskLaunchCancelledRunnable;
+
// keeps track of the state of the filter for tasks in recents view
private final RecentsFilterState mFilterState = new RecentsFilterState();
@@ -1195,6 +1198,21 @@
}
}
+ /**
+ * This is a one-time callback when touching in live tile mode. It's reset to null right
+ * after it's called.
+ */
+ public void setTaskLaunchCancelledRunnable(Runnable onTaskLaunchCancelledRunnable) {
+ mOnTaskLaunchCancelledRunnable = onTaskLaunchCancelledRunnable;
+ }
+
+ public void onTaskLaunchedInLiveTileModeCancelled() {
+ if (mOnTaskLaunchCancelledRunnable != null) {
+ mOnTaskLaunchCancelledRunnable.run();
+ mOnTaskLaunchCancelledRunnable = null;
+ }
+ }
+
private void executeSideTaskLaunchCallback() {
if (mSideTaskLaunchCallback != null) {
mSideTaskLaunchCallback.executeAllAndDestroy();
@@ -1811,9 +1829,7 @@
mFocusedTaskViewId = newFocusedTaskView != null && !enableGridOnlyOverview()
? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID;
updateTaskSize();
- if (newFocusedTaskView != null) {
- newFocusedTaskView.setOrientationState(mOrientationState);
- }
+ updateChildTaskOrientations();
TaskView newRunningTaskView = null;
if (hasAnyValidTaskIds(runningTaskId)) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index b971f67..5057c38 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -632,7 +632,7 @@
return;
}
mModalness = modalness;
- mIconView.setContentAlpha(1 - modalness);
+ mIconView.setModalAlpha(1 - modalness);
mDigitalWellBeingToast.updateBannerOffset(modalness);
}
@@ -1720,12 +1720,11 @@
int expectedWidth;
int expectedHeight;
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ final int thumbnailPadding = deviceProfile.overviewTaskThumbnailTopMarginPx;
+ final Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
+ final int taskWidth = lastComputedTaskSize.width();
+ final int taskHeight = lastComputedTaskSize.height();
if (deviceProfile.isTablet) {
- final int thumbnailPadding = deviceProfile.overviewTaskThumbnailTopMarginPx;
- final Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
- final int taskWidth = lastComputedTaskSize.width();
- final int taskHeight = lastComputedTaskSize.height();
-
int boxWidth;
int boxHeight;
boolean isFocusedTask = isFocusedTask();
@@ -1758,8 +1757,10 @@
} else {
nonGridScale = 1f;
boxTranslationY = 0f;
- expectedWidth = ViewGroup.LayoutParams.MATCH_PARENT;
- expectedHeight = ViewGroup.LayoutParams.MATCH_PARENT;
+ expectedWidth = enableOverviewIconMenu() ? taskWidth : LayoutParams.MATCH_PARENT;
+ expectedHeight = enableOverviewIconMenu()
+ ? taskHeight + thumbnailPadding
+ : LayoutParams.MATCH_PARENT;
}
setNonGridScale(nonGridScale);
diff --git a/quickstep/src/com/android/quickstep/views/TaskViewIcon.java b/quickstep/src/com/android/quickstep/views/TaskViewIcon.java
index 4e82725..94739cb 100644
--- a/quickstep/src/com/android/quickstep/views/TaskViewIcon.java
+++ b/quickstep/src/com/android/quickstep/views/TaskViewIcon.java
@@ -43,6 +43,11 @@
void setContentAlpha(float alpha);
/**
+ * Sets the opacity of the view for modal state.
+ */
+ void setModalAlpha(float alpha);
+
+ /**
* Returns this icon view's drawable.
*/
@Nullable Drawable getDrawable();
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index b12d98b..37dde10 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -26,6 +26,8 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -36,11 +38,14 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.text.TextUtils;
+import androidx.test.core.content.pm.ApplicationInfoBuilder;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.Flags;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
import com.android.launcher3.util.LauncherLayoutBuilder;
@@ -50,6 +55,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,6 +67,9 @@
@RunWith(AndroidJUnit4.class)
public final class WidgetsPredicationUpdateTaskTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private AppWidgetProviderInfo mApp1Provider1;
private AppWidgetProviderInfo mApp1Provider2;
private AppWidgetProviderInfo mApp2Provider1;
@@ -75,6 +84,7 @@
@Before
public void setup() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_CATEGORIZED_WIDGET_SUGGESTIONS);
mModelHelper = new LauncherModelHelper();
mUserHandle = myUserHandle();
@@ -93,6 +103,12 @@
allWidgets = Arrays.asList(mApp1Provider1, mApp1Provider2, mApp2Provider1,
mApp4Provider1, mApp4Provider2, mApp5Provider1);
+ doAnswer(i -> {
+ String pkg = i.getArgument(0);
+ return ApplicationInfoBuilder.newBuilder().setPackageName(pkg).setName(
+ "App " + pkg).build();
+ }).when(mModelHelper.sandboxContext.getPackageManager())
+ .getApplicationInfo(anyString(), anyInt());
AppWidgetManager manager = mModelHelper.sandboxContext.spyService(AppWidgetManager.class);
doReturn(allWidgets).when(manager).getInstalledProviders();
doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
@@ -140,12 +156,16 @@
// 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
// excluded from the result.
// 2. app3 doesn't have a widget.
- // 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
+ // 3. only 1 widget is picked from app1 because we only want to promote one widget
+ // per app.
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
.stream()
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
.collect(Collectors.toList());
assertThat(recommendedWidgets).hasSize(2);
+ recommendedWidgets.forEach(pendingAddWidgetInfo ->
+ assertThat(pendingAddWidgetInfo.recommendationCategory).isNotNull()
+ );
assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
});
@@ -179,6 +199,9 @@
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
.collect(Collectors.toList());
assertThat(recommendedWidgets).hasSize(2);
+ recommendedWidgets.forEach(pendingAddWidgetInfo ->
+ assertThat(pendingAddWidgetInfo.recommendationCategory).isNotNull()
+ );
// Another widget from the same package
assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
@@ -192,7 +215,7 @@
}
private WidgetsPredictionUpdateTask newWidgetsPredicationTask(List<AppTarget> appTargets) {
- return new WidgetsPredictionUpdateTask(
+ return new WidgetsPredictionUpdateTask(
new PredictorState(CONTAINER_WIDGETS_PREDICTION, "test_widgets_prediction"),
appTargets);
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
index 092a623..7c1b7f3 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.platform.test.annotations.PlatinumTest;
+import com.android.launcher3.tapl.OverviewTask.OverviewSplitTask;
import com.android.launcher3.tapl.OverviewTaskMenu;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.util.rule.TestStabilityRule;
@@ -85,7 +86,8 @@
taskMenu.touchOutsideTaskMenuToDismiss();
OverviewTaskMenu splitMenu =
- mLauncher.goHome().switchToOverview().getCurrentTask().tapSplitTaskMenu();
+ mLauncher.goHome().switchToOverview().getCurrentTask().tapMenu(
+ OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT);
assertTrue("App info item not appearing in expanded split task's menu.",
splitMenu.hasMenuItem("App info"));
splitMenu.touchOutsideTaskMenuToDismiss();
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java
index 36c591e..a050464 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java
@@ -22,6 +22,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.tapl.KeyboardQuickSwitch;
+import com.android.launcher3.tapl.LaunchedAppState;
+import com.android.launcher3.tapl.Taskbar;
import com.android.launcher3.taskbar.KeyboardQuickSwitchController;
import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -36,6 +38,7 @@
private enum TestSurface {
HOME(true),
LAUNCHED_APP(false),
+ TASKBAR_ALL_APPS(false),
HOME_ALL_APPS(true),
WIDGETS(true);
@@ -82,6 +85,11 @@
}
@Test
+ public void testDismiss_fromTaskbarAllApps() {
+ runTest(TestSurface.TASKBAR_ALL_APPS, TestCase.DISMISS);
+ }
+
+ @Test
public void testDismiss_fromHomeAllApps() {
runTest(TestSurface.HOME_ALL_APPS, TestCase.DISMISS);
}
@@ -102,6 +110,11 @@
}
@Test
+ public void testLaunchLastTask_fromTaskbarAllApps() {
+ runTest(TestSurface.TASKBAR_ALL_APPS, TestCase.LAUNCH_LAST_APP);
+ }
+
+ @Test
public void testLaunchLastTask_fromHomeAllApps() {
runTest(TestSurface.HOME_ALL_APPS, TestCase.LAUNCH_LAST_APP);
}
@@ -122,6 +135,11 @@
}
@Test
+ public void testLaunchSelectedTask_fromTaskbarAllApps() {
+ runTest(TestSurface.TASKBAR_ALL_APPS, TestCase.LAUNCH_SELECTED_APP);
+ }
+
+ @Test
public void testLaunchSelectedTask_fromHomeAllApps() {
runTest(TestSurface.HOME_ALL_APPS, TestCase.LAUNCH_SELECTED_APP);
}
@@ -142,6 +160,11 @@
}
@Test
+ public void testLaunchOverviewTask_fromTaskbarAllApps() {
+ runTest(TestSurface.TASKBAR_ALL_APPS, TestCase.LAUNCH_OVERVIEW);
+ }
+
+ @Test
public void testLaunchOverviewTask_fromHomeAllApps() {
runTest(TestSurface.HOME_ALL_APPS, TestCase.LAUNCH_OVERVIEW);
}
@@ -165,6 +188,12 @@
mLauncher.setIgnoreTaskbarVisibility(true);
kqs = mLauncher.getLaunchedAppState().showQuickSwitchView();
break;
+ case TASKBAR_ALL_APPS:
+ LaunchedAppState launchedApp = mLauncher.getLaunchedAppState();
+ Taskbar taskbar = mLauncher.isTransientTaskbar()
+ ? launchedApp.swipeUpToUnstashTaskbar() : launchedApp.getTaskbar();
+ kqs = taskbar.openAllApps().showQuickSwitchView();
+ break;
case HOME_ALL_APPS:
kqs = mLauncher.goHome().switchToAllApps().showQuickSwitchView();
break;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index fcb8320..360d1a7 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -35,9 +35,9 @@
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.Until;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.tapl.LaunchedAppState;
import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
import com.android.launcher3.tapl.Overview;
@@ -394,7 +394,7 @@
READ_DEVICE_CONFIG_PERMISSION);
// Debug if we need to goHome to prevent wrong previous state b/315525621
mLauncher.goHome();
- assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get());
+ assumeFalse(Flags.enablePredictiveBackGesture());
mLauncher.getWorkspace().switchToAllApps().pressBackToWorkspace();
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index 1e33635..b7546c7 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
+import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
@@ -40,6 +41,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -103,10 +105,18 @@
.getSplitScreenMenuItem()
.click();
- mLauncher.getLaunchedAppState()
- .getTaskbar()
- .getAppIcon(CALCULATOR_APP_NAME)
- .launchIntoSplitScreen();
+ if (enableSplitContextually()) {
+ // We're staying in all apps, use same instance
+ mLauncher.getAllApps()
+ .getAppIcon(CALCULATOR_APP_NAME)
+ .launchIntoSplitScreen();
+ } else {
+ // We're in overview, use taskbar instance
+ mLauncher.getLaunchedAppState()
+ .getTaskbar()
+ .getAppIcon(CALCULATOR_APP_NAME)
+ .launchIntoSplitScreen();
+ }
}
@Test
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
index 3465f23..b0e91e4 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
@@ -28,7 +28,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.Flags;
import com.android.launcher3.tapl.LauncherInstrumentation.TrackpadGestureType;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -77,7 +77,7 @@
@NavigationModeSwitch(mode = ZERO_BUTTON)
public void pressBack() throws Exception {
assumeTrue(mLauncher.isTablet());
- assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get());
+ assumeFalse(Flags.enablePredictiveBackGesture());
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
try {
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index b365173..9fa4b79 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -163,8 +163,7 @@
helper.sandboxContext.allow(SystemUiProxy.INSTANCE);
int rotation = mDisplaySize.x > mDisplaySize.y
? Surface.ROTATION_90 : Surface.ROTATION_0;
- CachedDisplayInfo cdi =
- new CachedDisplayInfo(mDisplaySize, rotation, new Rect());
+ CachedDisplayInfo cdi = new CachedDisplayInfo(mDisplaySize, rotation);
WindowBounds wm = new WindowBounds(
new Rect(0, 0, mDisplaySize.x, mDisplaySize.y),
mDisplayInsets);
@@ -186,7 +185,7 @@
ArrayMap<CachedDisplayInfo, List<WindowBounds>> perDisplayBoundsCache =
new ArrayMap<>();
- perDisplayBoundsCache.put(cdi.normalize(), allBounds);
+ perDisplayBoundsCache.put(cdi.normalize(wmProxy), allBounds);
Configuration configuration = new Configuration();
configuration.densityDpi = mDensityDpi;
diff --git a/res/drawable/ic_encrypted_with_background.xml b/res/drawable/ic_private_space_with_background.xml
similarity index 65%
rename from res/drawable/ic_encrypted_with_background.xml
rename to res/drawable/ic_private_space_with_background.xml
index c439b55..59a33dd 100644
--- a/res/drawable/ic_encrypted_with_background.xml
+++ b/res/drawable/ic_private_space_with_background.xml
@@ -21,6 +21,9 @@
android:pathData="M48 24A24 24 0 0 1 0 24A24 24 0 0 1 48 24Z"
android:fillColor="?attr/materialColorOutlineVariant" />
<path
- android:pathData="M24.0002 10.667L13.3335 14.667V22.787C13.3335 29.5203 17.8802 35.8003 24.0002 37.3337C30.1202 35.8003 34.6668 29.5203 34.6668 22.787V14.667L24.0002 10.667ZM32.0002 22.787C32.0002 28.1203 28.6002 33.0537 24.0002 34.5603C19.4002 33.0537 16.0002 28.1337 16.0002 22.787V16.5203L24.0002 13.5203L32.0002 16.5203V22.787ZM25.2402 23.6937L26.0002 28.0003H22.0002L22.7602 23.6937C21.9068 23.2537 21.3335 22.3603 21.3335 21.3337C21.3335 19.867 22.5335 18.667 24.0002 18.667C25.4668 18.667 26.6668 19.867 26.6668 21.3337C26.6668 22.3603 26.0935 23.2537 25.2402 23.6937Z"
+ android:pathData="M33.3333 14.6667V33.3333H14.6667V14.6667H33.3333ZM33.3333 12H14.6667C13.2 12 12 13.2 12 14.6667V33.3333C12 34.8 13.2 36 14.6667 36H33.3333C34.8 36 36 34.8 36 33.3333V14.6667C36 13.2 34.8 12 33.3333 12Z"
+ android:fillColor="?attr/materialColorOnSurface" />
+ <path
+ android:pathData="M25.2397 24.3597L25.9997 28.6663H21.9997L22.7597 24.3597C21.9063 23.9197 21.333 23.0263 21.333 21.9997C21.333 20.533 22.533 19.333 23.9997 19.333C25.4663 19.333 26.6663 20.533 26.6663 21.9997C26.6663 23.0263 26.093 23.9197 25.2397 24.3597Z"
android:fillColor="?attr/materialColorOnSurface" />
</vector>
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index 47bf9e7..009359c 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -45,7 +45,7 @@
android:visibility="gone"
android:textSize="18sp"
android:layout_below="@id/search_and_recommendations_container"
- tools:text="No widgets available" />
+ tools:text="@string/no_widgets_available" />
<!-- Fast scroller popup -->
<TextView
diff --git a/res/layout/widgets_list_row_header_two_pane.xml b/res/layout/widgets_list_row_header_two_pane.xml
index c0a6ea8..bdb2aed 100644
--- a/res/layout/widgets_list_row_header_two_pane.xml
+++ b/res/layout/widgets_list_row_header_two_pane.xml
@@ -23,6 +23,7 @@
android:importantForAccessibility="yes"
android:focusable="true"
launcher:appIconSize="48dp"
+ launcher:collapsable="false"
android:descendantFocusability="afterDescendants"
android:background="@drawable/bg_widgets_header_two_pane" >
diff --git a/res/layout/widgets_two_pane_sheet.xml b/res/layout/widgets_two_pane_sheet.xml
index 01c1b10..cd7f2e1 100644
--- a/res/layout/widgets_two_pane_sheet.xml
+++ b/res/layout/widgets_two_pane_sheet.xml
@@ -47,6 +47,16 @@
android:textColor="?attr/widgetPickerTitleColor"
android:textSize="24sp" />
+ <TextView
+ android:id="@+id/no_widgets_text"
+ style="@style/PrimaryHeadline"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textSize="18sp"
+ android:visibility="gone"
+ tools:text="@string/no_widgets_available" />
+
<LinearLayout
android:id="@+id/linear_layout_container"
android:layout_width="match_parent"
@@ -57,6 +67,9 @@
android:id="@+id/recycler_view_container"
android:layout_width="0dp"
android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingBottom="24dp"
android:layout_gravity="start"
android:layout_weight="0.33">
<TextView
@@ -90,15 +103,6 @@
android:gravity="end"
android:layout_gravity="end"
android:orientation="horizontal">
- <TextView
- android:id="@+id/no_widgets_text"
- style="@style/PrimaryHeadline"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:textSize="18sp"
- android:visibility="gone"
- tools:text="No widgets available" />
<ScrollView
android:id="@+id/right_pane_scroll_view"
android:layout_width="match_parent"
diff --git a/res/layout/widgets_two_pane_sheet_paged_view.xml b/res/layout/widgets_two_pane_sheet_paged_view.xml
index 4a7749b..887efb8 100644
--- a/res/layout/widgets_two_pane_sheet_paged_view.xml
+++ b/res/layout/widgets_two_pane_sheet_paged_view.xml
@@ -22,6 +22,8 @@
android:gravity="start"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:layout_gravity="start"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_alignParentStart="true">
<com.android.launcher3.widget.picker.WidgetPagedView
android:id="@+id/widgets_view_pager"
diff --git a/res/layout/widgets_two_pane_sheet_recyclerview.xml b/res/layout/widgets_two_pane_sheet_recyclerview.xml
index 8b48abb..f3d3b16 100644
--- a/res/layout/widgets_two_pane_sheet_recyclerview.xml
+++ b/res/layout/widgets_two_pane_sheet_recyclerview.xml
@@ -21,6 +21,7 @@
android:layout_height="match_parent"
android:gravity="start"
android:layout_gravity="start"
+ android:clipChildren="false"
android:layout_alignParentStart="true">
<com.android.launcher3.widget.picker.WidgetsRecyclerView
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 754f0cb..4a0b5e8 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -582,6 +582,7 @@
<declare-styleable name="WidgetsListRowHeader">
<attr name="appIconSize" format="dimension" />
+ <attr name="collapsable" format="boolean" />
</declare-styleable>
<attr name="materialColorOnSecondaryFixedVariant" format="color" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 7312164..3aa4a77 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -196,6 +196,8 @@
<dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
<dimen name="widget_list_entry_spacing">2dp</dimen>
<dimen name="widget_list_horizontal_margin">16dp</dimen>
+ <!-- Margin applied to the recycler view with search bar & the list of widget apps below it. -->
+ <dimen name="widget_list_left_pane_horizontal_margin">0dp</dimen>
<dimen name="widget_list_horizontal_margin_two_pane">24dp</dimen>
<dimen name="widget_preview_shadow_blur">0.5dp</dimen>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index baa1ee3..2f0c096 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -213,6 +213,7 @@
mIsRtl = (getResources().getConfiguration().getLayoutDirection()
== View.LAYOUT_DIRECTION_RTL);
mDeviceProfile = mActivity.getDeviceProfile();
+ mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
final int defaultIconSize;
@@ -243,7 +244,6 @@
defaultIconSize = mDeviceProfile.iconSizePx;
}
- mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,
defaultIconSize);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e9545c8..bf4f6c3 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -1010,7 +1010,7 @@
* Returns the amount of extra (or unused) vertical space.
*/
private int updateAvailableDimensions(Resources res) {
- iconCenterVertically = mIsScalableGrid || mIsResponsiveGrid;
+ iconCenterVertically = (mIsScalableGrid || mIsResponsiveGrid) && isVerticalBarLayout();
if (mIsResponsiveGrid) {
iconSizePx = mResponsiveWorkspaceCellSpec.getIconSize();
@@ -1738,15 +1738,8 @@
// The hotseat icons will be placed in the middle of the hotseat cells.
// Changing the hotseatCellHeightPx is not affecting hotseat icon positions
// in vertical bar layout.
- // Workspace icons are moved up by a small factor. The variable diffOverlapFactor
- // is set to account for that difference.
- float diffOverlapFactor = mIsResponsiveGrid ? 0
- : iconSizePx * (ICON_OVERLAP_FACTOR - 1) / 2;
-
- int paddingTop = Math.max((int) (mInsets.top + cellLayoutPaddingPx.top
- - diffOverlapFactor), 0);
- int paddingBottom = Math.max((int) (mInsets.bottom + cellLayoutPaddingPx.bottom
- + diffOverlapFactor), 0);
+ int paddingTop = Math.max((int) (mInsets.top + cellLayoutPaddingPx.top), 0);
+ int paddingBottom = Math.max((int) (mInsets.bottom + cellLayoutPaddingPx.bottom), 0);
if (isSeascape()) {
hotseatBarPadding.set(mInsets.left + mHotseatBarEdgePaddingPx, paddingTop,
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 1b2cebd..60a6be6 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -34,6 +34,7 @@
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ArchiveCompatibilityParams;
import android.os.UserHandle;
import android.util.Log;
@@ -108,9 +109,11 @@
launcherApps.registerCallback(callbacks);
mOnTerminateCallback.add(() ->
mContext.getSystemService(LauncherApps.class).unregisterCallback(callbacks));
- if (Flags.enableSupportForArchiving()) {
- launcherApps.setArchiveCompatibilityOptions(/* enableIconOverlay= */ true,
- /* enableUnarchivalConfirmation= */ false);
+
+ if (Utilities.enableSupportForArchiving()) {
+ ArchiveCompatibilityParams params = new ArchiveCompatibilityParams();
+ params.setEnableUnarchivalConfirmation(false);
+ launcherApps.setArchiveCompatibility(params);
}
SimpleBroadcastReceiver modelChangeReceiver =
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 2b886e4..d44438f 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -830,4 +830,10 @@
// No-Op
}
}
+
+ /** Encapsulates two flag checks into a single one. */
+ public static boolean enableSupportForArchiving() {
+ return Flags.enableSupportForArchiving()
+ || getSystemProperty("pm.archiving.enabled", "false").equals("true");
+ }
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index e65e614..a846e68 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -415,6 +415,8 @@
screenId, coordinates[0], coordinates[1]);
bindItem(info, accessibility);
} else if (item instanceof FolderInfo fi) {
+ Workspace<?> workspace = mContext.getWorkspace();
+ workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
mContext.getModelWriter().addItemToDatabase(fi,
LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, coordinates[0],
coordinates[1]);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index ae2849e..6acfcd0 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -80,6 +80,7 @@
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.UserCache;
@@ -1535,7 +1536,11 @@
// No animations will occur when changes occur to the items in this RecyclerView.
mRecyclerView.setItemAnimator(null);
onInitializeRecyclerView(mRecyclerView);
- FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mRecyclerView);
+ // Use ViewGroupFocusHelper for SearchRecyclerView to draw focus outline for the
+ // buttons in the view (e.g. query builder button and setting button)
+ FocusedItemDecorator focusedItemDecorator = isSearch() ? new FocusedItemDecorator(
+ new ViewGroupFocusHelper(mRecyclerView)) : new FocusedItemDecorator(
+ mRecyclerView);
mRecyclerView.addItemDecoration(focusedItemDecorator);
mOnFocusChangeListener = focusedItemDecorator.getFocusListener();
mAdapter.setIconFocusListener(mOnFocusChangeListener);
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
index bc55597..fcdfaa6 100644
--- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
+++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
@@ -55,6 +55,9 @@
assert quietModeButton != null;
addQuietModeButton(quietModeButton);
+ //Trigger lock/unlock action from header.
+ addHeaderOnClickListener(parent);
+
//Add image and action for private space settings button
ImageButton settingsButton = parent.findViewById(R.id.ps_settings_button);
assert settingsButton != null;
@@ -71,26 +74,35 @@
case STATE_ENABLED -> {
quietModeButton.setVisibility(View.VISIBLE);
quietModeButton.setImageResource(R.drawable.bg_ps_lock_button);
- quietModeButton.setOnClickListener(
- view -> {
- mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_LOCK_TAP);
- mPrivateProfileManager.lockPrivateProfile();
- });
+ quietModeButton.setOnClickListener(view -> lockAction());
}
case STATE_DISABLED -> {
quietModeButton.setVisibility(View.VISIBLE);
quietModeButton.setImageResource(R.drawable.bg_ps_unlock_button);
- quietModeButton.setOnClickListener(
- view -> {
- mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
- mPrivateProfileManager.unlockPrivateProfile((this::
- onPrivateProfileUnlocked));
- });
+ quietModeButton.setOnClickListener(view -> unLockAction());
}
default -> quietModeButton.setVisibility(View.GONE);
}
}
+ private void addHeaderOnClickListener(RelativeLayout header) {
+ if (mPrivateProfileManager.getCurrentState() == STATE_DISABLED) {
+ header.setOnClickListener(view -> unLockAction());
+ } else {
+ header.setOnClickListener(null);
+ }
+ }
+
+ private void unLockAction() {
+ mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
+ mPrivateProfileManager.unlockPrivateProfile((this::onPrivateProfileUnlocked));
+ }
+
+ private void lockAction() {
+ mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_LOCK_TAP);
+ mPrivateProfileManager.lockPrivateProfile();
+ }
+
private void addPrivateSpaceSettingsButton(ImageButton settingsButton) {
if (mPrivateProfileManager.getCurrentState() == STATE_ENABLED
&& mPrivateProfileManager.isPrivateSpaceSettingsAvailable()) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 40c368b..e2902e9 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -199,11 +199,6 @@
"ENABLE_SMARTSPACE_REMOVAL", DISABLED, "Enable SmartSpace removal for "
+ "home screen");
- // TODO(Block 10): Clean up flags
- public static final BooleanFlag ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION = getDebugFlag(270614790,
- "ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION", DISABLED,
- "Enables predictive back animation from all apps and widgets to home");
-
// TODO(Block 11): Clean up flags
public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(270395274,
"FOLDABLE_SINGLE_PAGE", DISABLED, "Use a single page for the workspace");
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index f058ae4..284b31e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -29,6 +29,7 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
@@ -633,6 +634,20 @@
}
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ boolean shouldCenterIcon = mActivity.getDeviceProfile().iconCenterVertically;
+ if (shouldCenterIcon) {
+ int iconSize = mActivity.getDeviceProfile().iconSizePx;
+ Paint.FontMetrics fm = mFolderName.getPaint().getFontMetrics();
+ int cellHeightPx = iconSize + mFolderName.getCompoundDrawablePadding()
+ + (int) Math.ceil(fm.bottom - fm.top);
+ setPadding(getPaddingLeft(), (MeasureSpec.getSize(heightMeasureSpec)
+ - cellHeightPx) / 2, getPaddingRight(), getPaddingBottom());
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
/** Sets the visibility of the icon's title text */
public void setTextVisible(boolean visible) {
if (visible) {
diff --git a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
index 2476a6f..5e2832f 100644
--- a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
+++ b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
@@ -20,12 +20,12 @@
import android.view.View;
import android.view.View.OnFocusChangeListener;
-import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
-
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
import androidx.recyclerview.widget.RecyclerView.State;
+import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
+
/**
* {@link ItemDecoration} for drawing and animating focused view background.
*/
@@ -37,12 +37,17 @@
mHelper = new SimpleFocusIndicatorHelper(container);
}
+ public FocusedItemDecorator(FocusIndicatorHelper focusIndicatorHelper) {
+ mHelper = focusIndicatorHelper;
+ }
+
public OnFocusChangeListener getFocusListener() {
return mHelper;
}
@Override
- public void onDraw(Canvas c, RecyclerView parent, State state) {
+ public void onDrawOver(Canvas c, RecyclerView parent, State state) {
+ // Use onDrawOver so focus outline is always visible
mHelper.draw(c);
}
}
diff --git a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
index fde220c..f9bd343 100644
--- a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
+++ b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
@@ -50,10 +50,18 @@
}
private void computeLocationRelativeToContainer(View child, Rect outRect) {
- View parent = (View) child.getParent();
+ if (child == null) {
+ return;
+ }
+
outRect.left += child.getX();
outRect.top += child.getY();
+ if (child.getParent() == null || !(child.getParent() instanceof View)) {
+ return;
+ }
+
+ View parent = (View) child.getParent();
if (parent != mContainer) {
if (parent instanceof PagedView) {
PagedView page = (PagedView) parent;
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index b41f011..8659471 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -16,7 +16,6 @@
package com.android.launcher3.model;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
@@ -34,6 +33,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AppFilter;
+import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.Callbacks;
@@ -330,7 +330,7 @@
PackageManagerHelper.getLoadingProgress(info),
PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
applicationInfo.intent = launchIntent;
- if (enableSupportForArchiving()) {
+ if (Utilities.enableSupportForArchiving()) {
// In case an app is archived, the respective item flag corresponding to
// archiving should also be applied during package updates
if (info.getActivityInfo().isArchived) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 736b80a..71ab51c 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
@@ -421,7 +420,7 @@
final HashMap<PackageUserKey, SessionInfo> installingPkgs =
mSessionHelper.getActiveSessions();
- if (enableSupportForArchiving()) {
+ if (Utilities.enableSupportForArchiving()) {
mInstallingPkgsCached = installingPkgs;
}
installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
@@ -656,7 +655,7 @@
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfo app = apps.get(i);
AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user), quietMode);
- if (enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
+ if (Utilities.enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
// For archived apps, include progress info in case there is a pending
// install session post restart of device.
String appPackageName = app.getApplicationInfo().packageName;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 529a8f9..a41b663 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.model;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED;
@@ -39,6 +38,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
@@ -274,7 +274,7 @@
PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
// In case an app is archived, we need to make sure that archived state
// in WorkspaceItemInfo is refreshed.
- if (enableSupportForArchiving() && !activities.isEmpty()) {
+ if (Utilities.enableSupportForArchiving() && !activities.isEmpty()) {
boolean newArchivalState = activities.get(
0).getActivityInfo().isArchived;
if (newArchivalState != si.isArchived()) {
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 44c41c1..31ae7c2 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -26,7 +26,6 @@
import android.text.TextUtils
import android.util.Log
import android.util.LongSparseArray
-import com.android.launcher3.Flags
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
@@ -323,7 +322,7 @@
}
if (
(c.restoreFlag != 0 ||
- Flags.enableSupportForArchiving() &&
+ Utilities.enableSupportForArchiving() &&
activityInfo != null &&
activityInfo.applicationInfo.isArchived) && !TextUtils.isEmpty(targetPkg)
) {
@@ -338,7 +337,7 @@
null // For archived apps, include progress info in case there is
// a pending install session post restart of device.
||
- (Flags.enableSupportForArchiving() &&
+ (Utilities.enableSupportForArchiving() &&
activityInfo.applicationInfo.isArchived)
) {
val installProgress = (si.getProgress() * 100).toInt()
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index 72eda6c..b213fe3 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -16,7 +16,6 @@
package com.android.launcher3.model.data;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
import android.content.ComponentName;
@@ -179,7 +178,7 @@
if (PackageManagerHelper.isAppSuspended(appInfo)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
}
- if (enableSupportForArchiving() && lai.getActivityInfo().isArchived) {
+ if (Utilities.enableSupportForArchiving() && lai.getActivityInfo().isArchived) {
info.runtimeStatusFlags |= FLAG_ARCHIVED;
}
info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index c8ab09c..e46c502 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -16,14 +16,13 @@
package com.android.launcher3.model.data;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
-
import android.content.Context;
import android.content.Intent;
import android.os.Process;
import androidx.annotation.Nullable;
+import com.android.launcher3.Utilities;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags;
import com.android.launcher3.icons.FastBitmapDrawable;
@@ -158,7 +157,7 @@
/**
* Returns true if the app corresponding to the item is archived. */
public boolean isArchived() {
- if (!enableSupportForArchiving()) {
+ if (!Utilities.enableSupportForArchiving()) {
return false;
}
return (runtimeStatusFlags & FLAG_ARCHIVED) != 0;
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index fd1b64f..df369c6 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -365,6 +365,13 @@
@Override
public void setMarkersCount(int numMarkers) {
mNumPages = numMarkers;
+
+ // If the last page gets removed we want to go to the previous page.
+ if (mNumPages == mActivePage) {
+ mActivePage--;
+ CURRENT_POSITION.set(this, (float) mActivePage);
+ }
+
requestLayout();
}
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 0d47462..605ef16 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -16,8 +16,6 @@
package com.android.launcher3.pm;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
-
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
@@ -33,6 +31,7 @@
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.SessionCommitReceiver;
+import com.android.launcher3.Utilities;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.util.IntArray;
@@ -228,7 +227,8 @@
public boolean verifySessionInfo(@Nullable final PackageInstaller.SessionInfo sessionInfo) {
// For archived apps we always want to show promise icons and the checks below don't apply.
- if (enableSupportForArchiving() && sessionInfo != null && sessionInfo.isUnarchival()) {
+ if (Utilities.enableSupportForArchiving() && sessionInfo != null
+ && sessionInfo.isUnarchival()) {
return true;
}
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index e4a2045..eacbc11 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.pm;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle;
import static com.android.launcher3.pm.PackageInstallInfo.STATUS_FAILED;
import static com.android.launcher3.pm.PackageInstallInfo.STATUS_INSTALLED;
@@ -32,6 +31,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.PackageUserKey;
import java.lang.ref.WeakReference;
@@ -80,7 +80,8 @@
helper.tryQueuePromiseAppIcon(sessionInfo);
- if (enableSupportForArchiving() && sessionInfo != null && sessionInfo.isUnarchival()) {
+ if (Utilities.enableSupportForArchiving() && sessionInfo != null
+ && sessionInfo.isUnarchival()) {
// For archived apps, icon could already be present on the workspace. To make sure
// the icon state is updated, we send a change event.
callback.onPackageStateChanged(PackageInstallInfo.fromInstallingState(sessionInfo));
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 5f17959..f1d837c 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -31,8 +31,10 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.ShortcutUtil;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.picker.WidgetRecommendationCategory;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -41,6 +43,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -218,6 +221,32 @@
.collect(Collectors.toList());
}
+ /** Returns the recommended widgets mapped by their category. */
+ public Map<WidgetRecommendationCategory, List<WidgetItem>> getCategorizedRecommendedWidgets() {
+ Map<ComponentKey, WidgetItem> allWidgetItems = mAllWidgets.stream()
+ .filter(entry -> entry instanceof WidgetsListContentEntry)
+ .flatMap(entry -> entry.mWidgets.stream())
+ .distinct()
+ .collect(Collectors.toMap(
+ widget -> new ComponentKey(widget.componentName, widget.user),
+ Function.identity()
+ ));
+ return mRecommendedWidgets.stream()
+ .filter(itemInfo -> itemInfo instanceof PendingAddWidgetInfo)
+ .collect(Collectors.groupingBy(
+ it -> ((PendingAddWidgetInfo) it).recommendationCategory,
+ Collectors.collectingAndThen(
+ Collectors.toList(),
+ list -> list.stream()
+ .map(it -> allWidgetItems.get(
+ new ComponentKey(it.getTargetComponent(),
+ it.user)))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList())
+ )
+ ));
+ }
+
public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
return mAllWidgets.stream()
.filter(row -> row instanceof WidgetsListContentEntry
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index fbbfea9..0af7e67 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -22,9 +22,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Flags;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.SecondaryDropTarget;
import com.android.launcher3.Utilities;
@@ -49,9 +47,10 @@
* onClickListener that depends on the item that the shortcut services.
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
- * @param <T>
+ *
+ * @param <T> extends {@link ActivityContext}
*/
-public abstract class SystemShortcut<T extends Context & ActivityContext> extends ItemInfo
+public abstract class SystemShortcut<T extends ActivityContext> extends ItemInfo
implements View.OnClickListener {
private final int mIconResId;
@@ -100,24 +99,25 @@
return mAccessibilityActionId == action;
}
- public interface Factory<T extends Context & ActivityContext> {
+ public interface Factory<T extends ActivityContext> {
- @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo, View originalView);
+ @Nullable
+ SystemShortcut<T> getShortcut(T context, ItemInfo itemInfo, @NonNull View originalView);
}
- public static final Factory<Launcher> WIDGETS = (launcher, itemInfo, originalView) -> {
+ public static final Factory<ActivityContext> WIDGETS = (context, itemInfo, originalView) -> {
if (itemInfo.getTargetComponent() == null) return null;
final List<WidgetItem> widgets =
- launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
+ context.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
if (widgets.isEmpty()) {
return null;
}
- return new Widgets(launcher, itemInfo, originalView);
+ return new Widgets(context, itemInfo, originalView);
};
- public static class Widgets extends SystemShortcut<Launcher> {
- public Widgets(Launcher target, ItemInfo itemInfo, View originalView) {
+ public static class Widgets<T extends ActivityContext> extends SystemShortcut<T> {
+ public Widgets(T target, ItemInfo itemInfo, @NonNull View originalView) {
super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo,
originalView);
}
@@ -134,14 +134,14 @@
}
}
- public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
+ public static final Factory<ActivityContext> APP_INFO = AppInfo::new;
- public static class AppInfo<T extends Context & ActivityContext> extends SystemShortcut<T> {
+ public static class AppInfo<T extends ActivityContext> extends SystemShortcut<T> {
@Nullable
private SplitAccessibilityInfo mSplitA11yInfo;
- public AppInfo(T target, ItemInfo itemInfo, View originalView) {
+ public AppInfo(T target, ItemInfo itemInfo, @NonNull View originalView) {
super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
itemInfo, originalView);
}
@@ -180,7 +180,7 @@
public void onClick(View view) {
dismissTaskMenuView(mTarget);
Rect sourceBounds = Utilities.getViewBounds(view);
- new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
+ new PackageManagerHelper(view.getContext()).startDetailsActivityForInfo(
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
.log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
@@ -200,8 +200,11 @@
}
}
- public static final Factory<Launcher> PRIVATE_PROFILE_INSTALL =
- (launcher, itemInfo, originalView) -> {
+ public static final Factory<ActivityContext> PRIVATE_PROFILE_INSTALL =
+ (context, itemInfo, originalView) -> {
+ if (originalView == null) {
+ return null;
+ }
if (itemInfo.getTargetComponent() == null
|| !(itemInfo instanceof com.android.launcher3.model.data.AppInfo)
|| !itemInfo.getContainerInfo().hasAllAppsContainer()
@@ -210,7 +213,7 @@
}
PrivateProfileManager privateProfileManager =
- launcher.getAppsView().getPrivateProfileManager();
+ context.getAppsView().getPrivateProfileManager();
if (privateProfileManager == null || !privateProfileManager.isEnabled()) {
return null;
}
@@ -221,30 +224,28 @@
}
// Do not show shortcut if an app is already installed to the space
ComponentName targetComponent = itemInfo.getTargetComponent();
- if (launcher.getAppsView()
- .getAppsStore()
- .getApp(new ComponentKey(targetComponent, privateProfileUser))
- != null) {
+ if (context.getAppsView().getAppsStore().getApp(
+ new ComponentKey(targetComponent, privateProfileUser)) != null) {
return null;
}
// Do not show shortcut for settings
String[] packagesToSkip =
- launcher.getResources()
+ originalView.getContext().getResources()
.getStringArray(R.array.skip_private_profile_shortcut_packages);
if (Arrays.asList(packagesToSkip).contains(targetComponent.getPackageName())) {
return null;
}
- return new InstallToPrivateProfile(
- launcher, itemInfo, originalView, privateProfileUser);
+ return new InstallToPrivateProfile<>(
+ context, itemInfo, originalView, privateProfileUser);
};
- static class InstallToPrivateProfile extends SystemShortcut<Launcher> {
+ static class InstallToPrivateProfile<T extends ActivityContext> extends SystemShortcut<T> {
UserHandle mSpaceUser;
- InstallToPrivateProfile(
- Launcher target, ItemInfo itemInfo, View originalView, UserHandle spaceUser) {
+ InstallToPrivateProfile(T target, ItemInfo itemInfo, @NonNull View originalView,
+ UserHandle spaceUser) {
// TODO(b/302666597): update icon once available
super(
R.drawable.ic_install_to_private,
@@ -271,8 +272,11 @@
}
}
- public static final Factory<BaseDraggingActivity> INSTALL =
+ public static final Factory<ActivityContext> INSTALL =
(activity, itemInfo, originalView) -> {
+ if (originalView == null) {
+ return null;
+ }
boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
&& ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
@@ -280,18 +284,19 @@
if (itemInfo instanceof com.android.launcher3.model.data.AppInfo) {
com.android.launcher3.model.data.AppInfo
appInfo = (com.android.launcher3.model.data.AppInfo) itemInfo;
- isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
+ isInstantApp = InstantAppResolver.newInstance(
+ originalView.getContext()).isInstantApp(appInfo);
}
boolean enabled = supportsWebUI || isInstantApp;
if (!enabled) {
return null;
}
return new Install(activity, itemInfo, originalView);
- };
+ };
- public static class Install extends SystemShortcut<BaseDraggingActivity> {
+ public static class Install<T extends ActivityContext> extends SystemShortcut<T> {
- public Install(BaseDraggingActivity target, ItemInfo itemInfo, View originalView) {
+ public Install(T target, ItemInfo itemInfo, @NonNull View originalView) {
super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
target, itemInfo, originalView);
}
@@ -306,16 +311,16 @@
}
}
- public static final Factory<Launcher> DONT_SUGGEST_APP = (activity, itemInfo, originalView) -> {
- if (!itemInfo.isPredictedItem()) {
- return null;
- }
- return new DontSuggestApp(activity, itemInfo, originalView);
- };
+ public static final Factory<ActivityContext> DONT_SUGGEST_APP =
+ (activity, itemInfo, originalView) -> {
+ if (!itemInfo.isPredictedItem()) {
+ return null;
+ }
+ return new DontSuggestApp<>(activity, itemInfo, originalView);
+ };
- private static class DontSuggestApp extends SystemShortcut<Launcher> {
- DontSuggestApp(Launcher target, ItemInfo itemInfo,
- View originalView) {
+ private static class DontSuggestApp<T extends ActivityContext> extends SystemShortcut<T> {
+ DontSuggestApp(T target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_block_no_shadow, R.string.dismiss_prediction_label, target,
itemInfo, originalView);
}
@@ -329,36 +334,36 @@
}
}
- public static final Factory<Launcher> UNINSTALL_APP = (activity, itemInfo, originalView) -> {
- if (!Flags.enablePrivateSpace()) {
- return null;
- }
- if (!UserCache.getInstance(activity.getApplicationContext()).getUserInfo(
- itemInfo.user).isPrivate()) {
- // If app is not Private Space app.
- return null;
- }
- ComponentName cn = SecondaryDropTarget.getUninstallTarget(activity.getApplicationContext(),
- itemInfo);
- if (cn == null) {
- // If component name is null, don't show uninstall shortcut.
- // System apps will have component name as null.
- return null;
- }
- return new UninstallApp(activity, itemInfo, originalView, cn);
- };
+ public static final Factory<ActivityContext> UNINSTALL_APP =
+ (activityContext, itemInfo, originalView) -> {
+ if (originalView == null) {
+ return null;
+ }
+ if (!Flags.enablePrivateSpace()) {
+ return null;
+ }
+ if (!UserCache.INSTANCE.get(originalView.getContext()).getUserInfo(
+ itemInfo.user).isPrivate()) {
+ // If app is not Private Space app.
+ return null;
+ }
+ ComponentName cn = SecondaryDropTarget.getUninstallTarget(originalView.getContext(),
+ itemInfo);
+ if (cn == null) {
+ // If component name is null, don't show uninstall shortcut.
+ // System apps will have component name as null.
+ return null;
+ }
+ return new UninstallApp(activityContext, itemInfo, originalView, cn);
+ };
- private static class UninstallApp extends SystemShortcut<Launcher> {
- private static final String TAG = "UninstallApp";
- Context mContext;
- @NonNull
- ComponentName mComponentName;
+ private static class UninstallApp<T extends ActivityContext> extends SystemShortcut<T> {
+ @NonNull ComponentName mComponentName;
- UninstallApp(Launcher target, ItemInfo itemInfo, View originalView,
+ UninstallApp(T target, ItemInfo itemInfo, @NonNull View originalView,
@NonNull ComponentName cn) {
super(R.drawable.ic_uninstall_no_shadow, R.string.uninstall_drop_target_label, target,
itemInfo, originalView);
- mContext = target.getApplicationContext();
mComponentName = cn;
}
@@ -366,7 +371,7 @@
@Override
public void onClick(View view) {
dismissTaskMenuView(mTarget);
- SecondaryDropTarget.performUninstall(mContext, mComponentName, mItemInfo);
+ SecondaryDropTarget.performUninstall(view.getContext(), mComponentName, mItemInfo);
mTarget.getStatsLogManager()
.logger()
.withItemInfo(mItemInfo)
@@ -374,8 +379,8 @@
}
}
- public static <T extends Context & ActivityContext> void dismissTaskMenuView(T activity) {
+ public static <T extends ActivityContext> void dismissTaskMenuView(T activity) {
AbstractFloatingView.closeOpenViews(activity, true,
- AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+ AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
}
}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 5636405..1623ad8 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
+import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.Activity;
@@ -208,6 +210,11 @@
return response;
}
+ case TestProtocol.REQUEST_GET_SPLIT_SELECTION_ACTIVE:
+ response.putBoolean(TEST_INFO_RESPONSE_FIELD, enableSplitContextually()
+ && Launcher.ACTIVITY_TRACKER.getCreatedActivity().isSplitSelectionActive());
+ return response;
+
case TestProtocol.REQUEST_ENABLE_ROTATION:
MAIN_EXECUTOR.submit(() ->
Launcher.ACTIVITY_TRACKER.getCreatedActivity().getRotationHelper()
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index f47ab90..4abefc7 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.touch;
-import static com.android.launcher3.Flags.enableSupportForArchiving;
import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_BIND_PENDING_APPWIDGET;
import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_RECONFIGURE_APPWIDGET;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
@@ -41,6 +40,7 @@
import android.widget.Toast;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.BuildConfig;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
@@ -331,7 +331,7 @@
// Check for abandoned promise
if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()
- && (!enableSupportForArchiving() || !shortcut.isArchived())) {
+ && (!Utilities.enableSupportForArchiving() || !shortcut.isArchived())) {
String packageName = shortcut.getIntent().getComponent() != null
? shortcut.getIntent().getComponent().getPackageName()
: shortcut.getIntent().getPackage();
@@ -353,18 +353,18 @@
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
- Intent intent;
- if (item instanceof ItemInfoWithIcon
- && (((ItemInfoWithIcon) item).runtimeStatusFlags
- & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
- ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
- intent = ApiWrapper.getAppMarketActivityIntent(launcher,
- appInfo.getTargetComponent().getPackageName(), Process.myUserHandle());
- } else {
- intent = item.getIntent();
- if (item instanceof AppInfo
- && (((ItemInfoWithIcon) item).runtimeStatusFlags
+ Intent intent = item.getIntent();
+ if (item instanceof ItemInfoWithIcon itemInfoWithIcon) {
+ if ((itemInfoWithIcon.runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ intent = ApiWrapper.getAppMarketActivityIntent(launcher,
+ itemInfoWithIcon.getTargetComponent().getPackageName(),
+ Process.myUserHandle());
+ } else if ((itemInfoWithIcon.runtimeStatusFlags
& ItemInfoWithIcon.FLAG_PRIVATE_SPACE_INSTALL_APP) != 0) {
+ intent = ApiWrapper.getAppMarketActivityIntent(launcher,
+ BuildConfig.APPLICATION_ID,
+ launcher.getAppsView().getPrivateProfileManager().getProfileUser());
launcher.getStatsLogManager().logger().log(
LAUNCHER_PRIVATE_SPACE_INSTALL_APP_BUTTON_TAP);
}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 1419dc4..ff95212 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -362,10 +362,10 @@
WindowManagerProxy wmProxy,
Map<CachedDisplayInfo, List<WindowBounds>> perDisplayBoundsCache) {
CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext);
- normalizedDisplayInfo = displayInfo.normalize();
+ normalizedDisplayInfo = displayInfo.normalize(wmProxy);
rotation = displayInfo.rotation;
currentSize = displayInfo.size;
- cutout = displayInfo.cutout;
+ cutout = WindowManagerProxy.getSafeInsets(displayInfo.cutout);
Configuration config = displayInfoContext.getResources().getConfiguration();
fontScale = config.fontScale;
diff --git a/src/com/android/launcher3/util/window/CachedDisplayInfo.java b/src/com/android/launcher3/util/window/CachedDisplayInfo.java
index 23f37aa..c5084ad 100644
--- a/src/com/android/launcher3/util/window/CachedDisplayInfo.java
+++ b/src/com/android/launcher3/util/window/CachedDisplayInfo.java
@@ -16,13 +16,16 @@
package com.android.launcher3.util.window;
import static com.android.launcher3.util.RotationUtils.deltaRotation;
-import static com.android.launcher3.util.RotationUtils.rotateRect;
import static com.android.launcher3.util.RotationUtils.rotateSize;
+import android.graphics.Insets;
import android.graphics.Point;
-import android.graphics.Rect;
+import android.view.DisplayCutout;
import android.view.Surface;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.util.Objects;
/**
@@ -30,36 +33,40 @@
*/
public class CachedDisplayInfo {
+ private static final DisplayCutout NO_CUTOUT =
+ new DisplayCutout(Insets.NONE, null, null, null, null);
+
public final Point size;
public final int rotation;
- public final Rect cutout;
+ @NonNull
+ public final DisplayCutout cutout;
public CachedDisplayInfo() {
this(new Point(0, 0), 0);
}
public CachedDisplayInfo(Point size, int rotation) {
- this(size, rotation, new Rect());
+ this(size, rotation, NO_CUTOUT);
}
- public CachedDisplayInfo(Point size, int rotation, Rect cutout) {
+ public CachedDisplayInfo(Point size, int rotation, @Nullable DisplayCutout cutout) {
this.size = size;
this.rotation = rotation;
- this.cutout = cutout;
+ this.cutout = cutout == null ? NO_CUTOUT : cutout;
}
/**
* Returns a CachedDisplayInfo where the properties are normalized to {@link Surface#ROTATION_0}
*/
- public CachedDisplayInfo normalize() {
+ public CachedDisplayInfo normalize(WindowManagerProxy windowManagerProxy) {
if (rotation == Surface.ROTATION_0) {
return this;
}
Point newSize = new Point(size);
rotateSize(newSize, deltaRotation(rotation, Surface.ROTATION_0));
- Rect newCutout = new Rect(cutout);
- rotateRect(newCutout, deltaRotation(rotation, Surface.ROTATION_0));
+ DisplayCutout newCutout = windowManagerProxy.rotateCutout(
+ cutout, size.x, size.y, rotation, Surface.ROTATION_0);
return new CachedDisplayInfo(newSize, Surface.ROTATION_0, newCutout);
}
@@ -79,11 +86,16 @@
CachedDisplayInfo that = (CachedDisplayInfo) o;
return rotation == that.rotation
&& Objects.equals(size, that.size)
- && Objects.equals(cutout, that.cutout);
+ && cutout.getSafeInsetLeft() == that.cutout.getSafeInsetLeft()
+ && cutout.getSafeInsetTop() == that.cutout.getSafeInsetTop()
+ && cutout.getSafeInsetRight() == that.cutout.getSafeInsetRight()
+ && cutout.getSafeInsetBottom() == that.cutout.getSafeInsetBottom();
}
@Override
public int hashCode() {
- return Objects.hash(size, rotation, cutout);
+ return Objects.hash(size, rotation,
+ cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
+ cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
}
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 209a3dc..32f1736 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -95,7 +95,7 @@
*/
public ArrayMap<CachedDisplayInfo, List<WindowBounds>> estimateInternalDisplayBounds(
Context displayInfoContext) {
- CachedDisplayInfo info = getDisplayInfo(displayInfoContext).normalize();
+ CachedDisplayInfo info = getDisplayInfo(displayInfoContext).normalize(this);
List<WindowBounds> bounds = estimateWindowBounds(displayInfoContext, info);
ArrayMap<CachedDisplayInfo, List<WindowBounds>> result = new ArrayMap<>();
result.put(info, bounds);
@@ -186,10 +186,9 @@
* Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations
*/
protected List<WindowBounds> estimateWindowBounds(Context context,
- CachedDisplayInfo displayInfo) {
+ final CachedDisplayInfo displayInfo) {
int densityDpi = context.getResources().getConfiguration().densityDpi;
- int rotation = displayInfo.rotation;
- Rect safeCutout = displayInfo.cutout;
+ final int rotation = displayInfo.rotation;
int minSize = Math.min(displayInfo.size.x, displayInfo.size.y);
int swDp = (int) dpiFromPx(minSize, densityDpi);
@@ -247,8 +246,9 @@
statusBarHeight = statusBarHeightLandscape;
}
- Rect insets = new Rect(safeCutout);
- rotateRect(insets, rotationChange);
+ DisplayCutout rotatedCutout = rotateCutout(
+ displayInfo.cutout, displayInfo.size.x, displayInfo.size.y, rotation, i);
+ Rect insets = getSafeInsets(rotatedCutout);
insets.top = Math.max(insets.top, statusBarHeight);
insets.bottom = Math.max(insets.bottom, navBarHeight);
@@ -298,8 +298,7 @@
Point size = new Point();
Display display = getDisplay(displayInfoContext);
display.getRealSize(size);
- Rect cutoutRect = new Rect();
- return new CachedDisplayInfo(size, rotation, cutoutRect);
+ return new CachedDisplayInfo(size, rotation);
}
}
@@ -309,13 +308,8 @@
@TargetApi(Build.VERSION_CODES.S)
protected CachedDisplayInfo getDisplayInfo(WindowMetrics windowMetrics, int rotation) {
Point size = new Point(windowMetrics.getBounds().right, windowMetrics.getBounds().bottom);
- Rect cutoutRect = new Rect();
- DisplayCutout cutout = windowMetrics.getWindowInsets().getDisplayCutout();
- if (cutout != null) {
- cutoutRect.set(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
- cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
- }
- return new CachedDisplayInfo(size, rotation, cutoutRect);
+ return new CachedDisplayInfo(size, rotation,
+ windowMetrics.getWindowInsets().getDisplayCutout());
}
/**
@@ -355,6 +349,16 @@
}
/**
+ * Returns a DisplayCutout which represents a rotated version of the original
+ */
+ protected DisplayCutout rotateCutout(DisplayCutout original, int startWidth, int startHeight,
+ int fromRotation, int toRotation) {
+ Rect safeCutout = getSafeInsets(original);
+ rotateRect(safeCutout, deltaRotation(fromRotation, toRotation));
+ return new DisplayCutout(Insets.of(safeCutout), null, null, null, null);
+ }
+
+ /**
* Returns the current navigation mode from resource.
*/
public NavigationMode getNavigationMode(Context context) {
@@ -373,4 +377,12 @@
return Utilities.ATLEAST_S ? NavigationMode.NO_BUTTON :
NavigationMode.THREE_BUTTONS;
}
+
+ /**
+ * @see DisplayCutout#getSafeInsets
+ */
+ public static Rect getSafeInsets(DisplayCutout cutout) {
+ return new Rect(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
+ cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
+ }
}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index c5317e3..230a651 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -155,6 +155,19 @@
}
/**
+ * Handle user tapping on unsupported target when in split selection mode.
+ * See {@link #isSplitSelectionActive()}
+ *
+ * @return {@code true} if this method will handle the incorrect target selection,
+ * {@code false} if it could not be handled or if not possible to handle based on
+ * current split state
+ */
+ default boolean handleIncorrectSplitTargetSelection() {
+ // Overridden
+ return false;
+ }
+
+ /**
* The root view to support drag-and-drop and popup support.
*/
BaseDragLayer getDragLayer();
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index 76b6fde..c60e1a4 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -175,7 +175,6 @@
if (!mTmpPosition.equals(mIconPosition)) {
mIconPosition.set(mTmpPosition);
- sendIconInfo();
LayoutParams lp = (LayoutParams) mSurfaceView.getLayoutParams();
lp.width = Math.round(mIconPosition.width());
@@ -184,6 +183,9 @@
lp.topMargin = Math.round(mIconPosition.top);
}
}
+
+ sendIconInfo();
+
if (mIcon != null && iconChanged && !mIconBounds.isEmpty()) {
// Record the icon display
setCurrentIconVisible(true);
@@ -197,7 +199,7 @@
}
private void sendIconInfo() {
- if (mContract != null && !mIconPosition.isEmpty()) {
+ if (mContract != null) {
mContract.sendEndPosition(mIconPosition, mLauncher, mSurfaceView.getSurfaceControl());
}
}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 9de7f62..145ad80 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -72,14 +72,21 @@
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mContentHorizontalMargin = getResources().getDimensionPixelSize(
- R.dimen.widget_list_horizontal_margin);
+ mContentHorizontalMargin = getWidgetListHorizontalMargin();
mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
R.dimen.widget_cell_horizontal_padding);
mNavBarScrimPaint = new Paint();
mNavBarScrimPaint.setColor(Themes.getNavBarScrimColor(mActivityContext));
}
+ /**
+ * Returns the margins to be applied to the left and right of the widget apps list.
+ */
+ protected int getWidgetListHorizontalMargin() {
+ return getResources().getDimensionPixelSize(
+ R.dimen.widget_list_horizontal_margin);
+ }
+
protected int getScrimColor(Context context) {
return context.getResources().getColor(R.color.widgets_picker_scrim);
}
@@ -142,8 +149,7 @@
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
- @Px int contentHorizontalMargin = getResources().getDimensionPixelSize(
- R.dimen.widget_list_horizontal_margin);
+ @Px int contentHorizontalMargin = getWidgetListHorizontalMargin();
if (contentHorizontalMargin != mContentHorizontalMargin) {
onContentHorizontalMarginChanged(contentHorizontalMargin);
mContentHorizontalMargin = contentHorizontalMargin;
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index ccf4b2e..a501960 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -27,6 +27,7 @@
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.widget.picker.WidgetRecommendationCategory;
import com.android.launcher3.widget.util.WidgetSizes;
/**
@@ -42,6 +43,16 @@
public Bundle bindOptions = null;
public int sourceContainer;
+ public WidgetRecommendationCategory recommendationCategory = null;
+
+ public PendingAddWidgetInfo(
+ LauncherAppWidgetProviderInfo i,
+ int container,
+ WidgetRecommendationCategory recommendationCategory) {
+ this(i, container);
+ this.recommendationCategory = recommendationCategory;
+ }
+
public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, int container) {
if (i.isCustomWidget()) {
itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
index d99c5d1..801b1f6 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
@@ -80,6 +80,28 @@
/** Maps application category to an appropriate displayable category. */
private static WidgetRecommendationCategory getCategoryFromApplicationCategory(
Context context, int applicationCategory, String componentName) {
+ // Weather categories don't map to a specific application category, so, we maintain an
+ // allowlist.
+ String[] weatherRecommendationAllowlist =
+ context.getResources().getStringArray(R.array.weather_recommendations);
+ for (String allowedWeatherComponentName : weatherRecommendationAllowlist) {
+ if (componentName.equalsIgnoreCase(allowedWeatherComponentName)) {
+ return new WidgetRecommendationCategory(
+ R.string.weather_widget_recommendation_category_label, /*order=*/3);
+ }
+ }
+
+ // Fitness categories don't map to a specific application category, so, we maintain an
+ // allowlist.
+ String[] fitnessRecommendationAllowlist =
+ context.getResources().getStringArray(R.array.fitness_recommendations);
+ for (String allowedFitnessComponentName : fitnessRecommendationAllowlist) {
+ if (componentName.equalsIgnoreCase(allowedFitnessComponentName)) {
+ return new WidgetRecommendationCategory(
+ R.string.fitness_widget_recommendation_category_label, /*order=*/2);
+ }
+ }
+
if (applicationCategory == ApplicationInfo.CATEGORY_PRODUCTIVITY) {
return new WidgetRecommendationCategory(
R.string.productivity_widget_recommendation_category_label, /*order=*/0);
@@ -99,26 +121,6 @@
/*order=*/4);
}
- // Fitness & weather categories don't map to a specific application category, so, we
- // maintain an allowlist.
- String[] weatherRecommendationAllowlist =
- context.getResources().getStringArray(R.array.weather_recommendations);
- for (String allowedWeatherComponentName : weatherRecommendationAllowlist) {
- if (componentName.equalsIgnoreCase(allowedWeatherComponentName)) {
- return new WidgetRecommendationCategory(
- R.string.weather_widget_recommendation_category_label, /*order=*/2);
- }
- }
-
- String[] fitnessRecommendationAllowlist =
- context.getResources().getStringArray(R.array.fitness_recommendations);
- for (String allowedFitnessComponentName : fitnessRecommendationAllowlist) {
- if (componentName.equalsIgnoreCase(allowedFitnessComponentName)) {
- return new WidgetRecommendationCategory(
- R.string.fitness_widget_recommendation_category_label, /*order=*/3);
- }
- }
-
return new WidgetRecommendationCategory(
R.string.others_widget_recommendation_category_label, /*order=*/5);
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index e9a590b..28bae59 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -173,6 +173,7 @@
protected WidgetsSearchBar mSearchBar;
protected TextView mHeaderTitle;
protected RecyclerViewFastScroller mFastScroller;
+ protected int mBottomPadding;
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -182,7 +183,8 @@
.stream()
.anyMatch(user -> mUserCache.getUserInfo(user).isWork());
mWorkWidgetsFilter = entry -> mHasWorkProfile
- && mUserCache.getUserInfo(entry.mPkgItem.user).isWork();
+ && mUserCache.getUserInfo(entry.mPkgItem.user).isWork()
+ && !mUserManagerState.isUserQuiet(entry.mPkgItem.user);
mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
@@ -369,15 +371,16 @@
@Override
public void setInsets(Rect insets) {
super.setInsets(insets);
- int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
- setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, bottomPadding);
- setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, bottomPadding);
+ mBottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
+ setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, mBottomPadding);
+ setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, mBottomPadding);
if (mHasWorkProfile) {
- setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, bottomPadding);
+ setBottomPadding(mAdapters.get(AdapterHolder.WORK)
+ .mWidgetsRecyclerView, mBottomPadding);
}
- ((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = bottomPadding;
+ ((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = mBottomPadding;
- if (bottomPadding > 0) {
+ if (mBottomPadding > 0) {
setupNavBarColor();
} else {
clearNavBarColor();
@@ -386,6 +389,15 @@
requestLayout();
}
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ WindowInsets w = super.onApplyWindowInsets(insets);
+ if (mInsets.bottom != mNavBarScrimHeight) {
+ setInsets(mInsets);
+ }
+ return w;
+ }
+
private void setBottomPadding(RecyclerView recyclerView, int bottomPadding) {
recyclerView.setPadding(
recyclerView.getPaddingLeft(),
@@ -790,8 +802,9 @@
if (mDeviceProfile.isLandscape != dp.isLandscape && dp.isTablet && !dp.isTwoPanels) {
handleClose(false);
show(BaseActivity.fromContext(getContext()), false);
- } else {
+ } else if (!isTwoPane()) {
reset();
+ resetExpandedHeaders();
}
// When folding/unfolding the foldables, we need to switch between the regular widget picker
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index b5e7401..3383299 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -52,7 +52,11 @@
private static final int[] EXPANDED_DRAWABLE_STATE = new int[] {android.R.attr.state_expanded};
private final int mIconSize;
-
+ /**
+ * Indicates if the header is collapsable. For example, when displayed in a two pane layout,
+ * widget apps aren't collapsable.
+ */
+ private final boolean mIsCollapsable;
@Nullable private HandlerRunnable mIconLoadRequest;
@Nullable private Drawable mIconDrawable;
@Nullable private WidgetsListDrawableState mListDrawableState;
@@ -79,6 +83,7 @@
R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
grid.iconSizePx);
+ mIsCollapsable = a.getBoolean(R.styleable.WidgetsListRowHeader_collapsable, true);
}
@Override
@@ -87,32 +92,36 @@
mAppIcon = findViewById(R.id.app_icon);
mTitle = findViewById(R.id.app_title);
mSubtitle = findViewById(R.id.app_subtitle);
- setAccessibilityDelegate(new AccessibilityDelegate() {
+ // Lists that cannot collapse, don't need EXPAND or COLLAPSE accessibility actions.
+ if (mIsCollapsable) {
+ setAccessibilityDelegate(new AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- if (mIsExpanded) {
- info.removeAction(AccessibilityNodeInfo.ACTION_EXPAND);
- info.addAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
- } else {
- info.removeAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
- info.addAction(AccessibilityNodeInfo.ACTION_EXPAND);
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ if (mIsExpanded) {
+ info.removeAction(AccessibilityNodeInfo.ACTION_EXPAND);
+ info.addAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
+ } else {
+ info.removeAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
+ info.addAction(AccessibilityNodeInfo.ACTION_EXPAND);
+ }
+ super.onInitializeAccessibilityNodeInfo(host, info);
}
- super.onInitializeAccessibilityNodeInfo(host, info);
- }
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- switch (action) {
- case AccessibilityNodeInfo.ACTION_EXPAND:
- case AccessibilityNodeInfo.ACTION_COLLAPSE:
- callOnClick();
- return true;
- default:
- return super.performAccessibilityAction(host, action, args);
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_EXPAND:
+ case AccessibilityNodeInfo.ACTION_COLLAPSE:
+ callOnClick();
+ return true;
+ default:
+ return super.performAccessibilityAction(host, action, args);
+ }
}
- }
- });
+ });
+ }
}
/** Sets the expand toggle to expand / collapse. */
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 6656237..5a1ec87 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Outline;
+import android.graphics.Rect;
import android.os.Process;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -58,11 +59,13 @@
private FrameLayout mSuggestedWidgetsContainer;
private WidgetsListHeader mSuggestedWidgetsHeader;
+ private PackageUserKey mSuggestedWidgetsPackageUserKey;
private LinearLayout mRightPane;
private ScrollView mRightPaneScrollView;
private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
private int mActivePage = -1;
+ private PackageUserKey mSelectedHeader;
private final ViewOutlineProvider mViewOutlineProviderRightPane = new ViewOutlineProvider() {
@Override
@@ -124,6 +127,7 @@
mFastScroller.setVisibility(GONE);
}
+ /** Overrides onConfigurationChanged method from WidgetsFullSheet. Needed for b/319150904 */
@Override
protected void onConfigurationChanged(Configuration newConfig) {}
@@ -147,6 +151,23 @@
}
layoutParams.weight = layoutParams.width == 0 ? 0.33F : 0;
leftPane.setLayoutParams(layoutParams);
+ requestApplyInsets();
+ if (mSelectedHeader != null) {
+ if (mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey)) {
+ mSuggestedWidgetsHeader.callOnClick();
+ } else {
+ getHeaderChangeListener().onHeaderChanged(mSelectedHeader);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onWidgetsBound() {
+ super.onWidgetsBound();
+ if (!mHasRecommendedWidgets && mSelectedHeader == null) {
+ mAdapters.get(mActivePage).mWidgetsListAdapter.selectFirstHeaderEntry();
+ mAdapters.get(mActivePage).mWidgetsRecyclerView.scrollToTop();
}
}
@@ -195,6 +216,8 @@
mRightPane.removeAllViews();
mRightPane.addView(mRecommendedWidgetsTable);
mRightPaneScrollView.setScrollY(0);
+ mSuggestedWidgetsPackageUserKey = PackageUserKey.fromPackageItemInfo(packageItemInfo);
+ mSelectedHeader = mSuggestedWidgetsPackageUserKey;
});
mSuggestedWidgetsContainer.addView(mSuggestedWidgetsHeader);
}
@@ -273,6 +296,7 @@
return new HeaderChangeListener() {
@Override
public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) {
+ mSelectedHeader = selectedHeader;
WidgetsListContentEntry contentEntry = mActivityContext.getPopupDataProvider()
.getSelectedAppWidgets(selectedHeader);
@@ -303,6 +327,24 @@
}
@Override
+ public void setInsets(Rect insets) {
+ super.setInsets(insets);
+ FrameLayout rightPaneContainer = mContent.findViewById(R.id.right_pane_container);
+ rightPaneContainer.setPadding(
+ rightPaneContainer.getPaddingLeft(),
+ rightPaneContainer.getPaddingTop(),
+ rightPaneContainer.getPaddingRight(),
+ mBottomPadding);
+ requestLayout();
+ }
+
+ @Override
+ protected int getWidgetListHorizontalMargin() {
+ return getResources().getDimensionPixelSize(
+ R.dimen.widget_list_left_pane_horizontal_margin);
+ }
+
+ @Override
protected boolean isTwoPane() {
return true;
}
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
index 920ba6f..361247b 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
@@ -80,8 +80,8 @@
hotseatQsbSpace: 0.0px (0.0dp)
hotseatQsbHeight: 0.0px (0.0dp)
springLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)
- getHotseatLayoutPadding(context).top: 64.0px (24.380953dp)
- getHotseatLayoutPadding(context).bottom: 112.0px (42.666668dp)
+ getHotseatLayoutPadding(context).top: 74.0px (28.190475dp)
+ getHotseatLayoutPadding(context).bottom: 103.0px (39.238094dp)
getHotseatLayoutPadding(context).left: 42.0px (16.0dp)
getHotseatLayoutPadding(context).right: 63.0px (24.0dp)
numShownHotseatIcons: 5
@@ -123,8 +123,8 @@
dropTargetBarSizePx: 95.0px (36.190475dp)
dropTargetBarBottomMarginPx: 16.0px (6.095238dp)
getCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)
- getCellLayoutSpringLoadShrunkBottom(): 952.0px (362.66666dp)
+ getCellLayoutSpringLoadShrunkBottom(): 961.0px (366.09525dp)
workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
- getWorkspaceSpringLoadScale(): 0.79639447px (0.30338836dp)
+ getWorkspaceSpringLoadScale(): 0.8059385px (0.30702418dp)
getCellLayoutHeight(): 943.0px (359.2381dp)
getCellLayoutWidth(): 2073.0px (789.7143dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
index 65460ec..d93ec58 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
@@ -80,8 +80,8 @@
hotseatQsbSpace: 0.0px (0.0dp)
hotseatQsbHeight: 0.0px (0.0dp)
springLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)
- getHotseatLayoutPadding(context).top: 64.0px (24.380953dp)
- getHotseatLayoutPadding(context).bottom: 49.0px (18.666666dp)
+ getHotseatLayoutPadding(context).top: 74.0px (28.190475dp)
+ getHotseatLayoutPadding(context).bottom: 40.0px (15.238095dp)
getHotseatLayoutPadding(context).left: 42.0px (16.0dp)
getHotseatLayoutPadding(context).right: 189.0px (72.0dp)
numShownHotseatIcons: 5
@@ -123,8 +123,8 @@
dropTargetBarSizePx: 95.0px (36.190475dp)
dropTargetBarBottomMarginPx: 16.0px (6.095238dp)
getCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)
- getCellLayoutSpringLoadShrunkBottom(): 1008.0px (384.0dp)
+ getCellLayoutSpringLoadShrunkBottom(): 1017.0px (387.42856dp)
workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
- getWorkspaceSpringLoadScale(): 0.8021869px (0.305595dp)
+ getWorkspaceSpringLoadScale(): 0.8111332px (0.3090031dp)
getCellLayoutHeight(): 1006.0px (383.2381dp)
getCellLayoutWidth(): 1947.0px (741.7143dp)
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 2f9945d..8d40ff2 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -150,6 +150,7 @@
public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing";
public static final String REQUEST_GET_OVERVIEW_CURRENT_PAGE_INDEX =
"get-overview-current-page-index";
+ public static final String REQUEST_GET_SPLIT_SELECTION_ACTIVE = "get-split-selection-active";
public static final String REQUEST_ENABLE_ROTATION = "enable_rotation";
public static final String REQUEST_ENABLE_SUGGESTION = "enable-suggestion";
public static final String REQUEST_MODEL_QUEUE_CLEARED = "model-queue-cleared";
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
new file mode 100644
index 0000000..3f37563
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
@@ -0,0 +1,96 @@
+/*
+ * 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.util;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+
+import android.content.ContextWrapper;
+
+import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.popup.PopupDataProvider;
+
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * {@link ContextWrapper} around {@link ActivityContextWrapper} with internal Launcher interface for
+ * testing.
+ *
+ * There are 2 constructors in this class. The base context can be {@link SandboxContext} or
+ * Instrumentation target context.
+ * Using {@link SandboxContext} as base context allows custom implementations for
+ * MainThreadInitializedObject providers.
+ */
+
+public class TestSandboxModelContextWrapper extends ActivityContextWrapper implements
+ BgDataModel.Callbacks {
+
+ protected AllAppsStore<ActivityContextWrapper> mAllAppsStore;
+ protected AlphabeticalAppsList<ActivityContextWrapper> mAppsList;
+
+ public final CountDownLatch mBindCompleted = new CountDownLatch(1);
+
+ protected ActivityAllAppsContainerView<ActivityContextWrapper> mAppsView;
+
+ private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {});
+ protected final UserCache mUserCache;
+
+ public TestSandboxModelContextWrapper(SandboxContext base) {
+ super(base);
+ mUserCache = base.getObject(UserCache.INSTANCE);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
+ mAppsView = new ActivityAllAppsContainerView<>(this));
+ mAppsList = mAppsView.getPersonalAppList();
+ mAllAppsStore = mAppsView.getAppsStore();
+ }
+
+ public TestSandboxModelContextWrapper() {
+ super(getInstrumentation().getTargetContext());
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
+ mAppsView = new ActivityAllAppsContainerView<>(this));
+ mUserCache = UserCache.getInstance(this);
+ mAppsList = mAppsView.getPersonalAppList();
+ mAllAppsStore = mAppsView.getAppsStore();
+ }
+ @Nullable
+ @Override
+ public PopupDataProvider getPopupDataProvider() {
+ return mPopupDataProvider;
+ }
+
+ @Override
+ public ActivityAllAppsContainerView<ActivityContextWrapper> getAppsView() {
+ return mAppsView;
+ }
+
+ @Override
+ public void bindAllApplications(AppInfo[] apps, int flags,
+ Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
+ mAllAppsStore.setApps(apps, flags, packageUserKeytoUidMap);
+ mBindCompleted.countDown();
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 10b428a..7fba33e 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -125,8 +125,6 @@
Log.e(TAG, "Failed to save accessibility hierarchy", ex);
}
- dumpCommand("logcat -d -s TestRunner", diagFile(description, "FilteredLogcat", "txt"));
-
// Dump bugreport
if (!sSavedBugreport) {
dumpCommand("bugreportz -s", diagFile(description, "Bugreport", "zip"));
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Background.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Background.java
index 9f2ce22..988aa94 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Background.java
@@ -16,6 +16,7 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.tapl.BaseOverview.TASK_RES_ID;
import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
@@ -116,10 +117,10 @@
// non-tablet overview, snapshots can be on either side of the swiped
// task, but we still check that they become visible after swiping and
// pausing.
- mLauncher.waitForOverviewObject("snapshot");
+ mLauncher.waitForOverviewObject(TASK_RES_ID);
if (mLauncher.isTablet()) {
List<UiObject2> tasks = mLauncher.getDevice().findObjects(
- mLauncher.getOverviewObjectSelector("snapshot"));
+ mLauncher.getOverviewObjectSelector(TASK_RES_ID));
final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
mLauncher.assertTrue(
"All tasks not to the left of the swiped task",
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/BaseOverview.java
index d337b91..ad95ecf 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -44,6 +44,7 @@
* Common overview panel for both Launcher and fallback recents
*/
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
+ protected static final String TASK_RES_ID = "task";
private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
@@ -281,10 +282,10 @@
// The widest, and most top-right task should be the current task
UiObject2 currentTask = Collections.max(taskViews,
- Comparator.comparingInt((UiObject2 t) -> t.getParent().getVisibleBounds().width())
- .thenComparingInt((UiObject2 t) -> t.getParent().getVisibleCenter().x)
+ Comparator.comparingInt((UiObject2 t) -> t.getVisibleBounds().width())
+ .thenComparingInt((UiObject2 t) -> t.getVisibleCenter().x)
.thenComparing(Comparator.comparing(
- (UiObject2 t) -> t.getParent().getVisibleCenter().y).reversed()));
+ (UiObject2 t) -> t.getVisibleCenter().y).reversed()));
return new OverviewTask(mLauncher, currentTask, this);
}
@@ -329,10 +330,11 @@
"want to get overview tasks")) {
verifyActiveContainer();
return mLauncher.getDevice().findObjects(
- mLauncher.getOverviewObjectSelector("snapshot"));
+ mLauncher.getOverviewObjectSelector(TASK_RES_ID));
}
}
+
int getTaskCount() {
return getTasks().size();
}
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 6c9f5ed..cdde605 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -28,7 +28,9 @@
import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID;
import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_GET_SPLIT_SELECTION_ACTIVE;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_NUM_ALL_APPS_COLUMNS;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
import android.app.ActivityManager;
import android.app.Instrumentation;
@@ -878,7 +880,6 @@
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
- waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
if (is3PLauncher() && isTablet() && !isTransientTaskbar()) {
@@ -887,6 +888,12 @@
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
}
+ boolean splitSelectionActive = getTestInfo(REQUEST_GET_SPLIT_SELECTION_ACTIVE)
+ .getBoolean(TEST_INFO_RESPONSE_FIELD);
+ if (!splitSelectionActive) {
+ waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ } // do nothing, we expect that view
+
return waitForLauncherObject(APPS_RES_ID);
}
case OVERVIEW:
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/OverviewTask.java
index f383e99..afe5722 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -16,6 +16,10 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.DEFAULT;
+import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.SPLIT_TOP_OR_LEFT;
+
import android.graphics.Rect;
import androidx.annotation.NonNull;
@@ -34,9 +38,6 @@
*/
public final class OverviewTask {
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
- private static final String TASK_SNAPSHOT_1 = "snapshot";
- private static final String TASK_SNAPSHOT_2 = "bottomright_snapshot";
-
static final Pattern TASK_START_EVENT = Pattern.compile("startActivityFromRecentsAsync");
static final Pattern SPLIT_SELECT_EVENT = Pattern.compile("enterSplitSelect");
static final Pattern SPLIT_START_EVENT = Pattern.compile("launchSplitTasks");
@@ -64,15 +65,16 @@
return getCombinedSplitTaskHeight();
}
- return mTask.getVisibleBounds().height();
+ UiObject2 taskSnapshot1 = findObjectInTask(DEFAULT.snapshotRes);
+ return taskSnapshot1.getVisibleBounds().height();
}
/**
* Calculates the visible height for split tasks, containing 2 snapshot tiles and a divider.
*/
private int getCombinedSplitTaskHeight() {
- UiObject2 taskSnapshot1 = findObjectInTask(TASK_SNAPSHOT_1);
- UiObject2 taskSnapshot2 = findObjectInTask(TASK_SNAPSHOT_2);
+ UiObject2 taskSnapshot1 = findObjectInTask(SPLIT_TOP_OR_LEFT.snapshotRes);
+ UiObject2 taskSnapshot2 = findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes);
// If the split task is partly off screen, taskSnapshot1 can be invisible.
if (taskSnapshot1 == null) {
@@ -96,15 +98,16 @@
return getCombinedSplitTaskWidth();
}
- return mTask.getVisibleBounds().width();
+ UiObject2 taskSnapshot1 = findObjectInTask(DEFAULT.snapshotRes);
+ return taskSnapshot1.getVisibleBounds().width();
}
/**
* Calculates the visible width for split tasks, containing 2 snapshot tiles and a divider.
*/
private int getCombinedSplitTaskWidth() {
- UiObject2 taskSnapshot1 = findObjectInTask(TASK_SNAPSHOT_1);
- UiObject2 taskSnapshot2 = findObjectInTask(TASK_SNAPSHOT_2);
+ UiObject2 taskSnapshot1 = findObjectInTask(SPLIT_TOP_OR_LEFT.snapshotRes);
+ UiObject2 taskSnapshot2 = findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes);
int left = Math.min(
taskSnapshot1.getVisibleBounds().left, taskSnapshot2.getVisibleBounds().left);
@@ -115,15 +118,15 @@
}
int getTaskCenterX() {
- return mTask.getParent().getVisibleCenter().x;
+ return mTask.getVisibleCenter().x;
}
int getTaskCenterY() {
- return mTask.getParent().getVisibleCenter().y;
+ return mTask.getVisibleCenter().y;
}
float getExactCenterX() {
- return mTask.getParent().getVisibleBounds().exactCenterX();
+ return mTask.getVisibleBounds().exactCenterX();
}
UiObject2 getUiObject() {
@@ -225,11 +228,17 @@
/** Taps the task menu. Returns the task menu object. */
@NonNull
public OverviewTaskMenu tapMenu() {
+ return tapMenu(DEFAULT);
+ }
+
+ /** Taps the task menu of the split task. Returns the split task's menu object. */
+ @NonNull
+ public OverviewTaskMenu tapMenu(OverviewSplitTask task) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to tap the task menu")) {
mLauncher.clickLauncherObject(
- mLauncher.waitForObjectInContainer(mTask.getParent(), "icon"));
+ mLauncher.waitForObjectInContainer(mTask, task.iconAppRes));
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"tapped the task menu")) {
@@ -238,27 +247,31 @@
}
}
- /** Taps the task menu of the split task. Returns the split task's menu object. */
- @NonNull
- public OverviewTaskMenu tapSplitTaskMenu() {
- try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
- LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "want to tap the split task's menu")) {
- mLauncher.clickLauncherObject(
- mLauncher.waitForObjectInContainer(mTask.getParent(), "bottomRight_icon"));
-
- try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "tapped the split task's menu")) {
- return new OverviewTaskMenu(mLauncher);
- }
- }
- }
-
boolean isTaskSplit() {
- return findObjectInTask(TASK_SNAPSHOT_2) != null;
+ return findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes) != null;
}
private UiObject2 findObjectInTask(String resName) {
- return mTask.getParent().findObject(mLauncher.getOverviewObjectSelector(resName));
+ return mTask.findObject(mLauncher.getOverviewObjectSelector(resName));
+ }
+
+ /**
+ * Enum used to specify which task is retrieved when it is a split task.
+ */
+ public enum OverviewSplitTask {
+ // The main task when the task is not split.
+ DEFAULT("snapshot", "icon"),
+ // The first task in split task.
+ SPLIT_TOP_OR_LEFT("snapshot", "icon"),
+ // The second task in split task.
+ SPLIT_BOTTOM_OR_RIGHT("bottomright_snapshot", "bottomRight_icon");
+
+ public final String snapshotRes;
+ public final String iconAppRes;
+
+ OverviewSplitTask(String snapshotRes, String iconAppRes) {
+ this.snapshotRes = snapshotRes;
+ this.iconAppRes = iconAppRes;
+ }
}
}
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index d44ccf5..2905d85 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -17,9 +17,12 @@
import android.content.Context
import android.content.res.Configuration
-import android.content.res.Resources
import android.graphics.Point
import android.graphics.Rect
+import android.platform.test.rule.AllowedDevices
+import android.platform.test.rule.DeviceProduct
+import android.platform.test.rule.IgnoreLimit
+import android.platform.test.rule.LimitDevicesRule
import android.util.DisplayMetrics
import android.view.Surface
import androidx.test.core.app.ApplicationProvider
@@ -32,7 +35,6 @@
import com.android.launcher3.util.rule.TestStabilityRule
import com.android.launcher3.util.window.CachedDisplayInfo
import com.android.launcher3.util.window.WindowManagerProxy
-import com.android.wm.shell.Flags
import com.google.common.truth.Truth
import java.io.BufferedReader
import java.io.File
@@ -52,6 +54,8 @@
*
* For an implementation that mocks InvariantDeviceProfile, use [FakeInvariantDeviceProfileTest]
*/
+@AllowedDevices(allowed = [DeviceProduct.CF_PHONE])
+@IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
abstract class AbstractDeviceProfileTest {
protected val testContext: Context = InstrumentationRegistry.getInstrumentation().context
protected lateinit var context: SandboxContext
@@ -59,15 +63,11 @@
private val displayController: DisplayController = mock()
private val windowManagerProxy: WindowManagerProxy = mock()
private val launcherPrefs: LauncherPrefs = mock()
- private val allowLeftRightSplitInPortrait: Boolean = initAllowLeftRightSplitInPortrait()
- fun initAllowLeftRightSplitInPortrait() : Boolean {
- val res = Resources.getSystem();
- val resId = res.getIdentifier("config_leftRightSplitInPortrait", "bool", "android")
- return Flags.enableLeftRightSplitInPortrait() && resId > 0 && res.getBoolean(resId)
- }
@Rule @JvmField val testStabilityRule = TestStabilityRule()
+ @Rule @JvmField val limitDevicesRule = LimitDevicesRule()
+
class DeviceSpec(
val naturalSize: Pair<Int, Int>,
var densityDpi: Int,
@@ -124,8 +124,7 @@
) {
val (naturalX, naturalY) = deviceSpec.naturalSize
val windowsBounds = phoneWindowsBounds(deviceSpec, isGestureMode, naturalX, naturalY)
- val displayInfo =
- CachedDisplayInfo(Point(naturalX, naturalY), Surface.ROTATION_0, Rect(0, 0, 0, 0))
+ val displayInfo = CachedDisplayInfo(Point(naturalX, naturalY), Surface.ROTATION_0)
val perDisplayBoundsCache = mapOf(displayInfo to windowsBounds)
initializeCommonVars(
@@ -144,8 +143,7 @@
) {
val (naturalX, naturalY) = deviceSpec.naturalSize
val windowsBounds = tabletWindowsBounds(deviceSpec, naturalX, naturalY)
- val displayInfo =
- CachedDisplayInfo(Point(naturalX, naturalY), Surface.ROTATION_0, Rect(0, 0, 0, 0))
+ val displayInfo = CachedDisplayInfo(Point(naturalX, naturalY), Surface.ROTATION_0)
val perDisplayBoundsCache = mapOf(displayInfo to windowsBounds)
initializeCommonVars(
@@ -168,21 +166,13 @@
val unfoldedWindowsBounds =
tabletWindowsBounds(deviceSpecUnfolded, unfoldedNaturalX, unfoldedNaturalY)
val unfoldedDisplayInfo =
- CachedDisplayInfo(
- Point(unfoldedNaturalX, unfoldedNaturalY),
- Surface.ROTATION_0,
- Rect(0, 0, 0, 0)
- )
+ CachedDisplayInfo(Point(unfoldedNaturalX, unfoldedNaturalY), Surface.ROTATION_0)
val (foldedNaturalX, foldedNaturalY) = deviceSpecFolded.naturalSize
val foldedWindowsBounds =
phoneWindowsBounds(deviceSpecFolded, isGestureMode, foldedNaturalX, foldedNaturalY)
val foldedDisplayInfo =
- CachedDisplayInfo(
- Point(foldedNaturalX, foldedNaturalY),
- Surface.ROTATION_0,
- Rect(0, 0, 0, 0)
- )
+ CachedDisplayInfo(Point(foldedNaturalX, foldedNaturalY), Surface.ROTATION_0)
val perDisplayBoundsCache =
mapOf(
@@ -321,18 +311,6 @@
protected fun assertDump(dp: DeviceProfile, folderName: String, filename: String) {
val dump = dump(context!!, dp, "${folderName}_$filename.txt")
var expected = readDumpFromAssets(testContext, "$folderName/$filename.txt")
-
- // TODO(b/315230497): We don't currently have device-specific device profile dumps, so just
- // update the result before we do the comparison
- if (allowLeftRightSplitInPortrait) {
- val isLeftRightSplitInPortrait = when {
- allowLeftRightSplitInPortrait && dp.isTablet -> !dp.isLandscape
- else -> dp.isLandscape
- }
- expected = expected.replace(Regex("isLeftRightSplit:\\w+"),
- "isLeftRightSplit:$isLeftRightSplitInPortrait")
- }
-
Truth.assertThat(dump).isEqualTo(expected)
}
diff --git a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
index 30b5663..251a401 100644
--- a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
@@ -18,6 +18,10 @@
import android.content.Context
import android.graphics.PointF
import android.graphics.Rect
+import android.platform.test.rule.AllowedDevices
+import android.platform.test.rule.DeviceProduct
+import android.platform.test.rule.IgnoreLimit
+import android.platform.test.rule.LimitDevicesRule
import android.util.SparseArray
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.DeviceProfile.DEFAULT_DIMENSION_PROVIDER
@@ -27,6 +31,7 @@
import java.io.PrintWriter
import java.io.StringWriter
import org.junit.Before
+import org.junit.Rule
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -37,6 +42,8 @@
*
* For an implementation that creates InvariantDeviceProfile, use [AbstractDeviceProfileTest]
*/
+@AllowedDevices(allowed = [DeviceProduct.CF_PHONE])
+@IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
abstract class FakeInvariantDeviceProfileTest {
protected var context: Context? = null
@@ -49,6 +56,8 @@
protected var isGestureMode: Boolean = true
protected var isTransientTaskbar: Boolean = true
+ @Rule @JvmField val limitDevicesRule = LimitDevicesRule()
+
@Before
fun setUp() {
context = ApplicationProvider.getApplicationContext()
diff --git a/tests/src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java b/tests/src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java
index cd05a88..ef5af0f 100644
--- a/tests/src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java
@@ -60,5 +60,6 @@
} finally {
allApps.unfreeze();
}
+ mLauncher.goHome();
}
}
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
index a00498c..6fce4c6 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -29,8 +29,8 @@
import androidx.test.filters.FlakyTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.tapl.AllApps;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
@@ -207,7 +207,7 @@
public void testPressBackFromAllAppsToHome() {
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
READ_DEVICE_CONFIG_PERMISSION);
- assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get());
+ assumeFalse(Flags.enablePredictiveBackGesture());
mLauncher
.getWorkspace()
.switchToAllApps()
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
new file mode 100644
index 0000000..e459956
--- /dev/null
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -0,0 +1,357 @@
+/*
+ * 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.popup;
+
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.launcher3.Flags.FLAG_ENABLE_PRIVATE_SPACE;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.os.Process;
+import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.View;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.allapps.PrivateProfileManager;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
+import com.android.launcher3.util.TestSandboxModelContextWrapper;
+import com.android.launcher3.util.UserIconInfo;
+import com.android.launcher3.views.BaseDragLayer;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemShortcutTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+ private static final UserHandle PRIVATE_HANDLE = new UserHandle(11);
+ private static final UserHandle MAIN_HANDLE = Process.myUserHandle();
+ private View mView;
+ private ItemInfo mItemInfo;
+ private TestSandboxModelContextWrapper mTestContext;
+ private final SandboxModelContext mSandboxContext = new SandboxModelContext();
+ private PrivateProfileManager mPrivateProfileManager;
+ private PopupDataProvider mPopupDataProvider;
+ private AppInfo mAppInfo;
+ @Mock UserCache mUserCache;
+ @Mock BaseDragLayer mBaseDragLayer;
+ @Mock UserIconInfo mUserIconInfo;
+ @Mock LauncherActivityInfo mLauncherActivityInfo;
+ @Mock ApplicationInfo mApplicationInfo;
+ @Mock Intent mIntent;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSandboxContext.putObject(UserCache.INSTANCE, mUserCache);
+ mTestContext = new TestSandboxModelContextWrapper(mSandboxContext);
+ mView = new View(mSandboxContext);
+ spyOn(mTestContext);
+ spyOn(mSandboxContext);
+ doReturn(mBaseDragLayer).when(mTestContext).getDragLayer();
+
+ mItemInfo = new ItemInfo();
+
+ LauncherApps mLauncherApps = mSandboxContext.spyService(LauncherApps.class);
+ doReturn(mLauncherActivityInfo).when(mLauncherApps).resolveActivity(any(), any());
+ when(mLauncherActivityInfo.getApplicationInfo()).thenReturn(mApplicationInfo);
+
+ when(mUserCache.getUserInfo(any())).thenReturn(mUserIconInfo);
+ when(mBaseDragLayer.getChildCount()).thenReturn(0);
+ mPrivateProfileManager = mTestContext.getAppsView().getPrivateProfileManager();
+ spyOn(mPrivateProfileManager);
+ when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE);
+
+ mPopupDataProvider = mTestContext.getPopupDataProvider();
+ spyOn(mPopupDataProvider);
+ }
+
+ @After
+ public void tearDown() {
+ mSandboxContext.onDestroy();
+ }
+
+ @Test
+ public void testWidgetsForNullComponentName() {
+ assertNull(mItemInfo.getTargetComponent());
+ SystemShortcut systemShortcut = SystemShortcut.WIDGETS
+ .getShortcut(mTestContext, mItemInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testWidgetsForEmptyWidgetList() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ assertNotNull(mAppInfo.getTargetComponent());
+ doReturn(new ArrayList<>()).when(mPopupDataProvider).getWidgetsForPackageUser(any());
+ spyOn(mAppInfo);
+ SystemShortcut systemShortcut = SystemShortcut.WIDGETS
+ .getShortcut(mTestContext, mAppInfo, mView);
+ verify(mAppInfo, times(2)).getTargetComponent();
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testAppInfoShortcut() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ SystemShortcut systemShortcut = SystemShortcut.APP_INFO
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNotNull(systemShortcut);
+ }
+
+
+ @Test
+ public void testDontSuggestAppForNonPredictedItem() {
+ assertFalse(mItemInfo.isPredictedItem());
+ SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP
+ .getShortcut(mTestContext, mItemInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testDontSuggestAppForPredictedItem() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION;
+ assertTrue(mAppInfo.isPredictedItem());
+ SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNotNull(systemShortcut);
+ systemShortcut.onClick(mView);
+ }
+
+ @Test
+ public void testPrivateProfileInstallwithTargetComponentNull() {
+ assertNull(mItemInfo.getTargetComponent());
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mItemInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallNotAllAppsContainer() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION;
+
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertFalse(mAppInfo.getContainerInfo().hasAllAppsContainer());
+
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallNullPrivateProfileManager() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_ALL_APPS;
+ mPrivateProfileManager = null;
+
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
+ assertNull(mPrivateProfileManager);
+
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallPrivateProfileManagerDisabled() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_ALL_APPS;
+
+ assertNotNull(mPrivateProfileManager);
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
+
+ when(mPrivateProfileManager.isEnabled()).thenReturn(false);
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallNullPrivateProfileUser() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_ALL_APPS;
+ when(mPrivateProfileManager.getProfileUser()).thenReturn(null);
+
+ assertNotNull(mPrivateProfileManager);
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
+ assertNull(mPrivateProfileManager.getProfileUser());
+
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+
+ verify(mPrivateProfileManager, times(2)).getProfileUser();
+ assertNull(systemShortcut);
+ }
+
+ @Test
+ public void testPrivateProfileInstallNonNullPrivateProfileUser() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.container = CONTAINER_ALL_APPS;
+ when(mPrivateProfileManager.isEnabled()).thenReturn(true);
+ when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE);
+
+ assertNotNull(mAppInfo.getTargetComponent());
+ assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
+ assertNotNull(mPrivateProfileManager);
+ assertNotNull(mPrivateProfileManager.getProfileUser());
+ assertNull(mTestContext.getAppsView().getAppsStore().getApp(
+ new ComponentKey(mAppInfo.getTargetComponent(), PRIVATE_HANDLE)));
+
+ SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
+ .getShortcut(mTestContext, mAppInfo, mView);
+
+ verify(mPrivateProfileManager, times(3)).getProfileUser();
+ verify(mPrivateProfileManager).isEnabled();
+ assertNotNull(systemShortcut);
+ }
+
+ @Test
+ public void testInstallGetShortcutWithNonWorkSpaceItemInfo() {
+ SystemShortcut systemShortcut = SystemShortcut.INSTALL.getShortcut(
+ mTestContext, mItemInfo, mView);
+ Assert.assertNull(systemShortcut);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testInstallGetShortcutWithWorkSpaceItemInfo() {
+ mAppInfo = new AppInfo();
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ mAppInfo.intent = mIntent;
+ WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mAppInfo);
+ workspaceItemInfo.status = FLAG_SUPPORTS_WEB_UI;
+ SystemShortcut systemShortcut = SystemShortcut.INSTALL.getShortcut(
+ mTestContext, workspaceItemInfo, mView);
+ Assert.assertNotNull(systemShortcut);
+ }
+
+
+ @Test
+ @DisableFlags(FLAG_ENABLE_PRIVATE_SPACE)
+ public void testUninstallGetShortcutWithPrivateSpaceOff() {
+ SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
+ mTestContext, null, mView);
+ Assert.assertNull(systemShortcut);
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE)
+ public void testUninstallGetShortcutWithNonPrivateItemInfo() {
+ mAppInfo = new AppInfo();
+ mAppInfo.user = MAIN_HANDLE;
+ when(mUserIconInfo.isPrivate()).thenReturn(false);
+
+ SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
+ mTestContext, mAppInfo, mView);
+ verify(mUserIconInfo).isPrivate();
+ Assert.assertNull(systemShortcut);
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE)
+ public void testUninstallGetShortcutWithSystemItemInfo() {
+ mAppInfo = new AppInfo();
+ mAppInfo.user = PRIVATE_HANDLE;
+ mAppInfo.itemType = ITEM_TYPE_APPLICATION;
+ mAppInfo.intent = mIntent;
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ when(mLauncherActivityInfo.getComponentName()).thenReturn(mAppInfo.componentName);
+ when(mUserIconInfo.isPrivate()).thenReturn(true);
+ // System App
+ mApplicationInfo.flags = 1;
+
+ SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
+ mTestContext, mAppInfo, mView);
+ verify(mLauncherActivityInfo, times(0)).getComponentName();
+ Assert.assertNull(systemShortcut);
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE)
+ public void testUninstallGetShortcutWithPrivateItemInfo() {
+ mAppInfo = new AppInfo();
+ mAppInfo.user = PRIVATE_HANDLE;
+ mAppInfo.itemType = ITEM_TYPE_APPLICATION;
+ mAppInfo.intent = mIntent;
+ mAppInfo.componentName = new ComponentName(mTestContext, getClass());
+ when(mUserIconInfo.isPrivate()).thenReturn(true);
+ when(mLauncherActivityInfo.getComponentName()).thenReturn(mAppInfo.componentName);
+ // 3rd party app, not system app.
+ mApplicationInfo.flags = 0;
+
+ SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
+ mTestContext, mAppInfo, mView);
+
+ verify(mLauncherActivityInfo).getComponentName();
+ Assert.assertNotNull(systemShortcut);
+
+ systemShortcut.onClick(mView);
+ verify(mSandboxContext).startActivity(any());
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
index cb30854..f8f5dde 100644
--- a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
@@ -28,10 +28,13 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import android.platform.test.rule.ScreenRecordRule;
import android.util.Log;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
@@ -47,11 +50,14 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.Objects;
import java.util.function.Predicate;
+@LargeTest
+@RunWith(AndroidJUnit4.class)
public class TaplWorkProfileTest extends AbstractLauncherUiTest {
private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK;
@@ -64,6 +70,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
+ initialize(this);
String output =
mDevice.executeShellCommand(
"pm create-user --profileOf 0 --managed TestProfile");
@@ -139,7 +146,7 @@
// Staging; will be promoted to presubmit if stable
@TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
-
+ @ScreenRecordRule.ScreenRecord
@Test
public void toggleWorks() {
assumeTrue(mWorkProfileSetupSuccessful);
diff --git a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
index 8670d40..706ab27 100644
--- a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
+++ b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -95,8 +95,7 @@
whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
// Mock WindowManagerProxy
- val displayInfo =
- CachedDisplayInfo(Point(width, height), Surface.ROTATION_0, Rect(0, 0, 0, 0))
+ val displayInfo = CachedDisplayInfo(Point(width, height), Surface.ROTATION_0)
whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
whenever(windowManagerProxy.estimateInternalDisplayBounds(any()))
.thenAnswer(
@@ -135,8 +134,7 @@
@Test
@UiThreadTest
fun testRotation() {
- val displayInfo =
- CachedDisplayInfo(Point(height, width), Surface.ROTATION_90, Rect(0, 0, 0, 0))
+ val displayInfo = CachedDisplayInfo(Point(height, width), Surface.ROTATION_90)
whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
whenever(display.rotation).thenReturn(displayInfo.rotation)
val configuration =
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
new file mode 100644
index 0000000..b347f07
--- /dev/null
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.widget.picker;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.ActivityContextWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WidgetsListHeaderAccessibilityTest {
+ private Context mContext;
+ private LayoutInflater mLayoutInflater;
+ @Mock
+ private View.OnClickListener mOnClickListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = new ActivityContextWrapper(getApplicationContext());
+ mLayoutInflater = LayoutInflater.from(
+ new ContextThemeWrapper(mContext, R.style.WidgetContainerTheme));
+ }
+
+ @Test
+ public void singlePaneCollapsable_hasCustomAccessibilityActions() {
+ WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
+ R.layout.widgets_list_row_header,
+ new FrameLayout(mContext), false);
+
+ assertThat(header.getAccessibilityDelegate()).isNotNull();
+
+ header.setOnClickListener(mOnClickListener);
+ header.getAccessibilityDelegate().performAccessibilityAction(header,
+ AccessibilityNodeInfo.ACTION_EXPAND, null);
+ header.getAccessibilityDelegate().performAccessibilityAction(header,
+ AccessibilityNodeInfo.ACTION_COLLAPSE, null);
+
+ verify(mOnClickListener, times(2)).onClick(header);
+ }
+
+ @Test
+ public void twoPaneNonCollapsable_noCustomAccessibilityDelegate() {
+ WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
+ R.layout.widgets_list_row_header_two_pane,
+ new FrameLayout(mContext), false);
+
+ assertThat(header.getAccessibilityDelegate()).isNull();
+ }
+}