Merge "[PS] Update PS icon." 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..05e1535 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1677,7 +1677,7 @@
}
};
- if (fromPredictiveBack) {
+ if (fromPredictiveBack && rectFSpringAnim != null) {
rectFSpringAnim.addAnimatorListener(endListener);
} else {
anim.addListener(endListener);
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/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/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/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index d98e608..74ba006 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -31,6 +31,8 @@
import android.view.Window;
import android.view.WindowManager;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -51,12 +53,12 @@
private final Launcher mLauncher;
private final SystemUiProxy mSystemUiProxy;
- private final float mTouchSlop;
+ @VisibleForTesting final float mTouchSlop;
private int mLastAction;
private final SparseArray<PointF> mDownEvents;
/* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
- private boolean mCanIntercept;
+ @VisibleForTesting boolean mCanIntercept;
private boolean mIsTrackpadReverseScroll;
@@ -85,9 +87,9 @@
@Override
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- int action = ev.getActionMasked();
- int idx = ev.getActionIndex();
- int pid = ev.getPointerId(idx);
+ final int action = ev.getActionMasked();
+ final int idx = ev.getActionIndex();
+ final int pid = ev.getPointerId(idx);
if (action == ACTION_DOWN) {
mCanIntercept = canInterceptTouch(ev);
if (!mCanIntercept) {
@@ -135,7 +137,6 @@
.log(LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN);
setWindowSlippery(false);
mIsTrackpadReverseScroll = false;
- return true;
}
return true;
}
@@ -149,7 +150,8 @@
* Touches can slide out of the window but they cannot necessarily slide
* back in (unless the other window with touch focus permits it).
*/
- private void setWindowSlippery(boolean enable) {
+ @VisibleForTesting
+ void setWindowSlippery(boolean enable) {
Window w = mLauncher.getWindow();
WindowManager.LayoutParams wlp = w.getAttributes();
if (enable) {
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/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 3735cbf..5228420 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -24,6 +24,7 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.Flags.useActivityOverlay;
import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
import static com.android.launcher3.LauncherPrefs.backedUpItem;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
@@ -1159,6 +1160,14 @@
boolean launcherResumedThroughShellTransition =
gestureState.getActivityInterface().isResumed()
&& !previousGestureState.isRecentsAnimationRunning();
+ // If a task fragment within Launcher is resumed
+ boolean launcherChildActivityResumed = useActivityOverlay()
+ && runningTask != null
+ && runningTask.isHomeTask()
+ && mOverviewComponentObserver.isHomeAndOverviewSame()
+ && !launcherResumedThroughShellTransition
+ && !previousGestureState.isRecentsAnimationRunning();
+
if (gestureState.getActivityInterface().isInLiveTileMode()) {
return createOverviewInputConsumer(
previousGestureState,
@@ -1185,9 +1194,11 @@
? "launcher resumed through a shell transition"
: "forceOverviewInputConsumer == true"))
.append(", trying to use overview input consumer"));
- } else if (mDeviceState.isGestureBlockedTask(runningTask)) {
+ } else if (mDeviceState.isGestureBlockedTask(runningTask) || launcherChildActivityResumed) {
return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
- .append("is gesture-blocked task, trying to use default input consumer"));
+ .append(launcherChildActivityResumed
+ ? "is launcher child-task, trying to use default input consumer"
+ : "is gesture-blocked task, trying to use default input consumer"));
} else {
reasonString.append(SUBSTRING_PREFIX)
.append("using OtherActivityInputConsumer");
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..caffab1 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -153,7 +153,17 @@
controller.finish(true /* toRecents */, null /* onFinishComplete */,
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 d2b0540..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;
@@ -86,6 +87,7 @@
private final int mMinIconBackgroundHeight;
private final int mMaxIconBackgroundCornerRadius;
private final float mMinIconBackgroundCornerRadius;
+ private AnimatorSet mAnimator;
private int mMaxWidth = Integer.MAX_VALUE;
@@ -306,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();
}
@@ -316,11 +323,13 @@
}
protected void revealAnim(boolean isRevealing) {
+ cancelInProgressAnimations();
+
if (isRevealing) {
boolean isRtl = isLayoutRtl();
bringToFront();
((AnimatedVectorDrawable) mIconArrowView.getDrawable()).start();
- AnimatorSet anim = new AnimatorSet();
+ mAnimator = new AnimatorSet();
float backgroundScaleY = mMaxIconBackgroundHeight / (float) mMinIconBackgroundHeight;
float maxCornerSize = Math.min(mMaxIconBackgroundHeight / 2f,
mMaxIconBackgroundCornerRadius);
@@ -341,7 +350,7 @@
mIconTextMaxWidth + maxCornerSize);
}
});
- anim.playTogether(
+ mAnimator.playTogether(
expandedTextRevealAnim,
ObjectAnimator.ofFloat(mIconViewBackgroundCornersStart, SCALE_Y,
backgroundScaleY),
@@ -367,9 +376,9 @@
ObjectAnimator.ofFloat(mIconTextExpandedView, ALPHA, 1),
ObjectAnimator.ofFloat(mIconArrowView, TRANSLATION_X,
isRtl ? -arrowTranslationX : arrowTranslationX));
- anim.setDuration(MENU_BACKGROUND_REVEAL_DURATION);
- anim.setInterpolator(EMPHASIZED);
- anim.start();
+ mAnimator.setDuration(MENU_BACKGROUND_REVEAL_DURATION);
+ mAnimator.setInterpolator(EMPHASIZED);
+ mAnimator.start();
} else {
((AnimatedVectorDrawable) mIconArrowView.getDrawable()).reverse();
float maxCornerSize = Math.min(mMaxIconBackgroundHeight / 2f,
@@ -386,8 +395,8 @@
mIconTextExpandedView.getHeight() / 2f, 0);
}
});
- AnimatorSet anim = new AnimatorSet();
- anim.playTogether(
+ mAnimator = new AnimatorSet();
+ mAnimator.playTogether(
expandedTextClipAnim,
ObjectAnimator.ofFloat(mIconViewBackgroundCornersStart, SCALE_X, 1),
ObjectAnimator.ofFloat(mIconViewBackgroundCornersStart, SCALE_Y, 1),
@@ -403,9 +412,9 @@
ObjectAnimator.ofFloat(mIconTextCollapsedView, ALPHA, 1),
ObjectAnimator.ofFloat(mIconTextExpandedView, ALPHA, 0),
ObjectAnimator.ofFloat(mIconArrowView, TRANSLATION_X, 0));
- anim.setDuration(MENU_BACKGROUND_HIDE_DURATION);
- anim.setInterpolator(EMPHASIZED);
- anim.start();
+ mAnimator.setDuration(MENU_BACKGROUND_HIDE_DURATION);
+ mAnimator.setInterpolator(EMPHASIZED);
+ mAnimator.start();
}
}
@@ -425,6 +434,16 @@
mIconTextExpandedView.setAlpha(0);
mIconArrowView.setTranslationX(0);
((AnimatedVectorDrawable) mIconArrowView.getDrawable()).reset();
+ mAnimator = null;
+ }
+
+ private void cancelInProgressAnimations() {
+ // We null the `AnimatorSet` because it holds references to the `Animators` which aren't
+ // expecting to be mutable and will cause a crash if they are re-used.
+ if (mAnimator != null && mAnimator.isStarted()) {
+ mAnimator.cancel();
+ mAnimator = null;
+ }
}
@Override
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..fecbf08 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1811,9 +1811,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/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index d779276..cf50835 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -24,6 +24,7 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -69,6 +70,8 @@
private TextView mTaskName;
@Nullable
private AnimatorSet mOpenCloseAnimator;
+ @Nullable
+ private ValueAnimator mRevealAnimator;
@Nullable private Runnable mOnClosingStartCallback;
private TaskView mTaskView;
private TaskIdAttributeContainer mTaskContainer;
@@ -290,13 +293,18 @@
private void animateOpenOrClosed(boolean closing) {
if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
- mOpenCloseAnimator.end();
+ mOpenCloseAnimator.cancel();
}
mOpenCloseAnimator = new AnimatorSet();
-
- final Animator revealAnimator = createOpenCloseOutlineProvider()
- .createRevealAnimator(this, closing);
- revealAnimator.setInterpolator(enableOverviewIconMenu() ? Interpolators.EMPHASIZED
+ // If we're opening, we just start from the beginning as a new `TaskMenuView` is created
+ // each time we do the open animation so there will never be a partial value here.
+ float revealAnimationStartProgress = 0f;
+ if (closing && mRevealAnimator != null) {
+ revealAnimationStartProgress = 1f - mRevealAnimator.getAnimatedFraction();
+ }
+ mRevealAnimator = createOpenCloseOutlineProvider()
+ .createRevealAnimator(this, closing, revealAnimationStartProgress);
+ mRevealAnimator.setInterpolator(enableOverviewIconMenu() ? Interpolators.EMPHASIZED
: Interpolators.DECELERATE);
if (enableOverviewIconMenu()) {
@@ -349,7 +357,7 @@
mOpenCloseAnimator.playTogether(translationXAnim, menuTranslationXAnim);
}
- mOpenCloseAnimator.playTogether(revealAnimator,
+ mOpenCloseAnimator.playTogether(mRevealAnimator,
ObjectAnimator.ofFloat(
mTaskContainer.getThumbnailView(), DIM_ALPHA,
closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
@@ -378,6 +386,7 @@
mIsOpen = false;
resetOverviewIconMenu();
mActivity.getDragLayer().removeView(this);
+ mRevealAnimator = null;
}
private void resetOverviewIconMenu() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 11e721e..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);
}
@@ -1137,9 +1137,8 @@
DeviceProfile dp = mActivity.getDeviceProfile();
if (enableOverviewIconMenu() && iconView instanceof IconAppChipView) {
((IconAppChipView) iconView).revealAnim(/* isRevealing= */ true);
- return TaskMenuView.showForTask(menuContainer, () -> {
- ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ false);
- });
+ return TaskMenuView.showForTask(menuContainer,
+ () -> ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ false));
} else if (dp.isTablet) {
int alignedOptionIndex = 0;
if (getRecentsView().isOnGridBottomRow(menuContainer.getTaskView()) && dp.isLandscape) {
@@ -1721,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();
@@ -1759,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/launcher3/uioverrides/touchcontrollers/StatusBarTouchControllerTest.kt b/quickstep/tests/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchControllerTest.kt
new file mode 100644
index 0000000..119b862
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchControllerTest.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.uioverrides.touchcontrollers
+
+import android.view.MotionEvent
+import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.Launcher
+import com.android.launcher3.ui.AbstractLauncherUiTest
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarTouchControllerTest : AbstractLauncherUiTest() {
+ @Before
+ @Throws(Exception::class)
+ fun setup() {
+ super.setUp()
+ initialize(this)
+ }
+
+ @Test
+ fun interceptActionDown_canIntercept() {
+ executeOnLauncher { launcher ->
+ val underTest = StatusBarTouchController(launcher)
+ assertFalse(underTest.mCanIntercept)
+ val downEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+ underTest.onControllerInterceptTouchEvent(downEvent)
+
+ assertTrue(underTest.mCanIntercept)
+ }
+ }
+
+ @Test
+ fun interceptVerticalActionMove_handledAndSetSlippery() {
+ executeOnLauncher { launcher ->
+ val underTest = StatusBarTouchController(launcher)
+ val downEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ underTest.onControllerInterceptTouchEvent(downEvent)
+ val w = launcher.window
+ assertEquals(0, w.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY)
+ val moveEvent =
+ MotionEvent.obtain(
+ 2,
+ 2,
+ MotionEvent.ACTION_MOVE,
+ underTest.mTouchSlop,
+ underTest.mTouchSlop + 10,
+ 0
+ )
+
+ val handled = underTest.onControllerInterceptTouchEvent(moveEvent)
+
+ assertTrue(handled)
+ assertEquals(
+ WindowManager.LayoutParams.FLAG_SLIPPERY,
+ w.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY
+ )
+ }
+ }
+
+ @Test
+ fun interceptHorizontalActionMove_not_handled() {
+ executeOnLauncher { launcher ->
+ val underTest = StatusBarTouchController(launcher)
+ val downEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ underTest.onControllerInterceptTouchEvent(downEvent)
+ val moveEvent =
+ MotionEvent.obtain(
+ 2,
+ 2,
+ MotionEvent.ACTION_MOVE,
+ underTest.mTouchSlop + 10,
+ underTest.mTouchSlop,
+ 0
+ )
+
+ val handled = underTest.onControllerInterceptTouchEvent(moveEvent)
+
+ assertFalse(handled)
+ }
+ }
+
+ @Test
+ fun interceptActionMoveAsFirstGestureEvent_notCrashedNorHandled() {
+ executeOnLauncher { launcher ->
+ val underTest = StatusBarTouchController(launcher)
+ underTest.mCanIntercept = true
+ val moveEvent = MotionEvent.obtain(2, 2, MotionEvent.ACTION_MOVE, 10f, 10f, 0)
+
+ val handled = underTest.onControllerInterceptTouchEvent(moveEvent)
+
+ assertFalse(handled)
+ }
+ }
+
+ @Test
+ fun handleActionUp_setNotSlippery() {
+ executeOnLauncher { launcher: Launcher ->
+ val underTest = StatusBarTouchController(launcher)
+ underTest.mCanIntercept = true
+ underTest.setWindowSlippery(true)
+ val moveEvent = MotionEvent.obtain(2, 2, MotionEvent.ACTION_UP, 10f, 10f, 0)
+
+ val handled = underTest.onControllerTouchEvent(moveEvent)
+
+ assertTrue(handled)
+ assertEquals(
+ 0,
+ launcher.window.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY
+ )
+ }
+ }
+
+ @Test
+ fun handleActionCancel_setNotSlippery() {
+ executeOnLauncher { launcher ->
+ val underTest = StatusBarTouchController(launcher)
+ underTest.mCanIntercept = true
+ underTest.setWindowSlippery(true)
+ val moveEvent = MotionEvent.obtain(2, 2, MotionEvent.ACTION_CANCEL, 10f, 10f, 0)
+
+ val handled = underTest.onControllerTouchEvent(moveEvent)
+
+ assertTrue(handled)
+ assertEquals(
+ 0,
+ launcher.window.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY
+ )
+ }
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
index 3f806d1..7c1b7f3 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize;
import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
@@ -25,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;
@@ -86,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..c472ef2 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)
+ .launch(CALCULATOR_APP_PACKAGE);
+ } 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/values/dimens.xml b/res/values/dimens.xml
index 3016559..3aa4a77 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -56,7 +56,7 @@
<!-- App Widget resize frame -->
<dimen name="widget_handle_margin">13dp</dimen>
<dimen name="resize_frame_background_padding">24dp</dimen>
- <dimen name="resize_frame_margin">22dp</dimen>
+ <dimen name="resize_frame_margin">23dp</dimen>
<dimen name="resize_frame_invalid_drag_across_two_panel_opacity_margin">24dp</dimen>
<!-- App widget reconfigure button -->
@@ -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/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/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/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 4427a49..f9d047b 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -20,6 +20,7 @@
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.SuggestionSpan;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnFocusChangeListener;
@@ -42,6 +43,7 @@
implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener,
OnFocusChangeListener {
+ private static final String TAG = "AllAppsSearchBarController";
protected ActivityContext mLauncher;
protected SearchCallback<AdapterItem> mCallback;
protected ExtendedEditText mInput;
@@ -122,6 +124,7 @@
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
+ Log.i(TAG, "User tapped ime search button");
// selectFocusedView should return SearchTargetEvent that is passed onto onClick
return mLauncher.getAppsView().getMainAdapterProvider().launchHighlightedItem();
}
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/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 5e86bd6..96a8da9 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -33,6 +33,7 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemFactory;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -102,6 +103,11 @@
Objects.requireNonNull(item.getIntent()))) {
continue;
}
+
+ if (item instanceof ItemInfoWithIcon
+ && ((ItemInfoWithIcon) item).isArchived()) {
+ continue;
+ }
}
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
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/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index 9a3abd4..59f453a 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -18,10 +18,12 @@
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
+import static com.android.launcher3.Flags.enableSupportForArchiving;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.appwidget.AppWidgetManager;
@@ -276,6 +278,7 @@
return intent;
}
+ @SuppressWarnings("NewApi")
public Pair<ItemInfo, Object> getItemInfo(Context context) {
switch (itemType) {
case ITEM_TYPE_APPLICATION: {
@@ -297,6 +300,9 @@
} else {
lai = laiList.get(0);
si.intent = makeLaunchIntent(lai);
+ if (enableSupportForArchiving() && lai.getActivityInfo().isArchived) {
+ si.runtimeStatusFlags |= FLAG_ARCHIVED;
+ }
}
LauncherAppState.getInstance(context).getIconCache()
.getTitleAndIcon(si, () -> lai, usePackageIcon, false);
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/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 315b5e3..5636405 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -150,6 +150,12 @@
}, this::getCurrentActivity);
}
+ case TestProtocol.REQUEST_CELL_LAYOUT_BOARDER_HEIGHT: {
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ mDeviceProfile.cellLayoutBorderSpacePx.y);
+ return response;
+ }
+
case TestProtocol.REQUEST_SYSTEM_GESTURE_REGION: {
return getUIProperty(Bundle::putParcelable, activity -> {
WindowInsetsCompat insets = WindowInsetsCompat.toWindowInsetsCompat(
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index f47ab90..ded4da6 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;
@@ -331,7 +330,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();
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/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 2b5aaf5..50d8886 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,6 +16,8 @@
package com.android.launcher3.util;
+import static com.android.launcher3.Flags.enableSupportForArchiving;
+
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -112,8 +114,7 @@
@NonNull final UserHandle user, final int flags) {
try {
ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
- return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
- ? null : info;
+ return !isPackageInstalledOrArchived(info) || !info.enabled ? null : info;
} catch (PackageManager.NameNotFoundException e) {
return null;
}
@@ -253,4 +254,11 @@
}
return 100;
}
+
+ /** Returns true in case app is installed on the device or in archived state. */
+ @SuppressWarnings("NewApi")
+ private boolean isPackageInstalledOrArchived(ApplicationInfo info) {
+ return (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 || (
+ enableSupportForArchiving() && info.isArchived);
+ }
}
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..17d9276 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -182,7 +182,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));
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 6656237..26c04f5 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -303,6 +303,12 @@
}
@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/Android.bp b/tests/Android.bp
index 310e418..ed8609e 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -176,6 +176,7 @@
name: "launcher-testing-shared",
srcs: [
"multivalentTests/shared/com/android/launcher3/testing/shared/**/*.java",
+ "multivalentTests/shared/com/android/launcher3/testing/shared/**/*.kt"
],
resource_dirs: [],
manifest: "multivalentTests/shared/AndroidManifest.xml",
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 3e188e6..2f9945d 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -121,6 +121,7 @@
public static final String REQUEST_IS_TABLET = "is-tablet";
public static final String REQUEST_NUM_ALL_APPS_COLUMNS = "num-all-apps-columns";
public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
+ public static final String REQUEST_CELL_LAYOUT_BOARDER_HEIGHT = "cell-layout-boarder-height";
public static final String REQUEST_START_DRAG_THRESHOLD = "start-drag-threshold";
public static final String REQUEST_SHELL_DRAG_READY = "shell-drag-ready";
public static final String REQUEST_GET_ACTIVITIES_CREATED_COUNT =
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
index dbbdcf5..62f2259 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
@@ -66,7 +66,7 @@
}
public CellLayoutBoard(int width, int height) {
- mWidget = new char[width + 1][height + 1];
+ mWidget = new char[width][height];
this.mWidth = width;
this.mHeight = height;
for (int x = 0; x < mWidget.length; x++) {
@@ -371,8 +371,8 @@
s.append("\n");
maxX = Math.min(maxX, mWidget.length);
maxY = Math.min(maxY, mWidget[0].length);
- for (int y = 0; y <= maxY; y++) {
- for (int x = 0; x <= maxX; x++) {
+ for (int y = 0; y < maxY; y++) {
+ for (int x = 0; x < maxX; x++) {
s.append(mWidget[x][y]);
}
s.append('\n');
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
index 770024f..fcfb3db 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
@@ -27,7 +27,7 @@
* usually less than 100.
* @return a randomly generated board filled with icons and widgets.
*/
- open fun generateBoard(width: Int, height: Int, remainingEmptySpaces: Int): CellLayoutBoard? {
+ open fun generateBoard(width: Int, height: Int, remainingEmptySpaces: Int): CellLayoutBoard {
val cellLayoutBoard = CellLayoutBoard(width, height)
return fillBoard(cellLayoutBoard, Rect(0, 0, width, height), remainingEmptySpaces)
}
@@ -39,8 +39,8 @@
): CellLayoutBoard {
var remainingEmptySpaces = remainingEmptySpacesArg
if (area.height() * area.width() <= 0) return board
- val width = getRandom(1, area.width() - 1)
- val height = getRandom(1, area.height() - 1)
+ val width = getRandom(1, area.width())
+ val height = getRandom(1, area.height())
val x = area.left + getRandom(0, area.width() - width)
val y = area.top + getRandom(0, area.height() - height)
if (remainingEmptySpaces > 0) {
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 fef93b7..6c9f5ed 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -405,6 +405,11 @@
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ int getCellLayoutBoarderHeight() {
+ return getTestInfo(TestProtocol.REQUEST_CELL_LAYOUT_BOARDER_HEIGHT)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
int getFocusedTaskHeightForTablet() {
return getTestInfo(TestProtocol.REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET).getInt(
TestProtocol.TEST_INFO_RESPONSE_FIELD);
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/multivalentTests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
index d0573e0..3895302 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
@@ -57,7 +57,8 @@
Rect originalWidgetSize = widget.getVisibleBounds();
Point targetStart = bottomResizeHandle.getVisibleCenter();
Point targetDest = bottomResizeHandle.getVisibleCenter();
- targetDest.offset(0, originalWidgetSize.height());
+ targetDest.offset(0,
+ originalWidgetSize.height() + mLauncher.getCellLayoutBoarderHeight());
final long downTime = SystemClock.uptimeMillis();
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetStart,
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index d44ccf5..dbafe79 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -60,8 +60,8 @@
private val windowManagerProxy: WindowManagerProxy = mock()
private val launcherPrefs: LauncherPrefs = mock()
private val allowLeftRightSplitInPortrait: Boolean = initAllowLeftRightSplitInPortrait()
- fun initAllowLeftRightSplitInPortrait() : Boolean {
- val res = Resources.getSystem();
+ fun initAllowLeftRightSplitInPortrait(): Boolean {
+ val res = Resources.getSystem()
val resId = res.getIdentifier("config_leftRightSplitInPortrait", "bool", "android")
return Flags.enableLeftRightSplitInPortrait() && resId > 0 && res.getBoolean(resId)
}
@@ -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(
@@ -325,12 +315,16 @@
// 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")
+ 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/allapps/TaplAllAppsIconsWorkingTest.java b/tests/src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java
index 27a2c75..ba74244 100644
--- a/tests/src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java
@@ -62,5 +62,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 da0beb1..6fce4c6 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -16,7 +16,6 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.util.TestUtil.expectFail;
-import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -30,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;
@@ -208,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/celllayout/HotseatReorderUnitTest.kt b/tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
new file mode 100644
index 0000000..13dfd5e
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
@@ -0,0 +1,179 @@
+/*
+ * 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.celllayout
+
+import android.content.Context
+import android.graphics.Point
+import android.util.Log
+import android.view.View
+import androidx.core.view.get
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.CellLayout
+import com.android.launcher3.celllayout.board.CellLayoutBoard
+import com.android.launcher3.celllayout.board.IconPoint
+import com.android.launcher3.celllayout.board.PermutedBoardComparator
+import com.android.launcher3.celllayout.board.WidgetRect
+import com.android.launcher3.celllayout.testgenerator.RandomBoardGenerator
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.views.DoubleShadowBubbleTextView
+import java.util.Random
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+
+private class HotseatReorderTestCase(
+ val startBoard: CellLayoutBoard,
+ val endBoard: CellLayoutBoard
+) {
+ override fun toString(): String {
+ return "$startBoard#endBoard:\n$endBoard"
+ }
+}
+
+class HotseatReorderUnitTest {
+
+ private val applicationContext: Context =
+ ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+
+ @JvmField @Rule var cellLayoutBuilder = UnitTestCellLayoutBuilderRule()
+
+ /**
+ * This test generates random CellLayout configurations and then try to reorder it and makes
+ * sure the result is a valid board meaning it didn't remove any widget or icon.
+ */
+ @Test
+ fun generateValidTests() {
+ val generator = Random(Companion.SEED.toLong())
+ for (i in 0 until Companion.TOTAL_OF_CASES_GENERATED) {
+ // Using a new seed so that we can replicate the same test cases.
+ val seed = generator.nextInt()
+ Log.d(Companion.TAG, "Seed = $seed")
+
+ val testCase: HotseatReorderTestCase =
+ generateRandomTestCase(RandomBoardGenerator(Random(seed.toLong())))
+ Log.d(Companion.TAG, "testCase = $testCase")
+
+ Assert.assertTrue(
+ "invalid case $i",
+ PermutedBoardComparator().compare(testCase.startBoard, testCase.endBoard) == 0
+ )
+ }
+ }
+
+ private fun addViewInCellLayout(
+ cellLayout: CellLayout,
+ cellX: Int,
+ cellY: Int,
+ spanX: Int,
+ spanY: Int,
+ isWidget: Boolean
+ ) {
+ val cell =
+ if (isWidget) View(applicationContext)
+ else DoubleShadowBubbleTextView(applicationContext)
+ cell.layoutParams = CellLayoutLayoutParams(cellX, cellY, spanX, spanY)
+ cellLayout.addViewToCellLayout(
+ cell,
+ -1,
+ cell.id,
+ cell.layoutParams as CellLayoutLayoutParams,
+ true
+ )
+ }
+
+ private fun solve(board: CellLayoutBoard): CellLayout {
+ val cl = cellLayoutBuilder.createCellLayout(board.width, board.height, false)
+ // The views have to be sorted or the result can vary
+ board.icons
+ .map(IconPoint::getCoord)
+ .sortedWith(
+ Comparator.comparing { p: Any -> (p as Point).x }
+ .thenComparing { p: Any -> (p as Point).y }
+ )
+ .forEach { p ->
+ addViewInCellLayout(
+ cellLayout = cl,
+ cellX = p.x,
+ cellY = p.y,
+ spanX = 1,
+ spanY = 1,
+ isWidget = false
+ )
+ }
+ board.widgets
+ .sortedWith(
+ Comparator.comparing(WidgetRect::getCellX).thenComparing(WidgetRect::getCellY)
+ )
+ .forEach { widget ->
+ addViewInCellLayout(
+ cl,
+ widget.cellX,
+ widget.cellY,
+ widget.spanX,
+ widget.spanY,
+ isWidget = true
+ )
+ }
+ if (cl.makeSpaceForHotseatMigration(true)) {
+ commitTempPosition(cl)
+ }
+ return cl
+ }
+
+ private fun commitTempPosition(cellLayout: CellLayout) {
+ val count = cellLayout.shortcutsAndWidgets.childCount
+ for (i in 0 until count) {
+ val params = cellLayout.shortcutsAndWidgets[i].layoutParams as CellLayoutLayoutParams
+ params.cellX = params.tmpCellX
+ params.cellY = params.tmpCellY
+ }
+ }
+
+ private fun boardFromCellLayout(cellLayout: CellLayout): CellLayoutBoard {
+ val views = mutableListOf<View>()
+ for (i in 0 until cellLayout.shortcutsAndWidgets.childCount) {
+ views.add(cellLayout.shortcutsAndWidgets.getChildAt(i))
+ }
+ return CellLayoutTestUtils.viewsToBoard(views, cellLayout.countX, cellLayout.countY)
+ }
+
+ private fun generateRandomTestCase(
+ boardGenerator: RandomBoardGenerator
+ ): HotseatReorderTestCase {
+ val width: Int = boardGenerator.getRandom(3, Companion.MAX_BOARD_SIZE)
+ val height: Int = boardGenerator.getRandom(3, Companion.MAX_BOARD_SIZE)
+ val targetWidth: Int = boardGenerator.getRandom(1, width - 2)
+ val targetHeight: Int = boardGenerator.getRandom(1, height - 2)
+ val board: CellLayoutBoard =
+ boardGenerator.generateBoard(width, height, targetWidth * targetHeight)
+ val finishBoard: CellLayoutBoard = boardFromCellLayout(solve(board))
+ return HotseatReorderTestCase(board, finishBoard)
+ }
+
+ companion object {
+ private const val MAX_BOARD_SIZE = 13
+
+ /**
+ * There is nothing special about this numbers, the random seed is just to be able to
+ * reproduce the test cases and the height and width is a random number similar to what
+ * users expect on their devices
+ */
+ private const val SEED = -194162315
+ private const val TOTAL_OF_CASES_GENERATED = 300
+ private const val TAG = "HotseatReorderUnitTest"
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
index d96287f..7aa26a1 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
@@ -161,11 +161,12 @@
public int getWidgetId() throws InterruptedException {
Intent intent = blockingGetExtraIntent();
- assertNotNull(intent);
- assertEquals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
+ assertNotNull("Null EXTRA_INTENT", intent);
+ assertEquals("Intent action is not ACTION_APPWIDGET_CONFIGURE",
+ AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
LauncherAppWidgetInfo.NO_ID);
- assertNotSame(widgetId, LauncherAppWidgetInfo.NO_ID);
+ assertNotSame("Widget id is NO_ID", widgetId, LauncherAppWidgetInfo.NO_ID);
return widgetId;
}
}
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/util/PackageManagerHelperTest.java b/tests/src/com/android/launcher3/util/PackageManagerHelperTest.java
new file mode 100644
index 0000000..d1da5f4
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/PackageManagerHelperTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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 com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link PackageManagerHelper}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class PackageManagerHelperTest {
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private static final String TEST_PACKAGE = "com.android.test.package";
+ private static final int TEST_USER = 2;
+
+ private Context mContext;
+ private LauncherApps mLauncherApps;
+ private PackageManagerHelper mPackageManagerHelper;
+
+ @Before
+ public void setup() {
+ mContext = mock(Context.class);
+ mLauncherApps = mock(LauncherApps.class);
+ when(mContext.getSystemService(eq(LauncherApps.class))).thenReturn(mLauncherApps);
+ mPackageManagerHelper = new PackageManagerHelper(mContext);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
+ public void getApplicationInfo_archivedApp_appInfoIsNotNull()
+ throws PackageManager.NameNotFoundException {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.isArchived = true;
+ when(mLauncherApps.getApplicationInfo(TEST_PACKAGE, 0 /* flags */,
+ UserHandle.of(TEST_USER)))
+ .thenReturn(applicationInfo);
+
+ assertThat(mPackageManagerHelper.getApplicationInfo(TEST_PACKAGE, UserHandle.of(TEST_USER),
+ 0 /* flags */))
+ .isNotNull();
+ }
+}