Merge "Adding more information to logs like content description to help with debugging." into main
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 62cc0bb..6c7fe5b 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -19,10 +19,9 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.os.Debug;
-import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
@@ -145,7 +144,7 @@
notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
}
- if (!enableDesktopWindowingWallpaperActivity() && wasVisible != isVisible) {
+ if (!WALLPAPER_ACTIVITY.isEnabled(mLauncher) && wasVisible != isVisible) {
// TODO: b/333533253 - Remove after flag rollout
if (mVisibleDesktopTasksCount > 0) {
setLauncherViewsVisibility(View.INVISIBLE);
@@ -189,7 +188,7 @@
notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
}
- if (enableDesktopWindowingWallpaperActivity()) {
+ if (WALLPAPER_ACTIVITY.isEnabled(mLauncher)) {
return;
}
// TODO: b/333533253 - Clean up after flag rollout
@@ -289,7 +288,7 @@
* TODO: b/333533253 - Remove after flag rollout
*/
private void setLauncherViewsVisibility(int visibility) {
- if (enableDesktopWindowingWallpaperActivity()) {
+ if (WALLPAPER_ACTIVITY.isEnabled(mLauncher)) {
return;
}
if (DEBUG) {
@@ -314,7 +313,7 @@
* TODO: b/333533253 - Remove after flag rollout
*/
private void markLauncherPaused() {
- if (enableDesktopWindowingWallpaperActivity()) {
+ if (WALLPAPER_ACTIVITY.isEnabled(mLauncher)) {
return;
}
if (DEBUG) {
@@ -331,7 +330,7 @@
* TODO: b/333533253 - Remove after flag rollout
*/
private void markLauncherResumed() {
- if (enableDesktopWindowingWallpaperActivity()) {
+ if (WALLPAPER_ACTIVITY.isEnabled(mLauncher)) {
return;
}
if (DEBUG) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 0add1c4..93a023d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -21,7 +21,7 @@
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IGNORE_IN_APP;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -214,7 +214,7 @@
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
- if (!enableDesktopWindowingWallpaperActivity()
+ if (!WALLPAPER_ACTIVITY.isEnabled(mLauncher)
&& desktopController != null
&& desktopController.areDesktopTasksVisible()) {
// TODO: b/333533253 - Remove after flag rollout
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 3048243..800c594 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -315,6 +315,7 @@
new TaskbarTranslationController(this),
new TaskbarSpringOnStashController(this),
new TaskbarRecentAppsController(
+ this,
RecentsModel.INSTANCE.get(this),
LauncherActivityInterface.INSTANCE::getDesktopVisibilityController),
TaskbarEduTooltipController.newInstance(this),
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index 5c08116..2cb950c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.taskbar
+import android.content.Context
import androidx.annotation.VisibleForTesting
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.model.data.ItemInfo
@@ -26,8 +27,8 @@
import com.android.quickstep.RecentsModel
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
-import com.android.window.flags.Flags.enableDesktopWindowingMode
import com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE
import java.io.PrintWriter
/**
@@ -36,6 +37,7 @@
* - When in Desktop Mode: show the currently running (open) Tasks
*/
class TaskbarRecentAppsController(
+ context: Context,
private val recentsModel: RecentsModel,
// Pass a provider here instead of the actual DesktopVisibilityController instance since that
// instance might not be available when this constructor is called.
@@ -44,7 +46,7 @@
// TODO(b/335401172): unify DesktopMode checks in Launcher.
var canShowRunningApps =
- enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps()
+ DESKTOP_WINDOWING_MODE.isEnabled(context) && enableDesktopWindowingTaskbarRunningApps()
@VisibleForTesting
set(isEnabledFromTest) {
field = isEnabledFromTest
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index be6f690..df8c05c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -64,10 +64,10 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.QUICK_SWITCH_FROM_HOME_FALLBACK;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
-import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -201,6 +201,8 @@
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver;
import com.android.systemui.unfold.updates.RotationChangeProvider;
+import kotlin.Unit;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -213,8 +215,6 @@
import java.util.function.Predicate;
import java.util.stream.Stream;
-import kotlin.Unit;
-
public class QuickstepLauncher extends Launcher implements RecentsViewContainer {
private static final boolean TRACE_LAYOUTS =
SystemProperties.getBoolean("persist.debug.trace_layouts", false);
@@ -276,7 +276,7 @@
// TODO(b/337863494): Explore use of the same OverviewComponentObserver across launcher
OverviewComponentObserver overviewComponentObserver = new OverviewComponentObserver(
asContext(), deviceState);
- if (enableDesktopWindowingMode()) {
+ if (DESKTOP_WINDOWING_MODE.isEnabled(this)) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
getDepthController());
@@ -296,7 +296,7 @@
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
- if (enableDesktopWindowingMode()) {
+ if (DESKTOP_WINDOWING_MODE.isEnabled(this)) {
mDesktopVisibilityController = new DesktopVisibilityController(this);
mDesktopVisibilityController.registerSystemUiListener();
mSplitSelectStateController.initSplitFromDesktopController(this,
@@ -1004,7 +1004,7 @@
@Override
public void setResumed() {
- if (!enableDesktopWindowingWallpaperActivity()
+ if (!WALLPAPER_ACTIVITY.isEnabled(this)
&& mDesktopVisibilityController != null
&& mDesktopVisibilityController.areDesktopTasksVisible()
&& !mDesktopVisibilityController.isRecentsGestureInProgress()) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 20eaddc..b059186 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -62,6 +62,8 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -152,6 +154,8 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
+import kotlin.Unit;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -161,8 +165,6 @@
import java.util.OptionalInt;
import java.util.function.Consumer;
-import kotlin.Unit;
-
/**
* Handles the navigation gestures when Launcher is the default home activity.
*/
@@ -952,7 +954,7 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
- if (targets.hasDesktopTasks()) {
+ if (targets.hasDesktopTasks(mContext)) {
mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
} else {
int untrimmedAppCount = mRemoteTargetHandles.length;
@@ -1272,8 +1274,8 @@
TaskView currentPageTaskView = mRecentsView != null
? mRecentsView.getCurrentPageTaskView() : null;
- if (Flags.enableDesktopWindowingMode()
- && !(Flags.enableDesktopWindowingWallpaperActivity()
+ if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)
+ && !(WALLPAPER_ACTIVITY.isEnabled(mContext)
&& Flags.enableDesktopWindowingQuickSwitch())) {
if ((nextPageTaskView instanceof DesktopTaskView
|| currentPageTaskView instanceof DesktopTaskView)
@@ -1445,8 +1447,8 @@
setClampScrollOffset(false);
};
- if (Flags.enableDesktopWindowingMode()
- && !(Flags.enableDesktopWindowingWallpaperActivity()
+ if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)
+ && !(WALLPAPER_ACTIVITY.isEnabled(mContext)
&& Flags.enableDesktopWindowingQuickSwitch())) {
if (mRecentsView != null && (mRecentsView.getCurrentPageTaskView() != null
&& !(mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView))) {
@@ -2293,8 +2295,8 @@
mRecentsAnimationController, mRecentsAnimationTargets);
});
- if (Flags.enableDesktopWindowingMode()
- && !(Flags.enableDesktopWindowingWallpaperActivity()
+ if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)
+ && !(WALLPAPER_ACTIVITY.isEnabled(mContext)
&& Flags.enableDesktopWindowingQuickSwitch())) {
if (mRecentsView.getNextPageTaskView() instanceof DesktopTaskView
|| mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView) {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 4989831..95c86fa 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -20,13 +20,14 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.TaskInfo;
import android.content.ComponentName;
+import android.content.Context;
import android.os.Process;
import android.os.RemoteException;
import android.util.SparseBooleanArray;
@@ -58,6 +59,7 @@
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
+ private final Context mContext;
private final KeyguardManager mKeyguardManager;
private final LooperExecutor mMainThreadExecutor;
private final SystemUiProxy mSysUiProxy;
@@ -76,8 +78,10 @@
// Tasks are stored in order of least recently launched to most recently launched.
private ArrayList<ActivityManager.RunningTaskInfo> mRunningTasks;
- public RecentTasksList(LooperExecutor mainThreadExecutor, KeyguardManager keyguardManager,
- SystemUiProxy sysUiProxy, TopTaskTracker topTaskTracker) {
+ public RecentTasksList(Context context, LooperExecutor mainThreadExecutor,
+ KeyguardManager keyguardManager, SystemUiProxy sysUiProxy,
+ TopTaskTracker topTaskTracker) {
+ mContext = context;
mMainThreadExecutor = mainThreadExecutor;
mKeyguardManager = keyguardManager;
mChangeId = 1;
@@ -325,9 +329,9 @@
int numVisibleTasks = 0;
for (GroupedRecentTaskInfo rawTask : rawTasks) {
if (rawTask.getType() == TYPE_FREEFORM) {
- // TYPE_FREEFORM tasks is only created when enableDesktopWindowingMode() is true,
+ // TYPE_FREEFORM tasks is only created whenDESKTOP_WINDOWING_MODE.isEnabled is true,
// leftover TYPE_FREEFORM tasks created when flag was on should be ignored.
- if (enableDesktopWindowingMode()) {
+ if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)) {
GroupTask desktopTask = createDesktopTask(rawTask);
if (desktopTask != null) {
allTasks.add(desktopTask);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 18461a6..e84200d 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -27,7 +27,7 @@
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -147,7 +147,7 @@
mActionsView = findViewById(R.id.overview_actions_view);
getRootView().getSysUiScrim().getSysUIProgress().updateValue(0);
mDragLayer.recreateControllers();
- if (enableDesktopWindowingMode()) {
+ if (DESKTOP_WINDOWING_MODE.isEnabled(this)) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
null /* depthController */
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 82bb453..d104911 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -18,9 +18,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
import android.app.WindowConfiguration;
+import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.RemoteAnimationTarget;
@@ -54,8 +55,8 @@
*
* @return {@code true} if at least one target app is a desktop task
*/
- public boolean hasDesktopTasks() {
- if (!enableDesktopWindowingMode()) {
+ public boolean hasDesktopTasks(Context context) {
+ if (!DESKTOP_WINDOWING_MODE.isEnabled(context)) {
return false;
}
for (RemoteAnimationTarget target : apps) {
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index f2b6005..db03dac 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -90,7 +90,9 @@
private RecentsModel(Context context, IconProvider iconProvider) {
this(context,
- new RecentTasksList(MAIN_EXECUTOR,
+ new RecentTasksList(
+ context,
+ MAIN_EXECUTOR,
context.getSystemService(KeyguardManager.class),
SystemUiProxy.INSTANCE.get(context),
TopTaskTracker.INSTANCE.get(context)),
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 3f73959..f2db5af 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -23,8 +23,8 @@
import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING;
import static com.android.quickstep.util.LogUtils.splitFailureMessage;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -1444,7 +1444,8 @@
private boolean shouldEnableRunningTasksForDesktopMode() {
// TODO(b/335401172): unify DesktopMode checks in Launcher
- return enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps();
+ return DESKTOP_WINDOWING_MODE.isEnabled(mContext)
+ && enableDesktopWindowingTaskbarRunningApps();
}
private boolean handleMessageAsync(Message msg) {
diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
index 8e3d44f..31aca03 100644
--- a/quickstep/src/com/android/quickstep/util/AnimUtils.java
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -17,18 +17,28 @@
package com.android.quickstep.util;
import static com.android.app.animation.Interpolators.clampToProgress;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import android.animation.AnimatorSet;
+import android.annotation.NonNull;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.view.animation.Interpolator;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.views.RecentsViewContainer;
/**
* Utility class containing methods to help manage animations, interpolators, and timings.
*/
public class AnimUtils {
+ private static final int DURATION_DEFAULT_SPLIT_DISMISS = 350;
+
/**
* Fetches device-specific timings for the Overview > Split animation
* (splitscreen initiated from Overview).
@@ -59,6 +69,33 @@
}
/**
+ * Synchronizes the timing for the split dismiss animation to the current transition to
+ * NORMAL (launcher home/workspace)
+ */
+ public static void goToNormalStateWithSplitDismissal(@NonNull StateManager stateManager,
+ @NonNull RecentsViewContainer container,
+ @NonNull StatsLogManager.LauncherEvent exitReason,
+ @NonNull SplitAnimationController animationController) {
+ StateAnimationConfig config = new StateAnimationConfig();
+ BaseState startState = stateManager.getState();
+ long duration = startState.getTransitionDuration(container.asContext(),
+ false /*isToState*/);
+ if (duration == 0) {
+ // Case where we're in contextual on workspace (NORMAL), which by default has 0
+ // transition duration
+ duration = DURATION_DEFAULT_SPLIT_DISMISS;
+ }
+ config.duration = duration;
+ AnimatorSet stateAnim = stateManager.createAtomicAnimation(
+ startState, NORMAL, config);
+ AnimatorSet dismissAnim = animationController
+ .createPlaceholderDismissAnim(container, exitReason, duration);
+ stateAnim.play(dismissAnim);
+ stateManager.setCurrentAnimation(stateAnim, NORMAL);
+ stateAnim.start();
+ }
+
+ /**
* Returns a IRemoteCallback which completes the provided list as a result
*/
public static IRemoteCallback completeRunnableListCallback(RunnableList list) {
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index e48a7c6..d20d0a5 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -26,7 +26,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
-import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.annotation.TargetApi;
import android.content.Context;
@@ -54,6 +54,7 @@
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.systemui.shared.recents.model.Task;
@@ -91,10 +92,12 @@
protected void handleStartHome(boolean animated) {
StateManager stateManager = getStateManager();
animated &= stateManager.shouldAnimateStateChange();
- stateManager.goToState(NORMAL, animated);
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.getSplitAnimationController()
- .playPlaceholderDismissAnim(mContainer, LAUNCHER_SPLIT_SELECTION_EXIT_HOME);
+ if (mSplitSelectStateController.isSplitSelectActive()) {
+ AnimUtils.goToNormalStateWithSplitDismissal(stateManager, mContainer,
+ LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
+ mSplitSelectStateController.getSplitAnimationController());
+ } else {
+ stateManager.goToState(NORMAL, animated);
}
AbstractFloatingView.closeAllOpenViews(mContainer, animated);
}
@@ -265,7 +268,7 @@
super.onGestureAnimationStart(runningTasks, rotationTouchHelper);
DesktopVisibilityController desktopVisibilityController =
mContainer.getDesktopVisibilityController();
- if (!enableDesktopWindowingWallpaperActivity() && desktopVisibilityController != null) {
+ if (!WALLPAPER_ACTIVITY.isEnabled(mContext) && desktopVisibilityController != null) {
// TODO: b/333533253 - Remove after flag rollout
desktopVisibilityController.setRecentsGestureStart();
}
@@ -288,7 +291,7 @@
}
}
super.onGestureAnimationEnd();
- if (!enableDesktopWindowingWallpaperActivity() && desktopVisibilityController != null) {
+ if (!WALLPAPER_ACTIVITY.isEnabled(mContext) && desktopVisibilityController != null) {
// TODO: b/333533253 - Remove after flag rollout
desktopVisibilityController.setRecentsGestureEnd(endTarget);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a3d6359..d63ac56 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -5498,7 +5498,7 @@
}
RemoteTargetGluer gluer;
- if (recentsAnimationTargets.hasDesktopTasks()) {
+ if (recentsAnimationTargets.hasDesktopTasks(mContext)) {
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy(), recentsAnimationTargets,
true /* forDesktop */);
mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets);
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 3d994e8..f6393e4 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -16,13 +16,11 @@
package com.android.quickstep.views;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON;
import static com.android.settingslib.widget.theme.R.dimen.settingslib_preferred_minimum_touch_target;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -42,9 +40,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
/**
@@ -57,7 +54,6 @@
public class SplitInstructionsView extends LinearLayout {
private static final int BOUNCE_DURATION = 250;
private static final float BOUNCE_HEIGHT = 20;
- private static final int DURATION_DEFAULT_SPLIT_DISMISS = 350;
private final RecentsViewContainer mContainer;
public boolean mIsCurrentlyAnimating = false;
@@ -165,25 +161,11 @@
private void exitSplitSelection() {
RecentsView recentsView = mContainer.getOverviewPanel();
SplitSelectStateController splitSelectController = recentsView.getSplitSelectController();
-
StateManager stateManager = recentsView.getStateManager();
- BaseState startState = stateManager.getState();
- long duration = startState.getTransitionDuration(mContainer.asContext(), false);
- if (duration == 0) {
- // Case where we're in contextual on workspace (NORMAL), which by default has 0
- // transition duration
- duration = DURATION_DEFAULT_SPLIT_DISMISS;
- }
- StateAnimationConfig config = new StateAnimationConfig();
- config.duration = duration;
- AnimatorSet stateAnim = stateManager.createAtomicAnimation(
- startState, NORMAL, config);
- AnimatorSet dismissAnim = splitSelectController.getSplitAnimationController()
- .createPlaceholderDismissAnim(mContainer,
- LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON, duration);
- stateAnim.play(dismissAnim);
- stateManager.setCurrentAnimation(stateAnim, NORMAL);
- stateAnim.start();
+
+ AnimUtils.goToNormalStateWithSplitDismissal(stateManager, mContainer,
+ LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON,
+ splitSelectController.getSplitAnimationController());
}
void ensureProperRotation() {
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index 27e761a..07c4ffc 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -18,6 +18,7 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
import android.os.Process
import android.os.UserHandle
@@ -56,6 +57,7 @@
@Mock private lateinit var mockIconCache: TaskIconCache
@Mock private lateinit var mockRecentsModel: RecentsModel
+ @Mock private lateinit var mockContext: Context
@Mock private lateinit var mockDesktopVisibilityController: DesktopVisibilityController
private var taskListChangeId: Int = 1
@@ -71,7 +73,9 @@
whenever(mockRecentsModel.iconCache).thenReturn(mockIconCache)
recentAppsController =
- TaskbarRecentAppsController(mockRecentsModel) { mockDesktopVisibilityController }
+ TaskbarRecentAppsController(mockContext, mockRecentsModel) {
+ mockDesktopVisibilityController
+ }
recentAppsController.init(taskbarControllers)
recentAppsController.canShowRunningApps = true
recentAppsController.canShowRecentApps = true
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index d049fbc..c213dbb 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -29,6 +29,7 @@
import android.app.ActivityManager;
import android.app.KeyguardManager;
+import android.content.Context;
import androidx.test.filters.SmallTest;
@@ -54,7 +55,9 @@
public class RecentTasksListTest {
@Mock
- private SystemUiProxy mockSystemUiProxy;
+ private Context mContext;
+ @Mock
+ private SystemUiProxy mSystemUiProxy;
@Mock
private TopTaskTracker mTopTaskTracker;
@@ -66,14 +69,14 @@
MockitoAnnotations.initMocks(this);
LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class);
KeyguardManager mockKeyguardManager = mock(KeyguardManager.class);
- mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManager,
- mockSystemUiProxy, mTopTaskTracker);
+ mRecentTasksList = new RecentTasksList(mContext, mockMainThreadExecutor,
+ mockKeyguardManager, mSystemUiProxy, mTopTaskTracker);
}
@Test
public void onRecentTasksChanged_doesNotFetchTasks() throws Exception {
mRecentTasksList.onRecentTasksChanged();
- verify(mockSystemUiProxy, times(0))
+ verify(mSystemUiProxy, times(0))
.getRecentTasks(anyInt(), anyInt());
}
@@ -81,7 +84,7 @@
public void loadTasksInBackground_onlyKeys_noValidTaskDescription() throws Exception {
GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(
new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo(), null);
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
@@ -94,7 +97,7 @@
@Test
public void loadTasksInBackground_GetRecentTasksException() throws Exception {
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenThrow(new SystemUiProxy.GetRecentTasksException("task load failed"));
RecentTasksList.TaskLoadResult taskList = mRecentTasksList.loadTasksInBackground(
@@ -113,7 +116,7 @@
task2.taskDescription = new ActivityManager.TaskDescription();
GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(task1, task2,
null);
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
@@ -132,7 +135,7 @@
createRecentTaskInfo(5 /* taskId */)};
GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forFreeformTasks(
tasks, Collections.emptySet() /* minimizedTaskIds */);
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(
@@ -158,7 +161,7 @@
Arrays.stream(new Integer[]{1, 4, 5}).collect(Collectors.toSet());
GroupedRecentTaskInfo recentTaskInfos =
GroupedRecentTaskInfo.forFreeformTasks(tasks, minimizedTaskIds);
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(
diff --git a/res/drawable/rounded_action_button.xml b/res/drawable/rounded_action_button.xml
index ddd3042..ebfa996 100644
--- a/res/drawable/rounded_action_button.xml
+++ b/res/drawable/rounded_action_button.xml
@@ -22,8 +22,5 @@
<stroke
android:width="1dp"
android:color="?attr/materialColorSurfaceContainerLow" />
- <padding
- android:left="@dimen/rounded_button_padding"
- android:right="@dimen/rounded_button_padding" />
</shape>
diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml
index c581ae3..a45d585 100644
--- a/res/layout/work_apps_edu.xml
+++ b/res/layout/work_apps_edu.xml
@@ -44,8 +44,7 @@
<FrameLayout
android:layout_width="@dimen/rounded_button_width"
android:layout_height="@dimen/rounded_button_width"
- android:background="@drawable/rounded_action_button"
- android:padding="@dimen/rounded_button_padding">
+ android:background="@drawable/rounded_action_button">
<ImageButton
android:id="@+id/action_btn"
android:layout_width="@dimen/x_icon_size"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 05724e2..af91b5a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -172,8 +172,6 @@
<dimen name="padded_rounded_button_height">48dp</dimen>
<dimen name="rounded_button_height">48dp</dimen>
<dimen name="rounded_button_radius">200dp</dimen>
- <dimen name="rounded_button_padding">8dp</dimen>
-
<!-- Widget tray -->
<dimen name="widget_cell_vertical_padding">8dp</dimen>
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index a448228..53fed20 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -746,7 +746,8 @@
* | +--+ |
* | |
* +----------------+
- * This would be case delta % 4 == 2:
+ * This would be case delta % 4 == 2: // This is case was reverted to previous behaviour which
+ * doesn't match the illustration due to b/353965234
* +-------------+
* | |
* | |
@@ -768,7 +769,6 @@
int delta) {
int rdelta = ((delta % 4) + 4) % 4;
int origLeft = inOutBounds.left;
- int origTop = inOutBounds.top;
switch (rdelta) {
case 0:
return;
@@ -780,8 +780,6 @@
return;
case 2:
inOutBounds.left = parentWidth - inOutBounds.right;
- inOutBounds.top = parentHeight - inOutBounds.bottom;
- inOutBounds.bottom = parentHeight - origTop;
inOutBounds.right = parentWidth - origLeft;
return;
case 3:
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index e44ea1d..a691e45 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -43,6 +43,7 @@
import android.view.animation.OvershootInterpolator;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
@@ -131,7 +132,8 @@
private float mCurrentPosition;
private float mFinalPosition;
private boolean mIsScrollPaused;
- private boolean mIsTwoPanels;
+ @VisibleForTesting
+ boolean mIsTwoPanels;
private ObjectAnimator mAnimator;
private @Nullable ObjectAnimator mAlphaAnimator;
@@ -477,6 +479,21 @@
return sTempRect;
}
+ @VisibleForTesting
+ int getActivePage() {
+ return mActivePage;
+ }
+
+ @VisibleForTesting
+ int getNumPages() {
+ return mNumPages;
+ }
+
+ @VisibleForTesting
+ float getCurrentPosition() {
+ return mCurrentPosition;
+ }
+
private class MyOutlineProver extends ViewOutlineProvider {
@Override
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index 24d58f3..c117be4 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -32,7 +32,6 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.Flags;
-import com.android.launcher3.Utilities;
import com.android.launcher3.util.PackageUserKey;
import java.lang.ref.WeakReference;
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 7339111..e861961 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -183,6 +183,11 @@
mUserToSerialMap.put(userHandle, info);
}
+ @VisibleForTesting
+ public void putToPreInstallCache(UserHandle userHandle, List<String> preInstalledApps) {
+ mUserToPreInstallAppMap.put(userHandle, preInstalledApps);
+ }
+
/**
* @see UserManager#getUserProfiles()
*/
diff --git a/src/com/android/launcher3/util/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java
index 3d4b409..f183f18 100644
--- a/src/com/android/launcher3/util/LogConfig.java
+++ b/src/com/android/launcher3/util/LogConfig.java
@@ -76,4 +76,5 @@
* When turned on, we enable zero state web data loader related logging.
*/
public static final String ZERO_WEB_DATA_LOADER = "ZeroStateWebDataLoaderLog";
+ public static final String SEARCH_TARGET_UTIL_LOG = "SearchTargetUtilLog";
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
index 5a26087..d0aa7a8 100644
--- a/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
@@ -376,9 +376,10 @@
Utilities.rotateBounds(rect, 100, 100, 1)
assertEquals(Rect(70, 40, 80, 80), rect)
- rect = Rect(20, 70, 60, 80)
- Utilities.rotateBounds(rect, 100, 100, 2)
- assertEquals(Rect(40, 20, 80, 30), rect)
+ // case removed for b/28435189
+ // rect = Rect(20, 70, 60, 80)
+ // Utilities.rotateBounds(rect, 100, 100, 2)
+ // assertEquals(Rect(40, 20, 80, 30), rect)
rect = Rect(20, 70, 60, 80)
Utilities.rotateBounds(rect, 100, 100, 3)
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt
index 1de99c5..d3e27b6 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt
@@ -23,6 +23,7 @@
import android.os.Looper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
@@ -114,6 +115,7 @@
underTest = SimpleBroadcastReceiver(Handler(Looper.getMainLooper()), intentConsumer)
underTest.register(context, completionRunnable, 1, "test_action_1", "test_action_2")
+ getInstrumentation().waitForIdleSync()
verify(context).registerReceiver(same(underTest), intentFilterCaptor.capture(), eq(1))
verify(completionRunnable).run()
@@ -136,6 +138,7 @@
underTest = SimpleBroadcastReceiver(Handler(Looper.getMainLooper()), intentConsumer)
underTest.unregisterReceiverSafely(context)
+ getInstrumentation().waitForIdleSync()
verify(context).unregisterReceiver(same(underTest))
}
diff --git a/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt b/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt
new file mode 100644
index 0000000..b491f17
--- /dev/null
+++ b/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt
@@ -0,0 +1,201 @@
+/*
+ * 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.folder
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.folder.FolderNameInfos.*
+import org.junit.Test
+import org.junit.runner.RunWith
+
+data class Label(val index: Int, val label: String, val score: Float)
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FolderNameInfosTest {
+
+ companion object {
+ val statusList =
+ listOf(
+ SUCCESS,
+ HAS_PRIMARY,
+ HAS_SUGGESTIONS,
+ ERROR_NO_PROVIDER,
+ ERROR_APP_LOOKUP_FAILED,
+ ERROR_ALL_APP_LOOKUP_FAILED,
+ ERROR_NO_LABELS_GENERATED,
+ ERROR_LABEL_LOOKUP_FAILED,
+ ERROR_ALL_LABEL_LOOKUP_FAILED,
+ ERROR_NO_PACKAGES,
+ )
+ }
+
+ @Test
+ fun status() {
+ assertStatus(statusList)
+ assertStatus(
+ listOf(
+ ERROR_NO_PROVIDER,
+ ERROR_APP_LOOKUP_FAILED,
+ ERROR_ALL_APP_LOOKUP_FAILED,
+ ERROR_NO_LABELS_GENERATED,
+ ERROR_LABEL_LOOKUP_FAILED,
+ ERROR_ALL_LABEL_LOOKUP_FAILED,
+ ERROR_NO_PACKAGES,
+ )
+ )
+ assertStatus(
+ listOf(
+ SUCCESS,
+ HAS_PRIMARY,
+ HAS_SUGGESTIONS,
+ )
+ )
+ assertStatus(
+ listOf(
+ SUCCESS,
+ HAS_PRIMARY,
+ HAS_SUGGESTIONS,
+ )
+ )
+ }
+
+ fun assertStatus(statusList: List<Int>) {
+ var infos = FolderNameInfos()
+ statusList.forEach { infos.setStatus(it) }
+ assert(infos.status() == statusList.sum()) {
+ "There is an overlap on the status constants!"
+ }
+ }
+
+ @Test
+ fun hasPrimary() {
+ assertHasPrimary(
+ createNameInfos(listOf(Label(0, "label", 1f)), statusList),
+ hasPrimary = true
+ )
+ assertHasPrimary(
+ createNameInfos(listOf(Label(1, "label", 1f)), statusList),
+ hasPrimary = false
+ )
+ assertHasPrimary(
+ createNameInfos(
+ listOf(Label(0, "label", 1f)),
+ listOf(
+ ERROR_NO_PROVIDER,
+ ERROR_APP_LOOKUP_FAILED,
+ ERROR_ALL_APP_LOOKUP_FAILED,
+ ERROR_NO_LABELS_GENERATED,
+ ERROR_LABEL_LOOKUP_FAILED,
+ ERROR_ALL_LABEL_LOOKUP_FAILED,
+ ERROR_NO_PACKAGES,
+ )
+ ),
+ hasPrimary = false
+ )
+ }
+
+ private fun assertHasPrimary(nameInfos: FolderNameInfos, hasPrimary: Boolean) =
+ assert(nameInfos.hasPrimary() == hasPrimary)
+
+ private fun createNameInfos(labels: List<Label>?, statusList: List<Int>?): FolderNameInfos {
+ val infos = FolderNameInfos()
+ labels?.forEach { infos.setLabel(it.index, it.label, it.score) }
+ statusList?.forEach { infos.setStatus(it) }
+ return infos
+ }
+
+ @Test
+ fun hasSuggestions() {
+ assertHasSuggestions(
+ createNameInfos(listOf(Label(0, "label", 1f)), null),
+ hasSuggestions = true
+ )
+ assertHasSuggestions(createNameInfos(null, null), hasSuggestions = false)
+ // There is a max of 4 suggestions
+ assertHasSuggestions(
+ createNameInfos(listOf(Label(5, "label", 1f)), null),
+ hasSuggestions = false
+ )
+ assertHasSuggestions(
+ createNameInfos(
+ listOf(
+ Label(0, "label", 1f),
+ Label(1, "label", 1f),
+ Label(2, "label", 1f),
+ Label(3, "label", 1f)
+ ),
+ null
+ ),
+ hasSuggestions = true
+ )
+ }
+
+ private fun assertHasSuggestions(nameInfos: FolderNameInfos, hasSuggestions: Boolean) =
+ assert(nameInfos.hasSuggestions() == hasSuggestions)
+
+ @Test
+ fun hasContains() {
+ assertContains(
+ createNameInfos(
+ listOf(
+ Label(0, "label1", 1f),
+ Label(1, "label2", 1f),
+ Label(2, "label3", 1f),
+ Label(3, "label4", 1f)
+ ),
+ null
+ ),
+ label = Label(-1, "label3", -1f),
+ contains = true
+ )
+ assertContains(
+ createNameInfos(
+ listOf(
+ Label(0, "label1", 1f),
+ Label(1, "label2", 1f),
+ Label(2, "label3", 1f),
+ Label(3, "label4", 1f)
+ ),
+ null
+ ),
+ label = Label(-1, "label5", -1f),
+ contains = false
+ )
+ assertContains(
+ createNameInfos(null, null),
+ label = Label(-1, "label1", -1f),
+ contains = false
+ )
+ assertContains(
+ createNameInfos(
+ listOf(
+ Label(0, "label1", 1f),
+ Label(1, "label2", 1f),
+ Label(2, "lAbel3", 1f),
+ Label(3, "lEbel4", 1f)
+ ),
+ null
+ ),
+ label = Label(-1, "LaBEl3", -1f),
+ contains = true
+ )
+ }
+
+ private fun assertContains(nameInfos: FolderNameInfos, label: Label, contains: Boolean) =
+ assert(nameInfos.contains(label.label) == contains)
+}
diff --git a/tests/src/com/android/launcher3/pageindicators/PageIndicatorDotsTest.kt b/tests/src/com/android/launcher3/pageindicators/PageIndicatorDotsTest.kt
new file mode 100644
index 0000000..9a8f957
--- /dev/null
+++ b/tests/src/com/android/launcher3/pageindicators/PageIndicatorDotsTest.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.pageindicators
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.util.ActivityContextWrapper
+import junit.framework.TestCase.assertEquals
+import org.junit.Test
+import org.mockito.Mockito
+
+class PageIndicatorDotsTest {
+
+ private val context: Context =
+ ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+ private val pageIndicatorDots: PageIndicatorDots = Mockito.spy(PageIndicatorDots(context))
+
+ @Test
+ fun `setActiveMarker should set the active page to the parameter passed`() {
+ pageIndicatorDots.setActiveMarker(2)
+
+ assertEquals(2, pageIndicatorDots.activePage)
+ }
+
+ @Test
+ fun `setActiveMarker should set the active page to the parameter passed divided by two in two panel layouts`() {
+ pageIndicatorDots.mIsTwoPanels = true
+
+ pageIndicatorDots.setActiveMarker(5)
+
+ assertEquals(2, pageIndicatorDots.activePage)
+ }
+
+ @Test
+ fun `setMarkersCount should set the number of pages to the passed parameter and if the last page gets removed we want to go to the previous page`() {
+ pageIndicatorDots.setMarkersCount(3)
+
+ assertEquals(3, pageIndicatorDots.numPages)
+ }
+
+ @Test
+ fun `for setMarkersCount if the last page gets removed we want to go to the previous page`() {
+ pageIndicatorDots.setActiveMarker(2)
+
+ pageIndicatorDots.setMarkersCount(2)
+
+ assertEquals(1, pageIndicatorDots.activePage)
+ assertEquals(pageIndicatorDots.activePage.toFloat(), pageIndicatorDots.currentPosition)
+ }
+}
diff --git a/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
new file mode 100644
index 0000000..b531adb
--- /dev/null
+++ b/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
@@ -0,0 +1,228 @@
+/*
+ * 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.pm
+
+import android.content.pm.LauncherApps
+import android.content.pm.PackageInstaller
+import android.os.Build
+import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.PackageUserKey
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class InstallSessionTrackerTest {
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
+ private val mockInstallSessionHelper: InstallSessionHelper = mock()
+ private val mockCallback: InstallSessionTracker.Callback = mock()
+ private val mockPackageInstaller: PackageInstaller = mock()
+
+ private val launcherModelHelper = LauncherModelHelper()
+ private val sandboxContext = launcherModelHelper.sandboxContext
+
+ lateinit var launcherApps: LauncherApps
+ lateinit var installSessionTracker: InstallSessionTracker
+
+ @Before
+ fun setup() {
+ launcherApps = sandboxContext.spyService(LauncherApps::class.java)
+ installSessionTracker =
+ InstallSessionTracker(
+ mockInstallSessionHelper,
+ mockCallback,
+ mockPackageInstaller,
+ launcherApps
+ )
+ }
+
+ @After
+ fun teardown() {
+ launcherModelHelper.destroy()
+ }
+
+ @Test
+ fun `onCreated triggers callbacks for setting up new install session`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ }
+ val expectedPackageKey = PackageUserKey("appPackageName", UserHandle(0))
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ // When
+ installSessionTracker.onCreated(expectedSessionId)
+ // Then
+ verify(mockCallback).onInstallSessionCreated(any())
+ verify(mockCallback).onUpdateSessionDisplay(expectedPackageKey, expectedSession)
+ verify(mockInstallSessionHelper).tryQueuePromiseAppIcon(expectedSession)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
+ fun `onCreated for unarchival triggers onPackageStateChanged`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ spy(PackageInstaller.SessionInfo()).apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ whenever(isUnarchival).thenReturn(true)
+ }
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ // When
+ installSessionTracker.onCreated(expectedSessionId)
+ // Then
+ verify(mockCallback).onPackageStateChanged(any())
+ }
+
+ @Test
+ fun `onFinished triggers onPackageStateChanged if session found in cache`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ }
+ val expectedPackageKey = PackageUserKey("appPackageName", UserHandle(0))
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ whenever(mockInstallSessionHelper.activeSessions)
+ .thenReturn(hashMapOf(expectedPackageKey to expectedSession))
+ // When
+ installSessionTracker.onFinished(expectedSessionId, /* success */ true)
+ // Then
+ verify(mockCallback).onPackageStateChanged(any())
+ }
+
+ @Test
+ fun `onFinished failure calls onSessionFailure and promise icon removal for existing icon`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedPackage = "appPackageName"
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = expectedPackage
+ userId = 0
+ }
+ val expectedPackageKey = PackageUserKey(expectedPackage, UserHandle(0))
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ whenever(mockInstallSessionHelper.activeSessions)
+ .thenReturn(hashMapOf(expectedPackageKey to expectedSession))
+ whenever(mockInstallSessionHelper.promiseIconAddedForId(expectedSessionId)).thenReturn(true)
+ // When
+ installSessionTracker.onFinished(expectedSessionId, /* success */ false)
+ // Then
+ verify(mockCallback).onSessionFailure(expectedPackage, expectedPackageKey.mUser)
+ verify(mockInstallSessionHelper).removePromiseIconId(expectedSessionId)
+ }
+
+ @Test
+ fun `onProgressChanged triggers onPackageStateChanged if verified session found`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ }
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ // When
+ installSessionTracker.onProgressChanged(expectedSessionId, /* progress */ 50f)
+ // Then
+ verify(mockCallback).onPackageStateChanged(any())
+ }
+
+ @Test
+ fun `onBadgingChanged triggers session display update and queues promise icon if verified`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ }
+ val expectedPackageKey = PackageUserKey("appPackageName", UserHandle(0))
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ // When
+ installSessionTracker.onBadgingChanged(expectedSessionId)
+ // Then
+ verify(mockCallback).onUpdateSessionDisplay(expectedPackageKey, expectedSession)
+ verify(mockInstallSessionHelper).tryQueuePromiseAppIcon(expectedSession)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+ fun `register triggers registerPackageInstallerSessionCallback for versions from Q`() {
+ // Given
+ whenever(
+ launcherApps.registerPackageInstallerSessionCallback(
+ MODEL_EXECUTOR,
+ installSessionTracker
+ )
+ )
+ .then { /* no-op */ }
+ // When
+ installSessionTracker.register()
+ // Then
+ verify(launcherApps)
+ .registerPackageInstallerSessionCallback(MODEL_EXECUTOR, installSessionTracker)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+ fun `unregister triggers unregisterPackageInstallerSessionCallback for versions from Q`() {
+ // Given
+ whenever(launcherApps.unregisterPackageInstallerSessionCallback(installSessionTracker))
+ .then { /* no-op */ }
+ // When
+ installSessionTracker.unregister()
+ // Then
+ verify(launcherApps).unregisterPackageInstallerSessionCallback(installSessionTracker)
+ }
+}
diff --git a/tests/src/com/android/launcher3/pm/UserCacheTest.kt b/tests/src/com/android/launcher3/pm/UserCacheTest.kt
new file mode 100644
index 0000000..b21219e
--- /dev/null
+++ b/tests/src/com/android/launcher3/pm/UserCacheTest.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.pm
+
+import android.os.Process.myUserHandle
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.TestUtil
+import com.android.launcher3.util.UserIconInfo
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class UserCacheTest {
+ private val launcherModelHelper = LauncherModelHelper()
+ private val sandboxContext = launcherModelHelper.sandboxContext
+ private lateinit var userCache: UserCache
+
+ @Before
+ fun setup() {
+ userCache = UserCache.getInstance(sandboxContext)
+ }
+
+ @After
+ fun teardown() {
+ launcherModelHelper.destroy()
+ }
+
+ @Test
+ fun `getBadgeDrawable only returns a UserBadgeDrawable given a user in the cache`() {
+ // Given
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_WORK)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(myUserHandle(), expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualDrawable = UserCache.getBadgeDrawable(sandboxContext, myUserHandle())
+ val unexpectedDrawable = UserCache.getBadgeDrawable(sandboxContext, UserHandle(66))
+ // Then
+ assertThat(actualDrawable).isNotNull()
+ assertThat(unexpectedDrawable).isNull()
+ }
+
+ @Test
+ fun `getPreInstallApps returns list of pre installed apps given a user`() {
+ // Given
+ val expectedApps = listOf("Google")
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToPreInstallCache(myUserHandle(), expectedApps)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualApps = userCache.getPreInstallApps(myUserHandle())
+ // Then
+ assertThat(actualApps).isEqualTo(expectedApps)
+ }
+
+ @Test
+ fun `getUserProfiles returns copy of UserCache profiles`() {
+ // Given
+ val expectedProfiles = listOf(myUserHandle())
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_MAIN)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(myUserHandle(), expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualProfiles = userCache.userProfiles
+ // Then
+ assertThat(actualProfiles).isEqualTo(expectedProfiles)
+ }
+
+ @Test
+ fun `getUserForSerialNumber returns user key matching given entry serial number`() {
+ // Given
+ val expectedSerial = 42L
+ val expectedProfile = UserHandle(42)
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_MAIN, expectedSerial)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(expectedProfile, expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualProfile = userCache.getUserForSerialNumber(expectedSerial)
+ // Then
+ assertThat(actualProfile).isEqualTo(expectedProfile)
+ }
+
+ @Test
+ fun `getUserInfo returns cached UserIconInfo given user key`() {
+ // Given
+ val expectedProfile = UserHandle(1)
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_WORK)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(expectedProfile, expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualIconInfo = userCache.getUserInfo(expectedProfile)
+ // Then
+ assertThat(actualIconInfo).isEqualTo(expectedIconInfo)
+ }
+
+ @Test
+ fun `getSerialNumberForUser returns cached UserIconInfo serial number given user key`() {
+ // Given
+ val expectedSerial = 42L
+ val expectedProfile = UserHandle(1)
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_WORK, expectedSerial)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(expectedProfile, expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualSerial = userCache.getSerialNumberForUser(expectedProfile)
+ // Then
+ assertThat(actualSerial).isEqualTo(expectedSerial)
+ }
+}