Merge "Remove disruptive home screen announcement when scrolling" into main
diff --git a/quickstep/res/drawable/bg_bubble_dismiss_circle.xml b/quickstep/res/drawable/bg_bubble_dismiss_circle.xml
deleted file mode 100644
index b793eec..0000000
--- a/quickstep/res/drawable/bg_bubble_dismiss_circle.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 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.
- -->
-
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
-
- <stroke
- android:width="2dp"
- android:color="@android:color/system_accent1_600" />
-
- <solid android:color="@android:color/system_accent1_600" />
-</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_bubble_dismiss_white.xml b/quickstep/res/drawable/ic_bubble_dismiss_white.xml
deleted file mode 100644
index b15111b..0000000
--- a/quickstep/res/drawable/ic_bubble_dismiss_white.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2023 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="20dp"
- android:height="20dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
- android:fillColor="@android:color/system_neutral1_50"/>
-</vector>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index b221b22..b404bb5 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -467,9 +467,9 @@
<dimen name="bubblebar_icon_elevation">1dp</dimen>
<!-- Bubble bar dismiss view -->
- <dimen name="bubblebar_dismiss_target_size">96dp</dimen>
+ <dimen name="bubblebar_dismiss_target_size">@dimen/floating_dismiss_background_size</dimen>
<dimen name="bubblebar_dismiss_target_small_size">60dp</dimen>
- <dimen name="bubblebar_dismiss_target_icon_size">24dp</dimen>
+ <dimen name="bubblebar_dismiss_target_icon_size">@dimen/floating_dismiss_icon_size</dimen>
<dimen name="bubblebar_dismiss_target_bottom_margin">50dp</dimen>
<dimen name="bubblebar_dismiss_floating_gradient_height">548dp</dimen>
<dimen name="bubblebar_dismiss_zone_width">192dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 80da467..5a8fba6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -41,6 +41,7 @@
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.taskbar.bubbles.BubbleBarController;
+import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory;
@@ -86,7 +87,7 @@
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
dp -> {
onStashedInAppChanged(dp);
- adjustHotseatForBubbleBar();
+ postAdjustHotseatForBubbleBar();
if (mControllers != null && mControllers.taskbarViewController != null) {
mControllers.taskbarViewController.onRotationChanged(dp);
}
@@ -275,13 +276,16 @@
}
}
- private void adjustHotseatForBubbleBar() {
+ private void postAdjustHotseatForBubbleBar() {
Hotseat hotseat = mLauncher.getHotseat();
- if (mControllers.bubbleControllers.isEmpty() || hotseat == null) return;
- boolean hiddenForBubbles =
- mControllers.bubbleControllers.get().bubbleBarViewController.isHiddenForNoBubbles();
- if (hiddenForBubbles) return;
- hotseat.post(() -> adjustHotseatForBubbleBar(/* isBubbleBarVisible= */ true));
+ if (hotseat == null || !isBubbleBarVisible()) return;
+ hotseat.post(() -> adjustHotseatForBubbleBar(isBubbleBarVisible()));
+ }
+
+ private boolean isBubbleBarVisible() {
+ BubbleControllers bubbleControllers = mControllers.bubbleControllers.orElse(null);
+ return bubbleControllers != null
+ && bubbleControllers.bubbleBarViewController.isBubbleBarVisible();
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index c769e04..060173a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -189,6 +189,7 @@
private final TaskbarControllers mControllers;
private final WindowManager mWindowManager;
+ private final boolean mIsPrimaryDisplay;
private DeviceProfile mDeviceProfile;
private WindowManager.LayoutParams mWindowLayoutParams;
private WindowManager.LayoutParams mLastUpdatedLayoutParams;
@@ -234,11 +235,12 @@
public TaskbarActivityContext(Context windowContext,
@Nullable Context navigationBarPanelContext, DeviceProfile launcherDp,
- TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
- unfoldTransitionProgressProvider,
- @NonNull DesktopVisibilityController desktopVisibilityController) {
+ TaskbarNavButtonController buttonController,
+ ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
+ @NonNull DesktopVisibilityController desktopVisibilityController,
+ boolean isPrimaryDisplay) {
super(windowContext);
-
+ mIsPrimaryDisplay = isPrimaryDisplay;
mNavigationBarPanelContext = navigationBarPanelContext;
applyDeviceProfile(launcherDp);
final Resources resources = getResources();
@@ -627,7 +629,8 @@
*/
private WindowManager.LayoutParams createAllWindowParams() {
final int windowType =
- ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL;
+ (ENABLE_TASKBAR_NAVBAR_UNIFICATION && mIsPrimaryDisplay) ? TYPE_NAVIGATION_BAR
+ : TYPE_NAVIGATION_BAR_PANEL;
WindowManager.LayoutParams windowLayoutParams =
createDefaultWindowLayoutParams(windowType, TaskbarActivityContext.WINDOW_TITLE);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 3e9a073..13f9a51 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -592,9 +592,12 @@
}
}
- public void setWallpaperVisible(boolean isVisible) {
+ /**
+ * Sets wallpaper visibility for specific display.
+ */
+ public void setWallpaperVisible(int displayId, boolean isVisible) {
mSharedState.wallpaperVisible = isVisible;
- TaskbarActivityContext taskbar = getTaskbarForDisplay(getDefaultDisplayId());
+ TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
if (taskbar != null) {
taskbar.setWallpaperVisible(isVisible);
}
@@ -797,7 +800,7 @@
private TaskbarActivityContext createTaskbarActivityContext(DeviceProfile dp, int displayId) {
TaskbarActivityContext newTaskbar = new TaskbarActivityContext(mWindowContext,
mNavigationBarPanelContext, dp, mDefaultNavButtonController,
- mUnfoldProgressProvider, mDesktopVisibilityController);
+ mUnfoldProgressProvider, mDesktopVisibilityController, isDefaultDisplay(displayId));
addTaskbarToMap(displayId, newTaskbar);
return newTaskbar;
@@ -845,6 +848,10 @@
addTaskbarRootLayoutToMap(displayId, newTaskbarRootLayout);
}
+ private boolean isDefaultDisplay(int displayId) {
+ return displayId == getDefaultDisplayId();
+ }
+
/**
* Retrieves the root layout of the taskbar for the specified display.
*
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 7dd40af..502c001 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -84,8 +84,6 @@
private static final String TAG = "TaskbarStashController";
private static final boolean DEBUG = false;
- private static boolean sEnableSoftwareImeForTests = false;
-
/**
* Def. value for @param shouldBubblesFollow in
* {@link #updateAndAnimateTransientTaskbar(boolean)} */
@@ -1172,13 +1170,13 @@
}
// Do not stash if pinned taskbar, hardware keyboard is attached and no IME is docked
- if (isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)
+ if (mActivity.isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)
&& !mActivity.isImeDocked()) {
return false;
}
// Do not stash if hardware keyboard is attached, in 3 button nav and desktop windowing mode
- if (isHardwareKeyboard()
+ if (mActivity.isHardwareKeyboard()
&& mActivity.isThreeButtonNav()
&& mControllers.taskbarDesktopModeController
.getAreDesktopTasksVisibleAndNotInOverview()) {
@@ -1193,21 +1191,6 @@
return mIsImeShowing || mIsImeSwitcherShowing;
}
- private boolean isHardwareKeyboard() {
- return mActivity.isHardwareKeyboard() && !sEnableSoftwareImeForTests;
- }
-
- /**
- * Overrides {@link #isHardwareKeyboard()} to {@code false} for testing, if enabled.
- * <p>
- * Virtual devices are sometimes in hardware keyboard mode, leading to an inconsistent
- * testing environment.
- */
- @VisibleForTesting
- static void enableSoftwareImeForTests(boolean enable) {
- sEnableSoftwareImeForTests = enable;
- }
-
/**
* Updates the proper flag to indicate whether the task bar should be stashed.
*
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 438478f..60de066 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -84,6 +84,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiTranslateDelegate;
@@ -130,8 +131,6 @@
private static final int TRANSITION_FADE_IN_DURATION = 167;
private static final int TRANSITION_FADE_OUT_DURATION = 83;
- private static boolean sEnableModelLoadingForTests = true;
-
private final TaskbarActivityContext mActivity;
private final TaskbarView mTaskbarView;
private final MultiValueAlpha mTaskbarIconAlpha;
@@ -243,7 +242,8 @@
mTaskbarIconTranslationXForPinning.updateValue(pinningValue);
mModelCallbacks.init(controllers);
- if (mActivity.isUserSetupComplete() && sEnableModelLoadingForTests) {
+ if (mActivity.isUserSetupComplete()
+ && !(mActivity.getApplicationContext() instanceof SandboxContext)) {
// Only load the callbacks if user setup is completed
controllers.runAfterInit(() -> LauncherAppState.getInstance(mActivity).getModel()
.addCallbacksAndLoad(mModelCallbacks));
@@ -1215,12 +1215,6 @@
mModelCallbacks.dumpLogs(prefix + "\t", pw);
}
- /** Enables model loading for tests. */
- @VisibleForTesting
- public static void enableModelLoadingForTests(boolean enable) {
- sEnableModelLoadingForTests = enable;
- }
-
private ObjectAnimator createTaskbarIconsShiftAnimator(float translationX) {
ObjectAnimator animator = mIconsTranslationXForNavbar.animateToValue(translationX);
animator.setStartDelay(FADE_OUT_ANIM_POSITION_DURATION_MS);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 9683f8b..b63cf02 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -339,13 +339,15 @@
// clear restored state
mBubbleBarViewController.removeAllBubbles();
mBubbles.clear();
+ mBubbleBarViewController.showOverflow(update.showOverflow);
}
BubbleBarBubble bubbleToSelect = null;
if (Flags.enableOptionalBubbleOverflow()
&& update.showOverflowChanged && !update.showOverflow && update.addedBubble != null
- && update.removedBubbles.isEmpty()) {
+ && update.removedBubbles.isEmpty()
+ && !mBubbles.isEmpty()) {
// A bubble was added from the overflow (& now it's empty / not showing)
mBubbles.put(update.addedBubble.getKey(), update.addedBubble);
mBubbleBarViewController.removeOverflowAndAddBubble(update.addedBubble);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 37c6194..0d0feff 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -729,7 +729,8 @@
Runnable onEndRunnable) {
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) mIconSize, (int) mIconSize,
Gravity.LEFT);
- boolean isOverflowSelected = mSelectedBubbleView.isOverflow();
+ boolean isOverflowSelected =
+ mSelectedBubbleView != null && mSelectedBubbleView.isOverflow();
boolean removingOverflow = removedBubble.isOverflow();
boolean addingOverflow = addedBubble.isOverflow();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index d00959e..569dd56 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -1142,6 +1142,7 @@
/** Removes all existing bubble views */
public void removeAllBubbles() {
+ mOverflowAdded = false;
mBarView.removeAllViews();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
index a8002a5..3245fd1e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
@@ -18,6 +18,7 @@
package com.android.launcher3.taskbar.bubbles
import com.android.launcher3.R
+import com.android.wm.shell.shared.R as SharedR
import com.android.wm.shell.shared.bubbles.DismissView
/**
@@ -36,8 +37,8 @@
bottomMarginResId = R.dimen.bubblebar_dismiss_target_bottom_margin,
floatingGradientHeightResId = R.dimen.bubblebar_dismiss_floating_gradient_height,
floatingGradientColorResId = android.R.color.system_neutral1_900,
- backgroundResId = R.drawable.bg_bubble_dismiss_circle,
- iconResId = R.drawable.ic_bubble_dismiss_white
+ backgroundResId = SharedR.drawable.floating_dismiss_background,
+ iconResId = SharedR.drawable.floating_dismiss_ic_close,
)
)
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index cecd2a9..6d744c2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -169,6 +169,7 @@
import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
@@ -224,7 +225,6 @@
SystemProperties.getBoolean("persist.debug.trace_layouts", false);
private static final String TRACE_RELAYOUT_CLASS =
SystemProperties.get("persist.debug.trace_request_layout_class", null);
- public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t";
@@ -264,7 +264,7 @@
private boolean mIsOverlayVisible;
- private final Runnable mOverviewTargetChangeRunnable = this::onOverviewTargetChanged;
+ private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged;
public static QuickstepLauncher getLauncher(Context context) {
return fromContext(context);
@@ -283,9 +283,6 @@
systemUiProxy, RecentsModel.INSTANCE.get(this),
() -> onStateBack());
RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(asContext());
- // TODO(b/337863494): Explore use of the same OverviewComponentObserver across launcher
- OverviewComponentObserver overviewComponentObserver = new OverviewComponentObserver(
- asContext(), deviceState);
if (DesktopModeStatus.canEnterDesktopMode(this)) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
@@ -294,7 +291,7 @@
overviewPanel.init(mActionsView, mSplitSelectStateController,
mDesktopRecentsTransitionController);
mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
- mSplitSelectStateController, overviewComponentObserver, deviceState);
+ mSplitSelectStateController, deviceState);
mSplitToWorkspaceController = new SplitToWorkspaceController(this,
mSplitSelectStateController);
mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
@@ -307,8 +304,7 @@
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(this)) {
- mSplitSelectStateController.initSplitFromDesktopController(this,
- overviewComponentObserver);
+ mSplitSelectStateController.initSplitFromDesktopController(this);
}
mHotseatPredictionController = new HotseatPredictionController(this);
@@ -556,10 +552,8 @@
mUnfoldTransitionProgressProvider.destroy();
}
- TISBinder binder = mTISBindHelper.getBinder();
- if (binder != null) {
- binder.unregisterOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
- }
+ OverviewComponentObserver.INSTANCE.get(this)
+ .removeOverviewChangeListener(mOverviewChangeListener);
mTISBindHelper.onDestroy();
if (mLauncherUnfoldAnimationController != null) {
@@ -701,6 +695,8 @@
QuickstepOnboardingPrefs.setup(this);
View.setTraceLayoutSteps(TRACE_LAYOUTS);
View.setTracedRequestLayoutClassClass(TRACE_RELAYOUT_CLASS);
+ OverviewComponentObserver.INSTANCE.get(this)
+ .addOverviewChangeListener(mOverviewChangeListener);
}
@Override
@@ -1051,7 +1047,7 @@
}
}
- private void onOverviewTargetChanged() {
+ private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) {
QuickstepTransitionManager transitionManager = getAppTransitionManager();
if (transitionManager != null) {
transitionManager.onOverviewTargetChange();
@@ -1064,7 +1060,6 @@
taskbarManager.setActivity(this);
}
mTISBindHelper.setPredictiveBackToHomeInProgress(mIsPredictiveBackToHomeInProgress);
- binder.registerOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index c81edcd..089706f 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -470,7 +470,7 @@
// Stops requesting focused after first view gets focused.
recentsView.getTaskViewAt(keyboardTaskFocusIndex).requestFocus() ||
recentsView.nextTaskView.requestFocus() ||
- recentsView.getTaskViewAt(0).requestFocus() ||
+ recentsView.getFirstTaskView().requestFocus() ||
recentsView.requestFocus()
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 1fc0401..64c1129 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -19,6 +19,7 @@
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -41,6 +42,11 @@
import com.android.launcher3.Flags;
import com.android.launcher3.R;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -48,16 +54,23 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
-import java.util.function.Consumer;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.inject.Inject;
/**
* Class to keep track of the current overview component based off user preferences and app updates
* and provide callers the relevant classes.
*/
+@LauncherAppSingleton
public final class OverviewComponentObserver {
private static final String TAG = "OverviewComponentObserver";
+ public static final DaggerSingletonObject<OverviewComponentObserver> INSTANCE =
+ new DaggerSingletonObject<>(LauncherAppComponent::getOverviewComponentObserver);
+
// We register broadcast receivers on main thread to avoid missing updates.
private final SimpleBroadcastReceiver mUserPreferenceChangeReceiver =
new SimpleBroadcastReceiver(MAIN_EXECUTOR, this::updateOverviewTargets);
@@ -65,14 +78,16 @@
new SimpleBroadcastReceiver(MAIN_EXECUTOR, this::updateOverviewTargets);
private final Context mContext;
- private final RecentsAnimationDeviceState mDeviceState;
+ private final RecentsDisplayModel mRecentsDisplayModel;
+
private final Intent mCurrentHomeIntent;
private final Intent mMyHomeIntent;
private final Intent mFallbackIntent;
private final SparseIntArray mConfigChangesMap = new SparseIntArray();
private final String mSetupWizardPkg;
- private Consumer<Boolean> mOverviewChangeListener = b -> { };
+ private final List<OverviewChangeListener> mOverviewChangeListeners =
+ new CopyOnWriteArrayList<>();
private String mUpdateRegisteredPackage;
private BaseContainerInterface mContainerInterface;
@@ -81,10 +96,13 @@
private boolean mIsDefaultHome;
private boolean mIsHomeDisabled;
-
- public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
+ @Inject
+ public OverviewComponentObserver(
+ @ApplicationContext Context context,
+ RecentsDisplayModel recentsDisplayModel,
+ DaggerSingletonTracker lifecycleTracker) {
mContext = context;
- mDeviceState = deviceState;
+ mRecentsDisplayModel = recentsDisplayModel;
mCurrentHomeIntent = createHomeIntent();
mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
@@ -108,21 +126,27 @@
mUserPreferenceChangeReceiver.register(mContext, ACTION_PREFERRED_ACTIVITY_CHANGED);
updateOverviewTargets();
+
+ lifecycleTracker.addCloseable(this::onDestroy);
+ }
+
+ /** Adds a listener for changes in {@link #isHomeAndOverviewSame()} */
+ public void addOverviewChangeListener(OverviewChangeListener overviewChangeListener) {
+ mOverviewChangeListeners.add(overviewChangeListener);
+ }
+
+ /** Removes a previously added listener */
+ public void removeOverviewChangeListener(OverviewChangeListener overviewChangeListener) {
+ mOverviewChangeListeners.remove(overviewChangeListener);
}
/**
- * Sets a listener for changes in {@link #isHomeAndOverviewSame()}
+ * Called to set home enabled/disabled state via systemUI
+ * @param isHomeDisabled
*/
- public void setOverviewChangeListener(Consumer<Boolean> overviewChangeListener) {
- // TODO(b/337861962): This method should be able to support multiple listeners instead of
- // one so that we can reuse the same instance of this class across multiple places
- mOverviewChangeListener = overviewChangeListener;
- }
-
- /** Called on {@link TouchInteractionService#onSystemUiFlagsChanged} */
- @UiThread
- public void onSystemUiStateChanged() {
- if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
+ public void setHomeDisabled(boolean isHomeDisabled) {
+ if (isHomeDisabled != mIsHomeDisabled) {
+ mIsHomeDisabled = isHomeDisabled;
updateOverviewTargets();
}
}
@@ -146,7 +170,6 @@
defaultHome = null;
}
- mIsHomeDisabled = mDeviceState.isHomeDisabled();
mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
// Set assistant visibility to 0 from launcher's perspective, ensures any elements that
@@ -182,8 +205,7 @@
if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
mContainerInterface =
- RecentsDisplayModel.getINSTANCE().get(mContext)
- .getFallbackWindowInterface(mDeviceState.getDisplayId());
+ mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
} else {
mContainerInterface = FallbackActivityInterface.INSTANCE;
}
@@ -206,13 +228,13 @@
ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED);
}
}
- mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
+ mOverviewChangeListeners.forEach(l -> l.onOverviewTargetChange(mIsHomeAndOverviewSame));
}
/**
* Clean up any registered receivers.
*/
- public void onDestroy() {
+ private void onDestroy() {
mUserPreferenceChangeReceiver.unregisterReceiverSafely(mContext);
unregisterOtherHomeAppUpdateReceiver();
}
@@ -296,11 +318,7 @@
*/
public static void startHomeIntentSafely(@NonNull Context context, @Nullable Bundle options,
@NonNull String reason) {
- RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(context);
- OverviewComponentObserver observer = new OverviewComponentObserver(context, deviceState);
- Intent intent = observer.getHomeIntent();
- observer.onDestroy();
- deviceState.destroy();
+ Intent intent = OverviewComponentObserver.INSTANCE.get(context).getHomeIntent();
startHomeIntentSafely(context, intent, options, reason);
}
@@ -320,6 +338,17 @@
}
}
+ /**
+ * Interface for listening to overview changes
+ */
+ public interface OverviewChangeListener {
+
+ /**
+ * Called when the overview target changes
+ */
+ void onOverviewTargetChange(boolean isHomeAndOverviewSame);
+ }
+
private static Intent createHomeIntent() {
return new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index a5d2f3e..a04ff2e 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -204,29 +204,15 @@
@Override
protected WindowInsets getWindowInsets() {
- RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(mContext);
- OverviewComponentObserver observer = new OverviewComponentObserver(mContext, rads);
- try {
- RecentsViewContainer container = observer.getContainerInterface().getCreatedContainer();
- WindowInsets insets = container == null
- ? null : container.getRootView().getRootWindowInsets();
-
- return insets == null ? super.getWindowInsets() : insets;
- } finally {
- observer.onDestroy();
- rads.destroy();
- }
+ RecentsViewContainer container = getRecentsViewContainer();
+ WindowInsets insets = container == null
+ ? null : container.getRootView().getRootWindowInsets();
+ return insets == null ? super.getWindowInsets() : insets;
}
private RecentsViewContainer getRecentsViewContainer() {
- RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(mContext);
- OverviewComponentObserver observer = new OverviewComponentObserver(mContext, rads);
- try {
- return observer.getContainerInterface().getCreatedContainer();
- } finally {
- observer.onDestroy();
- rads.destroy();
- }
+ return OverviewComponentObserver.INSTANCE.get(mContext)
+ .getContainerInterface().getCreatedContainer();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 85e2b6e..08d43c0 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -257,7 +257,7 @@
mRecentTasksChangedListener = null;
}
- private void initRunningTasks(ArrayList<RunningTaskInfo> runningTasks) {
+ private void initRunningTasks(List<RunningTaskInfo> runningTasks) {
// Tasks are retrieved in order of most recently launched/used to least recently launched.
mRunningTasks = new ArrayList<>(runningTasks);
Collections.reverse(mRunningTasks);
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
deleted file mode 100644
index 6a25ecb..0000000
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ /dev/null
@@ -1,1660 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-
-import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import static com.android.quickstep.util.LogUtils.splitFailureMessage;
-
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.IRemoteAnimationRunner;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.window.DesktopModeFlags;
-import android.window.IOnBackInvokedCallback;
-import android.window.RemoteTransition;
-import android.window.TaskSnapshot;
-import android.window.TransitionFilter;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.annotation.WorkerThread;
-
-import com.android.internal.logging.InstanceId;
-import com.android.internal.util.ScreenshotRequest;
-import com.android.internal.view.AppearanceRegion;
-import com.android.launcher3.dagger.ApplicationContext;
-import com.android.launcher3.dagger.LauncherAppSingleton;
-import com.android.launcher3.util.DaggerSingletonObject;
-import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.dagger.QuickstepBaseAppComponent;
-import com.android.quickstep.util.ActiveGestureProtoLogProxy;
-import com.android.quickstep.util.ContextualSearchInvoker;
-import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider;
-import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
-import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
-import com.android.systemui.shared.system.smartspace.SmartspaceState;
-import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
-import com.android.systemui.unfold.progress.IUnfoldAnimation;
-import com.android.systemui.unfold.progress.IUnfoldTransitionListener;
-import com.android.wm.shell.back.IBackAnimation;
-import com.android.wm.shell.bubbles.IBubbles;
-import com.android.wm.shell.bubbles.IBubblesListener;
-import com.android.wm.shell.common.pip.IPip;
-import com.android.wm.shell.common.pip.IPipAnimationListener;
-import com.android.wm.shell.desktopmode.IDesktopMode;
-import com.android.wm.shell.desktopmode.IDesktopTaskListener;
-import com.android.wm.shell.draganddrop.IDragAndDrop;
-import com.android.wm.shell.onehanded.IOneHanded;
-import com.android.wm.shell.recents.IRecentTasks;
-import com.android.wm.shell.recents.IRecentTasksListener;
-import com.android.wm.shell.recents.IRecentsAnimationController;
-import com.android.wm.shell.recents.IRecentsAnimationRunner;
-import com.android.wm.shell.shared.GroupedTaskInfo;
-import com.android.wm.shell.shared.IShellTransitions;
-import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
-import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
-import com.android.wm.shell.shared.split.SplitBounds;
-import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
-import com.android.wm.shell.splitscreen.ISplitScreen;
-import com.android.wm.shell.splitscreen.ISplitScreenListener;
-import com.android.wm.shell.splitscreen.ISplitSelectListener;
-import com.android.wm.shell.startingsurface.IStartingWindow;
-import com.android.wm.shell.startingsurface.IStartingWindowListener;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Holds the reference to SystemUI.
- */
-@LauncherAppSingleton
-public class SystemUiProxy implements ISystemUiProxy, NavHandle {
- private static final String TAG = "SystemUiProxy";
-
- public static final DaggerSingletonObject<SystemUiProxy> INSTANCE =
- new DaggerSingletonObject<>(QuickstepBaseAppComponent::getSystemUiProxy);
-
- private static final int MSG_SET_SHELF_HEIGHT = 1;
- private static final int MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2;
-
- private ISystemUiProxy mSystemUiProxy;
- private IPip mPip;
- private IBubbles mBubbles;
- private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
- private ISplitScreen mSplitScreen;
- private IOneHanded mOneHanded;
- private IShellTransitions mShellTransitions;
- private IStartingWindow mStartingWindow;
- private IRecentTasks mRecentTasks;
- private IBackAnimation mBackAnimation;
- private IDesktopMode mDesktopMode;
- private IUnfoldAnimation mUnfoldAnimation;
- private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
- MAIN_EXECUTOR.execute(() -> clearProxy());
- };
-
- // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
- // yet, and we'll need to set/register these listeners with SysUI when they do. Note that it is
- // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
- // in case SysUI needs to rebind.
- private IPipAnimationListener mPipAnimationListener;
- private IBubblesListener mBubblesListener;
- private ISplitScreenListener mSplitScreenListener;
- private ISplitSelectListener mSplitSelectListener;
- private IStartingWindowListener mStartingWindowListener;
- private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
- private String mLauncherActivityClass;
- private IRecentTasksListener mRecentTasksListener;
- private IUnfoldTransitionListener mUnfoldAnimationListener;
- private IDesktopTaskListener mDesktopTaskListener;
- private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
- new LinkedHashMap<>();
-
- private final List<Runnable> mStateChangeCallbacks = new ArrayList<>();
-
- private IBinder mOriginalTransactionToken = null;
- private IOnBackInvokedCallback mBackToLauncherCallback;
- private IRemoteAnimationRunner mBackToLauncherRunner;
- private IDragAndDrop mDragAndDrop;
- private final HomeVisibilityState mHomeVisibilityState = new HomeVisibilityState();
- private final FocusState mFocusState = new FocusState();
-
- // Used to dedupe calls to SystemUI
- private int mLastShelfHeight;
- private boolean mLastShelfVisible;
-
- // Used to dedupe calls to SystemUI
- private int mLastLauncherKeepClearAreaHeight;
- private boolean mLastLauncherKeepClearAreaHeightVisible;
-
- private final Context mContext;
- private final Handler mAsyncHandler;
-
- // TODO(141886704): Find a way to remove this
- @SystemUiStateFlags
- private long mLastSystemUiStateFlags;
-
- /**
- * This is a singleton pending intent that is used to start recents via Shell (which is a
- * different process). It is bare-bones, so it's expected that the component and options will
- * be provided via fill-in intent.
- */
- private final PendingIntent mRecentsPendingIntent;
-
- @Nullable
- private final ProxyUnfoldTransitionProvider mUnfoldTransitionProvider;
-
- @Inject
- public SystemUiProxy(@ApplicationContext Context context) {
- mContext = context;
- mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
- final Intent baseIntent = new Intent().setPackage(mContext.getPackageName());
- final ActivityOptions options = ActivityOptions.makeBasic()
- .setPendingIntentCreatorBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent,
- PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
- | Intent.FILL_IN_COMPONENT, options.toBundle());
-
- mUnfoldTransitionProvider =
- (enableUnfoldStateAnimation() && new ResourceUnfoldTransitionConfig().isEnabled())
- ? new ProxyUnfoldTransitionProvider() : null;
- }
-
- @Override
- public void onBackEvent(KeyEvent backEvent) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onBackEvent(backEvent);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onBackPressed", e);
- }
- }
- }
-
- @Override
- public void onImeSwitcherPressed() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onImeSwitcherPressed();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onImeSwitcherPressed", e);
- }
- }
- }
-
- @Override
- public void onImeSwitcherLongPress() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onImeSwitcherLongPress();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onImeSwitcherLongPress");
- }
- }
- }
-
- @Override
- public void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.updateContextualEduStats(isTrackpadGesture, gestureType);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call updateContextualEduStats");
- }
- }
- }
-
- @Override
- public void setHomeRotationEnabled(boolean enabled) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.setHomeRotationEnabled(enabled);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onBackPressed", e);
- }
- }
- }
-
- @Override
- public IBinder asBinder() {
- // Do nothing
- return null;
- }
-
- /**
- * Sets proxy state, including death linkage, various listeners, and other configuration objects
- */
- @MainThread
- public void setProxy(ISystemUiProxy proxy, IPip pip, IBubbles bubbles, ISplitScreen splitScreen,
- IOneHanded oneHanded, IShellTransitions shellTransitions,
- IStartingWindow startingWindow, IRecentTasks recentTasks,
- ISysuiUnlockAnimationController sysuiUnlockAnimationController,
- IBackAnimation backAnimation, IDesktopMode desktopMode,
- IUnfoldAnimation unfoldAnimation, IDragAndDrop dragAndDrop) {
- Preconditions.assertUIThread();
- unlinkToDeath();
- mSystemUiProxy = proxy;
- mPip = pip;
- mBubbles = bubbles;
- mSplitScreen = splitScreen;
- mOneHanded = oneHanded;
- mShellTransitions = shellTransitions;
- mStartingWindow = startingWindow;
- mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
- mRecentTasks = recentTasks;
- mBackAnimation = backAnimation;
- mDesktopMode = desktopMode;
- mUnfoldAnimation = enableUnfoldStateAnimation() ? null : unfoldAnimation;
- mDragAndDrop = dragAndDrop;
- linkToDeath();
- // re-attach the listeners once missing due to setProxy has not been initialized yet.
- setPipAnimationListener(mPipAnimationListener);
- setBubblesListener(mBubblesListener);
- registerSplitScreenListener(mSplitScreenListener);
- registerSplitSelectListener(mSplitSelectListener);
- mHomeVisibilityState.init(mShellTransitions);
- mFocusState.init(mShellTransitions);
- setStartingWindowListener(mStartingWindowListener);
- setLauncherUnlockAnimationController(
- mLauncherActivityClass, mLauncherUnlockAnimationController);
- new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
- setupTransactionQueue();
- registerRecentTasksListener(mRecentTasksListener);
- setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
- setUnfoldAnimationListener(mUnfoldAnimationListener);
- setDesktopTaskListener(mDesktopTaskListener);
- setAssistantOverridesRequested(new ContextualSearchInvoker(mContext)
- .getSysUiAssistOverrideInvocationTypes());
- mStateChangeCallbacks.forEach(Runnable::run);
-
- if (mUnfoldTransitionProvider != null) {
- if (unfoldAnimation != null) {
- try {
- unfoldAnimation.setListener(mUnfoldTransitionProvider);
- mUnfoldTransitionProvider.setActive(true);
- } catch (RemoteException e) {
- // Ignore
- }
- } else {
- mUnfoldTransitionProvider.setActive(false);
- }
- }
- }
-
- /**
- * Clear the proxy to release held resources and turn the majority of its operations into no-ops
- */
- @MainThread
- public void clearProxy() {
- setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null);
- }
-
- /**
- * Adds a callback to be notified whenever the active state changes
- */
- public void addOnStateChangeListener(Runnable callback) {
- mStateChangeCallbacks.add(callback);
- }
-
- /**
- * Removes a previously added state change callback
- */
- public void removeOnStateChangeListener(Runnable callback) {
- mStateChangeCallbacks.remove(callback);
- }
-
- // TODO(141886704): Find a way to remove this
- public void setLastSystemUiStateFlags(@SystemUiStateFlags long stateFlags) {
- mLastSystemUiStateFlags = stateFlags;
- }
-
- // TODO(141886704): Find a way to remove this
- @SystemUiStateFlags
- public long getLastSystemUiStateFlags() {
- return mLastSystemUiStateFlags;
- }
-
- public boolean isActive() {
- return mSystemUiProxy != null;
- }
-
- private void linkToDeath() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.asBinder().linkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to link sysui proxy death recipient");
- }
- }
- }
-
- private void unlinkToDeath() {
- if (mSystemUiProxy != null) {
- mSystemUiProxy.asBinder().unlinkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
- }
- }
-
- @Override
- public void startScreenPinning(int taskId) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.startScreenPinning(taskId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call startScreenPinning", e);
- }
- }
- }
-
- @Override
- public void onOverviewShown(boolean fromHome) {
- onOverviewShown(fromHome, TAG);
- }
-
- public void onOverviewShown(boolean fromHome, String tag) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onOverviewShown(fromHome);
- } catch (RemoteException e) {
- Log.w(tag, "Failed call onOverviewShown from: " + (fromHome ? "home" : "app"), e);
- }
- }
- }
-
- @MainThread
- @Override
- public void onStatusBarTouchEvent(MotionEvent event) {
- Preconditions.assertUIThread();
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onStatusBarTouchEvent(event);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onStatusBarTouchEvent with arg: " + event, e);
- }
- }
- }
-
- @Override
- public void onStatusBarTrackpadEvent(MotionEvent event) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onStatusBarTrackpadEvent(event);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onStatusBarTrackpadEvent with arg: " + event, e);
- }
- }
- }
-
- @Override
- public void onAssistantProgress(float progress) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onAssistantProgress(progress);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onAssistantProgress with progress: " + progress, e);
- }
- }
- }
-
- @Override
- public void onAssistantGestureCompletion(float velocity) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onAssistantGestureCompletion(velocity);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onAssistantGestureCompletion", e);
- }
- }
- }
-
- @Override
- public void startAssistant(Bundle args) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.startAssistant(args);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call startAssistant", e);
- }
- }
- }
-
- @Override
- public void setAssistantOverridesRequested(int[] invocationTypes) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.setAssistantOverridesRequested(invocationTypes);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setAssistantOverridesRequested", e);
- }
- }
- }
-
- @Override
- public void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.animateNavBarLongPress(isTouchDown, shrink, durationMs);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call animateNavBarLongPress", e);
- }
- }
- }
-
- @Override
- public void setOverrideHomeButtonLongPress(long duration, float slopMultiplier,
- boolean haptic) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.setOverrideHomeButtonLongPress(duration, slopMultiplier, haptic);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setOverrideHomeButtonLongPress", e);
- }
- }
- }
-
- @Override
- public void notifyAccessibilityButtonClicked(int displayId) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifyAccessibilityButtonClicked(displayId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifyAccessibilityButtonClicked", e);
- }
- }
- }
-
- @Override
- public void notifyAccessibilityButtonLongClicked() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifyAccessibilityButtonLongClicked();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifyAccessibilityButtonLongClicked", e);
- }
- }
- }
-
- @Override
- public void stopScreenPinning() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.stopScreenPinning();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call stopScreenPinning", e);
- }
- }
- }
-
- @Override
- public void notifyPrioritizedRotation(int rotation) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifyPrioritizedRotation(rotation);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifyPrioritizedRotation with arg: " + rotation, e);
- }
- }
- }
-
- @Override
- public void notifyTaskbarStatus(boolean visible, boolean stashed) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifyTaskbarStatus with arg: " +
- visible + ", " + stashed, e);
- }
- }
- }
-
- /**
- * NOTE: If called to suspend, caller MUST call this method to also un-suspend
- * @param suspend should be true to stop auto-hide, false to resume normal behavior
- */
- @Override
- public void notifyTaskbarAutohideSuspend(boolean suspend) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifyTaskbarAutohideSuspend(suspend);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifyTaskbarAutohideSuspend with arg: " +
- suspend, e);
- }
- }
- }
-
- @Override
- public void takeScreenshot(ScreenshotRequest request) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.takeScreenshot(request);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call takeScreenshot");
- }
- }
- }
-
- @Override
- public void expandNotificationPanel() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.expandNotificationPanel();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call expandNotificationPanel", e);
- }
- }
- }
-
- @Override
- public void toggleNotificationPanel() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.toggleNotificationPanel();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call toggleNotificationPanel", e);
- }
- }
- }
-
- @Override
- public void toggleQuickSettingsPanel() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.toggleQuickSettingsPanel();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call toggleQuickSettingsPanel", e);
- }
- }
- }
-
- //
- // Pip
- //
-
- /**
- * Sets the shelf height.
- */
- public void setShelfHeight(boolean visible, int shelfHeight) {
- Message.obtain(mAsyncHandler, MSG_SET_SHELF_HEIGHT,
- visible ? 1 : 0 , shelfHeight).sendToTarget();
- }
-
- @WorkerThread
- private void setShelfHeightAsync(int visibleInt, int shelfHeight) {
- boolean visible = visibleInt != 0;
- boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
- IPip pip = mPip;
- if (pip != null && changed) {
- mLastShelfVisible = visible;
- mLastShelfHeight = shelfHeight;
- try {
- pip.setShelfHeight(visible, shelfHeight);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setShelfHeight visible: " + visible
- + " height: " + shelfHeight, e);
- }
- }
- }
-
- /**
- * Sets the height of the keep clear area that is going to be reported by
- * the Launcher for the Hotseat.
- */
- public void setLauncherKeepClearAreaHeight(boolean visible, int height) {
- Message.obtain(mAsyncHandler, MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT,
- visible ? 1 : 0 , height).sendToTarget();
- }
-
- @WorkerThread
- private void setLauncherKeepClearAreaHeight(int visibleInt, int height) {
- boolean visible = visibleInt != 0;
- boolean changed = visible != mLastLauncherKeepClearAreaHeightVisible
- || height != mLastLauncherKeepClearAreaHeight;
- IPip pip = mPip;
- if (pip != null && changed) {
- mLastLauncherKeepClearAreaHeightVisible = visible;
- mLastLauncherKeepClearAreaHeight = height;
- try {
- pip.setLauncherKeepClearAreaHeight(visible, height);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setLauncherKeepClearAreaHeight visible: " + visible
- + " height: " + height, e);
- }
- }
- }
-
- /**
- * Sets listener to get pip animation callbacks.
- */
- public void setPipAnimationListener(IPipAnimationListener listener) {
- if (mPip != null) {
- try {
- mPip.setPipAnimationListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
- }
- }
- mPipAnimationListener = listener;
- }
-
- /**
- * @return Destination bounds of auto-pip animation, {@code null} if the animation is not ready.
- */
- @Nullable
- public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
- PictureInPictureParams pictureInPictureParams, int launcherRotation,
- Rect hotseatKeepClearArea) {
- if (mPip != null) {
- try {
- return mPip.startSwipePipToHome(componentName, activityInfo,
- pictureInPictureParams, launcherRotation, hotseatKeepClearArea);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call startSwipePipToHome", e);
- }
- }
- return null;
- }
-
- /**
- * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to
- * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell
- * should be responsible for cleaning up the overlay.
- */
- public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
- SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) {
- if (mPip != null) {
- try {
- mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay,
- appBounds, sourceRectHint);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call stopSwipePipToHome");
- }
- }
- }
-
- /**
- * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell
- * can use this callback to clean up its internal states.
- */
- public void abortSwipePipToHome(int taskId, ComponentName componentName) {
- if (mPip != null) {
- try {
- mPip.abortSwipePipToHome(taskId, componentName);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call abortSwipePipToHome");
- }
- }
- }
-
- /**
- * Sets the next pip animation type to be the alpha animation.
- */
- public void setPipAnimationTypeToAlpha() {
- if (mPip != null) {
- try {
- mPip.setPipAnimationTypeToAlpha();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e);
- }
- }
- }
-
- /**
- * Sets the app icon size in pixel used by Launcher all apps.
- */
- public void setLauncherAppIconSize(int iconSizePx) {
- if (mPip != null) {
- try {
- mPip.setLauncherAppIconSize(iconSizePx);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setLauncherAppIconSize", e);
- }
- }
- }
-
- //
- // Bubbles
- //
-
- /**
- * Sets the listener to be notified of bubble state changes.
- */
- public void setBubblesListener(IBubblesListener listener) {
- if (mBubbles != null) {
- try {
- if (mBubblesListener != null) {
- // Clear out any previous listener
- mBubbles.unregisterBubbleListener(mBubblesListener);
- }
- if (listener != null) {
- mBubbles.registerBubbleListener(listener);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call registerBubblesListener");
- }
- }
- mBubblesListener = listener;
- }
-
- /**
- * Tells SysUI to show the bubble with the provided key.
- * @param key the key of the bubble to show.
- * @param top top coordinate of bubble bar on screen
- */
- public void showBubble(String key, int top) {
- if (mBubbles != null) {
- try {
- mBubbles.showBubble(key, top);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call showBubble");
- }
- }
- }
-
- /**
- * Tells SysUI to remove all bubbles.
- */
- public void removeAllBubbles() {
- if (mBubbles == null) return;
- try {
- mBubbles.removeAllBubbles();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call removeAllBubbles");
- }
- }
-
- /**
- * Tells SysUI to collapse the bubbles.
- */
- public void collapseBubbles() {
- if (mBubbles != null) {
- try {
- mBubbles.collapseBubbles();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call collapseBubbles");
- }
- }
- }
-
- /**
- * Tells SysUI when the bubble is being dragged.
- * Should be called only when the bubble bar is expanded.
- * @param bubbleKey key of the bubble being dragged
- */
- public void startBubbleDrag(@Nullable String bubbleKey) {
- if (mBubbles == null) return;
- try {
- mBubbles.startBubbleDrag(bubbleKey);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call startBubbleDrag");
- }
- }
-
- /**
- * Tells SysUI when the bubble stops being dragged.
- * Should be called only when the bubble bar is expanded.
- *
- * @param location location of the bubble bar
- * @param top new top coordinate for bubble bar on screen
- */
- public void stopBubbleDrag(BubbleBarLocation location, int top) {
- if (mBubbles == null) return;
- try {
- mBubbles.stopBubbleDrag(location, top);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call stopBubbleDrag");
- }
- }
-
- /**
- * Tells SysUI to dismiss the bubble with the provided key.
- *
- * @param key the key of the bubble to dismiss.
- * @param timestamp the timestamp when the removal happened.
- */
- public void dragBubbleToDismiss(String key, long timestamp) {
- if (mBubbles == null) return;
- try {
- mBubbles.dragBubbleToDismiss(key, timestamp);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call dragBubbleToDismiss");
- }
- }
-
- /**
- * Tells SysUI to show user education relative to the reference point provided.
- * @param position the bubble bar top center position in Screen coordinates.
- */
- public void showUserEducation(Point position) {
- try {
- mBubbles.showUserEducation(position.x, position.y);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call showUserEducation");
- }
- }
-
- /**
- * Tells SysUI to update the bubble bar location to the new location.
- * @param location new location for the bubble bar
- * @param source what triggered the location update
- */
- public void setBubbleBarLocation(BubbleBarLocation location,
- @BubbleBarLocation.UpdateSource int source) {
- try {
- mBubbles.setBubbleBarLocation(location, source);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setBubbleBarLocation");
- }
- }
-
- /**
- * Tells SysUI the top coordinate of bubble bar on screen
- *
- * @param topOnScreen top coordinate for bubble bar on screen
- */
- public void updateBubbleBarTopOnScreen(int topOnScreen) {
- try {
- if (mBubbles != null) {
- mBubbles.updateBubbleBarTopOnScreen(topOnScreen);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call updateBubbleBarTopOnScreen");
- }
- }
-
- /**
- * Tells SysUI to show a shortcut bubble.
- *
- * @param info the shortcut info used to create or identify the bubble.
- */
- public void showShortcutBubble(ShortcutInfo info) {
- try {
- if (mBubbles != null) {
- mBubbles.showShortcutBubble(info);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call show bubble for shortcut");
- }
- }
-
- /**
- * Tells SysUI to show a bubble of an app.
- *
- * @param intent the intent used to create the bubble.
- */
- public void showAppBubble(Intent intent) {
- try {
- if (mBubbles != null) {
- mBubbles.showAppBubble(intent);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call show bubble for app");
- }
- }
-
- /** Tells SysUI to show the expanded view. */
- public void showExpandedView() {
- try {
- if (mBubbles != null) {
- mBubbles.showExpandedView();
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to call showExpandedView");
- }
- }
-
- //
- // Splitscreen
- //
-
- public void registerSplitScreenListener(ISplitScreenListener listener) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.registerSplitScreenListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call registerSplitScreenListener");
- }
- }
- mSplitScreenListener = listener;
- }
-
- public void unregisterSplitScreenListener(ISplitScreenListener listener) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.unregisterSplitScreenListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call unregisterSplitScreenListener");
- }
- }
- mSplitScreenListener = null;
- }
-
- public void registerSplitSelectListener(ISplitSelectListener listener) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.registerSplitSelectListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call registerSplitSelectListener");
- }
- }
- mSplitSelectListener = listener;
- }
-
- public void unregisterSplitSelectListener(ISplitSelectListener listener) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.unregisterSplitSelectListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call unregisterSplitSelectListener");
- }
- }
- mSplitSelectListener = null;
- }
-
- /** Start multiple tasks in split-screen simultaneously. */
- public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
- @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition,
- RemoteTransition remoteTransition, InstanceId instanceId) {
- if (mSystemUiProxy != null) {
- try {
- mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition,
- snapPosition, remoteTransition, instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage("startTasks", "RemoteException"), e);
- }
- }
- }
-
- public void startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1,
- int taskId, Bundle options2, @StagePosition int splitPosition,
- @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition,
- InstanceId instanceId) {
- if (mSystemUiProxy != null) {
- try {
- mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2,
- splitPosition, snapPosition, remoteTransition, instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage("startIntentAndTask", "RemoteException"), e);
- }
- }
- }
-
- public void startIntents(PendingIntent pendingIntent1, int userId1,
- @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2,
- int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2,
- @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition,
- RemoteTransition remoteTransition, InstanceId instanceId) {
- if (mSystemUiProxy != null) {
- try {
- mSplitScreen.startIntents(pendingIntent1, userId1, shortcutInfo1, options1,
- pendingIntent2, userId2, shortcutInfo2, options2, splitPosition,
- snapPosition, remoteTransition, instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage("startIntents", "RemoteException"), e);
- }
- }
- }
-
- public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId,
- Bundle options2, @StagePosition int splitPosition,
- @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition,
- InstanceId instanceId) {
- if (mSystemUiProxy != null) {
- try {
- mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
- splitPosition, snapPosition, remoteTransition, instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage("startShortcutAndTask", "RemoteException"), e);
- }
- }
- }
-
- public void startShortcut(String packageName, String shortcutId, int position,
- Bundle options, UserHandle user, InstanceId instanceId) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.startShortcut(packageName, shortcutId, position, options,
- user, instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage("startShortcut", "RemoteException"), e);
- }
- }
- }
-
- public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position,
- Bundle options, InstanceId instanceId) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.startIntent(intent, userId, fillInIntent, position, options,
- instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage("startIntent", "RemoteException"), e);
- }
- }
- }
-
- //
- // One handed
- //
-
- public void startOneHandedMode() {
- if (mOneHanded != null) {
- try {
- mOneHanded.startOneHanded();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call startOneHandedMode", e);
- }
- }
- }
-
- public void stopOneHandedMode() {
- if (mOneHanded != null) {
- try {
- mOneHanded.stopOneHanded();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call stopOneHandedMode", e);
- }
- }
- }
-
- //
- // Remote transitions
- //
-
- public void registerRemoteTransition(
- RemoteTransition remoteTransition, TransitionFilter filter) {
- if (mShellTransitions != null) {
- try {
- mShellTransitions.registerRemote(filter, remoteTransition);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call registerRemoteTransition");
- }
- }
- if (!mRemoteTransitions.containsKey(remoteTransition)) {
- mRemoteTransitions.put(remoteTransition, filter);
- }
- }
-
- public void unregisterRemoteTransition(RemoteTransition remoteTransition) {
- if (mShellTransitions != null) {
- try {
- mShellTransitions.unregisterRemote(remoteTransition);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call registerRemoteTransition");
- }
- }
- mRemoteTransitions.remove(remoteTransition);
- }
-
- public HomeVisibilityState getHomeVisibilityState() {
- return mHomeVisibilityState;
- }
-
- public FocusState getFocusState() {
- return mFocusState;
- }
-
- /**
- * Returns a surface which can be used to attach overlays to home task or null if
- * the task doesn't exist or sysui is not connected
- */
- @Nullable
- public SurfaceControl getHomeTaskOverlayContainer() {
- // Use a local reference as this method can be called on a worker thread, which can lead
- // to NullPointer exceptions if mShellTransitions is modified on the main thread.
- IShellTransitions shellTransitions = mShellTransitions;
- if (shellTransitions != null) {
- try {
- return mShellTransitions.getHomeTaskOverlayContainer();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call getOverlayContainerForTask", e);
- }
- }
- return null;
- }
-
- /**
- * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary
- * if Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
- */
- public void shareTransactionQueue() {
- if (mOriginalTransactionToken == null) {
- mOriginalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken();
- }
- setupTransactionQueue();
- }
-
- /**
- * Switch back to using Launcher's independent transaction queue.
- */
- public void unshareTransactionQueue() {
- if (mOriginalTransactionToken == null) {
- return;
- }
- SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
- mOriginalTransactionToken = null;
- }
-
- private void setupTransactionQueue() {
- if (mOriginalTransactionToken == null) {
- return;
- }
- if (mShellTransitions == null) {
- SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
- return;
- }
- final IBinder shellApplyToken;
- try {
- shellApplyToken = mShellTransitions.getShellApplyToken();
- } catch (RemoteException e) {
- Log.e(TAG, "Error getting Shell's apply token", e);
- return;
- }
- if (shellApplyToken == null) {
- Log.e(TAG, "Didn't receive apply token from Shell");
- return;
- }
- SurfaceControl.Transaction.setDefaultApplyToken(shellApplyToken);
- }
-
- //
- // Starting window
- //
-
- /**
- * Sets listener to get callbacks when launching a task.
- */
- public void setStartingWindowListener(IStartingWindowListener listener) {
- if (mStartingWindow != null) {
- try {
- mStartingWindow.setStartingWindowListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setStartingWindowListener", e);
- }
- }
- mStartingWindowListener = listener;
- }
-
- //
- // SmartSpace transitions
- //
-
- /**
- * Sets the instance of {@link ILauncherUnlockAnimationController} that System UI should use to
- * control the launcher side of the unlock animation. This will also cause us to dispatch the
- * current state of the smartspace to System UI (this will subsequently happen if the state
- * changes).
- */
- public void setLauncherUnlockAnimationController(
- String activityClass, ILauncherUnlockAnimationController controller) {
- if (mSysuiUnlockAnimationController != null) {
- try {
- mSysuiUnlockAnimationController.setLauncherUnlockController(
- activityClass, controller);
- if (controller != null) {
- controller.dispatchSmartspaceStateToSysui();
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setLauncherUnlockAnimationController", e);
- }
- }
- mLauncherActivityClass = activityClass;
- mLauncherUnlockAnimationController = controller;
- }
-
- /**
- * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
- * the unlock animation accordingly.
- */
- public void notifySysuiSmartspaceStateUpdated(SmartspaceState state) {
- if (mSysuiUnlockAnimationController != null) {
- try {
- mSysuiUnlockAnimationController.onLauncherSmartspaceStateUpdated(state);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifySysuiSmartspaceStateUpdated", e);
- e.printStackTrace();
- }
- }
- }
-
- //
- // Recents
- //
-
- public void registerRecentTasksListener(IRecentTasksListener listener) {
- if (mRecentTasks != null) {
- try {
- mRecentTasks.registerRecentTasksListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call registerRecentTasksListener", e);
- }
- }
- mRecentTasksListener = listener;
- }
-
- public void unregisterRecentTasksListener(IRecentTasksListener listener) {
- if (mRecentTasks != null) {
- try {
- mRecentTasks.unregisterRecentTasksListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call unregisterRecentTasksListener");
- }
- }
- mRecentTasksListener = null;
- }
-
- //
- // Back navigation transitions
- //
-
- /** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
- public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
- IRemoteAnimationRunner runner) {
- mBackToLauncherCallback = callback;
- mBackToLauncherRunner = runner;
- if (mBackAnimation == null || mBackToLauncherCallback == null) {
- return;
- }
- try {
- mBackAnimation.setBackToLauncherCallback(callback, runner);
- } catch (RemoteException | SecurityException e) {
- Log.e(TAG, "Failed call setBackToLauncherCallback", e);
- }
- }
-
- /** Clears the previously registered {@link IOnBackInvokedCallback}.
- *
- * @param callback The previously registered callback instance.
- */
- public void clearBackToLauncherCallback(IOnBackInvokedCallback callback) {
- if (mBackToLauncherCallback != callback) {
- return;
- }
- mBackToLauncherCallback = null;
- mBackToLauncherRunner = null;
- if (mBackAnimation == null) {
- return;
- }
- try {
- mBackAnimation.clearBackToLauncherCallback();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed call clearBackToLauncherCallback", e);
- }
- }
-
- /**
- * Called when the status bar color needs to be customized when back navigation.
- */
- public void customizeStatusBarAppearance(AppearanceRegion appearance) {
- if (mBackAnimation == null) {
- return;
- }
- try {
- mBackAnimation.customizeStatusBarAppearance(appearance);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed call useLauncherSysBarFlags", e);
- }
- }
-
- public static class GetRecentTasksException extends Exception {
- public GetRecentTasksException(String message) {
- super(message);
- }
-
- public GetRecentTasksException(String message, Throwable cause) {
- super(message, cause);
- }
- }
-
- /**
- * Retrieves a list of Recent tasks from ActivityManager.
- * @throws GetRecentTasksException if IRecentTasks is not initialized, or when we get
- * RemoteException from server side
- */
- public ArrayList<GroupedTaskInfo> getRecentTasks(int numTasks,
- int userId) throws GetRecentTasksException {
- if (mRecentTasks == null) {
- Log.e(TAG, "getRecentTasks() failed due to null mRecentTasks");
- throw new GetRecentTasksException("null mRecentTasks");
- }
- try {
- final GroupedTaskInfo[] rawTasks =
- mRecentTasks.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId);
- if (rawTasks == null) {
- return new ArrayList<>();
- }
- return new ArrayList<>(Arrays.asList(rawTasks));
- } catch (RemoteException e) {
- Log.e(TAG, "Failed call getRecentTasks", e);
- throw new GetRecentTasksException("Failed call getRecentTasks", e);
- }
- }
-
- /**
- * Gets the set of running tasks.
- */
- public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
- if (mRecentTasks != null && shouldEnableRunningTasksForDesktopMode()) {
- try {
- return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call getRunningTasks", e);
- }
- }
- return new ArrayList<>();
- }
-
- private boolean shouldEnableRunningTasksForDesktopMode() {
- return DesktopModeStatus.canEnterDesktopMode(mContext)
- && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue();
- }
-
- private boolean handleMessageAsync(Message msg) {
- switch (msg.what) {
- case MSG_SET_SHELF_HEIGHT:
- setShelfHeightAsync(msg.arg1, msg.arg2);
- return true;
- case MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT:
- setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2);
- return true;
- }
-
- return false;
- }
-
- //
- // Desktop Mode
- //
-
- /** Call shell to show all apps active on the desktop */
- public void showDesktopApps(int displayId, @Nullable RemoteTransition transition) {
- if (mDesktopMode != null) {
- try {
- mDesktopMode.showDesktopApps(displayId, transition);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call showDesktopApps", e);
- }
- }
- }
-
- /**
- * If task with the given id is on the desktop, bring it to front
- */
- public void showDesktopApp(int taskId, @Nullable RemoteTransition transition) {
- if (mDesktopMode != null) {
- try {
- mDesktopMode.showDesktopApp(taskId, transition);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call showDesktopApp", e);
- }
- }
- }
-
- /** Call shell to get number of visible freeform tasks */
- public int getVisibleDesktopTaskCount(int displayId) {
- if (mDesktopMode != null) {
- try {
- return mDesktopMode.getVisibleTaskCount(displayId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call getVisibleDesktopTaskCount", e);
- }
- }
- return 0;
- }
-
- /** Set a listener on shell to get updates about desktop task state */
- public void setDesktopTaskListener(@Nullable IDesktopTaskListener listener) {
- mDesktopTaskListener = listener;
- if (mDesktopMode != null) {
- try {
- mDesktopMode.setTaskListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setDesktopTaskListener", e);
- }
- }
- }
-
- /** Perform cleanup transactions after animation to split select is complete */
- public void onDesktopSplitSelectAnimComplete(ActivityManager.RunningTaskInfo taskInfo) {
- if (mDesktopMode != null) {
- try {
- mDesktopMode.onDesktopSplitSelectAnimComplete(taskInfo);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onDesktopSplitSelectAnimComplete", e);
- }
- }
- }
-
- /** Call shell to move a task with given `taskId` to desktop */
- public void moveToDesktop(int taskId, DesktopModeTransitionSource transitionSource,
- @Nullable RemoteTransition transition) {
- if (mDesktopMode != null) {
- try {
- mDesktopMode.moveToDesktop(taskId, transitionSource, transition);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call moveToDesktop", e);
- }
- }
- }
-
- /** Call shell to remove the desktop that is on given `displayId` */
- public void removeDesktop(int displayId) {
- if (mDesktopMode != null) {
- try {
- mDesktopMode.removeDesktop(displayId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call removeDesktop", e);
- }
- }
- }
-
- /** Call shell to move a task with given `taskId` to external display. */
- public void moveToExternalDisplay(int taskId) {
- if (mDesktopMode != null) {
- try {
- mDesktopMode.moveToExternalDisplay(taskId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call moveToExternalDisplay", e);
- }
- }
- }
-
- //
- // Unfold transition
- //
-
- /** Sets the unfold animation lister to sysui. */
- public void setUnfoldAnimationListener(IUnfoldTransitionListener callback) {
- mUnfoldAnimationListener = callback;
- if (mUnfoldAnimation == null) {
- return;
- }
- try {
- Log.d(TAG, "Registering unfold animation receiver");
- mUnfoldAnimation.setListener(callback);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed call setUnfoldAnimationListener", e);
- }
- }
-
- @Nullable
- public ProxyUnfoldTransitionProvider getUnfoldTransitionProvider() {
- return mUnfoldTransitionProvider;
- }
-
- //
- // Recents
- //
-
- /**
- * Starts the recents activity. The caller should manage the thread on which this is called.
- */
- public boolean startRecentsActivity(Intent intent, ActivityOptions options,
- RecentsAnimationListener listener, boolean useSyntheticRecentsTransition) {
- if (mRecentTasks == null) {
- ActiveGestureProtoLogProxy.logRecentTasksMissing();
- return false;
- }
- final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() {
- @Override
- public void onAnimationStart(IRecentsAnimationController controller,
- RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
- Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras) {
- // Aidl bundles need to explicitly set class loader
- // https://developer.android.com/guide/components/aidl#Bundles
- if (extras != null) {
- extras.setClassLoader(SplitBounds.class.getClassLoader());
- }
- listener.onAnimationStart(new RecentsAnimationControllerCompat(controller), apps,
- wallpapers, homeContentInsets, minimizedHomeBounds, extras);
- }
-
- @Override
- public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
- listener.onAnimationCanceled(
- ThumbnailData.wrap(taskIds, taskSnapshots));
- }
-
- @Override
- public void onTasksAppeared(RemoteAnimationTarget[] apps) {
- listener.onTasksAppeared(apps);
- }
- };
- final Bundle optsBundle = options.toBundle();
- if (useSyntheticRecentsTransition) {
- optsBundle.putBoolean("is_synthetic_recents_transition", true);
- }
- try {
- mRecentTasks.startRecentsTransition(mRecentsPendingIntent, intent, optsBundle,
- mContext.getIApplicationThread(), runner);
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Error starting recents via shell", e);
- return false;
- }
- }
-
- //
- // Drag and drop
- //
-
- /**
- * For testing purposes. Returns `true` only if the shell drop target has shown and
- * drawn and is ready to handle drag events and the subsequent drop.
- */
- public boolean isDragAndDropReady() {
- if (mDragAndDrop == null) {
- return false;
- }
- try {
- return mDragAndDrop.isReadyToHandleDrag();
- } catch (RemoteException e) {
- Log.e(TAG, "Error querying drag state", e);
- return false;
- }
- }
-
- public void dump(PrintWriter pw) {
- pw.println(TAG + ":");
-
- pw.println("\tmSystemUiProxy=" + mSystemUiProxy);
- pw.println("\tmPip=" + mPip);
- pw.println("\tmPipAnimationListener=" + mPipAnimationListener);
- pw.println("\tmBubbles=" + mBubbles);
- pw.println("\tmBubblesListener=" + mBubblesListener);
- pw.println("\tmSplitScreen=" + mSplitScreen);
- pw.println("\tmSplitScreenListener=" + mSplitScreenListener);
- pw.println("\tmSplitSelectListener=" + mSplitSelectListener);
- pw.println("\tmOneHanded=" + mOneHanded);
- pw.println("\tmShellTransitions=" + mShellTransitions);
- pw.println("\tmHomeVisibilityState=" + mHomeVisibilityState);
- pw.println("\tmFocusState=" + mFocusState);
- pw.println("\tmStartingWindow=" + mStartingWindow);
- pw.println("\tmStartingWindowListener=" + mStartingWindowListener);
- pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController);
- pw.println("\tmLauncherActivityClass=" + mLauncherActivityClass);
- pw.println("\tmLauncherUnlockAnimationController=" + mLauncherUnlockAnimationController);
- pw.println("\tmRecentTasks=" + mRecentTasks);
- pw.println("\tmRecentTasksListener=" + mRecentTasksListener);
- pw.println("\tmBackAnimation=" + mBackAnimation);
- pw.println("\tmBackToLauncherCallback=" + mBackToLauncherCallback);
- pw.println("\tmBackToLauncherRunner=" + mBackToLauncherRunner);
- pw.println("\tmDesktopMode=" + mDesktopMode);
- pw.println("\tmDesktopTaskListener=" + mDesktopTaskListener);
- pw.println("\tmUnfoldAnimation=" + mUnfoldAnimation);
- pw.println("\tmUnfoldAnimationListener=" + mUnfoldAnimationListener);
- pw.println("\tmDragAndDrop=" + mDragAndDrop);
- }
-
- /**
- * Adds all interfaces held by this proxy to the bundle
- */
- @VisibleForTesting
- public void addAllInterfaces(Bundle out) {
- QuickStepContract.addInterface(mSystemUiProxy, out);
- QuickStepContract.addInterface(mPip, out);
- QuickStepContract.addInterface(mBubbles, out);
- QuickStepContract.addInterface(mSysuiUnlockAnimationController, out);
- QuickStepContract.addInterface(mSplitScreen, out);
- QuickStepContract.addInterface(mOneHanded, out);
- QuickStepContract.addInterface(mShellTransitions, out);
- QuickStepContract.addInterface(mStartingWindow, out);
- QuickStepContract.addInterface(mRecentTasks, out);
- QuickStepContract.addInterface(mBackAnimation, out);
- QuickStepContract.addInterface(mDesktopMode, out);
- QuickStepContract.addInterface(mUnfoldAnimation, out);
- QuickStepContract.addInterface(mDragAndDrop, out);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
new file mode 100644
index 0000000..75694af
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -0,0 +1,1270 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.app.PictureInPictureParams
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ShortcutInfo
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.Bundle
+import android.os.Handler
+import android.os.IBinder
+import android.os.Message
+import android.os.RemoteException
+import android.os.UserHandle
+import android.util.Log
+import android.view.IRemoteAnimationRunner
+import android.view.KeyEvent
+import android.view.MotionEvent
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS
+import android.window.IOnBackInvokedCallback
+import android.window.RemoteTransition
+import android.window.TaskSnapshot
+import android.window.TransitionFilter
+import androidx.annotation.MainThread
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import com.android.internal.logging.InstanceId
+import com.android.internal.util.ScreenshotRequest
+import com.android.internal.view.AppearanceRegion
+import com.android.launcher3.Flags
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.util.DaggerSingletonObject
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.Preconditions
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
+import com.android.quickstep.util.ActiveGestureProtoLogProxy
+import com.android.quickstep.util.ContextualSearchInvoker
+import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider
+import com.android.systemui.shared.recents.ISystemUiProxy
+import com.android.systemui.shared.recents.model.ThumbnailData.Companion.wrap
+import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat
+import com.android.systemui.shared.system.RecentsAnimationListener
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.SmartspaceState
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
+import com.android.systemui.unfold.progress.IUnfoldAnimation
+import com.android.systemui.unfold.progress.IUnfoldTransitionListener
+import com.android.wm.shell.back.IBackAnimation
+import com.android.wm.shell.bubbles.IBubbles
+import com.android.wm.shell.bubbles.IBubblesListener
+import com.android.wm.shell.common.pip.IPip
+import com.android.wm.shell.common.pip.IPipAnimationListener
+import com.android.wm.shell.desktopmode.IDesktopMode
+import com.android.wm.shell.desktopmode.IDesktopTaskListener
+import com.android.wm.shell.draganddrop.IDragAndDrop
+import com.android.wm.shell.onehanded.IOneHanded
+import com.android.wm.shell.recents.IRecentTasks
+import com.android.wm.shell.recents.IRecentTasksListener
+import com.android.wm.shell.recents.IRecentsAnimationController
+import com.android.wm.shell.recents.IRecentsAnimationRunner
+import com.android.wm.shell.shared.GroupedTaskInfo
+import com.android.wm.shell.shared.IShellTransitions
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.UpdateSource
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
+import com.android.wm.shell.shared.split.SplitBounds
+import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition
+import com.android.wm.shell.splitscreen.ISplitScreen
+import com.android.wm.shell.splitscreen.ISplitScreenListener
+import com.android.wm.shell.splitscreen.ISplitSelectListener
+import com.android.wm.shell.startingsurface.IStartingWindow
+import com.android.wm.shell.startingsurface.IStartingWindowListener
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/** Holds the reference to SystemUI. */
+@LauncherAppSingleton
+class SystemUiProxy @Inject constructor(@ApplicationContext private val context: Context) :
+ NavHandle {
+
+ private var systemUiProxy: ISystemUiProxy? = null
+ private var pip: IPip? = null
+ private var bubbles: IBubbles? = null
+ private var sysuiUnlockAnimationController: ISysuiUnlockAnimationController? = null
+ private var splitScreen: ISplitScreen? = null
+ private var oneHanded: IOneHanded? = null
+ private var shellTransitions: IShellTransitions? = null
+ private var startingWindow: IStartingWindow? = null
+ private var recentTasks: IRecentTasks? = null
+ private var backAnimation: IBackAnimation? = null
+ private var desktopMode: IDesktopMode? = null
+ private var unfoldAnimation: IUnfoldAnimation? = null
+
+ private val systemUiProxyDeathRecipient =
+ IBinder.DeathRecipient { Executors.MAIN_EXECUTOR.execute { clearProxy() } }
+
+ // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
+ // yet, and we'll need to set/register these listeners with SysUI when they do. Note that it is
+ // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
+ // in case SysUI needs to rebind.
+ private var pipAnimationListener: IPipAnimationListener? = null
+ private var bubblesListener: IBubblesListener? = null
+ private var splitScreenListener: ISplitScreenListener? = null
+ private var splitSelectListener: ISplitSelectListener? = null
+ private var startingWindowListener: IStartingWindowListener? = null
+ private var launcherUnlockAnimationController: ILauncherUnlockAnimationController? = null
+ private var launcherActivityClass: String? = null
+ private var recentTasksListener: IRecentTasksListener? = null
+ private var unfoldAnimationListener: IUnfoldTransitionListener? = null
+ private var desktopTaskListener: IDesktopTaskListener? = null
+ private val remoteTransitions = LinkedHashMap<RemoteTransition, TransitionFilter>()
+
+ private val stateChangeCallbacks: MutableList<Runnable> = ArrayList()
+
+ private var originalTransactionToken: IBinder? = null
+ private var backToLauncherCallback: IOnBackInvokedCallback? = null
+ private var backToLauncherRunner: IRemoteAnimationRunner? = null
+ private var dragAndDrop: IDragAndDrop? = null
+ val homeVisibilityState = HomeVisibilityState()
+ private val focusState = FocusState()
+
+ // Used to dedupe calls to SystemUI
+ private var lastShelfHeight = 0
+ private var lastShelfVisible = false
+
+ // Used to dedupe calls to SystemUI
+ private var lastLauncherKeepClearAreaHeight = 0
+ private var lastLauncherKeepClearAreaHeightVisible = false
+
+ private val asyncHandler =
+ Handler(Executors.UI_HELPER_EXECUTOR.looper) { handleMessageAsync(it) }
+
+ // TODO(141886704): Find a way to remove this
+ @SystemUiStateFlags var lastSystemUiStateFlags: Long = 0
+
+ /**
+ * This is a singleton pending intent that is used to start recents via Shell (which is a
+ * different process). It is bare-bones, so it's expected that the component and options will be
+ * provided via fill-in intent.
+ */
+ private val recentsPendingIntent =
+ PendingIntent.getActivity(
+ context,
+ 0,
+ Intent().setPackage(context.packageName),
+ PendingIntent.FLAG_MUTABLE or
+ PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or
+ Intent.FILL_IN_COMPONENT,
+ ActivityOptions.makeBasic()
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ )
+ .toBundle(),
+ )
+
+ val unfoldTransitionProvider: ProxyUnfoldTransitionProvider? =
+ if ((Flags.enableUnfoldStateAnimation() && ResourceUnfoldTransitionConfig().isEnabled))
+ ProxyUnfoldTransitionProvider()
+ else null
+
+ private inline fun executeWithErrorLog(
+ errorMsg: () -> String,
+ tag: String = TAG,
+ callback: () -> Any?,
+ ) {
+ try {
+ callback.invoke()
+ } catch (e: RemoteException) {
+ Log.w(tag, errorMsg.invoke(), e)
+ }
+ }
+
+ fun onBackEvent(backEvent: KeyEvent?) =
+ executeWithErrorLog({ "Failed call onBackPressed" }) {
+ systemUiProxy?.onBackEvent(backEvent)
+ }
+
+ fun onImeSwitcherPressed() =
+ executeWithErrorLog({ "Failed call onImeSwitcherPressed" }) {
+ systemUiProxy?.onImeSwitcherPressed()
+ }
+
+ fun onImeSwitcherLongPress() =
+ executeWithErrorLog({ "Failed call onImeSwitcherLongPress" }) {
+ systemUiProxy?.onImeSwitcherLongPress()
+ }
+
+ fun updateContextualEduStats(isTrackpadGesture: Boolean, gestureType: String) =
+ executeWithErrorLog({ "Failed call updateContextualEduStats" }) {
+ systemUiProxy?.updateContextualEduStats(isTrackpadGesture, gestureType)
+ }
+
+ fun setHomeRotationEnabled(enabled: Boolean) =
+ executeWithErrorLog({ "Failed call setHomeRotationEnabled" }) {
+ systemUiProxy?.setHomeRotationEnabled(enabled)
+ }
+
+ /**
+ * Sets proxy state, including death linkage, various listeners, and other configuration objects
+ */
+ @MainThread
+ fun setProxy(
+ proxy: ISystemUiProxy?,
+ pip: IPip?,
+ bubbles: IBubbles?,
+ splitScreen: ISplitScreen?,
+ oneHanded: IOneHanded?,
+ shellTransitions: IShellTransitions?,
+ startingWindow: IStartingWindow?,
+ recentTasks: IRecentTasks?,
+ sysuiUnlockAnimationController: ISysuiUnlockAnimationController?,
+ backAnimation: IBackAnimation?,
+ desktopMode: IDesktopMode?,
+ unfoldAnimation: IUnfoldAnimation?,
+ dragAndDrop: IDragAndDrop?,
+ ) {
+ Preconditions.assertUIThread()
+ unlinkToDeath()
+ systemUiProxy = proxy
+ this.pip = pip
+ this.bubbles = bubbles
+ this.splitScreen = splitScreen
+ this.oneHanded = oneHanded
+ this.shellTransitions = shellTransitions
+ this.startingWindow = startingWindow
+ this.sysuiUnlockAnimationController = sysuiUnlockAnimationController
+ this.recentTasks = recentTasks
+ this.backAnimation = backAnimation
+ this.desktopMode = desktopMode
+ this.unfoldAnimation = if (Flags.enableUnfoldStateAnimation()) null else unfoldAnimation
+ this.dragAndDrop = dragAndDrop
+ linkToDeath()
+ // re-attach the listeners once missing due to setProxy has not been initialized yet.
+ setPipAnimationListener(pipAnimationListener)
+ setBubblesListener(bubblesListener)
+ registerSplitScreenListener(splitScreenListener)
+ registerSplitSelectListener(splitSelectListener)
+ homeVisibilityState.init(this.shellTransitions)
+ focusState.init(this.shellTransitions)
+ setStartingWindowListener(startingWindowListener)
+ setLauncherUnlockAnimationController(
+ launcherActivityClass,
+ launcherUnlockAnimationController,
+ )
+ LinkedHashMap(remoteTransitions).forEach { (remoteTransition, filter) ->
+ registerRemoteTransition(remoteTransition, filter)
+ }
+ setupTransactionQueue()
+ registerRecentTasksListener(recentTasksListener)
+ setBackToLauncherCallback(backToLauncherCallback, backToLauncherRunner)
+ setUnfoldAnimationListener(unfoldAnimationListener)
+ setDesktopTaskListener(desktopTaskListener)
+ setAssistantOverridesRequested(
+ ContextualSearchInvoker(context).getSysUiAssistOverrideInvocationTypes()
+ )
+ stateChangeCallbacks.forEach { it.run() }
+
+ if (unfoldTransitionProvider != null) {
+ if (unfoldAnimation != null) {
+ try {
+ unfoldAnimation.setListener(unfoldTransitionProvider)
+ unfoldTransitionProvider.isActive = true
+ } catch (e: RemoteException) {
+ // Ignore
+ }
+ } else {
+ unfoldTransitionProvider.isActive = false
+ }
+ }
+ }
+
+ /**
+ * Clear the proxy to release held resources and turn the majority of its operations into no-ops
+ */
+ @MainThread
+ fun clearProxy() =
+ setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null)
+
+ /** Adds a callback to be notified whenever the active state changes */
+ fun addOnStateChangeListener(callback: Runnable) = stateChangeCallbacks.add(callback)
+
+ /** Removes a previously added state change callback */
+ fun removeOnStateChangeListener(callback: Runnable) = stateChangeCallbacks.remove(callback)
+
+ fun isActive() = systemUiProxy != null
+
+ private fun linkToDeath() =
+ executeWithErrorLog({ "Failed to link sysui proxy death recipient" }) {
+ systemUiProxy?.asBinder()?.linkToDeath(systemUiProxyDeathRecipient, 0 /* flags */)
+ }
+
+ private fun unlinkToDeath() =
+ systemUiProxy?.asBinder()?.unlinkToDeath(systemUiProxyDeathRecipient, 0 /* flags */)
+
+ fun startScreenPinning(taskId: Int) =
+ executeWithErrorLog({ "Failed call startScreenPinning" }) {
+ systemUiProxy?.startScreenPinning(taskId)
+ }
+
+ fun onOverviewShown(fromHome: Boolean, tag: String = TAG) =
+ executeWithErrorLog(
+ { "Failed call onOverviewShown from: ${(if (fromHome) "home" else "app")}" },
+ tag = tag,
+ ) {
+ systemUiProxy?.onOverviewShown(fromHome)
+ }
+
+ @MainThread
+ fun onStatusBarTouchEvent(event: MotionEvent) {
+ Preconditions.assertUIThread()
+ executeWithErrorLog({ "Failed call onStatusBarTouchEvent with arg: $event" }) {
+ systemUiProxy?.onStatusBarTouchEvent(event)
+ }
+ }
+
+ fun onStatusBarTrackpadEvent(event: MotionEvent) =
+ executeWithErrorLog({ "Failed call onStatusBarTrackpadEvent with arg: $event" }) {
+ systemUiProxy?.onStatusBarTrackpadEvent(event)
+ }
+
+ fun onAssistantProgress(progress: Float) =
+ executeWithErrorLog({ "Failed call onAssistantProgress with progress: $progress" }) {
+ systemUiProxy?.onAssistantProgress(progress)
+ }
+
+ fun onAssistantGestureCompletion(velocity: Float) =
+ executeWithErrorLog({ "Failed call onAssistantGestureCompletion" }) {
+ systemUiProxy?.onAssistantGestureCompletion(velocity)
+ }
+
+ fun startAssistant(args: Bundle) =
+ executeWithErrorLog({ "Failed call startAssistant" }) {
+ systemUiProxy?.startAssistant(args)
+ }
+
+ fun setAssistantOverridesRequested(invocationTypes: IntArray) =
+ executeWithErrorLog({ "Failed call setAssistantOverridesRequested" }) {
+ systemUiProxy?.setAssistantOverridesRequested(invocationTypes)
+ }
+
+ override fun animateNavBarLongPress(isTouchDown: Boolean, shrink: Boolean, durationMs: Long) =
+ executeWithErrorLog({ "Failed call animateNavBarLongPress" }) {
+ systemUiProxy?.animateNavBarLongPress(isTouchDown, shrink, durationMs)
+ }
+
+ fun setOverrideHomeButtonLongPress(duration: Long, slopMultiplier: Float, haptic: Boolean) =
+ executeWithErrorLog({ "Failed call setOverrideHomeButtonLongPress" }) {
+ systemUiProxy?.setOverrideHomeButtonLongPress(duration, slopMultiplier, haptic)
+ }
+
+ fun notifyAccessibilityButtonClicked(displayId: Int) =
+ executeWithErrorLog({ "Failed call notifyAccessibilityButtonClicked" }) {
+ systemUiProxy?.notifyAccessibilityButtonClicked(displayId)
+ }
+
+ fun notifyAccessibilityButtonLongClicked() =
+ executeWithErrorLog({ "Failed call notifyAccessibilityButtonLongClicked" }) {
+ systemUiProxy?.notifyAccessibilityButtonLongClicked()
+ }
+
+ fun stopScreenPinning() =
+ executeWithErrorLog({ "Failed call stopScreenPinning" }) {
+ systemUiProxy?.stopScreenPinning()
+ }
+
+ fun notifyPrioritizedRotation(rotation: Int) =
+ executeWithErrorLog({ "Failed call notifyPrioritizedRotation with arg: $rotation" }) {
+ systemUiProxy?.notifyPrioritizedRotation(rotation)
+ }
+
+ fun notifyTaskbarStatus(visible: Boolean, stashed: Boolean) =
+ executeWithErrorLog({ "Failed call notifyTaskbarStatus with arg: $visible, $stashed" }) {
+ systemUiProxy?.notifyTaskbarStatus(visible, stashed)
+ }
+
+ /**
+ * NOTE: If called to suspend, caller MUST call this method to also un-suspend. [suspend] should
+ * be `true` to stop auto-hide, `false` to resume normal behavior
+ */
+ fun notifyTaskbarAutohideSuspend(suspend: Boolean) =
+ executeWithErrorLog({ "Failed call notifyTaskbarAutohideSuspend with arg: $suspend" }) {
+ systemUiProxy?.notifyTaskbarAutohideSuspend(suspend)
+ }
+
+ fun takeScreenshot(request: ScreenshotRequest) =
+ executeWithErrorLog({ "Failed call takeScreenshot" }) {
+ systemUiProxy?.takeScreenshot(request)
+ }
+
+ fun expandNotificationPanel() =
+ executeWithErrorLog({ "Failed call expandNotificationPanel" }) {
+ systemUiProxy?.expandNotificationPanel()
+ }
+
+ fun toggleNotificationPanel() =
+ executeWithErrorLog({ "Failed call toggleNotificationPanel" }) {
+ systemUiProxy?.toggleNotificationPanel()
+ }
+
+ fun toggleQuickSettingsPanel() =
+ executeWithErrorLog({ "Failed call toggleQuickSettingsPanel" }) {
+ systemUiProxy?.toggleQuickSettingsPanel()
+ }
+
+ //
+ // Pip
+ //
+ /** Sets the shelf height. */
+ fun setShelfHeight(visible: Boolean, shelfHeight: Int) =
+ Message.obtain(asyncHandler, MSG_SET_SHELF_HEIGHT, if (visible) 1 else 0, shelfHeight)
+ .sendToTarget()
+
+ @WorkerThread
+ private fun setShelfHeightAsync(visibleInt: Int, shelfHeight: Int) {
+ val visible = visibleInt != 0
+ val changed = visible != lastShelfVisible || shelfHeight != lastShelfHeight
+ val pip = pip
+ if (pip != null && changed) {
+ lastShelfVisible = visible
+ lastShelfHeight = shelfHeight
+ executeWithErrorLog({
+ "Failed call setShelfHeight visible: $visible height: $shelfHeight"
+ }) {
+ pip.setShelfHeight(visible, shelfHeight)
+ }
+ }
+ }
+
+ /**
+ * Sets the height of the keep clear area that is going to be reported by the Launcher for the
+ * Hotseat.
+ */
+ fun setLauncherKeepClearAreaHeight(visible: Boolean, height: Int) =
+ Message.obtain(
+ asyncHandler,
+ MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT,
+ if (visible) 1 else 0,
+ height,
+ )
+ .sendToTarget()
+
+ @WorkerThread
+ private fun setLauncherKeepClearAreaHeight(visibleInt: Int, height: Int) {
+ val visible = visibleInt != 0
+ val changed =
+ visible != lastLauncherKeepClearAreaHeightVisible ||
+ height != lastLauncherKeepClearAreaHeight
+ val pip = pip
+ if (pip != null && changed) {
+ lastLauncherKeepClearAreaHeightVisible = visible
+ lastLauncherKeepClearAreaHeight = height
+ executeWithErrorLog({
+ "Failed call setLauncherKeepClearAreaHeight visible: $visible height: $height"
+ }) {
+ pip.setLauncherKeepClearAreaHeight(visible, height)
+ }
+ }
+ }
+
+ /** Sets listener to get pip animation callbacks. */
+ fun setPipAnimationListener(listener: IPipAnimationListener?) {
+ executeWithErrorLog({ "Failed call setPinnedStackAnimationListener" }) {
+ pip?.setPipAnimationListener(listener)
+ }
+ pipAnimationListener = listener
+ }
+
+ /** @return Destination bounds of auto-pip animation, `null` if the animation is not ready. */
+ fun startSwipePipToHome(
+ componentName: ComponentName?,
+ activityInfo: ActivityInfo?,
+ pictureInPictureParams: PictureInPictureParams?,
+ launcherRotation: Int,
+ hotseatKeepClearArea: Rect?,
+ ): Rect? {
+ executeWithErrorLog({ "Failed call startSwipePipToHome" }) {
+ return pip?.startSwipePipToHome(
+ componentName,
+ activityInfo,
+ pictureInPictureParams,
+ launcherRotation,
+ hotseatKeepClearArea,
+ )
+ }
+ return null
+ }
+
+ /**
+ * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to
+ * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell
+ * should be responsible for cleaning up the overlay.
+ */
+ fun stopSwipePipToHome(
+ taskId: Int,
+ componentName: ComponentName?,
+ destinationBounds: Rect?,
+ overlay: SurfaceControl?,
+ appBounds: Rect?,
+ sourceRectHint: Rect?,
+ ) =
+ executeWithErrorLog({ "Failed call stopSwipePipToHome" }) {
+ pip?.stopSwipePipToHome(
+ taskId,
+ componentName,
+ destinationBounds,
+ overlay,
+ appBounds,
+ sourceRectHint,
+ )
+ }
+
+ /**
+ * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell can
+ * use this callback to clean up its internal states.
+ */
+ fun abortSwipePipToHome(taskId: Int, componentName: ComponentName?) =
+ executeWithErrorLog({ "Failed call abortSwipePipToHome" }) {
+ pip?.abortSwipePipToHome(taskId, componentName)
+ }
+
+ /** Sets the next pip animation type to be the alpha animation. */
+ fun setPipAnimationTypeToAlpha() =
+ executeWithErrorLog({ "Failed call setPipAnimationTypeToAlpha" }) {
+ pip?.setPipAnimationTypeToAlpha()
+ }
+
+ /** Sets the app icon size in pixel used by Launcher all apps. */
+ fun setLauncherAppIconSize(iconSizePx: Int) =
+ executeWithErrorLog({ "Failed call setLauncherAppIconSize" }) {
+ pip?.setLauncherAppIconSize(iconSizePx)
+ }
+
+ //
+ // Bubbles
+ //
+ /** Sets the listener to be notified of bubble state changes. */
+ fun setBubblesListener(listener: IBubblesListener?) {
+ executeWithErrorLog({ "Failed call registerBubblesListener" }) {
+ bubbles?.apply {
+ bubblesListener?.let { unregisterBubbleListener(it) }
+ listener?.let { registerBubbleListener(it) }
+ }
+ }
+ bubblesListener = listener
+ }
+
+ /**
+ * Tells SysUI to show the bubble with the provided key.
+ *
+ * @param key the key of the bubble to show.
+ * @param top top coordinate of bubble bar on screen
+ */
+ fun showBubble(key: String?, top: Int) =
+ executeWithErrorLog({ "Failed call showBubble" }) { bubbles?.showBubble(key, top) }
+
+ /** Tells SysUI to remove all bubbles. */
+ fun removeAllBubbles() =
+ executeWithErrorLog({ "Failed call removeAllBubbles" }) { bubbles?.removeAllBubbles() }
+
+ /** Tells SysUI to collapse the bubbles. */
+ fun collapseBubbles() =
+ executeWithErrorLog({ "Failed call collapseBubbles" }) { bubbles?.collapseBubbles() }
+
+ /**
+ * Tells SysUI when the bubble is being dragged. Should be called only when the bubble bar is
+ * expanded.
+ *
+ * @param bubbleKey key of the bubble being dragged
+ */
+ fun startBubbleDrag(bubbleKey: String?) =
+ executeWithErrorLog({ "Failed call startBubbleDrag" }) {
+ bubbles?.startBubbleDrag(bubbleKey)
+ }
+
+ /**
+ * Tells SysUI when the bubble stops being dragged. Should be called only when the bubble bar is
+ * expanded.
+ *
+ * @param location location of the bubble bar
+ * @param top new top coordinate for bubble bar on screen
+ */
+ fun stopBubbleDrag(location: BubbleBarLocation?, top: Int) =
+ executeWithErrorLog({ "Failed call stopBubbleDrag" }) {
+ bubbles?.stopBubbleDrag(location, top)
+ }
+
+ /**
+ * Tells SysUI to dismiss the bubble with the provided key.
+ *
+ * @param key the key of the bubble to dismiss.
+ * @param timestamp the timestamp when the removal happened.
+ */
+ fun dragBubbleToDismiss(key: String?, timestamp: Long) =
+ executeWithErrorLog({ "Failed call dragBubbleToDismiss" }) {
+ bubbles?.dragBubbleToDismiss(key, timestamp)
+ }
+
+ /**
+ * Tells SysUI to show user education relative to the reference point provided.
+ *
+ * @param position the bubble bar top center position in Screen coordinates.
+ */
+ fun showUserEducation(position: Point) =
+ executeWithErrorLog({ "Failed call showUserEducation" }) {
+ bubbles?.showUserEducation(position.x, position.y)
+ }
+
+ /**
+ * Tells SysUI to update the bubble bar location to the new location.
+ *
+ * @param location new location for the bubble bar
+ * @param source what triggered the location update
+ */
+ fun setBubbleBarLocation(location: BubbleBarLocation?, @UpdateSource source: Int) =
+ executeWithErrorLog({ "Failed call setBubbleBarLocation" }) {
+ bubbles?.setBubbleBarLocation(location, source)
+ }
+
+ /**
+ * Tells SysUI the top coordinate of bubble bar on screen
+ *
+ * @param topOnScreen top coordinate for bubble bar on screen
+ */
+ fun updateBubbleBarTopOnScreen(topOnScreen: Int) =
+ executeWithErrorLog({ "Failed call updateBubbleBarTopOnScreen" }) {
+ bubbles?.updateBubbleBarTopOnScreen(topOnScreen)
+ }
+
+ /**
+ * Tells SysUI to show a shortcut bubble.
+ *
+ * @param info the shortcut info used to create or identify the bubble.
+ */
+ fun showShortcutBubble(info: ShortcutInfo?) =
+ executeWithErrorLog({ "Failed call showShortcutBubble" }) {
+ bubbles?.showShortcutBubble(info)
+ }
+
+ /**
+ * Tells SysUI to show a bubble of an app.
+ *
+ * @param intent the intent used to create the bubble.
+ */
+ fun showAppBubble(intent: Intent?) =
+ executeWithErrorLog({ "Failed call showAppBubble" }) { bubbles?.showAppBubble(intent) }
+
+ /** Tells SysUI to show the expanded view. */
+ fun showExpandedView() =
+ executeWithErrorLog({ "Failed call showExpandedView" }) { bubbles?.showExpandedView() }
+
+ //
+ // Splitscreen
+ //
+ fun registerSplitScreenListener(listener: ISplitScreenListener?) {
+ executeWithErrorLog({ "Failed call registerSplitScreenListener" }) {
+ splitScreen?.registerSplitScreenListener(listener)
+ }
+ splitScreenListener = listener
+ }
+
+ fun unregisterSplitScreenListener(listener: ISplitScreenListener?) {
+ executeWithErrorLog({ "Failed call unregisterSplitScreenListener" }) {
+ splitScreen?.unregisterSplitScreenListener(listener)
+ }
+ splitScreenListener = null
+ }
+
+ fun registerSplitSelectListener(listener: ISplitSelectListener?) {
+ executeWithErrorLog({ "Failed call registerSplitSelectListener" }) {
+ splitScreen?.registerSplitSelectListener(listener)
+ }
+ splitSelectListener = listener
+ }
+
+ fun unregisterSplitSelectListener(listener: ISplitSelectListener?) {
+ executeWithErrorLog({ "Failed call unregisterSplitSelectListener" }) {
+ splitScreen?.unregisterSplitSelectListener(listener)
+ }
+ splitSelectListener = null
+ }
+
+ /** Start multiple tasks in split-screen simultaneously. */
+ fun startTasks(
+ taskId1: Int,
+ options1: Bundle?,
+ taskId2: Int,
+ options2: Bundle?,
+ @StagePosition splitPosition: Int,
+ @PersistentSnapPosition snapPosition: Int,
+ remoteTransition: RemoteTransition?,
+ instanceId: InstanceId?,
+ ) =
+ executeWithErrorLog({ "Failed call startTasks" }) {
+ splitScreen?.startTasks(
+ taskId1,
+ options1,
+ taskId2,
+ options2,
+ splitPosition,
+ snapPosition,
+ remoteTransition,
+ instanceId,
+ )
+ }
+
+ fun startIntentAndTask(
+ pendingIntent: PendingIntent?,
+ userId1: Int,
+ options1: Bundle?,
+ taskId: Int,
+ options2: Bundle?,
+ @StagePosition splitPosition: Int,
+ @PersistentSnapPosition snapPosition: Int,
+ remoteTransition: RemoteTransition?,
+ instanceId: InstanceId?,
+ ) =
+ executeWithErrorLog({ "Failed call startIntentAndTask" }) {
+ splitScreen?.startIntentAndTask(
+ pendingIntent,
+ userId1,
+ options1,
+ taskId,
+ options2,
+ splitPosition,
+ snapPosition,
+ remoteTransition,
+ instanceId,
+ )
+ }
+
+ fun startIntents(
+ pendingIntent1: PendingIntent?,
+ userId1: Int,
+ shortcutInfo1: ShortcutInfo?,
+ options1: Bundle?,
+ pendingIntent2: PendingIntent?,
+ userId2: Int,
+ shortcutInfo2: ShortcutInfo?,
+ options2: Bundle?,
+ @StagePosition splitPosition: Int,
+ @PersistentSnapPosition snapPosition: Int,
+ remoteTransition: RemoteTransition?,
+ instanceId: InstanceId?,
+ ) =
+ executeWithErrorLog({ "Failed call startIntents" }) {
+ splitScreen?.startIntents(
+ pendingIntent1,
+ userId1,
+ shortcutInfo1,
+ options1,
+ pendingIntent2,
+ userId2,
+ shortcutInfo2,
+ options2,
+ splitPosition,
+ snapPosition,
+ remoteTransition,
+ instanceId,
+ )
+ }
+
+ fun startShortcutAndTask(
+ shortcutInfo: ShortcutInfo?,
+ options1: Bundle?,
+ taskId: Int,
+ options2: Bundle?,
+ @StagePosition splitPosition: Int,
+ @PersistentSnapPosition snapPosition: Int,
+ remoteTransition: RemoteTransition?,
+ instanceId: InstanceId?,
+ ) =
+ executeWithErrorLog({ "Failed call startShortcutAndTask" }) {
+ splitScreen?.startShortcutAndTask(
+ shortcutInfo,
+ options1,
+ taskId,
+ options2,
+ splitPosition,
+ snapPosition,
+ remoteTransition,
+ instanceId,
+ )
+ }
+
+ fun startShortcut(
+ packageName: String?,
+ shortcutId: String?,
+ position: Int,
+ options: Bundle?,
+ user: UserHandle?,
+ instanceId: InstanceId?,
+ ) =
+ executeWithErrorLog({ "Failed call startShortcut" }) {
+ splitScreen?.startShortcut(packageName, shortcutId, position, options, user, instanceId)
+ }
+
+ fun startIntent(
+ intent: PendingIntent?,
+ userId: Int,
+ fillInIntent: Intent?,
+ position: Int,
+ options: Bundle?,
+ instanceId: InstanceId?,
+ ) =
+ executeWithErrorLog({ "Failed call startIntent" }) {
+ splitScreen?.startIntent(intent, userId, fillInIntent, position, options, instanceId)
+ }
+
+ //
+ // One handed
+ //
+ fun startOneHandedMode() =
+ executeWithErrorLog({ "Failed call startOneHandedMode" }) { oneHanded?.startOneHanded() }
+
+ fun stopOneHandedMode() =
+ executeWithErrorLog({ "Failed call stopOneHandedMode" }) { oneHanded?.stopOneHanded() }
+
+ //
+ // Remote transitions
+ //
+ fun registerRemoteTransition(remoteTransition: RemoteTransition?, filter: TransitionFilter) {
+ remoteTransition ?: return
+ executeWithErrorLog({ "Failed call registerRemoteTransition" }) {
+ shellTransitions?.registerRemote(filter, remoteTransition)
+ }
+ remoteTransitions.putIfAbsent(remoteTransition, filter)
+ }
+
+ fun unregisterRemoteTransition(remoteTransition: RemoteTransition?) {
+ executeWithErrorLog({ "Failed call unregisterRemoteTransition" }) {
+ shellTransitions?.unregisterRemote(remoteTransition)
+ }
+ remoteTransitions.remove(remoteTransition)
+ }
+
+ /**
+ * Returns a surface which can be used to attach overlays to home task or null if the task
+ * doesn't exist or sysui is not connected
+ */
+ fun getHomeTaskOverlayContainer(): SurfaceControl? {
+ executeWithErrorLog({ "Failed call getHomeTaskOverlayContainer" }) {
+ return shellTransitions?.homeTaskOverlayContainer
+ }
+ return null
+ }
+
+ /**
+ * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary if
+ * Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
+ */
+ fun shareTransactionQueue() {
+ if (originalTransactionToken == null) {
+ originalTransactionToken = Transaction.getDefaultApplyToken()
+ }
+ setupTransactionQueue()
+ }
+
+ /** Switch back to using Launcher's independent transaction queue. */
+ fun unshareTransactionQueue() {
+ if (originalTransactionToken == null) {
+ return
+ }
+ Transaction.setDefaultApplyToken(originalTransactionToken)
+ originalTransactionToken = null
+ }
+
+ private fun setupTransactionQueue() =
+ executeWithErrorLog({ "Error getting Shell's apply token" }) {
+ val token: IBinder =
+ shellTransitions?.shellApplyToken ?: originalTransactionToken ?: return
+ Transaction.setDefaultApplyToken(token)
+ }
+
+ //
+ // Starting window
+ //
+ /** Sets listener to get callbacks when launching a task. */
+ fun setStartingWindowListener(listener: IStartingWindowListener?) {
+ executeWithErrorLog({ "Failed call setStartingWindowListener" }) {
+ startingWindow?.setStartingWindowListener(listener)
+ }
+ startingWindowListener = listener
+ }
+
+ //
+ // SmartSpace transitions
+ //
+ /**
+ * Sets the instance of [ILauncherUnlockAnimationController] that System UI should use to
+ * control the launcher side of the unlock animation. This will also cause us to dispatch the
+ * current state of the smartspace to System UI (this will subsequently happen if the state
+ * changes).
+ */
+ fun setLauncherUnlockAnimationController(
+ activityClass: String?,
+ controller: ILauncherUnlockAnimationController?,
+ ) {
+ executeWithErrorLog({ "Failed call setLauncherUnlockAnimationController" }) {
+ sysuiUnlockAnimationController?.apply {
+ setLauncherUnlockController(activityClass, controller)
+ controller?.dispatchSmartspaceStateToSysui()
+ }
+ }
+ launcherActivityClass = activityClass
+ launcherUnlockAnimationController = controller
+ }
+
+ /**
+ * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
+ * the unlock animation accordingly.
+ */
+ fun notifySysuiSmartspaceStateUpdated(state: SmartspaceState?) =
+ executeWithErrorLog({ "Failed call notifySysuiSmartspaceStateUpdated" }) {
+ sysuiUnlockAnimationController?.onLauncherSmartspaceStateUpdated(state)
+ }
+
+ //
+ // Recents
+ //
+ fun registerRecentTasksListener(listener: IRecentTasksListener?) {
+ executeWithErrorLog({ "Failed call registerRecentTasksListener" }) {
+ recentTasks?.registerRecentTasksListener(listener)
+ }
+ recentTasksListener = listener
+ }
+
+ fun unregisterRecentTasksListener(listener: IRecentTasksListener?) {
+ executeWithErrorLog({ "Failed call unregisterRecentTasksListener" }) {
+ recentTasks?.unregisterRecentTasksListener(listener)
+ }
+ recentTasksListener = null
+ }
+
+ //
+ // Back navigation transitions
+ //
+ /** Sets the launcher [android.window.IOnBackInvokedCallback] to shell */
+ fun setBackToLauncherCallback(
+ callback: IOnBackInvokedCallback?,
+ runner: IRemoteAnimationRunner?,
+ ) {
+ backToLauncherCallback = callback
+ backToLauncherRunner = runner
+ if (callback == null) return
+ try {
+ backAnimation?.setBackToLauncherCallback(callback, runner)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Failed call setBackToLauncherCallback", e)
+ } catch (e: SecurityException) {
+ Log.e(TAG, "Failed call setBackToLauncherCallback", e)
+ }
+ }
+
+ /**
+ * Clears the previously registered [IOnBackInvokedCallback].
+ *
+ * @param callback The previously registered callback instance.
+ */
+ fun clearBackToLauncherCallback(callback: IOnBackInvokedCallback) {
+ if (backToLauncherCallback !== callback) {
+ return
+ }
+ backToLauncherCallback = null
+ backToLauncherRunner = null
+ executeWithErrorLog({ "Failed call clearBackToLauncherCallback" }) {
+ backAnimation?.clearBackToLauncherCallback()
+ }
+ }
+
+ /** Called when the status bar color needs to be customized when back navigation. */
+ fun customizeStatusBarAppearance(appearance: AppearanceRegion?) =
+ executeWithErrorLog({ "Failed call customizeStatusBarAppearance" }) {
+ backAnimation?.customizeStatusBarAppearance(appearance)
+ }
+
+ class GetRecentTasksException : Exception {
+ constructor(message: String?) : super(message)
+
+ constructor(message: String?, cause: Throwable?) : super(message, cause)
+ }
+
+ /**
+ * Retrieves a list of Recent tasks from ActivityManager.
+ *
+ * @throws GetRecentTasksException if IRecentTasks is not initialized, or when we get
+ * RemoteException from server side
+ */
+ @Throws(GetRecentTasksException::class)
+ fun getRecentTasks(numTasks: Int, userId: Int): ArrayList<GroupedTaskInfo> {
+ if (recentTasks == null) {
+ Log.e(TAG, "getRecentTasks() failed due to null mRecentTasks")
+ throw GetRecentTasksException("null mRecentTasks")
+ }
+ try {
+ val rawTasks =
+ recentTasks?.getRecentTasks(
+ numTasks,
+ ActivityManager.RECENT_IGNORE_UNAVAILABLE,
+ userId,
+ ) ?: return ArrayList()
+ return ArrayList(rawTasks.asList())
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Failed call getRecentTasks", e)
+ throw GetRecentTasksException("Failed call getRecentTasks", e)
+ }
+ }
+
+ /** Gets the set of running tasks. */
+ fun getRunningTasks(numTasks: Int): List<RunningTaskInfo> {
+ if (!shouldEnableRunningTasksForDesktopMode()) return emptyList()
+ executeWithErrorLog({ "Failed call getRunningTasks" }) {
+ return recentTasks?.getRunningTasks(numTasks)?.asList() ?: emptyList()
+ }
+ return emptyList()
+ }
+
+ private fun shouldEnableRunningTasksForDesktopMode(): Boolean =
+ DesktopModeStatus.canEnterDesktopMode(context) &&
+ ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue
+
+ private fun handleMessageAsync(msg: Message): Boolean {
+ return when (msg.what) {
+ MSG_SET_SHELF_HEIGHT -> {
+ setShelfHeightAsync(msg.arg1, msg.arg2)
+ true
+ }
+
+ MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT -> {
+ setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2)
+ true
+ }
+
+ else -> false
+ }
+ }
+
+ //
+ // Desktop Mode
+ //
+ /** Call shell to show all apps active on the desktop */
+ fun showDesktopApps(displayId: Int, transition: RemoteTransition?) =
+ executeWithErrorLog({ "Failed call showDesktopApps" }) {
+ desktopMode?.showDesktopApps(displayId, transition)
+ }
+
+ /** If task with the given id is on the desktop, bring it to front */
+ fun showDesktopApp(taskId: Int, transition: RemoteTransition?) =
+ executeWithErrorLog({ "Failed call showDesktopApp" }) {
+ desktopMode?.showDesktopApp(taskId, transition)
+ }
+
+ /** Call shell to get number of visible freeform tasks */
+ fun getVisibleDesktopTaskCount(displayId: Int): Int {
+ executeWithErrorLog({ "Failed call getVisibleDesktopTaskCount" }) {
+ return desktopMode?.getVisibleTaskCount(displayId) ?: 0
+ }
+ return 0
+ }
+
+ /** Set a listener on shell to get updates about desktop task state */
+ fun setDesktopTaskListener(listener: IDesktopTaskListener?) {
+ desktopTaskListener = listener
+ executeWithErrorLog({ "Failed call setDesktopTaskListener" }) {
+ desktopMode?.setTaskListener(listener)
+ }
+ }
+
+ /** Perform cleanup transactions after animation to split select is complete */
+ fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo?) =
+ executeWithErrorLog({ "Failed call onDesktopSplitSelectAnimComplete" }) {
+ desktopMode?.onDesktopSplitSelectAnimComplete(taskInfo)
+ }
+
+ /** Call shell to move a task with given `taskId` to desktop */
+ fun moveToDesktop(
+ taskId: Int,
+ transitionSource: DesktopModeTransitionSource?,
+ transition: RemoteTransition?,
+ ) =
+ executeWithErrorLog({ "Failed call moveToDesktop" }) {
+ desktopMode?.moveToDesktop(taskId, transitionSource, transition)
+ }
+
+ /** Call shell to remove the desktop that is on given `displayId` */
+ fun removeDesktop(displayId: Int) =
+ executeWithErrorLog({ "Failed call removeDesktop" }) {
+ desktopMode?.removeDesktop(displayId)
+ }
+
+ /** Call shell to move a task with given `taskId` to external display. */
+ fun moveToExternalDisplay(taskId: Int) =
+ executeWithErrorLog({ "Failed call moveToExternalDisplay" }) {
+ desktopMode?.moveToExternalDisplay(taskId)
+ }
+
+ //
+ // Unfold transition
+ //
+ /** Sets the unfold animation lister to sysui. */
+ fun setUnfoldAnimationListener(callback: IUnfoldTransitionListener?) {
+ unfoldAnimationListener = callback
+ executeWithErrorLog({ "Failed call setUnfoldAnimationListener" }) {
+ unfoldAnimation?.setListener(callback)
+ }
+ }
+
+ //
+ // Recents
+ //
+ /** Starts the recents activity. The caller should manage the thread on which this is called. */
+ fun startRecentsActivity(
+ intent: Intent?,
+ options: ActivityOptions,
+ listener: RecentsAnimationListener,
+ useSyntheticRecentsTransition: Boolean,
+ ): Boolean {
+ executeWithErrorLog({ "Error starting recents via shell" }) {
+ recentTasks?.startRecentsTransition(
+ recentsPendingIntent,
+ intent,
+ options.toBundle().apply {
+ if (useSyntheticRecentsTransition) {
+ putBoolean("is_synthetic_recents_transition", true)
+ }
+ },
+ context.iApplicationThread,
+ RecentsAnimationListenerStub(listener),
+ )
+ ?: run {
+ ActiveGestureProtoLogProxy.logRecentTasksMissing()
+ return false
+ }
+ return true
+ }
+ return false
+ }
+
+ private class RecentsAnimationListenerStub(val listener: RecentsAnimationListener) :
+ IRecentsAnimationRunner.Stub() {
+ override fun onAnimationStart(
+ controller: IRecentsAnimationController,
+ apps: Array<RemoteAnimationTarget>?,
+ wallpapers: Array<RemoteAnimationTarget>?,
+ homeContentInsets: Rect?,
+ minimizedHomeBounds: Rect?,
+ extras: Bundle?,
+ ) =
+ listener.onAnimationStart(
+ RecentsAnimationControllerCompat(controller),
+ apps,
+ wallpapers,
+ homeContentInsets,
+ minimizedHomeBounds,
+ extras?.apply {
+ // Aidl bundles need to explicitly set class loader
+ // https://developer.android.com/guide/components/aidl#Bundles
+ classLoader = SplitBounds::class.java.classLoader
+ },
+ )
+
+ override fun onAnimationCanceled(taskIds: IntArray?, taskSnapshots: Array<TaskSnapshot>?) =
+ listener.onAnimationCanceled(wrap(taskIds, taskSnapshots))
+
+ override fun onTasksAppeared(apps: Array<RemoteAnimationTarget>?) =
+ listener.onTasksAppeared(apps)
+ }
+
+ //
+ // Drag and drop
+ //
+ /**
+ * For testing purposes. Returns `true` only if the shell drop target has shown and drawn and is
+ * ready to handle drag events and the subsequent drop.
+ */
+ fun isDragAndDropReady(): Boolean {
+ executeWithErrorLog({ "Error querying drag state" }) {
+ return dragAndDrop?.isReadyToHandleDrag ?: false
+ }
+ return false
+ }
+
+ fun dump(pw: PrintWriter) {
+ pw.println("$TAG:")
+
+ pw.println("\tmSystemUiProxy=$systemUiProxy")
+ pw.println("\tmPip=$pip")
+ pw.println("\tmPipAnimationListener=$pipAnimationListener")
+ pw.println("\tmBubbles=$bubbles")
+ pw.println("\tmBubblesListener=$bubblesListener")
+ pw.println("\tmSplitScreen=$splitScreen")
+ pw.println("\tmSplitScreenListener=$splitScreenListener")
+ pw.println("\tmSplitSelectListener=$splitSelectListener")
+ pw.println("\tmOneHanded=$oneHanded")
+ pw.println("\tmShellTransitions=$shellTransitions")
+ pw.println("\tmHomeVisibilityState=" + homeVisibilityState)
+ pw.println("\tmFocusState=" + focusState)
+ pw.println("\tmStartingWindow=$startingWindow")
+ pw.println("\tmStartingWindowListener=$startingWindowListener")
+ pw.println("\tmSysuiUnlockAnimationController=$sysuiUnlockAnimationController")
+ pw.println("\tmLauncherActivityClass=$launcherActivityClass")
+ pw.println("\tmLauncherUnlockAnimationController=$launcherUnlockAnimationController")
+ pw.println("\tmRecentTasks=$recentTasks")
+ pw.println("\tmRecentTasksListener=$recentTasksListener")
+ pw.println("\tmBackAnimation=$backAnimation")
+ pw.println("\tmBackToLauncherCallback=$backToLauncherCallback")
+ pw.println("\tmBackToLauncherRunner=$backToLauncherRunner")
+ pw.println("\tmDesktopMode=$desktopMode")
+ pw.println("\tmDesktopTaskListener=$desktopTaskListener")
+ pw.println("\tmUnfoldAnimation=$unfoldAnimation")
+ pw.println("\tmUnfoldAnimationListener=$unfoldAnimationListener")
+ pw.println("\tmDragAndDrop=$dragAndDrop")
+ }
+
+ /** Adds all interfaces held by this proxy to the bundle */
+ @VisibleForTesting
+ fun addAllInterfaces(out: Bundle) {
+ QuickStepContract.addInterface(systemUiProxy, out)
+ QuickStepContract.addInterface(pip, out)
+ QuickStepContract.addInterface(bubbles, out)
+ QuickStepContract.addInterface(sysuiUnlockAnimationController, out)
+ QuickStepContract.addInterface(splitScreen, out)
+ QuickStepContract.addInterface(oneHanded, out)
+ QuickStepContract.addInterface(shellTransitions, out)
+ QuickStepContract.addInterface(startingWindow, out)
+ QuickStepContract.addInterface(recentTasks, out)
+ QuickStepContract.addInterface(backAnimation, out)
+ QuickStepContract.addInterface(desktopMode, out)
+ QuickStepContract.addInterface(unfoldAnimation, out)
+ QuickStepContract.addInterface(dragAndDrop, out)
+ }
+
+ companion object {
+ private const val TAG = "SystemUiProxy"
+
+ @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getSystemUiProxy)
+
+ private const val MSG_SET_SHELF_HEIGHT = 1
+ private const val MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 9de96c7..f5cc518 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -90,6 +90,7 @@
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.OverviewCommandHelper.CommandType;
+import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
@@ -103,7 +104,6 @@
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -124,7 +124,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -150,8 +149,6 @@
private final WeakReference<TouchInteractionService> mTis;
- private final Set<Runnable> mOnOverviewTargetChangeListeners = new HashSet<>();
-
private TISBinder(TouchInteractionService tis) {
mTis = new WeakReference<>(tis);
}
@@ -308,10 +305,10 @@
@BinderThread
@Override
public void updateWallpaperVisibility(int displayId, boolean visible) {
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis ->
- executeForTaskbarManager(
- taskbarManager -> taskbarManager.setWallpaperVisible(visible))
- ));
+ MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
+ tis -> executeForTaskbarManager(
+ taskbarManager -> taskbarManager.setWallpaperVisible(displayId,
+ visible))));
}
@BinderThread
@@ -498,28 +495,11 @@
tis -> tis.mDeviceState.setGestureBlockingTaskId(taskId));
}
- /** Registers a listener to be run on Overview Target updates. */
- public void registerOverviewTargetChangeListener(@NonNull Runnable listener) {
- mOnOverviewTargetChangeListeners.add(listener);
- }
-
- /** Unregisters an OverviewTargetChange listener. */
- public void unregisterOverviewTargetChangeListener(@NonNull Runnable listener) {
- mOnOverviewTargetChangeListeners.remove(listener);
- }
-
- protected void onOverviewTargetChange() {
- Set<Runnable> listeners = new HashSet<>(mOnOverviewTargetChangeListeners);
- for (Runnable listener : listeners) {
- listener.run();
- }
- }
-
/** Refreshes the current overview target. */
public void refreshOverviewTarget() {
executeForTouchInteractionService(tis -> {
tis.mAllAppsActionManager.onDestroy();
- tis.onOverviewTargetChange(tis.mOverviewComponentObserver.isHomeAndOverviewSame());
+ tis.onOverviewTargetChanged(tis.mOverviewComponentObserver.isHomeAndOverviewSame());
});
}
}
@@ -601,6 +581,7 @@
private final Runnable mUserUnlockedRunnable = this::onUserUnlocked;
private final ScreenOnTracker.ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
+ private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged;
private final TaskbarNavButtonCallbacks mNavCallbacks = new TaskbarNavButtonCallbacks() {
@Override
@@ -619,7 +600,6 @@
}
};
- private ActivityManagerWrapper mAM;
private OverviewCommandHelper mOverviewCommandHelper;
private OverviewComponentObserver mOverviewComponentObserver;
private InputConsumerController mInputConsumer;
@@ -654,7 +634,6 @@
// Initialize anything here that is needed in direct boot mode.
// Everything else should be initialized in onUserUnlocked() below.
mMainChoreographer = Choreographer.getInstance();
- mAM = ActivityManagerWrapper.getInstance();
mDeviceState = new RecentsAnimationDeviceState(this, true);
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
mAllAppsActionManager = new AllAppsActionManager(
@@ -724,7 +703,7 @@
Log.d(TAG, "onUserUnlocked: userId=" + getUserId()
+ " instance=" + System.identityHashCode(this));
mTaskAnimationManager = new TaskAnimationManager(this, mDeviceState);
- mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
+ mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(this);
mOverviewCommandHelper = new OverviewCommandHelper(this,
mOverviewComponentObserver, mTaskAnimationManager);
mResetGestureInputConsumer = new ResetGestureInputConsumer(
@@ -740,8 +719,8 @@
// new ModelPreload().start(this);
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
- mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
- onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
+ mOverviewComponentObserver.addOverviewChangeListener(mOverviewChangeListener);
+ onOverviewTargetChanged(mOverviewComponentObserver.isHomeAndOverviewSame());
mTaskbarManager.onUserUnlocked();
}
@@ -766,7 +745,7 @@
}
}
- private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
+ private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) {
mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame);
RecentsViewContainer newOverviewContainer =
mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
@@ -778,7 +757,6 @@
mTaskbarManager.setRecentsViewContainer(newOverviewContainer);
}
}
- mTISBinder.onOverviewTargetChange();
}
private PendingIntent createAllAppsPendingIntent() {
@@ -797,7 +775,7 @@
if (LockedUserState.get(this).isUserUnlocked()) {
long systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
- mOverviewComponentObserver.onSystemUiStateChanged();
+ mOverviewComponentObserver.setHomeDisabled(mDeviceState.isHomeDisabled());
mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
mTaskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags);
}
@@ -818,7 +796,8 @@
sIsInitialized = false;
if (LockedUserState.get(this).isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
- mOverviewComponentObserver.onDestroy();
+ mOverviewComponentObserver.setHomeDisabled(false);
+ mOverviewComponentObserver.removeOverviewChangeListener(mOverviewChangeListener);
}
disposeEventHandlers("TouchInteractionService onDestroy()");
mDeviceState.destroy();
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
index 4255372..20a66dd 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -19,6 +19,7 @@
import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.model.WellbeingModel;
+import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.util.AsyncClockEventDelegate;
@@ -40,4 +41,6 @@
SystemUiProxy getSystemUiProxy();
RecentsDisplayModel getRecentsDisplayModel();
+
+ OverviewComponentObserver getOverviewComponentObserver();
}
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 99c2c1c..4995e77 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -70,6 +70,8 @@
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.util.Executors;
import com.android.quickstep.GestureState;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.util.LottieAnimationColorUtils;
import com.android.quickstep.util.TISBindHelper;
@@ -121,7 +123,7 @@
private TextView mHintView;
- private final Runnable mOverviewTargetChangeRunnable = this::onOverviewTargetChanged;
+ private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChange;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -197,6 +199,9 @@
setUpBackgroundAnimation(getDP().isTablet);
getIDP().addOnChangeListener(mOnIDPChangeListener);
+
+ OverviewComponentObserver.INSTANCE.get(this)
+ .addOverviewChangeListener(mOverviewChangeListener);
}
private InvariantDeviceProfile getIDP() {
@@ -286,7 +291,6 @@
private void onTISConnected(TISBinder binder) {
setSetupUIVisible(isResumed());
binder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
- binder.registerOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
binder.preloadOverviewForSUWAllSet();
TaskbarManager taskbarManager = binder.getTaskbarManager();
if (taskbarManager != null) {
@@ -294,11 +298,10 @@
}
}
- private void onOverviewTargetChanged() {
+ private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
TISBinder binder = mTISBindHelper.getBinder();
if (binder != null) {
binder.preloadOverviewForSUWAllSet();
- binder.unregisterOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
}
}
@@ -318,7 +321,6 @@
if (binder != null) {
setSetupUIVisible(false);
binder.setSwipeUpProxy(null);
- binder.unregisterOverviewTargetChangeListener(mOverviewTargetChangeRunnable);
}
}
@@ -346,6 +348,8 @@
if (!isChangingConfigurations()) {
dispatchLauncherAnimStartEnd();
}
+ OverviewComponentObserver.INSTANCE.get(this)
+ .removeOverviewChangeListener(mOverviewChangeListener);
}
private AnimatedFloat createSwipeUpProxy(GestureState state) {
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index e462706..1c4e7a7 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -86,10 +86,8 @@
SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
super(tutorialFragment, tutorialType);
RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext);
- OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState);
mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
- new GestureState(observer, -1));
- observer.onDestroy();
+ new GestureState(OverviewComponentObserver.INSTANCE.get(mContext), -1));
deviceState.destroy();
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext)
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index 8c26d8f..a315775 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -51,17 +51,32 @@
override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> {
if (forceRefresh) {
recentsModel.getTasks { result ->
- tasks.value =
- MapForStateFlow(
- result
- .flatMap { groupTask -> groupTask.tasks }
- .associateBy { it.key.id }
- .also {
- // Clean tasks that are not in the latest group tasks list.
- val tasksNoLongerVisible = it.keys.subtract(tasks.value.keys)
- removeTasks(tasksNoLongerVisible)
+ val recentTasks =
+ result
+ .flatMap { groupTask -> groupTask.tasks }
+ .associateBy { it.key.id }
+ .also { hashMap ->
+ // Clean tasks that are not in the latest group tasks list.
+ val tasksNoLongerVisible = hashMap.keys.subtract(tasks.value.keys)
+ removeTasks(tasksNoLongerVisible)
+
+ // Use pre-loaded thumbnail data and icon from the previous list.
+ // This reduces the Thumbnail loading time in the Overview and prevent
+ // empty thumbnail and icon.
+ val cache =
+ taskRequests.keys
+ .mapNotNull { key ->
+ val task = tasks.value[key] ?: return@mapNotNull null
+ key to Pair(task.thumbnail, task.icon)
+ }
+ .toMap()
+
+ hashMap.values.forEach { task ->
+ task.thumbnail = task.thumbnail ?: cache[task.key.id]?.first
+ task.icon = task.icon ?: cache[task.key.id]?.second
}
- )
+ }
+ tasks.value = MapForStateFlow(recentTasks)
}
}
return tasks.map { it.values.toList() }
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index e334695..20794bf 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -52,7 +52,10 @@
private val recentsCoroutineScope: CoroutineScope = RecentsDependencies.get()
private val dispatcherProvider: DispatcherProvider = RecentsDependencies.get()
- private lateinit var viewData: TaskThumbnailViewData
+ // This is initialised here and set in onAttachedToWindow because onLayout can be called before
+ // onAttachedToWindow so this property needs to be initialised as it is used below.
+ private var viewData: TaskThumbnailViewData = RecentsDependencies.get(this)
+
private lateinit var viewModel: TaskThumbnailViewModel
private lateinit var viewAttachedScope: CoroutineScope
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
index 3be8ea6..724fa40 100644
--- a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
@@ -34,7 +34,6 @@
import com.android.quickstep.BaseContainerInterface
import com.android.quickstep.DeviceConfigWrapper
import com.android.quickstep.OverviewComponentObserver
-import com.android.quickstep.RecentsAnimationDeviceState
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TopTaskTracker
import com.android.quickstep.views.RecentsView
@@ -212,14 +211,7 @@
@VisibleForTesting
fun getRecentsContainerInterface(): BaseContainerInterface<*, *>? {
- val rads = RecentsAnimationDeviceState(context)
- val observer = OverviewComponentObserver(context, rads)
- try {
- return observer.containerInterface
- } finally {
- observer.onDestroy()
- rads.destroy()
- }
+ return OverviewComponentObserver.INSTANCE.get(context).containerInterface
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 1eb91ae..c524286 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -583,10 +583,8 @@
/**
* Init {@code SplitFromDesktopController}
*/
- public void initSplitFromDesktopController(QuickstepLauncher launcher,
- OverviewComponentObserver overviewComponentObserver) {
- initSplitFromDesktopController(
- new SplitFromDesktopController(launcher, overviewComponentObserver));
+ public void initSplitFromDesktopController(QuickstepLauncher launcher) {
+ initSplitFromDesktopController(new SplitFromDesktopController(launcher));
}
@VisibleForTesting
@@ -853,10 +851,9 @@
private DesktopSplitSelectListenerImpl mSplitSelectListener;
private Drawable mAppIcon;
- public SplitFromDesktopController(QuickstepLauncher launcher,
- OverviewComponentObserver overviewComponentObserver) {
+ public SplitFromDesktopController(QuickstepLauncher launcher) {
mLauncher = launcher;
- mOverviewComponentObserver = overviewComponentObserver;
+ mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(launcher);
mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
R.dimen.split_placeholder_size);
mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 744c08c..0ba4083 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -63,12 +63,11 @@
public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
SplitSelectStateController controller,
- OverviewComponentObserver overviewComponentObserver,
RecentsAnimationDeviceState deviceState) {
mLauncher = launcher;
mController = controller;
mDeviceState = deviceState;
- mOverviewComponentObserver = overviewComponentObserver;
+ mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(launcher);
mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
R.dimen.split_placeholder_size);
@@ -106,7 +105,6 @@
}
public void onDestroy() {
- mOverviewComponentObserver.onDestroy();
mDeviceState.destroy();
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index b71c322..4660c51 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -246,7 +246,6 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -611,7 +610,8 @@
private int mKeyboardTaskFocusSnapAnimationDuration;
private int mKeyboardTaskFocusIndex = INVALID_PAGE;
- private int[] mDismissPrimaryTranslations;
+ private Map<TaskView, Integer> mTaskViewsDismissPrimaryTranslations =
+ new HashMap<TaskView, Integer>();
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -852,59 +852,6 @@
private int mTaskViewCount = 0;
- private final TaskViewsIterable mTaskViewsIterable = new TaskViewsIterable();
-
- public class TaskViewsIterable implements Iterable<TaskView> {
- @Override
- public TaskViewsIterator iterator() {
- return new TaskViewsIterator();
- }
- }
-
- // An Iterator to iterate all the current TaskViews inside the RecentsView.
- public class TaskViewsIterator implements Iterator<TaskView> {
- // Refers to the index of the `TaskView` that will be returned when `next()` is called.
- private int mNextIndex = 0;
-
- // The "limit" of this iterator. This is the number of children of the RecentsView when
- // the iterator was created. Adding & removing elements will invalidate the iteration
- // anyway (and cause next() to throw) so saving this value will guarantee that the
- // value of hasNext() remains stable and won't flap between true and false when elements
- // are added and removed from the RecentsView.
- private final int mLimit = getChildCount();
-
- TaskViewsIterator() {
- advanceIfNeeded();
- }
-
- @Override
- public boolean hasNext() {
- return mNextIndex < mLimit && mNextIndex < getChildCount();
- }
-
- @Override
- public TaskView next() {
- if (!hasNext()) {
- throw new IndexOutOfBoundsException(
- String.format("mNextIndex: %d, child count: %d", mNextIndex,
- getChildCount()));
- }
- TaskView taskView = requireTaskViewAt(mNextIndex);
- mNextIndex++;
- advanceIfNeeded();
- return taskView;
- }
-
- // Advances `mNextIndex` until it either points to a `TaskView` or to the end of the
- // Iterator.
- private void advanceIfNeeded() {
- while (mNextIndex < mLimit && mNextIndex < getChildCount() && !(getChildAt(
- mNextIndex) instanceof TaskView)) {
- mNextIndex++;
- }
- }
- }
-
@Nullable
public TaskView getFirstTaskView() {
return mUtils.getFirstTaskView();
@@ -2532,41 +2479,39 @@
return;
}
- int lower = 0;
- int upper = 0;
- int visibleStart = 0;
- int visibleEnd = 0;
+ int lowerIndex, upperIndex, visibleStart, visibleEnd;
if (showAsGrid()) {
int screenStart = getPagedOrientationHandler().getPrimaryScroll(this);
int pageOrientedSize = getPagedOrientationHandler().getMeasuredSize(this);
// For GRID_ONLY_OVERVIEW, use +/- 1 task column as visible area for preloading
// adjacent thumbnails, otherwise use +/-50% screen width
- int extraWidth = enableGridOnlyOverview() ? getLastComputedTaskSize().width()
- + getPageSpacing() : pageOrientedSize / 2;
+ int extraWidth =
+ enableGridOnlyOverview() ? getLastComputedTaskSize().width() + getPageSpacing()
+ : pageOrientedSize / 2;
+ lowerIndex = upperIndex = 0;
visibleStart = screenStart - extraWidth;
visibleEnd = screenStart + pageOrientedSize + extraWidth;
} else {
int centerPageIndex = getPageNearestToCenterOfScreen();
int numChildren = getChildCount();
- lower = Math.max(0, centerPageIndex - 2);
- upper = Math.min(centerPageIndex + 2, numChildren - 1);
+ lowerIndex = Math.max(0, centerPageIndex - 2);
+ upperIndex = Math.min(centerPageIndex + 2, numChildren - 1);
+ visibleStart = visibleEnd = 0;
}
List<Integer> visibleTaskIds = new ArrayList<>();
-
// Update the task data for the in/visible children
- for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView taskView = requireTaskViewAt(i);
+ getTaskViews().forEachWithIndexInParent((index, taskView) -> {
List<TaskContainer> containers = taskView.getTaskContainers();
if (containers.isEmpty()) {
- continue;
+ return;
}
boolean visible;
if (showAsGrid()) {
visible = isTaskViewWithinBounds(taskView, visibleStart, visibleEnd,
- mDismissPrimaryTranslations != null ? mDismissPrimaryTranslations[i] : 0);
+ mTaskViewsDismissPrimaryTranslations.getOrDefault(taskView, 0));
} else {
- visible = lower <= i && i <= upper;
+ visible = index >= lowerIndex && index <= upperIndex;
}
if (visible) {
// Default update all non-null tasks, then remove running ones
@@ -2578,7 +2523,7 @@
tasksToUpdate.stream().map((task) -> task.key.id).toList());
}
if (tasksToUpdate.isEmpty()) {
- continue;
+ return;
}
int visibilityChanges = 0;
for (Task task : tasksToUpdate) {
@@ -2612,7 +2557,7 @@
taskView.onTaskListVisibilityChanged(false /* visible */, visibilityChanges);
}
}
- }
+ });
if (enableRefactorTaskThumbnail()) {
mRecentsViewModel.updateVisibleTasks(visibleTaskIds);
}
@@ -3224,7 +3169,7 @@
* Skips rebalance.
*/
private void updateGridProperties() {
- updateGridProperties(Integer.MAX_VALUE);
+ updateGridProperties(null);
}
/**
@@ -3234,10 +3179,10 @@
* This method only calculates the potential position and depends on {@link #setGridProgress} to
* apply the actual scaling and translation.
*
- * @param startRebalanceAfter which view index to start rebalancing from. Use Integer.MAX_VALUE
- * to skip rebalance
+ * @param lastVisibleTaskViewDuringDismiss which TaskView to start rebalancing from. Use
+ * `null` to skip rebalance.
*/
- private void updateGridProperties(int startRebalanceAfter) {
+ private void updateGridProperties(TaskView lastVisibleTaskViewDuringDismiss) {
if (!hasTaskViews()) {
return;
}
@@ -3250,19 +3195,10 @@
float topAccumulatedTranslationX = 0;
float bottomAccumulatedTranslationX = 0;
- // Contains whether the child index is in top or bottom of grid (for non-focused task)
- // Different from mTopRowIdSet, which contains the taskViewId of what task is in top row
- IntSet topSet = new IntSet();
- IntSet bottomSet = new IntSet();
-
- final int taskCount = getTaskViewCount();
- // Horizontal grid translation for each task
- float[] gridTranslations = new float[taskCount];
+ // Horizontal grid translation for each task.
+ Map<TaskView, Float> gridTranslations = new HashMap<>();
TaskView lastLargeTaskView = mUtils.getLastLargeTaskView();
- int lastLargeTaskIndex =
- (lastLargeTaskView == null) ? Integer.MAX_VALUE : indexOfChild(lastLargeTaskView);
- Set<Integer> largeTasksIndices = new HashSet<>();
int focusedTaskShift = 0;
int largeTaskWidthAndSpacing = 0;
int snappedTaskRowWidth = 0;
@@ -3275,29 +3211,46 @@
if (!mAnyTaskHasBeenDismissed) {
mTopRowIdSet.clear();
}
- for (int i = 0; i < taskCount; i++) {
- TaskView taskView = requireTaskViewAt(i);
+
+ // Consecutive task views in the top row or bottom row, which means another one set will
+ // be cleared up while starting to add TaskViews to one of them. Also means only one of
+ // them can be non-empty at most.
+ Set<TaskView> lastTopTaskViews = new HashSet<>();
+ Set<TaskView> lastBottomTaskViews = new HashSet<>();
+
+ int largeTasksCount = 0;
+ // True if the last large TaskView has been visited during the TaskViews iteration.
+ boolean encounteredLastLargeTaskView = false;
+ // True if the highest index visible TaskView has been visited during the TaskViews
+ // iteration.
+ boolean encounteredLastVisibleTaskView = false;
+ for (TaskView taskView : getTaskViews()) {
+ if (taskView == lastLargeTaskView) {
+ encounteredLastLargeTaskView = true;
+ }
+ if (taskView == lastVisibleTaskViewDuringDismiss) {
+ encounteredLastVisibleTaskView = true;
+ }
+ float gridTranslation = 0f;
int taskWidthAndSpacing = taskView.getLayoutParams().width + mPageSpacing;
// Evenly distribute tasks between rows unless rearranging due to task dismissal, in
// which case keep tasks in their respective rows. For the running task, don't join
// the grid.
- boolean isLargeTile = taskView.isLargeTile();
-
- if (isLargeTile) {
+ if (taskView.isLargeTile()) {
+ largeTasksCount++;
// DesktopTaskView`s are hidden during split select state, so we shouldn't count
// them when calculating row width.
if (!(taskView instanceof DesktopTaskView && isSplitSelectionActive())) {
topRowWidth += taskWidthAndSpacing;
bottomRowWidth += taskWidthAndSpacing;
}
- gridTranslations[i] += focusedTaskShift;
- gridTranslations[i] += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
+ gridTranslation += focusedTaskShift;
+ gridTranslation += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
// Center view vertically in case it's from different orientation.
taskView.setGridTranslationY((mLastComputedTaskSize.height() + taskTopMargin
- taskView.getLayoutParams().height) / 2f);
- largeTasksIndices.add(i);
largeTaskWidthAndSpacing = taskWidthAndSpacing;
if (taskView == snappedTaskView) {
@@ -3305,9 +3258,9 @@
snappedTaskRowWidth = taskWidthAndSpacing;
}
} else {
- if (i > lastLargeTaskIndex) {
+ if (encounteredLastLargeTaskView) {
// For tasks after the last large task, shift by large task's width and spacing.
- gridTranslations[i] +=
+ gridTranslation +=
mIsRtl ? largeTaskWidthAndSpacing : -largeTaskWidthAndSpacing;
} else {
// For task before the focused task, accumulate the width and spacing to
@@ -3316,10 +3269,10 @@
}
int taskViewId = taskView.getTaskViewId();
- // Rebalance the grid starting after a certain index
boolean isTopRow;
if (mAnyTaskHasBeenDismissed) {
- if (i > startRebalanceAfter) {
+ // Rebalance the grid starting after a certain index.
+ if (encounteredLastVisibleTaskView) {
mTopRowIdSet.remove(taskViewId);
isTopRow = topRowWidth <= bottomRowWidth;
} else {
@@ -3336,47 +3289,43 @@
} else {
topRowWidth += taskWidthAndSpacing;
}
- topSet.add(i);
mTopRowIdSet.add(taskViewId);
-
taskView.setGridTranslationY(mTaskGridVerticalDiff);
// Move horizontally into empty space.
float widthOffset = 0;
- for (int j = i - 1; !topSet.contains(j) && j >= 0; j--) {
- if (largeTasksIndices.contains(j)) {
- continue;
- }
- widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+ for (TaskView bottomTaskView : lastBottomTaskViews) {
+ widthOffset += bottomTaskView.getLayoutParams().width + mPageSpacing;
}
float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
- gridTranslations[i] += topAccumulatedTranslationX + currentTaskTranslationX;
+ gridTranslation += topAccumulatedTranslationX + currentTaskTranslationX;
topAccumulatedTranslationX += currentTaskTranslationX;
+ lastTopTaskViews.add(taskView);
+ lastBottomTaskViews.clear();
} else {
bottomRowWidth += taskWidthAndSpacing;
- bottomSet.add(i);
// Move into bottom row.
taskView.setGridTranslationY(mTopBottomRowHeightDiff + mTaskGridVerticalDiff);
// Move horizontally into empty space.
float widthOffset = 0;
- for (int j = i - 1; !bottomSet.contains(j) && j >= 0; j--) {
- if (largeTasksIndices.contains(j)) {
- continue;
- }
- widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+ for (TaskView topTaskView : lastTopTaskViews) {
+ widthOffset += topTaskView.getLayoutParams().width + mPageSpacing;
}
float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
- gridTranslations[i] += bottomAccumulatedTranslationX + currentTaskTranslationX;
+ gridTranslation += bottomAccumulatedTranslationX + currentTaskTranslationX;
bottomAccumulatedTranslationX += currentTaskTranslationX;
+ lastBottomTaskViews.add(taskView);
+ lastTopTaskViews.clear();
}
if (taskView == snappedTaskView) {
snappedTaskRowWidth = isTopRow ? topRowWidth : bottomRowWidth;
}
}
+ gridTranslations.put(taskView, gridTranslation);
}
// We need to maintain snapped task's page scroll invariant between quick switch and
@@ -3387,22 +3336,22 @@
if (snappedTaskView != null) {
snappedTaskNonGridScrollAdjustment = snappedTaskView.getScrollAdjustment(
/*gridEnabled=*/false);
- snappedTaskGridTranslationX = gridTranslations[snappedPage];
+ snappedTaskGridTranslationX = gridTranslations.get(snappedTaskView);
}
// Use the accumulated translation of the row containing the last task.
- float clearAllAccumulatedTranslation = topSet.contains(taskCount - 1)
+ float clearAllAccumulatedTranslation = !lastTopTaskViews.isEmpty()
? topAccumulatedTranslationX : bottomAccumulatedTranslationX;
// If the last task is on the shorter row, ClearAllButton will embed into the shorter row
// which is not what we want. Compensate the width difference of the 2 rows in that case.
float shorterRowCompensation = 0;
if (topRowWidth <= bottomRowWidth) {
- if (topSet.contains(taskCount - 1)) {
+ if (!lastTopTaskViews.isEmpty()) {
shorterRowCompensation = bottomRowWidth - topRowWidth;
}
} else {
- if (bottomSet.contains(taskCount - 1)) {
+ if (!lastBottomTaskViews.isEmpty()) {
shorterRowCompensation = topRowWidth - bottomRowWidth;
}
}
@@ -3418,8 +3367,7 @@
// for ClearAllButton translation. The space at the left side of the large task will be
// empty and it should be move ClearAllButton further away as well.
// TODO(b/359573248): Validate the translation for ClearAllButton for grid only.
- boolean hasOnlyLargeTasks = taskCount == largeTasksIndices.size();
- if (enableLargeDesktopWindowingTile() && hasOnlyLargeTasks) {
+ if (enableLargeDesktopWindowingTile() && largeTasksCount == getTaskViewCount()) {
longRowWidth = largeTaskWidthAndSpacing;
}
@@ -3443,7 +3391,7 @@
float clearAllTotalTranslationX =
clearAllAccumulatedTranslation + clearAllShorterRowCompensation
+ clearAllShortTotalWidthTranslation + snappedTaskNonGridScrollAdjustment;
- if (!largeTasksIndices.isEmpty()) {
+ if (largeTasksCount > 0) {
// Shift by focused task's width and spacing if a task is focused.
clearAllTotalTranslationX +=
mIsRtl ? largeTaskWidthAndSpacing : -largeTaskWidthAndSpacing;
@@ -3467,10 +3415,10 @@
}
}
- for (int i = 0; i < taskCount; i++) {
- TaskView taskView = requireTaskViewAt(i);
- taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX
- + snappedTaskNonGridScrollAdjustment);
+ for (TaskView taskView : getTaskViews()) {
+ taskView.setGridTranslationX(
+ gridTranslations.get(taskView) - snappedTaskGridTranslationX
+ + snappedTaskNonGridScrollAdjustment);
}
final TaskView runningTask = getRunningTaskView();
@@ -3935,7 +3883,7 @@
slidingTranslation += mIsRtl ? newClearAllShortTotalWidthTranslation
: -newClearAllShortTotalWidthTranslation;
}
- mDismissPrimaryTranslations = new int[taskCount];
+ mTaskViewsDismissPrimaryTranslations.clear();
int lastTaskViewIndex = indexOfChild(mUtils.getLastTaskView());
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
@@ -4047,7 +3995,7 @@
clampToProgress(dismissInterpolator, animationStartProgress,
animationEndProgress));
anim.add(dismissAnimator);
- mDismissPrimaryTranslations[i] = (int) finalTranslation;
+ mTaskViewsDismissPrimaryTranslations.put(taskView, (int) finalTranslation);
distanceFromDismissedTask++;
}
}
@@ -4177,7 +4125,7 @@
mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
}
}
- } else if (dismissedIndex < pageToSnapTo || pageToSnapTo == taskCount - 1) {
+ } else if (dismissedIndex < pageToSnapTo || pageToSnapTo == lastTaskViewIndex) {
pageToSnapTo--;
}
boolean isHomeTaskDismissed = dismissedTaskView == getHomeTaskView();
@@ -4209,7 +4157,8 @@
// Rebalance tasks in the grid
int highestVisibleTaskIndex = getHighestVisibleTaskIndex();
if (highestVisibleTaskIndex < Integer.MAX_VALUE) {
- TaskView taskView = requireTaskViewAt(highestVisibleTaskIndex);
+ final TaskView taskView = requireTaskViewAt(
+ highestVisibleTaskIndex);
boolean shouldRebalance;
int screenStart = getPagedOrientationHandler().getPrimaryScroll(
@@ -4237,7 +4186,7 @@
}
if (shouldRebalance) {
- updateGridProperties(highestVisibleTaskIndex);
+ updateGridProperties(taskView);
updateScrollSynchronously();
}
}
@@ -4283,7 +4232,7 @@
updateCurrentTaskActionsVisibility();
onDismissAnimationEnds();
mPendingAnimation = null;
- mDismissPrimaryTranslations = null;
+ mTaskViewsDismissPrimaryTranslations.clear();
}
});
}
@@ -4358,7 +4307,7 @@
);
if (view instanceof TaskView) {
- mDismissPrimaryTranslations[index] = scrollDiffPerPage;
+ mTaskViewsDismissPrimaryTranslations.put((TaskView) view, scrollDiffPerPage);
}
if (mEnableDrawingLiveTile && view instanceof TaskView
&& ((TaskView) view).isRunningTask()) {
@@ -4792,8 +4741,8 @@
/**
* Returns iterable [TaskView] children.
*/
- public TaskViewsIterable getTaskViews() {
- return mTaskViewsIterable;
+ public RecentsViewUtils.TaskViewsIterable getTaskViews() {
+ return mUtils.getTaskViews();
}
public void setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener listener) {
@@ -5226,20 +5175,16 @@
SplitAnimationTimings timings = AnimUtils.getDeviceOverviewToSplitTimings(
mContainer.getDeviceProfile().isTablet);
if (enableLargeDesktopWindowingTile()) {
- TaskView currentPageTaskView = getCurrentPageTaskView();
- TaskView nextPageTaskView = getTaskViewAt(mCurrentPage + 1);
- TaskView previousPageTaskView = getTaskViewAt(mCurrentPage - 1);
- for (TaskView taskView : getTaskViews()) {
+ getTaskViews().forEachWithIndexInParent((index, taskView) -> {
if (taskView instanceof DesktopTaskView) {
// Setting pivot to scale down from screen centre.
- if (taskView == previousPageTaskView || taskView == currentPageTaskView
- || taskView == nextPageTaskView) {
+ if (isTaskViewVisible(taskView)) {
float pivotX = 0f;
- if (taskView == previousPageTaskView) {
+ if (index < mCurrentPage) {
pivotX = mIsRtl ? taskView.getWidth() / 2f - mPageSpacing
- taskView.getWidth()
: taskView.getWidth() / 2f + mPageSpacing + taskView.getWidth();
- } else if (taskView == currentPageTaskView) {
+ } else if (index == mCurrentPage) {
pivotX = taskView.getWidth() / 2f;
} else {
pivotX = mIsRtl ? taskView.getWidth() + mPageSpacing
@@ -5258,7 +5203,7 @@
clampToProgress(deskTopFadeInterPolator, 0f,
timings.getDesktopFadeSplitAnimationEndOffset()));
}
- }
+ });
}
}
@@ -6168,44 +6113,34 @@
mClearAllButton.setScrollOffsetPrimary(mIsRtl ? clearAllWidthDiff : -clearAllWidthDiff);
}
- boolean pageScrollChanged = false;
-
+ int[] oldPageScrolls = Arrays.copyOf(outPageScrolls, outPageScrolls.length);
int clearAllIndex = indexOfChild(mClearAllButton);
int clearAllScroll = 0;
int clearAllWidth = getPagedOrientationHandler().getPrimarySize(mClearAllButton);
if (clearAllIndex != -1 && clearAllIndex < outPageScrolls.length) {
float scrollDiff = mClearAllButton.getScrollAdjustment(showAsFullscreen, showAsGrid);
- clearAllScroll = newPageScrolls[clearAllIndex] + (int) scrollDiff;
- if (outPageScrolls[clearAllIndex] != clearAllScroll) {
- pageScrollChanged = true;
- outPageScrolls[clearAllIndex] = clearAllScroll;
- }
+ clearAllScroll = newPageScrolls[clearAllIndex] + Math.round(scrollDiff);
+ outPageScrolls[clearAllIndex] = clearAllScroll;
}
int lastTaskScroll = getLastTaskScroll(clearAllScroll, clearAllWidth);
- for (int i = 0; i < getChildCount(); i++) {
- TaskView taskView = getTaskViewAt(i);
- if (taskView == null) {
- continue;
- }
+ getTaskViews().forEachWithIndexInParent((index, taskView) -> {
float scrollDiff = taskView.getScrollAdjustment(showAsGrid);
- int pageScroll = newPageScrolls[i] + Math.round(scrollDiff);
+ int pageScroll = newPageScrolls[index] + Math.round(scrollDiff);
if ((mIsRtl && pageScroll < lastTaskScroll)
|| (!mIsRtl && pageScroll > lastTaskScroll)) {
pageScroll = lastTaskScroll;
}
- if (outPageScrolls[i] != pageScroll) {
- pageScrollChanged = true;
- outPageScrolls[i] = pageScroll;
- }
+ outPageScrolls[index] = pageScroll;
if (DEBUG) {
- Log.d(TAG, "getPageScrolls - outPageScrolls[" + i + "]: " + outPageScrolls[i]);
+ Log.d(TAG,
+ "getPageScrolls - outPageScrolls[" + index + "]: " + outPageScrolls[index]);
}
- }
+ });
if (DEBUG) {
Log.d(TAG, "getPageScrolls - clearAllScroll: " + clearAllScroll);
}
- return pageScrollChanged;
+ return !Arrays.equals(oldPageScrolls, outPageScrolls);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index ccf22ce..3ac3349 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -22,12 +22,15 @@
import com.android.quickstep.util.GroupTask
import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
import com.android.systemui.shared.recents.model.ThumbnailData
+import java.util.function.BiConsumer
/**
* Helper class for [RecentsView]. This util class contains refactored and extracted functions from
* RecentsView to facilitate the implementation of unit tests.
*/
class RecentsViewUtils(private val recentsView: RecentsView<*, *>) {
+ val taskViews = TaskViewsIterable(recentsView)
+
/** Takes a screenshot of all [taskView] and return map of taskId to the screenshot */
fun screenshotTasks(taskView: TaskView): Map<Int, ThumbnailData> {
val recentsAnimationController = recentsView.recentsAnimationController ?: return emptyMap()
@@ -47,27 +50,37 @@
return otherTasks + desktopTasks
}
+ class TaskViewsIterable(val recentsView: RecentsView<*, *>) : Iterable<TaskView> {
+ /** Iterates TaskViews when its index inside the RecentsView is needed. */
+ fun forEachWithIndexInParent(consumer: BiConsumer<Int, TaskView>) {
+ recentsView.children.forEachIndexed { index, child ->
+ (child as? TaskView)?.let { consumer.accept(index, it) }
+ }
+ }
+
+ override fun iterator(): Iterator<TaskView> =
+ recentsView.children.mapNotNull { it as? TaskView }.iterator()
+ }
+
/** Counts [TaskView]s that are [DesktopTaskView] instances. */
- fun getDesktopTaskViewCount(): Int = recentsView.taskViews.count { it is DesktopTaskView }
+ fun getDesktopTaskViewCount(): Int = taskViews.count { it is DesktopTaskView }
/** Returns a list of all large TaskView Ids from [TaskView]s */
- fun getLargeTaskViewIds(): List<Int> =
- recentsView.taskViews.filter { it.isLargeTile }.map { it.taskViewId }
+ fun getLargeTaskViewIds(): List<Int> = taskViews.filter { it.isLargeTile }.map { it.taskViewId }
/** Counts [TaskView]s that are large tiles. */
- fun getLargeTileCount(): Int = recentsView.taskViews.count { it.isLargeTile }
+ fun getLargeTileCount(): Int = taskViews.count { it.isLargeTile }
/** Returns the first TaskView that should be displayed as a large tile. */
fun getFirstLargeTaskView(): TaskView? =
- recentsView.taskViews.firstOrNull {
+ taskViews.firstOrNull {
it.isLargeTile && !(recentsView.isSplitSelectionActive && it is DesktopTaskView)
}
/** Returns the expected focus task. */
fun getExpectedFocusedTask(): TaskView? =
- if (enableLargeDesktopWindowingTile())
- recentsView.taskViews.firstOrNull { it !is DesktopTaskView }
- else recentsView.taskViews.firstOrNull()
+ if (enableLargeDesktopWindowingTile()) taskViews.firstOrNull { it !is DesktopTaskView }
+ else taskViews.firstOrNull()
/**
* Returns the [TaskView] that should be the current page during task binding, in the following
@@ -81,20 +94,20 @@
fun getExpectedCurrentTask(runningTaskView: TaskView?, focusedTaskView: TaskView?): TaskView? =
runningTaskView
?: focusedTaskView
- ?: recentsView.taskViews.firstOrNull { it !is DesktopTaskView }
- ?: recentsView.taskViews.lastOrNull()
+ ?: taskViews.firstOrNull { it !is DesktopTaskView }
+ ?: taskViews.lastOrNull()
/** Returns the first TaskView if it exists, or null otherwise. */
- fun getFirstTaskView(): TaskView? = recentsView.taskViews.firstOrNull()
+ fun getFirstTaskView(): TaskView? = taskViews.firstOrNull()
/** Returns the last TaskView if it exists, or null otherwise. */
- fun getLastTaskView(): TaskView? = recentsView.taskViews.lastOrNull()
+ fun getLastTaskView(): TaskView? = taskViews.lastOrNull()
/** Returns the first TaskView that is not large */
- fun getFirstSmallTaskView(): TaskView? = recentsView.taskViews.firstOrNull { !it.isLargeTile }
+ fun getFirstSmallTaskView(): TaskView? = taskViews.firstOrNull { !it.isLargeTile }
/** Returns the last TaskView that should be displayed as a large tile. */
- fun getLastLargeTaskView(): TaskView? = recentsView.taskViews.lastOrNull { it.isLargeTile }
+ fun getLastLargeTaskView(): TaskView? = taskViews.lastOrNull { it.isLargeTile }
/**
* Gets the list of accessibility children. Currently all the children of RecentsViews are
@@ -108,23 +121,23 @@
nonRunningTaskCarouselHidden: Boolean,
runningTaskView: TaskView? = recentsView.runningTaskView,
): TaskView? =
- recentsView.taskViews.firstOrNull {
+ taskViews.firstOrNull {
it.isVisibleInCarousel(runningTaskView, nonRunningTaskCarouselHidden)
}
/** Returns the last [TaskView], with some tasks possibly hidden in the carousel. */
fun getLastTaskViewInCarousel(nonRunningTaskCarouselHidden: Boolean): TaskView? =
- recentsView.taskViews.lastOrNull {
+ taskViews.lastOrNull {
it.isVisibleInCarousel(recentsView.runningTaskView, nonRunningTaskCarouselHidden)
}
/** Returns if any small tasks are fully visible */
fun isAnySmallTaskFullyVisible(): Boolean =
- recentsView.taskViews.any { !it.isLargeTile && recentsView.isTaskViewFullyVisible(it) }
+ taskViews.any { !it.isLargeTile && recentsView.isTaskViewFullyVisible(it) }
/** Apply attachAlpha to all [TaskView] accordingly to different conditions. */
fun applyAttachAlpha(nonRunningTaskCarouselHidden: Boolean) {
- recentsView.taskViews.forEach { taskView ->
+ taskViews.forEach { taskView ->
taskView.attachAlpha =
if (taskView == recentsView.runningTaskView) {
RUNNING_TASK_ATTACH_ALPHA.get(recentsView)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
index cfa12e2..785e585 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
@@ -17,6 +17,7 @@
package com.android.launcher3.taskbar
import android.animation.AnimatorTestRule
+import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING
import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER
@@ -34,6 +35,10 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
@RunWith(LauncherMultivalentJUnit::class)
@EmulatedDevices(["pixelTablet2023"])
@@ -43,11 +48,10 @@
val context =
TaskbarWindowSandboxContext.create { builder ->
builder.bindSystemUiProxy(
- object : SystemUiProxy(this) {
- override fun notifyTaskbarAutohideSuspend(suspend: Boolean) {
- super.notifyTaskbarAutohideSuspend(suspend)
- latestSuspendNotification = suspend
- }
+ spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) { proxy ->
+ doAnswer { latestSuspendNotification = it.getArgument(0) }
+ .whenever(proxy)
+ .notifyTaskbarAutohideSuspend(anyOrNull())
}
)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
index eb13b55..36e8a82 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -21,6 +21,7 @@
import android.content.Intent
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
@@ -50,6 +51,10 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
@RunWith(LauncherMultivalentJUnit::class)
@EmulatedDevices(["pixelTablet2023"])
@@ -66,10 +71,10 @@
val context =
TaskbarWindowSandboxContext.create { builder ->
builder.bindSystemUiProxy(
- object : SystemUiProxy(this) {
- override fun setDesktopTaskListener(listener: IDesktopTaskListener?) {
- desktopTaskListener = listener
- }
+ spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) { proxy ->
+ doAnswer { desktopTaskListener = it.getArgument(0) }
+ .whenever(proxy)
+ .setDesktopTaskListener(anyOrNull())
}
)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
index 4c94067..360f019 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
@@ -20,9 +20,9 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
-import android.view.KeyEvent
import android.view.View.GONE
import android.view.View.VISIBLE
+import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.launcher3.taskbar.rules.TaskbarModeRule
@@ -44,6 +44,10 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
@RunWith(LauncherMultivalentJUnit::class)
@EmulatedDevices(["pixelTablet2023"])
@@ -53,11 +57,8 @@
val context =
TaskbarWindowSandboxContext.create { builder ->
builder.bindSystemUiProxy(
- object : SystemUiProxy(this) {
- override fun onBackEvent(backEvent: KeyEvent?) {
- super.onBackEvent(backEvent)
- backPressed = true
- }
+ spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) {
+ doAnswer { backPressed = true }.whenever(it).onBackEvent(anyOrNull())
}
)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
index 5e438bd..588c22c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
@@ -56,8 +56,8 @@
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING
import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.TruthJUnit.assume
import org.junit.After
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -82,11 +82,6 @@
private val activityContext by taskbarUnitTestRule::activityContext
- // Disable hardware keyboard mode during tests.
- @Before fun enableSoftwareIme() = TaskbarStashController.enableSoftwareImeForTests(true)
-
- @After fun resetIme() = TaskbarStashController.enableSoftwareImeForTests(false)
-
@After fun cancelTimeoutIfExists() = stashController.cancelTimeoutIfExists()
@Test
@@ -544,6 +539,8 @@
@Test
@TaskbarMode(PINNED)
fun testAnimatePinnedTaskbar_imeShown_replacesIconsWithHandle() {
+ assume().that(activityContext.isHardwareKeyboard).isFalse()
+
getInstrumentation().runOnMainSync {
stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, false)
animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
@@ -555,6 +552,8 @@
@Test
@TaskbarMode(PINNED)
fun testAnimatePinnedTaskbar_imeHidden_replacesHandleWithIcons() {
+ assume().that(activityContext.isHardwareKeyboard).isFalse()
+
getInstrumentation().runOnMainSync {
stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
animatorTestRule.advanceTimeBy(0)
@@ -571,6 +570,8 @@
@Test
@TaskbarMode(PINNED)
fun testAnimatePinnedTaskbar_imeHidden_verifyAnimationDuration() {
+ assume().that(activityContext.isHardwareKeyboard).isFalse()
+
// Start with IME shown.
getInstrumentation().runOnMainSync {
stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
@@ -596,6 +597,8 @@
@Test
@TaskbarMode(THREE_BUTTONS)
fun testAnimateThreeButtonsTaskbar_imeShown_hidesIconsAndBg() {
+ assume().that(activityContext.isHardwareKeyboard).isFalse()
+
getInstrumentation().runOnMainSync {
stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, false)
animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
@@ -607,6 +610,8 @@
@Test
@TaskbarMode(THREE_BUTTONS)
fun testAnimateThreeButtonsTaskbar_imeHidden_showsIconsAndBg() {
+ assume().that(activityContext.isHardwareKeyboard).isFalse()
+
getInstrumentation().runOnMainSync {
stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, false)
animatorTestRule.advanceTimeBy(TASKBAR_STASH_DURATION_FOR_IME)
@@ -625,6 +630,8 @@
@Test
@TaskbarMode(PINNED)
fun testSetSystemGestureInProgress_whileImeShown_unstashesTaskbar() {
+ assume().that(activityContext.isHardwareKeyboard).isFalse()
+
getInstrumentation().runOnMainSync {
stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
animatorTestRule.advanceTimeBy(0)
@@ -641,6 +648,19 @@
@Test
@TaskbarMode(PINNED)
+ fun testSysuiStateImeShowingInApp_hardwareKeyboardWithPinnedMode_notStashedForIme() {
+ assume().that(activityContext.isHardwareKeyboard).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ stashController.updateStateForFlag(FLAG_IN_APP, true)
+ stashController.updateStateForSysuiFlags(SYSUI_STATE_IME_SHOWING, true)
+ }
+
+ assertThat(stashController.isStashed).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
fun testUnlockTransition_pinnedMode_fadesOutHandle() {
getInstrumentation().runOnMainSync {
stashController.updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, true)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 5471072..2f773b3 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -245,6 +245,10 @@
animator.onStashStateChangingWhileAnimating()
}
+ // The physics animation test util posts the cancellation to the looper thread, so we have
+ // to wait again and let it finish.
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
// verify that the hide animation was canceled
assertThat(animatorScheduler.delayedBlock).isNull()
assertThat(animator.isAnimating).isFalse()
@@ -1296,7 +1300,7 @@
animator.animateBubbleInForStashed(updatedBubble, isExpanding = false)
}
- // since animation was interrupted there shouldn`t be additional calls to adjust window
+ // since animation was interrupted there shouldn't be additional calls to adjust window
assertThat(bubbleBarParentViewController.timesInvoked).isEqualTo(1)
InstrumentationRegistry.getInstrumentation().runOnMainSync {}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index cd4e78b..07b32af 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -29,7 +29,6 @@
import com.android.launcher3.taskbar.TaskbarControllers
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks
-import com.android.launcher3.taskbar.TaskbarViewController
import com.android.launcher3.taskbar.bubbles.BubbleControllers
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
@@ -130,15 +129,12 @@
}
try {
- TaskbarViewController.enableModelLoadingForTests(false)
-
// Required to complete initialization.
instrumentation.runOnMainSync { taskbarManager.onUserUnlocked() }
base.evaluate()
} finally {
instrumentation.runOnMainSync { taskbarManager.destroy() }
- TaskbarViewController.enableModelLoadingForTests(true)
}
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
index 50ba55c..41877c9 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
@@ -77,8 +77,7 @@
val deviceState = mock(RecentsAnimationDeviceState::class.java)
whenever(deviceState.rotationTouchHelper).thenReturn(mock(RotationTouchHelper::class.java))
-
- gestureState = spy(GestureState(OverviewComponentObserver(sandboxContext, deviceState), 0))
+ gestureState = spy(GestureState(OverviewComponentObserver.INSTANCE.get(sandboxContext), 0))
underTest =
LauncherSwipeHandlerV2(
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
index eaeb513..9e99a0b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
@@ -20,10 +20,12 @@
import java.util.function.Consumer
class FakeRecentTasksDataSource : RecentTasksDataSource {
- var taskList: List<GroupTask> = listOf()
+ private var taskList: List<GroupTask> = listOf()
override fun getTasks(callback: Consumer<List<GroupTask>>?): Int {
- callback?.accept(taskList)
+ // Makes a copy of the GroupTask to create a new GroupTask instance and to simulate
+ // RecentsModel::getTasks behavior.
+ callback?.accept(taskList.map { it.copy() })
return 0
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index e0e7f28..624310b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -117,6 +117,20 @@
}
@Test
+ fun whenThumbnailIsLoaded_getAllTaskData_usesPreviousLoadedThumbnailAndIcon() =
+ testScope.runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+ val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
+
+ systemUnderTest.setVisibleTasks(setOf(1))
+ assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
+
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+ assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
+ }
+
+ @Test
fun getCurrentThumbnailByIdReturnsThumbnailWithLoadedThumbnails() =
testScope.runTest {
recentsModel.seedTasks(defaultTaskList)
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 695211b..5bb2fad 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -64,6 +64,7 @@
import com.android.launcher3.util.rule.TestIsolationRule;
import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.launcher3.util.rule.ViewCaptureRule;
+import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
@@ -330,7 +331,7 @@
return recentsViewContainer.<RecentsView>getOverviewPanel().getTaskViewCount();
}
- private class OverviewUpdateHandler {
+ private class OverviewUpdateHandler implements OverviewChangeListener {
final RecentsAnimationDeviceState mRads;
final OverviewComponentObserver mObserver;
@@ -339,19 +340,24 @@
OverviewUpdateHandler() {
Context ctx = getInstrumentation().getTargetContext();
mRads = new RecentsAnimationDeviceState(ctx);
- mObserver = new OverviewComponentObserver(ctx, mRads);
+ mObserver = OverviewComponentObserver.INSTANCE.get(ctx);
mChangeCounter = new CountDownLatch(1);
if (mObserver.getHomeIntent().getComponent()
.getPackageName().equals(mOtherLauncherActivity.packageName)) {
// Home already same
mChangeCounter.countDown();
} else {
- mObserver.setOverviewChangeListener(b -> mChangeCounter.countDown());
+ mObserver.addOverviewChangeListener(this);
}
}
+ @Override
+ public void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
+ mChangeCounter.countDown();
+ }
+
void destroy() {
- mObserver.onDestroy();
+ mObserver.removeOverviewChangeListener(this);
mRads.destroy();
}
}
diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml
index 0e2c19a..a19d13a 100644
--- a/res/layout/work_apps_edu.xml
+++ b/res/layout/work_apps_edu.xml
@@ -46,6 +46,8 @@
android:layout_width="@dimen/rounded_button_width"
android:layout_height="@dimen/rounded_button_width"
android:layout_marginTop="@dimen/work_edu_card_button_margin_top"
+ android:clickable="true"
+ android:contentDescription="@string/accessibility_close"
android:gravity="center"
android:background="@drawable/inset_rounded_action_button">
<ImageButton
@@ -54,7 +56,7 @@
android:clickable="false"
android:scaleType="centerInside"
android:layout_gravity="center"
- android:contentDescription="@string/accessibility_close"
+ android:importantForAccessibility="no"
android:background="@android:color/transparent"
android:src="@drawable/ic_close_work_edu" />
</FrameLayout>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 94b1a2a..b38efc2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -869,7 +869,6 @@
case REQUEST_CREATE_SHORTCUT:
completeAddShortcut(intent, info.container, screenId,
cellPos.cellX, cellPos.cellY, info);
- announceForAccessibility(R.string.item_added_to_workspace);
break;
case REQUEST_CREATE_APPWIDGET:
completeAddAppWidget(appWidgetId, info, null, null, false, true, null);
@@ -1570,7 +1569,6 @@
if (!enableAddAppWidgetViaConfigActivityV2() || hostView.getParent() == null) {
mWorkspace.addInScreen(hostView, launcherInfo);
}
- announceForAccessibility(R.string.item_added_to_workspace);
// Show the widget resize frame.
if (hostView instanceof LauncherAppWidgetHostView) {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 68a6e62..81d6631 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -24,6 +24,7 @@
import androidx.annotation.Nullable;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
@@ -48,7 +49,6 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.ArrowPopup;
import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.shortcuts.DeepShortcutTextView;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.IntArray;
@@ -305,7 +305,6 @@
info.spanX, info.spanY);
host.requestLayout();
mContext.getModelWriter().updateItemInDatabase(info);
- announceConfirmation(mContext.getString(R.string.widget_resized, info.spanX, info.spanY));
return true;
}
@@ -405,6 +404,11 @@
*/
public boolean addToWorkspace(ItemInfo item, boolean accessibility,
@Nullable Consumer<Boolean> finishCallback) {
+ // Dismiss widget resize frame if it is showing. The frame marks its cells as unoccupied
+ // while it is showing, so findSpaceOnWorkspace may try to use those cells.
+ AbstractFloatingView.closeOpenViews(mContext, /* animate= */ false,
+ AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
+
final int[] coordinates = new int[2];
final int screenId = findSpaceOnWorkspace(item, coordinates);
if (screenId == -1) {
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 84d6a6f..65b0662 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -114,9 +114,7 @@
LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
View child = mView.getChildAt(x, y);
- if (child == null || child == dragInfo.item) {
- return mContext.getString(R.string.item_moved);
- } else {
+ if (child != null && child != dragInfo.item) {
ItemInfo info = (ItemInfo) child.getTag();
if (Folder.willAccept(info)) {
return mContext.getString(R.string.folder_created);
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 29b9e77..9afe06c 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -261,12 +261,14 @@
writer.println(prefix + "\tAllAppsStore Apps[] size: " + mApps.length);
for (int i = 0; i < mApps.length; i++) {
writer.println(String.format(Locale.getDefault(),
- "%s\tPackage index, name, class, and description: %d/%s:%s, %s",
+ "%s\tPackage index, name, class, description, bitmap flag: %d/%s:%s, %s, %s+%s",
prefix,
i,
mApps[i].componentName.getPackageName(),
mApps[i].componentName.getClassName(),
- mApps[i].contentDescription));
+ mApps[i].contentDescription,
+ Integer.toBinaryString(mApps[i].bitmap.flags),
+ Integer.toBinaryString(mApps[i].bitmap.creationFlags)));
}
}
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 5defef3..e68e3c9 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -680,6 +680,7 @@
closeOpenFolder(openFolder);
mContent.bindItems(items);
+ mContent.setCanAnnouncePageDescriptionForFolder(true);
centerAboutIcon();
mItemsInvalidated = true;
updateTextViewFocus();
@@ -813,6 +814,7 @@
@Override
protected void handleClose(boolean animate) {
mIsOpen = false;
+ mContent.setCanAnnouncePageDescriptionForFolder(false);
if (!animate && mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
mCurrentAnimator.cancel();
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index fe26194..8d751e6 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -100,6 +100,8 @@
// animating or is open.
private boolean mViewsBound = false;
+ private boolean mCanAnnouncePageDescription;
+
public FolderPagedView(Context context, AttributeSet attrs) {
this(
context,
@@ -170,6 +172,19 @@
mViewsBound = true;
}
+ void setCanAnnouncePageDescriptionForFolder(boolean canAnnounce) {
+ mCanAnnouncePageDescription = canAnnounce;
+ }
+
+ private boolean canAnnouncePageDescriptionForFolder() {
+ return mCanAnnouncePageDescription;
+ }
+
+ @Override
+ protected boolean canAnnouncePageDescription() {
+ return super.canAnnouncePageDescription() && canAnnouncePageDescriptionForFolder();
+ }
+
/**
* Removes all the icons from the folder
*/
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 7367f2e..eaca6c5 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -139,7 +139,7 @@
switch (path) {
case KEY_SHAPE_OPTIONS: {
- if (Flags.newCustomizationPickerUi()) {
+ if (Flags.newCustomizationPickerUi() && Flags.enableLauncherIconShapes()) {
MatrixCursor cursor = new MatrixCursor(new String[]{
KEY_SHAPE_KEY, KEY_SHAPE_TITLE, KEY_PATH, KEY_IS_DEFAULT});
List<AppShape> shapes = AppShapesProvider.INSTANCE.getShapes();
@@ -355,7 +355,7 @@
renderer.hideBottomRow(message.getData().getBoolean(KEY_HIDE_BOTTOM_ROW));
break;
case MESSAGE_ID_UPDATE_SHAPE:
- if (Flags.newCustomizationPickerUi()) {
+ if (Flags.newCustomizationPickerUi() && Flags.enableLauncherIconShapes()) {
String shapeKey = message.getData().getString(KEY_SHAPE_KEY);
Optional<AppShape> optionalShape = AppShapesProvider.INSTANCE.getShapes()
.stream()
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index ab4105c..0ebd69f 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -107,9 +107,9 @@
IconProvider iconProvider) {
super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */, iconProvider);
- mLauncherApps = mContext.getSystemService(LauncherApps.class);
- mUserManager = UserCache.INSTANCE.get(mContext);
- mInstantAppResolver = InstantAppResolver.newInstance(mContext);
+ mLauncherApps = context.getSystemService(LauncherApps.class);
+ mUserManager = UserCache.INSTANCE.get(context);
+ mInstantAppResolver = InstantAppResolver.newInstance(context);
mWidgetCategoryBitmapInfos = new SparseArray<>();
mCancelledTask = new CancellableTask(() -> null, MAIN_EXECUTOR, c -> { });
@@ -117,7 +117,7 @@
}
@Override
- protected long getSerialNumberForUser(@NonNull UserHandle user) {
+ public long getSerialNumberForUser(@NonNull UserHandle user) {
return mUserManager.getSerialNumberForUser(user);
}
@@ -129,7 +129,7 @@
@NonNull
@Override
public BaseIconFactory getIconFactory() {
- return LauncherIcons.obtain(mContext);
+ return LauncherIcons.obtain(context);
}
/**
@@ -151,7 +151,7 @@
// This will clear all pending updates
getUpdateHandler();
- mIconDb.close();
+ iconDb.close();
}
/**
@@ -231,7 +231,7 @@
* Fill in {@code info} with the icon for {@code si}
*/
public void getShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
- getShortcutIcon(info, new CacheableShortcutInfo(si, mContext));
+ getShortcutIcon(info, new CacheableShortcutInfo(si, context));
}
/**
@@ -279,7 +279,7 @@
String override = shortcutInfo.getExtras() == null ? null
: shortcutInfo.getExtras().getString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE);
if (!TextUtils.isEmpty(override)
- && InstallSessionHelper.INSTANCE.get(mContext)
+ && InstallSessionHelper.INSTANCE.get(context)
.isTrustedPackage(pkg, shortcutInfo.getUserHandle())) {
pkg = override;
} else {
@@ -364,11 +364,11 @@
String componentNameQuery = TextUtils.join(
",", Collections.nCopies(queryParams.length - 1, "?"));
- return mIconDb.query(
- useLowResIcons ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES,
- IconDB.COLUMN_COMPONENT
+ return iconDb.query(
+ useLowResIcons ? COLUMNS_LOW_RES : COLUMNS_HIGH_RES,
+ COLUMN_COMPONENT
+ " IN ( " + componentNameQuery + " )"
- + " AND " + IconDB.COLUMN_USER + " = ?",
+ + " AND " + COLUMN_USER + " = ?",
queryParams);
}
@@ -428,7 +428,7 @@
/* user = */ sectionKey.first,
/* useLowResIcons = */ sectionKey.second)) {
// Database title and icon loading
- int componentNameColumnIndex = c.getColumnIndexOrThrow(IconDB.COLUMN_COMPONENT);
+ int componentNameColumnIndex = c.getColumnIndexOrThrow(COLUMN_COMPONENT);
while (c.moveToNext()) {
ComponentName cn = ComponentName.unflattenFromString(
c.getString(componentNameColumnIndex));
@@ -525,9 +525,9 @@
return;
}
- WidgetSection widgetSection = WidgetSections.getWidgetSections(mContext)
+ WidgetSection widgetSection = WidgetSections.getWidgetSections(context)
.get(infoInOut.widgetCategory);
- infoInOut.title = mContext.getString(widgetSection.mSectionTitle);
+ infoInOut.title = context.getString(widgetSection.mSectionTitle);
infoInOut.contentDescription = getUserBadgedLabel(infoInOut.title, infoInOut.user);
final BitmapInfo cachedBitmap = mWidgetCategoryBitmapInfos.get(infoInOut.widgetCategory);
if (cachedBitmap != null) {
@@ -535,9 +535,9 @@
return;
}
- try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+ try (LauncherIcons li = LauncherIcons.obtain(context)) {
final BitmapInfo tempBitmap = li.createBadgedIconBitmap(
- mContext.getDrawable(widgetSection.mSectionDrawable),
+ context.getDrawable(widgetSection.mSectionDrawable),
new BaseIconFactory.IconOptions());
mWidgetCategoryBitmapInfos.put(infoInOut.widgetCategory, tempBitmap);
infoInOut.bitmap = getBadgedIcon(tempBitmap, infoInOut.user);
@@ -606,7 +606,7 @@
/** Log persistently to FileLog.d for debugging. */
@Override
- protected void logdPersistently(String tag, String message, @Nullable Exception e) {
- FileLog.d(tag, message, e);
+ protected void logPersistently(@NonNull String message, @Nullable Exception e) {
+ FileLog.d(BaseIconCache.TAG, message, e);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index d84a219..d042b1d 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -290,16 +290,28 @@
}
setCurrentPage(requestedPage);
mPageIndicator.setActiveMarker(requestedPage);
- mRecommendationPageTitle.setText(mCategoryTitles.get(requestedPage));
+ updatePageTitle(requestedPage);
}
}
@Override
+ protected boolean canAnnouncePageDescription() {
+ // Disable announcement as our page title reads out the needed page description
+ return false;
+ }
+
+ private void updatePageTitle(int requestedPage) {
+ String title = mCategoryTitles.get(requestedPage);
+ mRecommendationPageTitle.setText(title);
+ mRecommendationPageTitle.setContentDescription(title + ", " + getCurrentPageDescription());
+ }
+
+ @Override
protected void notifyPageSwitchListener(int prevPage) {
if (getPageCount() > 1) {
// Since the title is outside the paging scroll, we update the title on page switch.
int nextPage = getNextPage();
- mRecommendationPageTitle.setText(mCategoryTitles.get(nextPage));
+ updatePageTitle(nextPage);
mPageSwitchListeners.forEach(listener -> listener.accept(nextPage));
super.notifyPageSwitchListener(prevPage);
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
index bae74c8..9dbed74 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
@@ -62,7 +62,11 @@
private var cursor =
MatrixCursor(
- arrayOf(IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, IconDB.COLUMN_FRESHNESS_ID)
+ arrayOf(
+ BaseIconCache.COLUMN_ROWID,
+ BaseIconCache.COLUMN_COMPONENT,
+ BaseIconCache.COLUMN_FRESHNESS_ID,
+ )
)
private lateinit var updateHandlerUnderTest: IconCacheUpdateHandler
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index 4d181ff..d2229c4 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -124,7 +124,7 @@
`when`(app.invariantDeviceProfile).thenReturn(idp)
`when`(launcherBinder.newIdleLock(any())).thenReturn(idleLock)
`when`(idleLock.awaitLocked(1000)).thenReturn(false)
- `when`(iconCache.updateHandler).thenReturn(iconCacheUpdateHandler)
+ `when`(iconCache.getUpdateHandler()).thenReturn(iconCacheUpdateHandler)
`when`(widgetsFilterDataProvider.getDefaultWidgetsFilter()).thenReturn(Predicate { true })
context.putObject(UserCache.INSTANCE, userCache)