viewOrder) {
- if (isExpanded() || mWidthAnimator.isRunning()) {
+ if (isExpanded() || (mWidthAnimator != null && mWidthAnimator.isRunning())) {
mReorderRunnable = () -> doReorder(viewOrder);
} else {
doReorder(viewOrder);
@@ -1192,7 +1200,7 @@ public class BubbleBarView extends FrameLayout {
mUpdateSelectedBubbleAfterCollapse = updateSelectedBubbleAfterCollapse;
}
- void setController(Controller controller) {
+ public void setController(Controller controller) {
mController = controller;
}
@@ -1310,20 +1318,38 @@ public class BubbleBarView extends FrameLayout {
}
/**
- * Sets whether the bubble bar is expanded or collapsed.
+ * Update bubble bar expanded state.
*/
public void setExpanded(boolean isBarExpanded) {
- if (mIsBarExpanded != isBarExpanded) {
- mIsBarExpanded = isBarExpanded;
- updateArrowForSelected(/* shouldAnimate= */ false);
- setOrUnsetClickListener();
+ setExpandedInternal(isBarExpanded, false);
+ }
+
+ /**
+ * Update bubble bar expanded state with animation.
+ *
+ * Also triggers a talkback announcement for accessibility.
+ */
+ public void animateExpanded(boolean isBarExpanded) {
+ setExpandedInternal(isBarExpanded, true);
+ }
+
+ private void setExpandedInternal(boolean isBarExpanded, boolean animate) {
+ if (mIsBarExpanded == isBarExpanded) return;
+ mIsBarExpanded = isBarExpanded;
+ updateArrowForSelected(/* shouldAnimate= */ false);
+ setOrUnsetClickListener();
+ if (animate) {
mWidthAnimator = createExpansionAnimator(isBarExpanded);
mWidthAnimator.start();
- updateBubbleAccessibilityStates();
announceExpandedStateChange();
+ } else {
+ onExpandedChanged();
}
+ updateBubbleAccessibilityStates();
+ mController.onBubbleBarExpandedStateChanged(mIsBarExpanded);
}
+
/**
* Returns whether the bubble bar is expanded.
*/
@@ -1335,7 +1361,7 @@ public class BubbleBarView extends FrameLayout {
* Returns whether the bubble bar is expanding.
*/
public boolean isExpanding() {
- return mWidthAnimator.isRunning() && mIsBarExpanded;
+ return mWidthAnimator != null && mWidthAnimator.isRunning() && mIsBarExpanded;
}
/**
@@ -1584,26 +1610,7 @@ public class BubbleBarView extends FrameLayout {
animator.setInterpolator(Interpolators.EMPHASIZED);
addAnimationCallBacks(animator,
/* onStart= */ () -> mBubbleBarBackground.showArrow(true),
- /* onEnd= */ () -> {
- mBubbleBarBackground.showArrow(mIsBarExpanded);
- if (!mIsBarExpanded && mReorderRunnable != null) {
- mReorderRunnable.run();
- mReorderRunnable = null;
- }
- // If the bar was just collapsed and the overflow was the last bubble that was
- // selected, set the first bubble as selected.
- if (!mIsBarExpanded && mUpdateSelectedBubbleAfterCollapse != null
- && mSelectedBubbleView != null
- && mSelectedBubbleView.getBubble() instanceof BubbleBarOverflow) {
- BubbleView firstBubble = (BubbleView) getChildAt(0);
- mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey());
- }
- // If the bar was just expanded, remove the dot from the selected bubble.
- if (mIsBarExpanded && mSelectedBubbleView != null) {
- mSelectedBubbleView.markSeen();
- }
- updateLayoutParams();
- },
+ /* onEnd= */ this::onExpandedChanged,
/* onUpdate= */ anim -> {
updateBubblesLayoutProperties(mBubbleBarLocation);
invalidate();
@@ -1611,6 +1618,27 @@ public class BubbleBarView extends FrameLayout {
return animator;
}
+ private void onExpandedChanged() {
+ mBubbleBarBackground.showArrow(mIsBarExpanded);
+ if (!mIsBarExpanded && mReorderRunnable != null) {
+ mReorderRunnable.run();
+ mReorderRunnable = null;
+ }
+ // If the bar was just collapsed and the overflow was the last bubble that was
+ // selected, set the first bubble as selected.
+ if (!mIsBarExpanded && mUpdateSelectedBubbleAfterCollapse != null
+ && mSelectedBubbleView != null
+ && mSelectedBubbleView.getBubble() instanceof BubbleBarOverflow) {
+ BubbleView firstBubble = (BubbleView) getChildAt(0);
+ mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey());
+ }
+ // If the bar was just expanded, remove the dot from the selected bubble.
+ if (mIsBarExpanded && mSelectedBubbleView != null) {
+ mSelectedBubbleView.markSeen();
+ }
+ updateLayoutParams();
+ }
+
/**
* Returns the distance between the top left corner of the bubble bar to the center of the dot
* of the selected bubble.
@@ -1644,7 +1672,7 @@ public class BubbleBarView extends FrameLayout {
}
/** Interface for BubbleBarView to communicate with its controller. */
- interface Controller {
+ public interface Controller {
/** Returns the translation Y that the bubble bar should have. */
float getBubbleBarTranslationY();
@@ -1664,5 +1692,8 @@ public class BubbleBarView extends FrameLayout {
/** Notifies the controller that bubble bar is being dragged */
void setIsDragging(boolean dragging);
+
+ /** Notifies the controller that bubble bar expanded state changed */
+ void onBubbleBarExpandedStateChanged(boolean expanded);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 2ed72d7824..648ad9f2a2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -43,33 +43,25 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
-
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarInsetsController;
import com.android.launcher3.taskbar.TaskbarSharedState;
import com.android.launcher3.taskbar.TaskbarStashController;
-import com.android.launcher3.taskbar.bubbles.BubbleBarLocationDropTarget.BubbleBarDragListener;
import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator;
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutController;
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutPositioner;
import com.android.launcher3.taskbar.bubbles.flyout.FlyoutCallbacks;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.SystemUiProxy;
import com.android.wm.shell.Flags;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
-import com.android.wm.shell.shared.bubbles.DeviceConfig;
import java.io.PrintWriter;
import java.util.List;
@@ -128,60 +120,6 @@ public class BubbleBarViewController {
updateTranslationY();
setBubbleBarScaleAndPadding(pinningProgress);
});
- private final BubbleBarDragListener mDragListener = new BubbleBarDragListener() {
-
- @Override
- public void getBubbleBarLocationHitRect(@NonNull BubbleBarLocation bubbleBarLocation,
- Rect outRect) {
- Point screenSize = DisplayController.INSTANCE.get(mActivity).getInfo().currentSize;
- outRect.top = screenSize.y - mBubbleBarDropTargetSize;
- outRect.bottom = screenSize.y;
- if (bubbleBarLocation.isOnLeft(mBarView.isLayoutRtl())) {
- outRect.left = 0;
- outRect.right = mBubbleBarDropTargetSize;
- } else {
- outRect.left = screenSize.x - mBubbleBarDropTargetSize;
- outRect.right = screenSize.x;
- }
- }
-
- @Override
- public void onLauncherItemDroppedOverBubbleBarDragZone(@NonNull BubbleBarLocation location,
- @NonNull ItemInfo itemInfo) {
- AbstractFloatingView.closeAllOpenViews(mActivity);
- if (itemInfo instanceof WorkspaceItemInfo) {
- ShortcutInfo shortcutInfo = ((WorkspaceItemInfo) itemInfo).getDeepShortcutInfo();
- if (shortcutInfo != null) {
- mSystemUiProxy.showShortcutBubble(shortcutInfo, location);
- return;
- }
- }
- Intent itemIntent = itemInfo.getIntent();
- if (itemIntent != null && itemIntent.getComponent() != null) {
- itemIntent.setPackage(itemIntent.getComponent().getPackageName());
- mSystemUiProxy.showAppBubble(itemIntent, itemInfo.user, location);
- }
- }
-
- @Override
- public void onLauncherItemDraggedOutsideBubbleBarDropZone() {
- onItemDraggedOutsideBubbleBarDropZone();
- mSystemUiProxy.showBubbleDropTarget(/* show = */ false);
- }
-
- @Override
- public void onLauncherItemDraggedOverBubbleBarDragZone(
- @NonNull BubbleBarLocation location) {
- onDragItemOverBubbleBarDragZone(location);
- mSystemUiProxy.showBubbleDropTarget(/* show = */ true, location);
- }
-
- @NonNull
- @Override
- public View getDropView() {
- return mBarView;
- }
- };
// Modified when swipe up is happening on the bubble bar or task bar.
private float mBubbleBarSwipeUpTranslationY;
@@ -207,11 +145,8 @@ public class BubbleBarViewController {
private BubbleBarFlyoutController mBubbleBarFlyoutController;
private BubbleBarPinController mBubbleBarPinController;
private TaskbarSharedState mTaskbarSharedState;
- private final BubbleBarLocationDropTarget mBubbleBarLeftDropTarget;
- private final BubbleBarLocationDropTarget mBubbleBarRightDropTarget;
private final TimeSource mTimeSource = System::currentTimeMillis;
private final int mTaskbarTranslationDelta;
- private final int mBubbleBarDropTargetSize;
@Nullable
private BubbleBarBoundsChangeListener mBoundsChangeListener;
@@ -229,15 +164,6 @@ public class BubbleBarViewController {
R.dimen.bubblebar_transient_taskbar_min_distance);
mDragElevation = res.getDimensionPixelSize(R.dimen.dragged_bubble_elevation);
mTaskbarTranslationDelta = getBubbleBarTranslationDeltaForTaskbar(activity);
- if (DeviceConfig.isSmallTablet(mActivity)) {
- mBubbleBarDropTargetSize = res.getDimensionPixelSize(com.android.wm.shell.R.dimen.drag_zone_bubble_fold);
- } else {
- mBubbleBarDropTargetSize = res.getDimensionPixelSize(com.android.wm.shell.R.dimen.drag_zone_bubble_tablet);
- }
- mBubbleBarLeftDropTarget = new BubbleBarLocationDropTarget(BubbleBarLocation.LEFT,
- mDragListener);
- mBubbleBarRightDropTarget = new BubbleBarLocationDropTarget(BubbleBarLocation.RIGHT,
- mDragListener);
}
/** Initializes controller. */
@@ -270,7 +196,7 @@ public class BubbleBarViewController {
if (!mBubbleStashController.isTransientTaskBar()) {
// TODO(b/380274085) for transient taskbar mode, the click is also handled by the input
// consumer. This check can be removed once b/380274085 is fixed.
- mBarView.setOnClickListener(v -> setExpanded(!mBarView.isExpanded()));
+ mBarView.setOnClickListener(v -> animateExpanded(!mBarView.isExpanded()));
}
mBarView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@@ -286,7 +212,7 @@ public class BubbleBarViewController {
mBarView.setController(new BubbleBarView.Controller() {
@Override
public float getBubbleBarTranslationY() {
- return mBubbleStashController.getBubbleBarTranslationY();
+ return mBubbleStashController.getTargetTranslationYForState();
}
@Override
@@ -298,7 +224,7 @@ public class BubbleBarViewController {
@Override
public void expandBubbleBar() {
- BubbleBarViewController.this.setExpanded(
+ BubbleBarViewController.this.animateExpanded(
/* isExpanded= */ true, /* maybeShowEdu*/ true);
}
@@ -317,6 +243,14 @@ public class BubbleBarViewController {
public void setIsDragging(boolean dragging) {
mBubbleBarContainer.setElevation(dragging ? mDragElevation : 0);
}
+
+ @Override
+ public void onBubbleBarExpandedStateChanged(boolean expanded) {
+ if (expanded && !mTaskbarStashController.isStashed()) {
+ mTaskbarStashController.updateAndAnimateTransientTaskbar(true /* stash */,
+ false /* shouldBubblesFollow */);
+ }
+ }
});
mBubbleViewController = new BubbleView.Controller() {
@@ -346,18 +280,6 @@ public class BubbleBarViewController {
};
}
- /** Adds bubble bar locations drop zones to the drag controller. */
- public void addBubbleBarDropTargets(DragController> dragController) {
- dragController.addDropTarget(mBubbleBarLeftDropTarget);
- dragController.addDropTarget(mBubbleBarRightDropTarget);
- }
-
- /** Removes bubble bar locations drop zones to the drag controller. */
- public void removeBubbleBarDropTargets(DragController> dragController) {
- dragController.removeDropTarget(mBubbleBarLeftDropTarget);
- dragController.removeDropTarget(mBubbleBarRightDropTarget);
- }
-
/** Returns animated float property responsible for pinning transition animation. */
public AnimatedFloat getBubbleBarPinning() {
return mBubbleBarPinning;
@@ -424,7 +346,7 @@ public class BubbleBarViewController {
@Override
public void flyoutClicked() {
interruptAnimationForTouch();
- setExpanded(/* isExpanded= */ true, /* maybeShowEdu*/ true);
+ animateExpanded(/* isExpanded= */ true, /* maybeShowEdu*/ true);
}
};
}
@@ -438,6 +360,11 @@ public class BubbleBarViewController {
};
}
+ /** Returns the overflow bubble. */
+ public BubbleBarOverflow getOverflowBubble() {
+ return mOverflowBubble;
+ }
+
private void onBubbleClicked(BubbleView bubbleView) {
if (mBubbleBarPinning.isAnimating()) return;
bubbleView.markSeen();
@@ -462,7 +389,7 @@ public class BubbleBarViewController {
}
private void collapseBubbleBar() {
- setExpanded(false);
+ animateExpanded(false);
mBubbleStashController.stashBubbleBar();
}
@@ -637,6 +564,12 @@ public class BubbleBarViewController {
return mBarView.isShowingDropTarget();
}
+ /** Tells bubble bar view if it should show the drop target. */
+ public void setShowingDropTarget(boolean showingDropTarget) {
+ mBarView.showDropTarget(showingDropTarget);
+ }
+
+ //TODO(b/411505605) remove unused IPC calls and code
/**
* Notifies the controller that a drag event is over the Bubble Bar drop zone. The controller
* will display the appropriate drop target and enter drop target mode. The controller will also
@@ -732,7 +665,8 @@ public class BubbleBarViewController {
public boolean isEventOverBubbleBar(MotionEvent event) {
if (!isBubbleBarVisible()) return false;
final Rect bounds = getBubbleBarBounds();
- final int bubbleBarTopOnScreen = mBarView.getRestingTopPositionOnScreen();
+ final int bubbleBarTopOnScreen =
+ mActivity.getScreenSize().y - mBarView.getTopToScreenBottom();
final float x = event.getX();
return event.getRawY() >= bubbleBarTopOnScreen && x >= bounds.left && x <= bounds.right;
}
@@ -807,7 +741,7 @@ public class BubbleBarViewController {
if (hidden) {
mBarView.dismiss(() -> {
updateVisibilityForStateChange();
- mBarView.setExpanded(false);
+ mBarView.animateExpanded(false);
adjustTaskbarAndHotseatToBubbleBarState(/* isBubbleBarExpanded= */ false);
mActivity.bubbleBarVisibilityChanged(/* isVisible= */ false);
});
@@ -930,7 +864,7 @@ public class BubbleBarViewController {
float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
APP_ICON_MEDIUM_DP, dm);
float smallMediumThreshold = (smallIconSize + mediumIconSize) / 2f;
- int taskbarIconSize = deviceProfile.taskbarIconSize;
+ int taskbarIconSize = deviceProfile.getTaskbarProfile().getIconSize();
return taskbarIconSize <= smallMediumThreshold
? res.getDimensionPixelSize(R.dimen.bubblebar_icon_size_small) :
res.getDimensionPixelSize(R.dimen.bubblebar_icon_size);
@@ -948,7 +882,7 @@ public class BubbleBarViewController {
float largeIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
APP_ICON_LARGE_DP, dm);
float mediumLargeThreshold = (mediumIconSize + largeIconSize) / 2f;
- return deviceProfile.taskbarIconSize >= mediumLargeThreshold
+ return deviceProfile.getTaskbarProfile().getIconSize() >= mediumLargeThreshold
? res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_large) :
res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
}
@@ -1025,9 +959,11 @@ public class BubbleBarViewController {
int persistentSpacingSize = res
.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_persistent_taskbar);
int persistentBubbleBarSize = persistentBubbleSize + persistentSpacingSize * 2;
- int persistentTaskbarHeight = activity.getPersistentTaskbarDeviceProfile().taskbarHeight;
+ int persistentTaskbarHeight =
+ activity.getPersistentTaskbarDeviceProfile().getTaskbarProfile().getHeight();
int persistentBubbleBarY = (persistentTaskbarHeight - persistentBubbleBarSize) / 2;
- int transientBubbleBarY = activity.getTransientTaskbarDeviceProfile().taskbarBottomMargin;
+ int transientBubbleBarY =
+ activity.getTransientTaskbarDeviceProfile().getTaskbarProfile().getBottomMargin();
return transientBubbleBarY - persistentBubbleBarY;
}
@@ -1101,7 +1037,7 @@ public class BubbleBarViewController {
if (mOverflowAdded == showOverflow) return;
mOverflowAdded = showOverflow;
if (mOverflowAdded) {
- mBarView.addBubble(mOverflowBubble.getView());
+ mBarView.addBubble(mOverflowBubble.getView(), /* suppressAnimation= */ true);
mOverflowBubble.getView().setOnClickListener(mBubbleClickListener);
mOverflowBubble.getView().setController(mBubbleViewController);
} else {
@@ -1148,10 +1084,7 @@ public class BubbleBarViewController {
if (b != null) {
BubbleView bubbleToSelectView =
bubbleToSelect == null ? null : bubbleToSelect.getView();
- mBarView.addBubble(b.getView(), bubbleToSelectView);
- b.getView().setOnClickListener(mBubbleClickListener);
- mBubbleDragController.setupBubbleView(b.getView());
- b.getView().setController(mBubbleViewController);
+ addBubbleView(b.getView(), suppressAnimation, bubbleToSelectView);
if (suppressAnimation || !(b instanceof BubbleBarBubble bubble)) {
// the bubble bar and handle are initialized as part of the first bubble animation.
@@ -1159,7 +1092,8 @@ public class BubbleBarViewController {
// ensure they've been initialized.
if (mTaskbarStashController.isInApp()
&& mBubbleStashController.isTransientTaskBar()
- && mTaskbarStashController.isStashed()) {
+ && mTaskbarStashController.isStashed()
+ && !isExpanded()) {
mBubbleStashController.stashBubbleBarImmediate();
} else {
mBubbleStashController.showBubbleBarImmediate();
@@ -1172,6 +1106,21 @@ public class BubbleBarViewController {
}
}
+ private void addBubbleView(BubbleView bubbleView, boolean suppressAnimation,
+ BubbleView selectedBubbleView) {
+ mBarView.addBubble(bubbleView, selectedBubbleView, suppressAnimation);
+ bubbleView.setOnClickListener(mBubbleClickListener);
+ mBubbleDragController.setupBubbleView(bubbleView);
+ bubbleView.setController(mBubbleViewController);
+ }
+
+ /**
+ * Restore a previous bubble that is stored in {@link TaskbarSharedState}.
+ */
+ public void restoreBubble(BubbleBarItem b) {
+ addBubbleView(b.getView(), /* suppressAnimation= */ true, /* bubbleToSelectView= */ null);
+ }
+
/** Animates the bubble bar to notify the user about a bubble change. */
public void animateBubbleNotification(BubbleBarBubble bubble, boolean isExpanding,
boolean isUpdate) {
@@ -1225,34 +1174,32 @@ public class BubbleBarViewController {
mBarView.setSelectedBubble(newlySelected.getView());
}
- /** @see #setExpanded(boolean, boolean) */
- public void setExpanded(boolean isExpanded) {
- setExpanded(isExpanded, /* maybeShowEdu= */ false);
+ /** @see #animateExpanded(boolean, boolean) */
+ public void animateExpanded(boolean isExpanded) {
+ animateExpanded(isExpanded, /* maybeShowEdu= */ false);
}
/**
- * Sets whether the bubble bar should be expanded (not unstashed, but have the contents
- * within it expanded). This method notifies SystemUI that the bubble bar is expanded and
- * showing a selected bubble. This method should ONLY be called from UI events originating
- * from Launcher.
+ * Sets whether the bubble bar should be animated to expanded state (not unstashed, but have
+ * the contents within it expanded). This method notifies SystemUI that the bubble bar is
+ * expanded and showing a selected bubble. This method should ONLY be called from UI events
+ * originating from Launcher.
*
* @param isExpanded whether the bar should be expanded
* @param maybeShowEdu whether we should show the edu view before expanding
*/
- public void setExpanded(boolean isExpanded, boolean maybeShowEdu) {
+ public void animateExpanded(boolean isExpanded, boolean maybeShowEdu) {
// if we're trying to expand try showing the edu view instead
if (maybeShowEdu && isExpanded && !mBarView.isExpanded() && maybeShowEduView()) {
return;
}
if (!mBubbleBarPinning.isAnimating() && isExpanded != mBarView.isExpanded()) {
- mBarView.setExpanded(isExpanded);
+ mBarView.animateExpanded(isExpanded);
adjustTaskbarAndHotseatToBubbleBarState(isExpanded);
if (!isExpanded) {
mSystemUiProxy.collapseBubbles();
} else {
mBubbleBarController.showSelectedBubble();
- mTaskbarStashController.updateAndAnimateTransientTaskbar(true /* stash */,
- false /* shouldBubblesFollow */);
}
}
}
@@ -1291,15 +1238,25 @@ public class BubbleBarViewController {
* Sets whether the bubble bar should be expanded. This method is used in response to UI events
* from SystemUI.
*/
- public void setExpandedFromSysui(boolean isExpanded) {
+ public void setExpandedFromSysui(boolean isExpanded, boolean animate) {
if (isNewBubbleAnimationRunningOrPending() && isExpanded) {
mBubbleBarViewAnimator.expandedWhileAnimating();
return;
}
- if (!isExpanded) {
- mBubbleStashController.stashBubbleBar();
+ if (animate) {
+ if (!isExpanded) {
+ mBubbleStashController.stashBubbleBar();
+ } else {
+ mBubbleStashController.showBubbleBar(true /* expand the bubbles */);
+ }
} else {
- mBubbleStashController.showBubbleBar(true /* expand the bubbles */);
+ if (!isExpanded) {
+ mBubbleStashController.stashBubbleBarImmediate();
+ } else {
+ mBubbleStashController.showBubbleBarImmediate();
+ mBarView.setExpanded(true);
+ adjustTaskbarAndHotseatToBubbleBarState(true);
+ }
}
}
@@ -1329,7 +1286,7 @@ public class BubbleBarViewController {
* Notifies SystemUI to expand the selected bubble when the bubble is released.
*/
public void onBubbleDragRelease(BubbleBarLocation location) {
- mSystemUiProxy.stopBubbleDrag(location, mBarView.getRestingTopPositionOnScreen());
+ mSystemUiProxy.stopBubbleDrag(location, mBarView.getTopToScreenBottom());
}
/** Handle given bubble being dismissed */
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
index c2118404e4..083829bd57 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
@@ -27,6 +27,8 @@ import com.android.launcher3.taskbar.bubbles.stashing.BubbleBarLocationOnDemandL
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.SystemUiProxy;
+import com.android.wm.shell.shared.bubbles.DragZoneFactory;
import java.io.PrintWriter;
import java.util.Optional;
@@ -44,6 +46,7 @@ public class BubbleControllers {
public final BubblePinController bubblePinController;
public final Optional bubbleBarSwipeController;
public final BubbleCreator bubbleCreator;
+ public final DragToBubbleController dragToBubbleController;
private final RunnableList mPostInitRunnables = new RunnableList();
@@ -62,6 +65,7 @@ public class BubbleControllers {
BubbleBarPinController bubbleBarPinController,
BubblePinController bubblePinController,
Optional bubbleBarSwipeController,
+ DragToBubbleController dragToBubbleController,
BubbleCreator bubbleCreator) {
this.bubbleBarController = bubbleBarController;
this.bubbleBarViewController = bubbleBarViewController;
@@ -73,6 +77,7 @@ public class BubbleControllers {
this.bubblePinController = bubblePinController;
this.bubbleBarSwipeController = bubbleBarSwipeController;
this.bubbleCreator = bubbleCreator;
+ this.dragToBubbleController = dragToBubbleController;
}
/**
@@ -115,12 +120,30 @@ public class BubbleControllers {
.get(ALPHA_INDEX_BUBBLE_BAR);
}
});
- bubbleDragController.init(/* bubbleControllers = */ this);
+ bubbleDragController.init(/* bubbleControllers = */ this, bubbleBarLocationListeners);
bubbleDismissController.init(/* bubbleControllers = */ this);
bubbleBarPinController.init(this, bubbleBarLocationListeners);
bubblePinController.init(this);
bubbleBarSwipeController.ifPresent(c -> c.init(this));
+ dragToBubbleController.init(bubbleBarViewController,
+ new DragZoneFactory.BubbleBarPropertiesProvider() {
+ @Override
+ public int getHeight() {
+ return (int) bubbleBarViewController.getBubbleBarCollapsedHeight();
+ }
+ @Override
+ public int getWidth() {
+ return (int) bubbleBarViewController.getBubbleBarCollapsedWidth();
+ }
+
+ @Override
+ public int getBottomPadding() {
+ return -(int) bubbleStashController.getBubbleBarTranslationY();
+ }
+ },
+ bubbleBarLocationListeners,
+ SystemUiProxy.INSTANCE.get(taskbarControllers.taskbarActivityContext));
mPostInitRunnables.executeAllAndDestroy();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
index fdc9fb0b40..27b30a1f3d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
@@ -18,7 +18,7 @@
package com.android.launcher3.taskbar.bubbles
import com.android.launcher3.R
-import com.android.wm.shell.R as SharedR
+import com.android.wm.shell.R as SharedR // WM and WMShared are "shared" or linked together.
import com.android.wm.shell.shared.bubbles.DismissView
/**
@@ -39,6 +39,7 @@ fun DismissView.setup() {
floatingGradientColorResId = android.R.color.system_neutral1_900,
backgroundResId = SharedR.drawable.floating_dismiss_background,
iconResId = SharedR.drawable.floating_dismiss_ic_close,
+ applyMarginOverNavBarInset = false,
)
)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 3df10c0ce0..7535d366cc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -30,12 +30,14 @@ import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.bubbles.BubbleBarController.BubbleBarLocationListener;
import com.android.wm.shell.shared.bubbles.BaseBubblePinController.LocationChangeListener;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.shared.bubbles.DragZone;
import com.android.wm.shell.shared.bubbles.DragZoneFactory;
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.BubbleBarPropertiesProvider;
import com.android.wm.shell.shared.bubbles.DragZoneFactory.DesktopWindowModeChecker;
import com.android.wm.shell.shared.bubbles.DragZoneFactory.SplitScreenModeChecker;
import com.android.wm.shell.shared.bubbles.DraggedObject;
@@ -88,6 +90,7 @@ public class BubbleDragController {
private BubbleDismissController mBubbleDismissController;
private BubbleBarPinController mBubbleBarPinController;
private BubblePinController mBubblePinController;
+ private BubbleBarLocationListener mBubbleBarLocationListener;
private final DropTargetManager mDropTargetManager;
private final DragZoneFactory mDragZoneFactory;
private final BubbleDragZoneChangedListener mBubbleDragZoneChangedListener;
@@ -113,8 +116,25 @@ public class BubbleDragController {
return false;
}
};
+ BubbleBarPropertiesProvider bubbleBarPropertiesProvider =
+ new BubbleBarPropertiesProvider() {
+ @Override
+ public int getHeight() {
+ return (int) mBubbleBarViewController.getBubbleBarCollapsedHeight();
+ }
+
+ @Override
+ public int getWidth() {
+ return (int) mBubbleBarViewController.getBubbleBarCollapsedWidth();
+ }
+
+ @Override
+ public int getBottomPadding() {
+ return (int) -mBubbleBarViewController.getBubbleBarTranslationY().value;
+ }
+ };
mDragZoneFactory = new DragZoneFactory(mActivity.getApplicationContext(), deviceConfig,
- splitScreenModeChecker, desktopWindowModeChecker);
+ splitScreenModeChecker, desktopWindowModeChecker, bubbleBarPropertiesProvider);
mBubbleDragZoneChangedListener = new BubbleDragZoneChangedListener();
mDropTargetManager = new DropTargetManager(mActivity.getApplicationContext(),
dropTargetParent, mBubbleDragZoneChangedListener);
@@ -125,12 +145,14 @@ public class BubbleDragController {
* Should be careful to only access things that were created in constructors for now, as some
* controllers may still be waiting for init().
*/
- public void init(@NonNull BubbleControllers bubbleControllers) {
+ public void init(@NonNull BubbleControllers bubbleControllers,
+ BubbleBarLocationListener bubbleBarLocationListener) {
mBubbleBarController = bubbleControllers.bubbleBarController;
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleDismissController = bubbleControllers.bubbleDismissController;
mBubbleBarPinController = bubbleControllers.bubbleBarPinController;
mBubblePinController = bubbleControllers.bubblePinController;
+ mBubbleBarLocationListener = bubbleBarLocationListener;
mBubbleDismissController.setListener(
stuck -> {
if (stuck) {
@@ -194,9 +216,7 @@ public class BubbleDragController {
protected void onDragUpdate(float x, float y, float newTx, float newTy) {
bubbleView.setDragTranslationX(newTx);
bubbleView.setTranslationY(newTy);
- if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
- mDropTargetManager.onDragUpdated((int) x, (int) y);
- } else {
+ if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
mBubblePinController.onDragUpdate(x, y);
}
}
@@ -305,9 +325,7 @@ public class BubbleDragController {
protected void onDragUpdate(float x, float y, float newTx, float newTy) {
bubbleBarView.setTranslationX(newTx);
bubbleBarView.setTranslationY(newTy);
- if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
- mDropTargetManager.onDragUpdated((int) x, (int) y);
- } else {
+ if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
mBubbleBarPinController.onDragUpdate(x, y);
}
}
@@ -570,7 +588,17 @@ public class BubbleDragController {
private void drag(@NonNull View view, @NonNull MotionEvent event, float dx, float dy,
float x, float y) {
- if (mBubbleDismissController.handleTouchEvent(event)) return;
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ // notify drop target manager about the new drag location regardless of whether we
+ // are in the dismiss zone so that it can keep track of the current zone and update
+ // the drop target view
+ mDropTargetManager.onDragUpdated((int) x, (int) y);
+ }
+ if (mBubbleDismissController.handleTouchEvent(event)) {
+ // if we're dragging within the dismiss target, return immediately; the dragged
+ // object is manipulated by the dismiss target
+ return;
+ }
final float newTx = mViewInitialPosition.x + dx;
final float newTy = mViewInitialPosition.y + dy;
onDragUpdate(x, y, newTx, newTy);
@@ -598,7 +626,7 @@ public class BubbleDragController {
}
} else {
mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(),
- onComplete);
+ onComplete);
}
}
mBubbleDismissController.hideDismissView();
@@ -650,7 +678,7 @@ public class BubbleDragController {
}
@Override
- public void onInitialDragZoneSet(@NonNull DragZone dragZone) {
+ public void onInitialDragZoneSet(@Nullable DragZone dragZone) {
mDragZone = dragZone;
if (dragZone instanceof DragZone.Bubble.Left) {
mBubbleBarLocation = BubbleBarLocation.LEFT;
@@ -660,25 +688,31 @@ public class BubbleDragController {
}
@Override
- public void onDragZoneChanged(@NonNull DraggedObject draggedObject, @NonNull DragZone from,
- @NonNull DragZone to) {
+ public void onDragZoneChanged(@NonNull DraggedObject draggedObject, @Nullable DragZone from,
+ @Nullable DragZone to) {
mDragZone = to;
if (to instanceof DragZone.Bubble.Left
&& mBubbleBarLocation != BubbleBarLocation.LEFT) {
if (draggedObject instanceof DraggedObject.Bubble) {
+ // listener will be notified by BubbleBarController
mBubbleBarController.animateBubbleBarLocation(BubbleBarLocation.LEFT);
+ } else {
+ // otherwise notify listener manually
+ mBubbleBarLocationListener.onBubbleBarLocationAnimated(BubbleBarLocation.LEFT);
}
mBubbleBarLocation = BubbleBarLocation.LEFT;
} else if (to instanceof DragZone.Bubble.Right
&& mBubbleBarLocation != BubbleBarLocation.RIGHT) {
if (draggedObject instanceof DraggedObject.Bubble) {
mBubbleBarController.animateBubbleBarLocation(BubbleBarLocation.RIGHT);
+ } else {
+ mBubbleBarLocationListener.onBubbleBarLocationAnimated(BubbleBarLocation.RIGHT);
}
mBubbleBarLocation = BubbleBarLocation.RIGHT;
}
}
@Override
- public void onDragEnded(@NonNull DragZone zone) {}
+ public void onDragEnded(@Nullable DragZone zone) {}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index ec540e0088..7e2b139f2b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -85,7 +85,6 @@ public class BubbleStashedHandleViewController {
mActivity = activity;
mStashedHandleView = stashedHandleView;
mStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1);
- mStashedHandleAlpha.setUpdateVisibility(true);
}
/** Initialize controller. */
@@ -105,7 +104,7 @@ public class BubbleStashedHandleViewController {
// Use values directly from device profile to avoid referencing other bubble controllers
// during init flow.
int maxTy = Math.max(deviceProfile.hotseatBarBottomSpacePx,
- deviceProfile.taskbarBottomMargin);
+ deviceProfile.getTaskbarProfile().getBottomMargin());
// Adjust handle view size to accommodate the handle morphing into the bubble bar
mStashedHandleView.getLayoutParams().height = barSize + maxTy;
@@ -349,7 +348,7 @@ public class BubbleStashedHandleViewController {
// the bounds of the handle only include the visible part, so we check that the Y coordinate
// is anywhere within the stashed height of bubble bar (same as taskbar stashed height).
- final int top = mActivity.getDeviceProfile().heightPx - mStashedBubbleBarHeight;
+ final int top = mActivity.getDeviceProfile().getDeviceProperties().getHeightPx() - mStashedBubbleBarHeight;
final float x = ev.getRawX();
return ev.getRawY() >= top && x >= mStashedHandleBounds.left
&& x <= mStashedHandleBounds.right;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
index 622fea2928..15fe35b3cc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -728,7 +728,7 @@ constructor(
}
private fun expandBubbleBar() {
- bubbleBarView.isExpanded = true
+ bubbleBarView.animateExpanded(true)
onExpanded.run()
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
index fea40f9211..4a2f029fd5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
@@ -64,6 +64,7 @@ constructor(
}
fun setUpAndShowFlyout(message: BubbleBarFlyoutMessage, onInit: () -> Unit, onEnd: () -> Unit) {
+ animator?.cancel()
flyout?.let(container::removeView)
val flyout = BubbleBarFlyoutView(container.context, positioner, flyoutScheduler)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
index ec272ac873..56622020e8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -186,12 +186,14 @@ interface BubbleStashController {
fun getHandleViewAlpha(): MultiPropertyFactory.MultiProperty? = null
/**
- * Returns bubble bar Y position according to [isBubblesShowingOnHome] and
- * [isBubblesShowingOnOverview] values. Default implementation only analyse
- * [isBubblesShowingOnHome] and return translationY to align with the hotseat vertical center.
- * For Other cases align bubbles with the taskbar.
+ * Default implementation only analyse [isBubblesShowingOnHome] and return value is equal to
+ * [targetTranslationYForState].
*/
val bubbleBarTranslationY: Float
+ get() = targetTranslationYForState
+
+ /** Returns bubble bar Y target position according to [isBubblesShowingOnHome] value. */
+ val targetTranslationYForState: Float
get() =
if (isBubblesShowingOnHome) {
bubbleBarTranslationYForHotseat
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt
index 886b9f02c2..2408274c9f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt
@@ -27,9 +27,9 @@ import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Task
class DeviceProfileDimensionsProviderAdapter(
private val taskbarActivityContext: TaskbarActivityContext
) : TaskbarHotseatDimensionsProvider {
- override fun getTaskbarBottomSpace(): Int = taskbarDp().taskbarBottomMargin
+ override fun getTaskbarBottomSpace(): Int = taskbarDp().taskbarProfile.bottomMargin
- override fun getTaskbarHeight(): Int = taskbarDp().taskbarHeight
+ override fun getTaskbarHeight(): Int = taskbarDp().taskbarProfile.height
private fun taskbarDp(): DeviceProfile = taskbarActivityContext.deviceProfile
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
index 9d8c0ed970..5c8746cb8b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
@@ -60,6 +60,10 @@ class PersistentBubbleStashController(
// if there are no bubbles, there's nothing to show, so just return.
return
}
+ if (transitionFromHome && inAppDisplayOverrideProgress != 0f) {
+ // was on -1 page and leaving it, - reset the inAppDisplayOverrideProgress
+ inAppDisplayOverrideProgress = 0f
+ }
// If we're transitioning anywhere, bubble bar should be collapsed
updateExpandedState(expand = false)
if (transitionFromHome || field == BubbleLauncherState.HOME) {
@@ -100,6 +104,10 @@ class PersistentBubbleStashController(
return -bubbleBarVerticalCenterForHome + bubbleBarHeight / 2
}
+ /**
+ * Returns bubble bar Y target position according to [isBubblesShowingOnHome] value. Value could
+ * be adjusted to the display override progress.
+ */
override val bubbleBarTranslationY: Float
get() =
if (inAppDisplayOverrideProgress > 0f && launcherState == BubbleLauncherState.HOME) {
@@ -112,7 +120,7 @@ class PersistentBubbleStashController(
Interpolators.LINEAR,
)
} else {
- super.bubbleBarTranslationY
+ targetTranslationYForState
}
override var inAppDisplayOverrideProgress: Float = 0f
@@ -238,7 +246,7 @@ class PersistentBubbleStashController(
}
if (bubbleBarViewController.isExpanded != expand) {
val maybeShowEdu = expand && bubbleBarGesture
- bubbleBarViewController.setExpanded(expand, maybeShowEdu)
+ bubbleBarViewController.animateExpanded(expand, maybeShowEdu)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index 82bb071bc3..00e79029e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -115,7 +115,16 @@ class TransientBubbleStashController(
}
// Only stash if we're in an app, otherwise we're in home or overview where we should
// be un-stashed
- updateStashedAndExpandedState(field == BubbleLauncherState.IN_APP, expand = false)
+ val stash = field == BubbleLauncherState.IN_APP
+ val expand =
+ if (stash) {
+ // Always collapse when we are stashing
+ false
+ } else {
+ // If unstashing, keep the current state
+ bubbleBarViewController.isExpanded
+ }
+ updateStashedAndExpandedState(stash, expand)
}
override var isSysuiLocked: Boolean = false
@@ -468,7 +477,7 @@ class TransientBubbleStashController(
// reset stash translation
translationYDuringStash.updateValue(0f)
bubbleBarBubbleTranslationY.updateValue(0f)
- bubbleBarViewController.isExpanded = false
+ bubbleBarViewController.animateExpanded(false)
}
taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
}
@@ -556,6 +565,7 @@ class TransientBubbleStashController(
) {
if (bubbleBarViewController.isHiddenForNoBubbles) {
// If there are no bubbles the bar and handle are invisible, nothing to do here.
+ cancelAnimation()
return
}
val isStashed = stash && !isBubblesShowingOnHome && !isBubblesShowingOnOverview
@@ -577,7 +587,7 @@ class TransientBubbleStashController(
}
if (bubbleBarViewController.isExpanded != expand) {
val maybeShowEdu = expand && bubbleBarGesture
- bubbleBarViewController.setExpanded(expand, maybeShowEdu)
+ bubbleBarViewController.animateExpanded(expand, maybeShowEdu)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index a1df21fa04..2807668107 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -49,11 +49,15 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
private var allAppsTouchTriggered = false
private var allAppsTouchRunnable: Runnable? = null
private var allAppsButtonTouchDelayMs: Long = ViewConfiguration.getLongPressTimeout().toLong()
+ private var isTaskbarInMinimalState = false
private lateinit var taskbarViewCallbacks: TaskbarViewCallbacks
override val spaceNeeded: Int
get() {
- return dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat())
+ return dpToPx(
+ activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat(),
+ activityContext,
+ )
}
init {
@@ -70,7 +74,12 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
setIconDrawable(drawable)
if (!activityContext.isTransientTaskbar) {
- setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
+ setPadding(
+ dpToPx(
+ (activityContext.taskbarSpecsEvaluator.taskbarIconPadding).toFloat(),
+ activityContext,
+ )
+ )
}
setForegroundTint(activityContext.getColor(R.color.all_apps_button_color))
}
@@ -102,29 +111,26 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
return getAllAppsButtonForExpressiveTheme()
}
val shouldSelectTransientIcon =
- isTransientTaskbar || (enableTaskbarPinning() && !activityContext.isThreeButtonNav)
+ isTransientTaskbar ||
+ (enableTaskbarPinning() &&
+ activityContext.taskbarFeatureEvaluator.supportsTransitionToTransientTaskbar)
return if (shouldSelectTransientIcon) R.drawable.ic_transient_taskbar_all_apps_search_button
else R.drawable.ic_taskbar_all_apps_search_button
}
@DrawableRes
private fun getAllAppsButtonForExpressiveTheme(): Int {
- return R.drawable.ic_taskbar_all_apps_search_button_expressive_theme
- }
-
- @DimenRes
- fun getAllAppsButtonTranslationXOffsetForExpressiveTheme(isTransientTaskbar: Boolean): Int {
- return if (isTransientTaskbar) {
- R.dimen.transient_taskbar_all_apps_button_translation_x_offset_for_expressive_theme
+ return if (isTaskbarInMinimalState) {
+ R.drawable.ic_taskbar_minimal_state_all_apps_search_button_expressive_theme
} else {
- R.dimen.taskbar_all_apps_search_button_translation_x_offset_for_expressive_theme
+ R.drawable.ic_taskbar_all_apps_search_button_expressive_theme
}
}
@DimenRes
fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int {
if (Flags.enableGsf()) {
- return getAllAppsButtonTranslationXOffsetForExpressiveTheme(isTransientTaskbar)
+ return R.dimen.taskbar_all_apps_search_button_translation_x_offset_for_expressive_theme
}
return if (isTransientTaskbar) {
R.dimen.transient_taskbar_all_apps_button_translation_x_offset
@@ -133,6 +139,14 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
}
}
+ /** Taskbar minimal state is that taskbar does not host anything other than all apps button. */
+ fun updateTaskbarMinimalState(isInMinimalState: Boolean) {
+ if (isTaskbarInMinimalState != isInMinimalState) {
+ isTaskbarInMinimalState = isInMinimalState
+ setUpIcon()
+ }
+ }
+
private fun onAllAppsButtonTouch(view: View, ev: MotionEvent): Boolean {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
index 08a1b48acf..96dc5c8447 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
@@ -40,7 +40,10 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
override val spaceNeeded: Int
get() {
- return dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat())
+ return dpToPx(
+ activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat(),
+ activityContext,
+ )
}
init {
@@ -53,7 +56,12 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
val drawable = getTaskbarDividerIcon()
setIconDrawable(drawable)
if (!activityContext.isTransientTaskbar) {
- setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
+ setPadding(
+ dpToPx(
+ activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat(),
+ activityContext,
+ )
+ )
}
}
@@ -67,8 +75,8 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
}
@SuppressLint("ClickableViewAccessibility")
- fun setUpCallbacks(callbacks: TaskbarViewCallbacks) {
- setOnLongClickListener(callbacks.taskbarDividerLongClickListener)
- setOnTouchListener(callbacks.taskbarDividerRightClickListener)
+ fun setUpCallbacks(callbacks: TaskbarViewCallbacks?) {
+ setOnLongClickListener(callbacks?.taskbarDividerLongClickListener)
+ setOnTouchListener(callbacks?.taskbarDividerRightClickListener)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
index a14c5a47e6..2de143e979 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
@@ -38,11 +38,17 @@ private constructor(private val taskbarActivityContext: TaskbarActivityContext)
get() = taskbarActivityContext.isTransientTaskbar
val isLandscape: Boolean
- get() = taskbarActivityContext.deviceProfile.isLandscape
+ get() = taskbarActivityContext.deviceProfile.deviceProperties.isLandscape
+
+ val isTnMinimalState: Boolean
+ get() = taskbarActivityContext.isTaskbarInMinimalState
val supportsPinningPopup: Boolean
get() = !hasNavButtons
+ val supportsTransitionToTransientTaskbar: Boolean
+ get() = !hasNavButtons && !taskbarActivityContext.showDesktopTaskbarForFreeformDisplay()
+
fun onDestroy() {
taskbarFeatureEvaluator = null
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
index f1ed6c5ea0..13c878ee6f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
@@ -35,7 +35,7 @@ class TaskbarSpecsEvaluator(
val taskbarIconPadding: Int =
if (
TaskbarIconSpecs.transientOrPinnedTaskbarIconPaddingSize.size > taskbarIconSize.size &&
- !taskbarFeatureEvaluator.hasNavButtons
+ taskbarFeatureEvaluator.supportsTransitionToTransientTaskbar
) {
(TaskbarIconSpecs.iconSize52dp.size - taskbarIconSize.size) / 2
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt b/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt
index 04e41bc3bd..5c5990eb24 100644
--- a/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt
@@ -16,15 +16,34 @@
package com.android.launcher3.taskbar.growth
import android.content.Context
+import android.os.Bundle
+import android.view.View
+import android.view.View.GONE
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.MarginLayoutParams
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.annotation.LayoutRes
+import androidx.core.view.updateLayoutParams
+import com.airbnb.lottie.LottieAnimationView
+import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_GROWTH_NUDGE_OPEN
import com.android.launcher3.taskbar.TaskbarControllers
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.views.ActivityContext
+import com.android.quickstep.util.LottieAnimationColorUtils
import java.io.PrintWriter
-/** Controls nudge lifecycles. */
+/**
+ * Controls nudge lifecycles.
+ *
+ * TODO: b/413718172 - Refactor to reduce code duplication with [TaskbarEduTooltipController].
+ */
class NudgeController(context: Context) : LoggableTaskbarController {
protected val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
@@ -36,26 +55,184 @@ class NudgeController(context: Context) : LoggableTaskbarController {
!activityContext.isTinyTaskbar
}
+ val isNudgeOpen: Boolean
+ get() = nudgeView?.isOpen == true
+
private lateinit var controllers: TaskbarControllers
+ private var nudgeView: NudgeView? = null
+
fun init(controllers: TaskbarControllers) {
this.controllers = controllers
}
- fun maybeShow(payload: NudgePayload) {
- if (!isNudgeEnabled || !DisplayController.isTransientTaskbar(activityContext)) {
+ fun maybeShow(model: NudgePayload) {
+ if (!isNudgeEnabled || !activityContext.isTransientTaskbar) {
return
}
- // TODO: b/398033012 - create and show nudge view based on the payload.
+
+ inflateNudgeContent(R.layout.growth_nudge)
+ nudgeView?.run {
+ allowTouchDismissal = false
+
+ fun updateButton(button: Button, buttonPayload: ButtonPayload?) {
+ if (buttonPayload != null) {
+ button.apply {
+ text = buttonPayload.label
+ setOnClickListener {
+ ActionPerformers.performActions(
+ /*actions=*/ buttonPayload.actions,
+ /*context=*/ activityContext,
+ /*dismissCallback=*/ ::hide,
+ )
+ }
+ }
+ } else {
+ button.visibility = GONE
+ }
+ }
+
+ fun updateImage(image: Image?) {
+ val imageView = requireViewById(R.id.image_view)
+ when (image) {
+ is Image.ResourceId -> {
+ imageView.setImageDrawable(context.getDrawable(image.resId))
+ }
+ null -> imageView.visibility = GONE
+ }
+ }
+
+ fun updateContent() {
+ // Update content.
+ val title = requireViewById(R.id.title)
+ title.text = model.titleText
+ val body = requireViewById(R.id.body)
+ body.text = model.bodyText
+ updateButton(requireViewById(R.id.primary_button), model.primaryButton)
+ updateButton(requireViewById(R.id.secondary_button), model.secondaryButton)
+ updateImage(model.image)
+ }
+
+ fun updateLayout() {
+ content.updateLayoutParams { width = MATCH_PARENT }
+ val sideSpacing =
+ resources.getDimensionPixelSize(R.dimen.nudge_default_position_side_spacing)
+ updateLayoutParams {
+ if (Utilities.isRtl(context.getResources())) {
+ rightMargin = sideSpacing
+ } else {
+ leftMargin = sideSpacing
+ }
+ width = resources.getDimensionPixelSize(R.dimen.nudge_width)
+ }
+ }
+
+ updateContent()
+ updateLayout()
+ show()
+ }
}
/** Closes the current [nudgeView]. */
fun hide() {
- // TODO: b/398033012 - hide the nudge view.
+ nudgeView?.close(true)
}
+ /** Initializes [nudgeView] with content from [contentResId]. */
+ private fun inflateNudgeContent(@LayoutRes contentResId: Int) {
+ val overlayContext = controllers.taskbarOverlayController.requestWindow()
+ val nudgeView =
+ overlayContext.layoutInflater.inflate(
+ R.layout.taskbar_nudge_container,
+ overlayContext.dragLayer,
+ false,
+ ) as NudgeView
+
+ controllers.taskbarAutohideSuspendController.updateFlag(
+ FLAG_AUTOHIDE_SUSPEND_GROWTH_NUDGE_OPEN,
+ true,
+ )
+
+ nudgeView.onCloseCallback = {
+ this.nudgeView = null
+ controllers.taskbarAutohideSuspendController.updateFlag(
+ FLAG_AUTOHIDE_SUSPEND_GROWTH_NUDGE_OPEN,
+ false,
+ )
+ controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
+ }
+ nudgeView.accessibilityDelegate = createAccessibilityDelegate()
+
+ overlayContext.layoutInflater.inflate(contentResId, nudgeView.content, true)
+ this.nudgeView = nudgeView
+ }
+
+ private fun createAccessibilityDelegate() =
+ object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?,
+ ): Boolean {
+ if (action == R.id.close) {
+ hide()
+ return true
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+
+ override fun onPopulateAccessibilityEvent(host: View, event: AccessibilityEvent) {
+ super.onPopulateAccessibilityEvent(host, event)
+ if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ event.text.add(host.context?.getText(R.string.nudge_a11y_title))
+ }
+ }
+
+ override fun onInitializeAccessibilityNodeInfo(
+ host: View,
+ info: AccessibilityNodeInfo,
+ ) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ info.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ R.id.close,
+ host.context?.getText(R.string.nudge_a11y_close),
+ )
+ )
+ }
+ }
+
override fun dumpLogs(prefix: String?, pw: PrintWriter?) {
pw?.println(prefix + "NudgeController:")
pw?.println("$prefix\tisNudgeEnabled=$isNudgeEnabled")
+ pw?.println("$prefix\tisOpen=$isNudgeOpen")
}
}
+
+/**
+ * Maps colors in the dark-themed Lottie assets to their light-themed equivalents.
+ *
+ * For instance, `".blue100" to R.color.lottie_blue400` means objects that are material blue100 in
+ * dark theme should be changed to material blue400 in light theme.
+ */
+private val DARK_TO_LIGHT_COLORS =
+ mapOf(
+ ".blue100" to R.color.lottie_blue400,
+ ".blue400" to R.color.lottie_blue600,
+ ".green100" to R.color.lottie_green400,
+ ".green400" to R.color.lottie_green600,
+ ".grey300" to R.color.lottie_grey600,
+ ".grey400" to R.color.lottie_grey700,
+ ".grey800" to R.color.lottie_grey200,
+ ".red400" to R.color.lottie_red600,
+ ".yellow100" to R.color.lottie_yellow400,
+ ".yellow400" to R.color.lottie_yellow600,
+ )
+
+private fun LottieAnimationView.supportLightTheme() {
+ if (Utilities.isDarkTheme(context)) {
+ return
+ }
+
+ LottieAnimationColorUtils.updateToColorResources(this, DARK_TO_LIGHT_COLORS, context.theme)
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt b/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt
index 7498cbc9b0..7c167b959d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/growth/NudgePayload.kt
@@ -26,8 +26,6 @@ sealed interface Action {
sealed class Image {
data class ResourceId(val resId: Int) : Image()
-
- data class Url(val url: String) : Image()
}
data class ButtonPayload(val label: String, val actions: List)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index 3712a76eab..918d5cf71e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -90,7 +90,7 @@ abstract class AbstractNavButtonLayoutter(
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_margin)
nearestTouchFrameLayoutParams.marginStart = phoneOrPortraitSetupMargin
nearestTouchFrameLayoutParams.bottomMargin =
- if (!deviceProfile.isLandscape) 0
+ if (!deviceProfile.deviceProperties.isLandscape) 0
else
phoneOrPortraitSetupMargin -
resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index a199dba0bd..17e63e8bd3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -90,7 +90,7 @@ class NavButtonLayoutFactory {
)
}
isPhoneNavMode -> {
- if (!deviceProfile.isLandscape) {
+ if (!deviceProfile.deviceProperties.isLandscape) {
navButtonsView.setIsVertical(false)
PhonePortraitNavLayoutter(
resources,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
index 9f7f07e773..00ba40c7f9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -25,6 +25,7 @@ import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Space
import com.android.launcher3.R
+import com.android.launcher3.Utilities
import com.android.launcher3.taskbar.TaskbarActivityContext
open class PhoneLandscapeNavLayoutter(
@@ -34,7 +35,7 @@ open class PhoneLandscapeNavLayoutter(
startContextualContainer: ViewGroup,
imeSwitcher: ImageView?,
a11yButton: ImageView?,
- space: Space?
+ space: Space?,
) :
AbstractNavButtonLayoutter(
resources,
@@ -43,11 +44,11 @@ open class PhoneLandscapeNavLayoutter(
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
- val totalHeight = context.deviceProfile.heightPx
+ val totalHeight = context.deviceProfile.deviceProperties.heightPx
val homeButtonHeight =
resources.getDimensionPixelSize(R.dimen.taskbar_phone_home_button_size)
val roundedCornerContentMargin =
@@ -112,9 +113,15 @@ open class PhoneLandscapeNavLayoutter(
open fun addThreeButtons() {
// Swap recents and back button
- navButtonContainer.addView(recentsButton)
- navButtonContainer.addView(homeButton)
- navButtonContainer.addView(backButton)
+ if (Utilities.isRtl(resources)) {
+ navButtonContainer.addView(backButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(recentsButton)
+ } else {
+ navButtonContainer.addView(recentsButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(backButton)
+ }
}
open fun repositionContextualButtons(buttonSize: Int) {
@@ -129,14 +136,14 @@ open class PhoneLandscapeNavLayoutter(
buttonSize,
roundedCornerContentMargin + contentPadding,
0,
- Gravity.TOP
+ Gravity.TOP,
)
repositionContextualContainer(
endContextualContainer,
buttonSize,
0,
roundedCornerContentMargin + contentPadding,
- Gravity.BOTTOM
+ Gravity.BOTTOM,
)
if (imeSwitcher != null) {
@@ -155,7 +162,7 @@ open class PhoneLandscapeNavLayoutter(
buttonSize: Int,
barAxisMarginTop: Int,
barAxisMarginBottom: Int,
- gravity: Int
+ gravity: Int,
) {
val contextualContainerParams = FrameLayout.LayoutParams(MATCH_PARENT, buttonSize)
contextualContainerParams.apply {
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
index 5b24ebfe1d..833a309e83 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -34,7 +34,7 @@ class PhonePortraitNavLayoutter(
startContextualContainer: ViewGroup,
imeSwitcher: ImageView?,
a11yButton: ImageView?,
- space: Space?
+ space: Space?,
) :
AbstractNavButtonLayoutter(
resources,
@@ -43,11 +43,11 @@ class PhonePortraitNavLayoutter(
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
- val totalWidth = context.deviceProfile.widthPx
+ val totalWidth = context.deviceProfile.deviceProperties.widthPx
val homeButtonWidth =
resources.getDimensionPixelSize(R.dimen.taskbar_phone_home_button_size)
val roundedCornerContentMargin =
@@ -63,7 +63,7 @@ class PhonePortraitNavLayoutter(
val navContainerParams =
FrameLayout.LayoutParams(
navButtonContainerWidth.toInt(),
- ViewGroup.LayoutParams.MATCH_PARENT
+ ViewGroup.LayoutParams.MATCH_PARENT,
)
navContainerParams.apply {
topMargin = 0
@@ -120,14 +120,14 @@ class PhonePortraitNavLayoutter(
contextualButtonWidth.toInt(),
roundedCornerContentMargin + contentPadding,
0,
- Gravity.START
+ Gravity.START,
)
repositionContextualContainer(
endContextualContainer,
contextualButtonWidth.toInt(),
0,
roundedCornerContentMargin + contentPadding,
- Gravity.END
+ Gravity.END,
)
startContextualContainer.addView(space, MATCH_PARENT, MATCH_PARENT)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
index f0b47f4caa..cf6f89857a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
@@ -24,6 +24,7 @@ import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Space
import com.android.launcher3.R
+import com.android.launcher3.Utilities
class PhoneSeascapeNavLayoutter(
resources: Resources,
@@ -32,7 +33,7 @@ class PhoneSeascapeNavLayoutter(
startContextualContainer: ViewGroup,
imeSwitcher: ImageView?,
a11yButton: ImageView?,
- space: Space?
+ space: Space?,
) :
PhoneLandscapeNavLayoutter(
resources,
@@ -41,14 +42,20 @@ class PhoneSeascapeNavLayoutter(
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
) {
override fun addThreeButtons() {
// Flip ordering of back and recents buttons
- navButtonContainer.addView(backButton)
- navButtonContainer.addView(homeButton)
- navButtonContainer.addView(recentsButton)
+ if (Utilities.isRtl(resources)) {
+ navButtonContainer.addView(recentsButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(backButton)
+ } else {
+ navButtonContainer.addView(backButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(recentsButton)
+ }
}
override fun repositionContextualButtons(buttonSize: Int) {
@@ -63,14 +70,14 @@ class PhoneSeascapeNavLayoutter(
buttonSize,
roundedCornerContentMargin + contentPadding,
0,
- Gravity.TOP
+ Gravity.TOP,
)
repositionContextualContainer(
endContextualContainer,
buttonSize,
0,
roundedCornerContentMargin + contentPadding,
- Gravity.BOTTOM
+ Gravity.BOTTOM,
)
startContextualContainer.addView(space, MATCH_PARENT, MATCH_PARENT)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index eb3fdeb99e..24998d4178 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -59,7 +59,9 @@ class SetupNavLayoutter(
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
val SUWTheme = SystemProperties.get(SUW_THEME_SYSTEM_PROPERTY, "")
- if (SUWTheme == GLIF_EXPRESSIVE_THEME || SUWTheme == GLIF_EXPRESSIVE_LIGHT_THEME) {
+ val expressiveThemeEnabled =
+ SUWTheme == GLIF_EXPRESSIVE_THEME || SUWTheme == GLIF_EXPRESSIVE_LIGHT_THEME
+ if (expressiveThemeEnabled && !context.isSimpleViewEnabled) {
return
}
// Since setup wizard only has back button enabled, it looks strange to be
@@ -70,15 +72,15 @@ class SetupNavLayoutter(
val deviceProfile: DeviceProfile = context.deviceProfile
navButtonsLayoutParams.marginEnd = 0
- navButtonsLayoutParams.gravity = Gravity.START
+ navButtonsLayoutParams.gravity = Gravity.START or Gravity.CENTER_VERTICAL
context.setTaskbarWindowSize(context.setupWindowSize)
// If SUW is on a large screen device that is landscape (or has a square aspect
// ratio) the back button has to be placed accordingly
if (
- deviceProfile.isTablet && deviceProfile.isLandscape ||
- (deviceProfile.aspectRatio > SQUARE_ASPECT_RATIO_BOTTOM_BOUND &&
- deviceProfile.aspectRatio < SQUARE_ASPECT_RATIO_UPPER_BOUND)
+ deviceProfile.deviceProperties.isTablet && deviceProfile.deviceProperties.isLandscape ||
+ (deviceProfile.deviceProperties.aspectRatio > SQUARE_ASPECT_RATIO_BOTTOM_BOUND &&
+ deviceProfile.deviceProperties.aspectRatio < SQUARE_ASPECT_RATIO_UPPER_BOUND)
) {
navButtonsLayoutParams.marginStart =
resources.getDimensionPixelSize(R.dimen.taskbar_back_button_suw_start_margin)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index a59e8a847b..bef432e668 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -35,7 +35,7 @@ class TaskbarNavLayoutter(
startContextualContainer: ViewGroup,
imeSwitcher: ImageView?,
a11yButton: ImageView?,
- space: Space?
+ space: Space?,
) :
AbstractNavButtonLayoutter(
resources,
@@ -44,7 +44,7 @@ class TaskbarNavLayoutter(
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
@@ -69,7 +69,7 @@ class TaskbarNavLayoutter(
val navButtonParams =
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.MATCH_PARENT
+ ViewGroup.LayoutParams.MATCH_PARENT,
)
navButtonParams.apply {
gravity = Gravity.END or Gravity.CENTER_VERTICAL
@@ -101,7 +101,7 @@ class TaskbarNavLayoutter(
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
- if (!context.deviceProfile.isGestureMode) {
+ if (!context.deviceProfile.deviceProperties.isGestureMode) {
val contextualMargin =
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_padding)
repositionContextualContainer(endContextualContainer, WRAP_CONTENT, 0, 0, Gravity.END)
@@ -110,7 +110,7 @@ class TaskbarNavLayoutter(
WRAP_CONTENT,
contextualMargin,
contextualMargin,
- Gravity.START
+ Gravity.START,
)
if (imeSwitcher != null) {
@@ -122,7 +122,7 @@ class TaskbarNavLayoutter(
val imeSwitcherButtonParams =
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
+ ViewGroup.LayoutParams.MATCH_PARENT,
)
imeSwitcherButtonParams.apply {
marginStart = imeStartMargin
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 6db300bf59..c964474912 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -16,12 +16,14 @@
package com.android.launcher3.taskbar.overlay;
import android.content.Context;
+import android.graphics.Point;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.taskbar.BaseTaskbarContext;
@@ -57,13 +59,14 @@ public class TaskbarOverlayContext extends BaseTaskbarContext {
Context windowContext,
TaskbarActivityContext taskbarContext,
TaskbarControllers controllers) {
- super(windowContext, taskbarContext.isPrimaryDisplay());
+ super(windowContext, taskbarContext.getDisplayId(), taskbarContext.isPrimaryDisplay());
mTaskbarContext = taskbarContext;
mOverlayController = controllers.taskbarOverlayController;
mDragController = new TaskbarDragController(this);
mDragController.init(controllers);
mDragLayer = new TaskbarOverlayDragLayer(this);
mStashedTaskbarHeight = controllers.taskbarStashController.getStashedHeight();
+ updateBlurStyle();
mUiController = controllers.uiController;
onViewCreated();
@@ -125,6 +128,20 @@ public class TaskbarOverlayContext extends BaseTaskbarContext {
return mDragLayer.findViewById(R.id.apps_view);
}
+ @Override
+ public boolean isAllAppsBackgroundBlurEnabled() {
+ return Flags.allAppsBlur() && mOverlayController != null
+ && mOverlayController.isBackgroundBlurEnabled();
+ }
+
+ /** Apply the blur or blur fallback style to the current theme. */
+ private void updateBlurStyle() {
+ if (!Flags.allAppsBlur()) {
+ return;
+ }
+ getTheme().applyStyle(getAllAppsBlurStyleResId(), true);
+ }
+
@Override
public View.OnClickListener getItemOnClickListener() {
return mTaskbarContext.getItemOnClickListener();
@@ -166,6 +183,11 @@ public class TaskbarOverlayContext extends BaseTaskbarContext {
return mTaskbarContext.isInDesktopMode();
}
+ @Override
+ public boolean isTaskbarShowingDesktopTasks() {
+ return mTaskbarContext.isTaskbarShowingDesktopTasks();
+ }
+
@Override
public boolean showLockedTaskbarOnHome() {
return mTaskbarContext.showLockedTaskbarOnHome();
@@ -177,8 +199,18 @@ public class TaskbarOverlayContext extends BaseTaskbarContext {
}
@Override
- public boolean isPrimaryDisplay() {
- return mTaskbarContext.isPrimaryDisplay();
+ public Point getScreenSize() {
+ return mTaskbarContext.getScreenSize();
+ }
+
+ @Override
+ public int getDisplayHeight() {
+ return mTaskbarContext.getDisplayHeight();
+ }
+
+ @Override
+ public void notifyConfigChanged() {
+ mTaskbarContext.notifyConfigChanged();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index 178fe9d6b6..4905287356 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -15,9 +15,11 @@
*/
package com.android.launcher3.taskbar.overlay;
+import static android.os.Trace.TRACE_TAG_APP;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.window.DesktopModeFlags.ENABLE_TASKBAR_OVERFLOW;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
@@ -26,8 +28,12 @@ import static com.android.launcher3.LauncherState.ALL_APPS;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
+import android.gui.EarlyWakeupInfo;
+import android.os.Binder;
+import android.os.Trace;
import android.util.Log;
import android.view.AttachedSurfaceControl;
+import android.view.CrossWindowBlurListeners;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -98,6 +104,10 @@ public final class TaskbarOverlayController {
private TaskbarControllers mControllers; // Initialized in init.
// True if we have alerted surface flinger of an expensive call for blur.
private boolean mInEarlyWakeUp;
+ /**
+ * Token for early wakeup requests to SurfaceFlinger.
+ */
+ private EarlyWakeupInfo mEarlyWakeupInfo = new EarlyWakeupInfo();
public TaskbarOverlayController(
TaskbarActivityContext taskbarContext, DeviceProfile launcherDeviceProfile) {
@@ -108,6 +118,8 @@ public final class TaskbarOverlayController {
mLauncherDeviceProfile = launcherDeviceProfile;
mMaxBlurRadius = mTaskbarContext.getResources().getDimensionPixelSize(
R.dimen.max_depth_blur_radius_enhanced);
+ mEarlyWakeupInfo.token = new Binder();
+ mEarlyWakeupInfo.trace = TaskbarOverlayController.class.getName();
}
/** Initialize the controller. */
@@ -121,7 +133,7 @@ public final class TaskbarOverlayController {
*/
public TaskbarOverlayContext requestWindow() {
if (mOverlayContext == null) {
- mOverlayContext = new TaskbarOverlayContext(
+ mOverlayContext = TaskbarOverlayContextFactory.newInstance(mWindowContext).create(
mWindowContext, mTaskbarContext, mControllers);
}
@@ -177,7 +189,7 @@ public final class TaskbarOverlayController {
return mLauncherDeviceProfile;
}
- /** Updates {@link DeviceProfile} instance for Taskbar's overlay window. */
+ /** Updates {@link deviceprofile} instance for Taskbar's overlay window. */
public void updateLauncherDeviceProfile(DeviceProfile dp) {
mLauncherDeviceProfile = dp;
Optional.ofNullable(mOverlayContext).ifPresent(c -> {
@@ -227,6 +239,11 @@ public final class TaskbarOverlayController {
radius = 0;
// intentionally falling through in case a non-0 blur was previously set.
}
+ if (!CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled()) {
+ Log.d(TAG, "setBackgroundBlurRadius: disabled, setting to 0");
+ radius = 0;
+ // intentionally falling through in case a non-0 blur was previously set.
+ }
if (mOverlayContext == null) {
Log.w(TAG, "setBackgroundBlurRadius: no overlay context");
return;
@@ -252,31 +269,42 @@ public final class TaskbarOverlayController {
return;
}
Log.v(TAG, "setBackgroundBlurRadius: " + radius);
- SurfaceControl.Transaction transaction =
+ final SurfaceControl.Transaction transaction =
new SurfaceControl.Transaction().setBackgroundBlurRadius(surfaceControl, radius);
- // Set early wake-up flags when we know we're executing an expensive operation, this way
- // SurfaceFlinger will adjust its internal offsets to avoid jank.
- boolean wantsEarlyWakeUp = radius > 0 && radius < mMaxBlurRadius;
- if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
- Log.d(TAG, "setBackgroundBlurRadius: setting early wakeup");
- try {
- transaction.setEarlyWakeupStart();
- } catch (NoSuchMethodError e) {
- // LC-Ignored: wtf?
+ try (transaction) {
+ // Set early wake-up flags when we know we're executing an expensive operation, this way
+ // SurfaceFlinger will adjust its internal offsets to avoid jank.
+ boolean wantsEarlyWakeUp = radius > 0 && radius < mMaxBlurRadius;
+ if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
+ Log.d(TAG, "setBackgroundBlurRadius: setting early wakeup with token "
+ + mEarlyWakeupInfo);
+ Trace.instantForTrack(TRACE_TAG_APP, TAG, "notifyRendererForGpuLoadUp");
+ dragLayerViewRoot.notifyRendererForGpuLoadUp("setBackgroundBlurRadius");
+ try {
+ transaction.setEarlyWakeupStart(mEarlyWakeupInfo);
+ } catch (NoSuchMethodError e) {
+ // LC-Ignored: wtf?
+ }
+ mInEarlyWakeUp = true;
+ } else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
+ Log.d(TAG, "setBackgroundBlurRadius: clearing early wakeup with token "
+ + mEarlyWakeupInfo);
+ try {
+ transaction.setEarlyWakeupEnd(mEarlyWakeupInfo);
+ } catch (NoSuchMethodError e) {
+ // LC-Ignored: wtf?
+ }
+ mInEarlyWakeUp = false;
}
- mInEarlyWakeUp = true;
- } else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
- Log.d(TAG, "setBackgroundBlurRadius: clearing early wakeup");
- try {
- transaction.setEarlyWakeupEnd();
- } catch (NoSuchMethodError e) {
- // LC-Ignored: wtf?
- }
- mInEarlyWakeUp = false;
- }
- rootSurfaceControl.applyTransactionOnDraw(transaction);
+ rootSurfaceControl.applyTransactionOnDraw(transaction);
+ }
+ }
+
+ boolean isBackgroundBlurEnabled() {
+ return BlurUtils.supportsBlursOnWindows()
+ && CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled();
}
/**
@@ -300,7 +328,7 @@ public final class TaskbarOverlayController {
@Override
protected void handleClose(boolean animate) {
if (!mIsOpen) return;
- if (Flags.taskbarOverflow()) {
+ if (ENABLE_TASKBAR_OVERFLOW.isTrue()) {
// Mark the view closed before attempting to remove it, so the drag layer does not
// schedule another call to close. Needed for taskbar overflow in case the KQS
// view shown for taskbar overflow needs to be reshown - delayed close call would
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index 41694ecee5..9ecffdc6b3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -71,6 +71,7 @@ public class TaskbarOverlayDragLayer extends
@Override
public void recreateControllers() {
+ super.recreateControllers();
List controllers = new ArrayList<>();
controllers.add(mContainer.getDragController());
controllers.addAll(mTouchControllers);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 966ae8bff6..eec0009437 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -39,14 +39,11 @@ import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
@@ -94,7 +91,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Path mRingPath = new Path();
private final int mNormalizedIconSize;
- private final Path mShapePath;
+ private Path mShapePath;
private final Matrix mTmpMatrix = new Matrix();
private final BlurMaskFilter mShadowFilter;
@@ -281,16 +278,6 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
invalidate();
}
- /**
- * prepares prediction icon for usage after bind
- */
- public void finishBinding(OnLongClickListener longClickListener) {
- setOnLongClickListener(longClickListener);
- ((CellLayoutLayoutParams) getLayoutParams()).canReorder = false;
- setTextVisibility(false);
- verifyHighRes();
- }
-
@Override
public void getIconBounds(Rect outBounds) {
super.getIconBounds(outBounds);
@@ -339,6 +326,9 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
}
private void updateRingPath() {
+ mShapePath = ThemeManager.INSTANCE.get(mContext)
+ .getIconShape()
+ .getPath(mNormalizedIconSize);
mRingPath.reset();
mTmpMatrix.reset();
mTmpMatrix.setTranslate(getOutlineOffsetX(), getOutlineOffsetY());
@@ -457,19 +447,6 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
};
}
- /**
- * Creates and returns a new instance of PredictedAppIcon from WorkspaceItemInfo
- */
- public static PredictedAppIcon createIcon(ViewGroup parent, WorkspaceItemInfo info) {
- PredictedAppIcon icon = (PredictedAppIcon) LayoutInflater.from(parent.getContext())
- .inflate(R.layout.predicted_app_icon, parent, false);
- icon.applyFromWorkspaceItem(info);
- Launcher launcher = Launcher.getLauncher(parent.getContext());
- icon.setOnClickListener(launcher.getItemOnClickListener());
- icon.setOnFocusChangeListener(launcher.getFocusHandler());
- return icon;
- }
-
private class AnimColorHolder {
public final AnimatedFloat progress = new AnimatedFloat(this::onUpdate, 1);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index acec8d6b5a..2b67c232be 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
@@ -23,15 +24,18 @@ import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE;
import static com.android.launcher3.Flags.enableExpressiveDismissTaskMotion;
+import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE;
import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
import static com.android.launcher3.LauncherSettings.Animation.VIEW_BACKGROUND;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.FLAG_SKIP_STATE_ANNOUNCEMENT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -51,6 +55,7 @@ import static com.android.launcher3.popup.SystemShortcut.BUBBLE_SHORTCUT;
import static com.android.launcher3.popup.SystemShortcut.DONT_SUGGEST_APP;
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
import static com.android.launcher3.popup.SystemShortcut.PRIVATE_PROFILE_INSTALL;
+import static com.android.launcher3.popup.SystemShortcut.REMOVE;
import static com.android.launcher3.popup.SystemShortcut.UNINSTALL_APP;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.ALL_APPS_PAGE_PROGRESS_INDEX;
@@ -66,6 +71,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -81,15 +87,12 @@ import android.graphics.RectF;
import android.hardware.display.DisplayManager;
import android.media.permission.SafeCloseable;
import android.os.Build;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.View;
@@ -110,6 +113,7 @@ import com.android.app.viewcapture.ViewCaptureFactory;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
+import com.android.launcher3.GestureNavContract;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -131,9 +135,9 @@ import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
-import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.PredictedContainerInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
import com.android.launcher3.statehandlers.DepthController;
@@ -163,6 +167,7 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.ObjectWrapper;
+import com.android.launcher3.util.OverviewCommandHelperProtoLogProxy;
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.RunnableList;
@@ -173,6 +178,7 @@ import com.android.launcher3.util.StableViewInfo;
import com.android.launcher3.util.StartActivityParams;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.FloatingIconView;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
@@ -180,6 +186,8 @@ import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService.TISBinder;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.AsyncClockEventDelegate;
import com.android.quickstep.util.LauncherUnfoldAnimationController;
@@ -198,6 +206,7 @@ import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.TaskView;
import com.android.systemui.animation.back.FlingOnBackAnimationCallback;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.unfold.RemoteUnfoldSharedComponent;
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -209,6 +218,7 @@ import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import kotlin.Unit;
@@ -236,7 +246,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t";
- private FixedContainerItems mAllAppsPredictions;
+ private PredictedContainerInfo mAllAppsPredictions;
private HotseatPredictionController mHotseatPredictionController;
private DepthController mDepthController;
private QuickstepTransitionManager mAppTransitionManager;
@@ -274,6 +284,8 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged;
+ private boolean mOverviewBlurEnabled;
+
private final TaskViewRecentsTouchContext mTaskViewRecentsTouchContext =
new TaskViewRecentsTouchContext() {
@Override
@@ -293,17 +305,16 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
}
};
- public static QuickstepLauncher getLauncher(Context context) {
- return fromContext(context);
- }
-
@Override
protected void setupViews() {
getAppWidgetHolder().setOnViewCreationCallback(new QuickstepInteractionHandler(this));
+ mDepthController = new DepthController(this);
+ mOverviewBlurEnabled = isOverviewBackgroundBlurEnabled();
+ getTheme().applyStyle(getOverviewBlurStyleResId(), true);
super.setupViews();
mActionsView = findViewById(R.id.overview_actions_view);
- RecentsView,?> overviewPanel = getOverviewPanel();
+ RecentsView, LauncherState> overviewPanel = getOverviewPanel();
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
mSplitSelectStateController =
new SplitSelectStateController(this, getStateManager(),
@@ -331,7 +342,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
}
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
- mDepthController = new DepthController(this);
+
if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(this)) {
mSplitSelectStateController.initSplitFromDesktopController(this);
}
@@ -360,16 +371,16 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
if (mAllAppsPredictions != null
&& (info.itemType == ITEM_TYPE_APPLICATION
|| info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) {
- int count = mAllAppsPredictions.items.size();
+ List items = mAllAppsPredictions.getContents();
+ int count = items.size();
for (int i = 0; i < count; i++) {
- ItemInfo targetInfo = mAllAppsPredictions.items.get(i);
+ ItemInfo targetInfo = items.get(i);
if (targetInfo.itemType == info.itemType
&& targetInfo.user.equals(info.user)
&& Objects.equals(targetInfo.getIntent(), info.getIntent())) {
logger.withRank(i);
break;
}
-
}
}
logger.log(LAUNCHER_APP_LAUNCH_TAP);
@@ -419,31 +430,19 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
@Override
public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) {
- // Only pause is taskbar controller is not present until the transition (if it exists) ends
- mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
PredictionRowView> predictionRowView =
getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class);
// Pause the prediction row updates until the transition (if it exists) ends.
predictionRowView.setPredictionUiUpdatePaused(true);
RunnableList result = super.startActivitySafely(v, intent, item);
if (result == null) {
- mHotseatPredictionController.setPauseUIUpdate(false);
predictionRowView.setPredictionUiUpdatePaused(false);
} else {
- result.add(() -> {
- mHotseatPredictionController.setPauseUIUpdate(false);
- predictionRowView.setPredictionUiUpdatePaused(false);
- });
+ result.add(() -> predictionRowView.setPredictionUiUpdatePaused(false));
}
return result;
}
- @Override
- public void startBinding() {
- super.startBinding();
- mHotseatPredictionController.verifyUIUpdateNotPaused();
- }
-
@Override
protected void onActivityFlagsChanged(int changeBits) {
if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
@@ -469,6 +468,31 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
super.showAllAppsFromIntent(alreadyOnHome);
}
+ @Override
+ public boolean isAllAppsBackgroundBlurEnabled() {
+ return mDepthController != null && mDepthController.isCrossWindowBlursEnabled()
+ && Flags.allAppsBlur();
+ }
+
+ @Override
+ public boolean isOverviewBackgroundBlurEnabled() {
+ return mDepthController != null && mDepthController.isCrossWindowBlursEnabled()
+ && enableOverviewBackgroundWallpaperBlur();
+ }
+
+ /** Apply the blur or blur fallback style to the current theme. */
+ public void updateBlurStyle() {
+ if (enableOverviewBackgroundWallpaperBlur()) {
+ if (isOverviewBackgroundBlurEnabled() != mOverviewBlurEnabled) {
+ mWallpaperThemeManager.recreateToUpdateTheme();
+ }
+ } else if (Flags.allAppsBlur()) {
+ // For all apps, we only need to update the scrim, which draws the panel. But if the
+ // activity was recreated above, this is unnecessary.
+ getAppsView().invalidateHeader();
+ }
+ }
+
protected void onItemClicked(View view) {
if (!mSplitToWorkspaceController.handleSecondAppSelectionForSplit(view)) {
super.getItemOnClickListener().onClick(view);
@@ -481,7 +505,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
}
@Override
- public Stream getSupportedShortcuts() {
+ public Stream getSupportedShortcuts(int container) {
// Order matters as it affects order of appearance in popup container
List shortcuts = new ArrayList(Arrays.asList(
APP_INFO, WellbeingModel.SHORTCUT_FACTORY, mHotseatPredictionController));
@@ -489,12 +513,15 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
shortcuts.addAll(getSplitShortcuts());
shortcuts.add(WIDGETS);
shortcuts.add(INSTALL);
+ if (Flags.enableLongPressRemoveShortcut()
+ && (container == CONTAINER_HOTSEAT || container == CONTAINER_DESKTOP
+ || /* Folder */ container > 0)) {
+ shortcuts.add(REMOVE);
+ }
+ shortcuts.add(DONT_SUGGEST_APP);
if (Flags.enablePrivateSpaceInstallShortcut()) {
shortcuts.add(PRIVATE_PROFILE_INSTALL);
}
- if (Flags.enableShortcutDontSuggestApp()) {
- shortcuts.add(DONT_SUGGEST_APP);
- }
if (Flags.enablePrivateSpace()) {
shortcuts.add(UNINSTALL_APP);
}
@@ -505,7 +532,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
}
private List> getSplitShortcuts() {
- if (!mDeviceProfile.isTablet || mSplitSelectStateController.isSplitSelectActive()) {
+ if (!mDeviceProfile.getDeviceProperties().isTablet() || mSplitSelectStateController.isSplitSelectActive()) {
return Collections.emptyList();
}
RecentsView recentsView = getOverviewPanel();
@@ -547,17 +574,20 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
}
@Override
- public void bindExtraContainerItems(FixedContainerItems item) {
- if (item.containerId == Favorites.CONTAINER_PREDICTION) {
- mAllAppsPredictions = item;
- PredictionRowView> predictionRowView =
- getAppsView().getFloatingHeaderView().findFixedRowByType(
- PredictionRowView.class);
- predictionRowView.setPredictedApps(item.items);
- } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
- mHotseatPredictionController.setPredictedItems(item);
- } else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) {
- getWidgetPickerDataProvider().setWidgetRecommendations(item.items);
+ public void bindPredictedContainerInfo(PredictedContainerInfo info) {
+ super.bindPredictedContainerInfo(info);
+ switch (info.id) {
+ case Favorites.CONTAINER_ALL_APPS_PREDICTION:
+ mAllAppsPredictions = info;
+ getAppsView().getFloatingHeaderView().findFixedRowByType(
+ PredictionRowView.class).setPredictedApps(info.getContents());
+ break;
+ case Favorites.CONTAINER_HOTSEAT_PREDICTION:
+ mHotseatPredictionController.setPredictedItems(info);
+ break;
+ case Favorites.CONTAINER_WIDGETS_PREDICTION:
+ getWidgetPickerDataProvider().setWidgetRecommendations(info.getContents());
+ break;
}
}
@@ -659,6 +689,15 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
}
}
+ @Override
+ protected void setTitle(@NonNull LauncherState state) {
+ if (state.hasFlag(FLAG_SKIP_STATE_ANNOUNCEMENT)) {
+ // Prevent accessibility title update announcement
+ getWindow().getAttributes().accessibilityTitle = getString(state.getTitle());
+ }
+ super.setTitle(state);
+ }
+
@Override
public TouchController[] createTouchControllers() {
NavigationMode mode = DisplayController.getNavigationMode(this);
@@ -693,8 +732,9 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
break;
}
- if (!getDeviceProfile().isMultiWindowMode) {
- list.add(new StatusBarTouchController(this));
+ if (!getDeviceProfile().getDeviceProperties().isMultiWindowMode()) {
+ list.add(new StatusBarTouchController(
+ this, () -> this.isInState(LauncherState.NORMAL)));
}
if (enableExpressiveDismissTaskMotion()) {
@@ -874,11 +914,26 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
@Override
protected void onNewIntent(Intent intent) {
+ boolean intentHasGnc = GestureNavContract.canBuildFromIntent(intent);
super.onNewIntent(intent);
OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper();
if (overviewCommandHelper != null) {
overviewCommandHelper.clearPendingCommands();
}
+ if (RecentsWindowFlags.getEnableOverviewInWindow() && !intentHasGnc) {
+ RecentsWindowManager defaultRecentsWindowManager =
+ RecentsWindowManager.REPOSITORY_INSTANCE.get(this).get(DEFAULT_DISPLAY);
+ if (defaultRecentsWindowManager != null) {
+ defaultRecentsWindowManager.cleanupRecentsWindow();
+ }
+ }
+ }
+
+ @Override
+ protected void logOnNewIntent(boolean alreadyOnHome, boolean shouldMoveToDefaultScreen,
+ String action, boolean internalStateHandled) {
+ OverviewCommandHelperProtoLogProxy.logOnNewIntent(alreadyOnHome, shouldMoveToDefaultScreen,
+ action, internalStateHandled);
}
public QuickstepTransitionManager getAppTransitionManager() {
@@ -897,7 +952,9 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
@Override
protected void handleGestureContract(Intent intent) {
- if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) {
+ if (GestureNavContract.isContractEnabled(intent)
+ && (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()
+ || RecentsWindowFlags.getEnableOverviewInWindow())) {
super.handleGestureContract(intent);
}
}
@@ -927,7 +984,8 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
if (mEnableWidgetDepth) {
getDepthController().widgetDepth.setValue(Utilities.mapToRange(
- progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth, EMPHASIZED));
+ progress, 0f, 1f, 0f,
+ getDeviceProfile().getBottomSheetProfile().getBottomSheetDepth(), EMPHASIZED));
}
}
@@ -971,6 +1029,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
mActiveOnBackAnimationCallback.onBackStarted(backEvent);
}
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Override
public void onBackInvokedCompat() {
// Recreate mActiveOnBackAnimationCallback if necessary to avoid NPE
@@ -1273,7 +1332,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
}
activityOptions.options.setLaunchDisplayId(
(v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
- : Display.DEFAULT_DISPLAY);
+ : DEFAULT_DISPLAY);
Utilities.allowBGLaunch(activityOptions.options);
return activityOptions;
}
@@ -1294,8 +1353,8 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
@Override
@BinderThread
- public void enterStageSplitFromRunningApp(boolean leftOrTop) {
- mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop);
+ public void enterStageSplitFromRunningApp(boolean leftOrTop, int displayId) {
+ mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop, displayId);
}
@Override
@@ -1384,6 +1443,11 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
.isInDesktopModeAndNotInOverview(getDisplayId());
}
+ @Override
+ public boolean shouldShowHomeBehindDesktop() {
+ return DesktopState.fromContext(this).getShouldShowHomeBehindDesktop();
+ }
+
@Override
public void dispatchDeviceProfileChanged() {
super.dispatchDeviceProfileChanged();
@@ -1406,7 +1470,9 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
/* callback= */ success -> mSplitSelectStateController.resetState(),
/* freezeTaskList= */ false,
- splitTask.getSplitBounds().snapPosition,
+ splitTask.getSplitBounds() == null
+ ? SNAP_TO_2_50_50
+ : splitTask.getSplitBounds().snapPosition,
remoteTransition);
}
@@ -1563,4 +1629,20 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
public void returnToHomescreen() {
getStateManager().goToState(LauncherState.NORMAL);
}
+
+ @Override
+ public int getOverviewBlurStyleResId() {
+ return isOverviewBackgroundBlurEnabled() ? R.style.OverviewBlurStyle
+ : R.style.OverviewBlurFallbackStyle;
+ }
+
+ @Override
+ public LauncherActivityInterface getContainerInterface() {
+ return LauncherActivityInterface.INSTANCE;
+ }
+
+ @Override
+ public SplitSelectStateController getSplitSelectStateController() {
+ return mSplitSelectStateController;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index 0d5ff2b67e..846c850a18 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -20,6 +20,7 @@ import static com.android.launcher3.uioverrides.QuickstepAppWidgetHostProvider.g
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.widget.ListenableAppWidgetHost.getWidgetHolderExecutor;
+import android.appwidget.AppWidgetEvent;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
@@ -44,6 +45,8 @@ import dagger.assisted.AssistedInject;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer;
/**
@@ -57,6 +60,10 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
AppWidgetHostView::updateAppWidget;
private static final UpdateKey KEY_VIEW_DATA_CHANGED =
AppWidgetHostView::onViewDataChanged;
+ private static final UpdateKey KEY_COLLECT_WIDGET_EVENT =
+ (view, event) -> {
+ event.merge(view.collectWidgetEvent());
+ };
private static final SparseArray sListeners =
new SparseArray<>();
@@ -266,6 +273,25 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
executeOnMainExecutor(KEY_VIEW_DATA_CHANGED, viewId);
}
+ @Nullable
+ @Override
+ public AppWidgetEvent collectWidgetEvent() {
+ if (!android.appwidget.flags.Flags.engagementMetrics()) return null;
+
+ CompletableFuture future = new CompletableFuture<>();
+ MAIN_EXECUTOR.execute(() -> {
+ AppWidgetEvent.Builder event = new AppWidgetEvent.Builder();
+ mListeningHolders.forEach(holder ->
+ holder.onWidgetUpdate(mWidgetId, KEY_COLLECT_WIDGET_EVENT, event));
+ future.complete(event.isEmpty() ? null : event.build());
+ });
+ try {
+ return future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ return null;
+ }
+ }
+
private void executeOnMainExecutor(UpdateKey key, T data) {
MAIN_EXECUTOR.execute(() -> mListeningHolders.forEach(holder ->
holder.onWidgetUpdate(mWidgetId, key, data)));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
index c9f791c6ee..927d94c2f6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
@@ -21,7 +21,6 @@ import com.android.app.animation.Interpolators.FINAL_FRAME
import com.android.app.animation.Interpolators.INSTANT
import com.android.app.animation.Interpolators.LINEAR
import com.android.launcher3.Flags.enableDesktopExplodedView
-import com.android.launcher3.Flags.enableGridOnlyOverview
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
import com.android.launcher3.LauncherState
import com.android.launcher3.anim.AnimatedFloat
@@ -37,6 +36,7 @@ import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE
import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X
import com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y
import com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW
+import com.android.launcher3.util.OverviewReleaseFlags.enableGridOnlyOverview
import com.android.quickstep.util.AnimUtils
import com.android.quickstep.views.AddDesktopButton
import com.android.quickstep.views.ClearAllButton
@@ -246,7 +246,10 @@ class RecentsViewStateController(private val launcher: QuickstepLauncher) :
launcher.deviceProfile,
)
- val timings = AnimUtils.getDeviceOverviewToSplitTimings(launcher.deviceProfile.isTablet)
+ val timings =
+ AnimUtils.getDeviceOverviewToSplitTimings(
+ launcher.deviceProfile.getDeviceProperties().isTablet
+ )
if (!goingToOverviewFromWorkspaceContextual) {
// This animation is already done for the contextual case, don't redo it
recentsView.createSplitSelectInitAnimation(
diff --git a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
index f8e873a851..abe0e23432 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
@@ -23,10 +23,11 @@ import android.content.IIntentReceiver
import android.content.IIntentSender
import android.content.Intent
import android.content.pm.ActivityInfo
-import android.content.pm.ApplicationInfo
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.content.pm.ShortcutInfo
+import android.graphics.Bitmap
+import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.os.Flags.allowPrivateProfile
@@ -34,23 +35,26 @@ import android.os.IBinder
import android.os.UserHandle
import android.os.UserManager
import android.util.ArrayMap
+import android.view.SurfaceControlViewHost
import android.widget.Toast
import android.window.RemoteTransition
+import android.window.ScreenCapture
+import com.android.launcher3.BaseActivity
import androidx.annotation.RequiresApi
import com.android.launcher3.Flags.enablePrivateSpace
-import com.android.launcher3.Flags.enablePrivateSpaceInstallShortcut
-import com.android.launcher3.Flags.privateSpaceAppInstallerButton
import com.android.launcher3.Flags.privateSpaceSysAppsSeparation
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.proxy.ProxyActivityStarter
+import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController
import com.android.launcher3.util.ApiWrapper
import com.android.launcher3.util.Executors
import com.android.launcher3.util.StartActivityParams
import com.android.launcher3.util.UserIconInfo
import com.android.quickstep.util.FadeOutRemoteTransition
+import java.util.function.Supplier
import javax.inject.Inject
import app.lawnchair.LawnchairApp
@@ -123,10 +127,7 @@ open class SystemApiWrapper @Inject constructor(@ApplicationContext context: Con
override fun getAppMarketActivityIntent(packageName: String, user: UserHandle): Intent {
return try {
- if (
- enablePrivateSpace() &&
- (privateSpaceAppInstallerButton() || enablePrivateSpaceInstallShortcut())
- )
+ if (allowPrivateProfile() && enablePrivateSpace())
ProxyActivityStarter.getLaunchIntent(
mContext,
StartActivityParams(null as PendingIntent?, 0).apply {
@@ -152,7 +153,7 @@ open class SystemApiWrapper @Inject constructor(@ApplicationContext context: Con
/** Returns an intent which can be used to open Private Space Settings. */
override fun getPrivateSpaceSettingsIntent(): Intent? {
return try {
- if (enablePrivateSpace())
+ if (allowPrivateProfile() && enablePrivateSpace())
ProxyActivityStarter.getLaunchIntent(
mContext,
StartActivityParams(null as PendingIntent?, 0).apply {
@@ -241,11 +242,24 @@ open class SystemApiWrapper @Inject constructor(@ApplicationContext context: Con
}
}
- override fun getApplicationInfoHash(appInfo: ApplicationInfo): String =
- (appInfo.sourceDir?.hashCode() ?: 0).toString() + " " + appInfo.longVersionCode
-
- override fun getRoundIconRes(appInfo: ApplicationInfo) = appInfo.roundIconRes
+ override fun createStatusBarTouchController(
+ launcher: BaseActivity,
+ isEnabledCheck: Supplier,
+ ): StatusBarTouchController? {
+ return StatusBarTouchController(launcher, isEnabledCheck)
+ }
override fun isFileDrawable(shortcutInfo: ShortcutInfo) =
shortcutInfo.hasIconFile() || shortcutInfo.hasIconUri()
+
+ override fun captureSnapshot(host: SurfaceControlViewHost, width: Int, height: Int): Bitmap =
+ ScreenCapture.captureLayers(
+ ScreenCapture.LayerCaptureArgs.Builder(host.surfacePackage!!.surfaceControl)
+ .setSourceCrop(Rect(0, 0, width, height))
+ .setAllowProtected(true)
+ .setHintForSeamlessTransition(true)
+ .build()
+ )
+ .asBitmap()
+ .copy(Bitmap.Config.ARGB_8888, true)
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 8a44f76c70..e2a624d4ca 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -20,6 +20,7 @@ import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import android.content.Context;
+import android.graphics.Color;
import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
@@ -29,6 +30,7 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.ScrimColors;
import com.android.quickstep.util.BaseDepthController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
@@ -137,7 +139,7 @@ public class AllAppsState extends LauncherState {
protected
float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
if (context.getDeviceProfile().shouldShowAllAppsOnSheet()) {
- return context.getDeviceProfile().bottomSheetDepth;
+ return context.getDeviceProfile().getBottomSheetProfile().getBottomSheetDepth();
} else {
// The scrim fades in at approximately 50% of the swipe gesture.
if (enableScalingRevealHomeAnimation()) {
@@ -151,6 +153,11 @@ public class AllAppsState extends LauncherState {
}
}
+ @Override
+ public boolean shouldBlurWorkspace(LauncherState targetState) {
+ return targetState == ALL_APPS || targetState == NORMAL;
+ }
+
@Override
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
PageAlphaProvider superPageAlphaProvider = super.getWorkspacePageAlphaProvider(launcher);
@@ -174,8 +181,7 @@ public class AllAppsState extends LauncherState {
}
private static boolean isWorkspaceVisible(DeviceProfile deviceProfile) {
- // Currently we hide the workspace with the all apps blur flag for simplicity.
- return deviceProfile.isTablet && !Flags.allAppsBlur();
+ return deviceProfile.getDeviceProperties().isTablet() || (Flags.allAppsSheetForHandheld() && Flags.allAppsBlur());
}
@Override
@@ -198,26 +204,24 @@ public class AllAppsState extends LauncherState {
@Override
public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
- return dp.isPhone && !dp.isLandscape;
+ return dp.getDeviceProperties().isPhone() && !dp.getDeviceProperties().isLandscape();
}
@Override
- public LauncherState getHistoryForState(LauncherState previousState) {
- return previousState == BACKGROUND_APP ? QUICK_SWITCH_FROM_HOME
- : previousState == OVERVIEW ? OVERVIEW : NORMAL;
- }
-
- @Override
- public int getWorkspaceScrimColor(Launcher launcher) {
+ public ScrimColors getWorkspaceScrimColor(Launcher launcher) {
+ int backgroundColor;
if (!launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
+ // Lawnchair-TODO-Colour: LawnchairUtilsKt.getAllAppsScrimColor(launcher) + materialColorSurfaceDim
+ // Always use an opaque scrim if there's no sheet.
+ backgroundColor = launcher.getResources().getColor(R.color.materialColorSurfaceDim);
+ } else if (!Flags.allAppsBlur()) {
+ // Lawnchair-TODO-Colour: LawnchairUtilsKt.getAllAppsScrimColor(launcher) + widgets_picker_scrim
+ // If there's a sheet but no blur, use the old scrim color.
+ backgroundColor = launcher.getResources().getColor(R.color.widgets_picker_scrim);
+ } else {
// Lawnchair-TODO-Colour: LawnchairUtilsKt.getAllAppsScrimColor(launcher) + allAppsScrimColor
- return Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
+ backgroundColor = Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
}
- if (Flags.allAppsBlur()) {
- // Lawnchair-TODO-Colour: LawnchairUtilsKt.getAllAppsScrimColor(launcher) + allAppsScrimColorOverBlur
- return Themes.getAttrColor(launcher, R.attr.allAppsScrimColorOverBlur);
- }
- // Lawnchair-TODO-Colour: LawnchairUtilsKt.getAllAppsScrimColor(launcher) + widgets_picker_scrim
- return launcher.getResources().getColor(R.color.widgets_picker_scrim);
+ return new ScrimColors(backgroundColor, /* foregroundColor */ Color.TRANSPARENT);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index a5b1ee7b53..508643afd7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.uioverrides.states;
-import static com.android.launcher3.Flags.enableDesktopWindowingCarouselDetach;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
@@ -25,6 +24,7 @@ import android.graphics.Color;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.views.ScrimColors;
import com.android.quickstep.util.BaseDepthController;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -35,7 +35,8 @@ import com.android.quickstep.views.RecentsView;
public class BackgroundAppState extends OverviewState {
private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_RECENTS_VIEW_VISIBLE
- | FLAG_WORKSPACE_INACCESSIBLE | FLAG_NON_INTERACTIVE | FLAG_CLOSE_POPUPS;
+ | FLAG_WORKSPACE_INACCESSIBLE | FLAG_NON_INTERACTIVE | FLAG_CLOSE_POPUPS
+ | FLAG_SKIP_STATE_ANNOUNCEMENT;
public BackgroundAppState(int id) {
this(id, LAUNCHER_STATE_BACKGROUND);
@@ -55,7 +56,7 @@ public class BackgroundAppState extends OverviewState {
launcher,
launcher.getDeviceProfile(),
recentsView.getPagedOrientationHandler(),
- recentsView.getSizeStrategy());
+ recentsView.getContainerInterface());
AllAppsTransitionController controller = launcher.getAllAppsController();
float scrollRange = Math.max(controller.getShiftRange(), 1);
float progressDelta = (transitionLength / scrollRange);
@@ -91,11 +92,6 @@ public class BackgroundAppState extends OverviewState {
return true;
}
- @Override
- public boolean detachDesktopCarousel() {
- return enableDesktopWindowingCarouselDetach();
- }
-
@Override
public boolean showExplodedDesktopView() {
return false;
@@ -114,8 +110,10 @@ public class BackgroundAppState extends OverviewState {
}
@Override
- public int getWorkspaceScrimColor(Launcher launcher) {
- return Color.TRANSPARENT;
+ public ScrimColors getWorkspaceScrimColor(Launcher launcher) {
+ return new ScrimColors(
+ /* backgroundColor= */ Color.TRANSPARENT,
+ /* foregroundColor= */ Color.TRANSPARENT);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index ae82f82c01..7348fb2eda 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.uioverrides.states;
-import static com.android.launcher3.Flags.enableGridOnlyOverview;
+import static com.android.launcher3.util.OverviewReleaseFlags.enableGridOnlyOverview;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import android.graphics.Rect;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index fe8ab36fe2..3cc9431cef 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -17,7 +17,6 @@ package com.android.launcher3.uioverrides.states;
import static com.android.app.animation.Interpolators.DECELERATE_2;
import static com.android.launcher3.Flags.enableDesktopExplodedView;
-import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
@@ -25,6 +24,8 @@ import android.content.Context;
import android.graphics.Rect;
import android.os.SystemProperties;
+import androidx.core.graphics.ColorUtils;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -32,11 +33,11 @@ import com.android.launcher3.R;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.ScrimColors;
import com.android.quickstep.util.BaseDepthController;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.BlurUtils;
import app.lawnchair.preferences.PreferenceManager;
import app.lawnchair.theme.color.tokens.ColorTokens;
@@ -87,7 +88,7 @@ public class OverviewState extends LauncherState {
recentsView.getTaskSize(sTempRect);
float scale;
DeviceProfile deviceProfile = launcher.getDeviceProfile();
- if (deviceProfile.isTwoPanels) {
+ if (deviceProfile.getDeviceProperties().isTwoPanels()) {
// In two panel layout, width does not include both panels or space between them, so
// use height instead. We do not use height for handheld, as cell layout can be
// shorter than a task and we want the workspace to scale down to task size.
@@ -122,9 +123,9 @@ public class OverviewState extends LauncherState {
int elements = CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS | ADD_DESK_BUTTON;
DeviceProfile dp = launcher.getDeviceProfile();
boolean showFloatingSearch;
- if (dp.isPhone) {
+ if (dp.getDeviceProperties().isPhone()) {
// Only show search in phone overview in portrait mode.
- showFloatingSearch = !dp.isLandscape;
+ showFloatingSearch = !dp.getDeviceProperties().isLandscape();
} else {
// Only show search in tablet overview if taskbar is not visible.
showFloatingSearch = !dp.isTaskbarPresent || isTaskbarStashed(launcher);
@@ -156,7 +157,7 @@ public class OverviewState extends LauncherState {
@Override
public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
- return dp.isPhone && !dp.isLandscape;
+ return dp.getDeviceProperties().isPhone() && !dp.getDeviceProperties().isLandscape();
}
@Override
@@ -165,22 +166,19 @@ public class OverviewState extends LauncherState {
}
@Override
- public int getWorkspaceScrimColor(Launcher launcher) {
+ public ScrimColors getWorkspaceScrimColor(Launcher launcher) {
// Lawnchair-TODO-Colour: LawnchairUtilsKt.getAllAppsScrimColor(launcher) + allAppsScrimColorOverBlur
// Lawnchair-TODO-Colour: LawnchairUtilsKt.getAllAppsScrimColor(launcher) + allAppsScrimColor
- return enableOverviewBackgroundWallpaperBlur() && BlurUtils.supportsBlursOnWindows()
- ? Themes.getAttrColor(launcher, R.attr.overviewScrimColorOverBlur)
- : Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
+ return new ScrimColors(
+ /* backgroundColor */ Themes.getAttrColor(launcher, R.attr.overviewScrimColor),
+ /* foregroundColor */ ColorUtils.compositeColors(
+ Themes.getAttrColor(launcher, R.attr.overviewScrimForegroundPrimary),
+ Themes.getAttrColor(launcher, R.attr.overviewScrimForegroundSecondary)));
}
@Override
public boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
- return deviceProfile.isTablet;
- }
-
- @Override
- public boolean detachDesktopCarousel() {
- return false;
+ return deviceProfile.getDeviceProperties().isTablet();
}
@Override
@@ -230,7 +228,7 @@ public class OverviewState extends LauncherState {
public void onBackInvoked(Launcher launcher) {
RecentsView recentsView = launcher.getOverviewPanel();
TaskView taskView = recentsView.getRunningTaskView();
- if (taskView != null) {
+ if (taskView != null && !taskView.isBeingDismissed()) {
if (recentsView.isTaskViewFullyVisible(taskView)) {
taskView.launchWithAnimation();
} else {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index dfad4096bc..4058b55637 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -17,12 +17,7 @@ package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
-import android.graphics.Color;
-
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.util.Themes;
/**
* State to indicate we are about to launch a recent task. Note that this state is only used when
@@ -43,19 +38,6 @@ public class QuickSwitchState extends BackgroundAppState {
return new ScaleAndTranslation(0.9f, 0, translationY);
}
- @Override
- public int getWorkspaceScrimColor(Launcher launcher) {
- if (launcher.areDesktopTasksVisible()) {
- // No scrim while desktop tasks are visible
- return Color.TRANSPARENT;
- }
- DeviceProfile dp = launcher.getDeviceProfile();
- if (dp.isTaskbarPresentInApps) {
- return launcher.getColor(R.color.taskbar_background);
- }
- return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
- }
-
@Override
public float getVerticalProgress(Launcher launcher) {
// Don't move all apps shelf while quick-switching (just let it fade).
@@ -76,4 +58,9 @@ public class QuickSwitchState extends BackgroundAppState {
public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
return false;
}
+
+ @Override
+ public boolean detachDesktopCarousel() {
+ return true;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 44f8bf1e07..3a6de197ac 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -220,7 +220,7 @@ public class QuickstepAtomicAnimationFactory extends
} else if (fromState == NORMAL && toState == ALL_APPS) {
AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mContainer, config);
} else if (fromState == OVERVIEW && toState == OVERVIEW_SPLIT_SELECT) {
- SplitAnimationTimings timings = mContainer.getDeviceProfile().isTablet
+ SplitAnimationTimings timings = mContainer.getDeviceProfile().getDeviceProperties().isTablet()
? SplitAnimationTimings.TABLET_OVERVIEW_TO_SPLIT
: SplitAnimationTimings.PHONE_OVERVIEW_TO_SPLIT;
config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index 2631fbf0b6..c2ec17ab3f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -43,7 +43,7 @@ public class SplitScreenSelectState extends OverviewState {
@Override
public int getTransitionDuration(ActivityContext context, boolean isToState) {
- if (isToState && context.getDeviceProfile().isTablet) {
+ if (isToState && context.getDeviceProfile().getDeviceProperties().isTablet()) {
return SplitAnimationTimings.TABLET_ENTER_DURATION;
} else if (isToState) {
return SplitAnimationTimings.PHONE_ENTER_DURATION;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index a4f8b8135c..dce158f63f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -28,6 +28,7 @@ import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
+import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.animation.AnimatorSet;
@@ -44,7 +45,6 @@ import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.DisplayController;
@@ -136,7 +136,7 @@ public class NavBarToHomeTouchController implements TouchController,
}
private float getShiftRange() {
- return mLauncher.getDeviceProfile().heightPx;
+ return mLauncher.getDeviceProfile().getDeviceProperties().getHeightPx();
}
@Override
@@ -157,10 +157,16 @@ public class NavBarToHomeTouchController implements TouchController,
AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_TASK_MENU);
} else if (mStartState == ALL_APPS) {
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
- builder.setFloat(allAppsController, ALL_APPS_PULL_BACK_TRANSLATION,
- -mPullbackDistance, PULLBACK_INTERPOLATOR);
- builder.setFloat(allAppsController, ALL_APPS_PULL_BACK_ALPHA,
- 0.5f, PULLBACK_INTERPOLATOR);
+ if (mLauncher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
+ allAppsController.setShouldScaleHeader(true);
+ builder.addAnimatedFloat(allAppsController.getAllAppScale(), 1f,
+ PREDICTIVE_BACK_MIN_SCALE, PULLBACK_INTERPOLATOR);
+ } else {
+ builder.setFloat(allAppsController, ALL_APPS_PULL_BACK_TRANSLATION,
+ -mPullbackDistance, PULLBACK_INTERPOLATOR);
+ builder.setFloat(allAppsController, ALL_APPS_PULL_BACK_ALPHA,
+ 0.5f, PULLBACK_INTERPOLATOR);
+ }
}
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topView != null) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index ff726e65d4..8d907f4183 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -17,7 +17,7 @@
package com.android.launcher3.uioverrides.touchcontrollers;
import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
+import static com.android.launcher3.LauncherAnimUtils.SCRIM_COLORS;
import static com.android.launcher3.LauncherAnimUtils.newSingleUseCancelListener;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.HINT_STATE;
@@ -47,6 +47,7 @@ import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.VibratorWrapper;
+import com.android.launcher3.views.ScrimColorsEvaluator;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.MotionPauseDetector;
@@ -62,6 +63,8 @@ import java.util.function.BiConsumer;
* first home screen instead of to Overview.
*/
public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouchController {
+
+ public static final String TAG = "NoButtonNavbarToOverviewTouchController";
private static final float ONE_HANDED_ACTIVATED_SLOP_MULTIPLIER = 2.5f;
// How much of the movement to use for translating overview after swipe and hold.
@@ -84,7 +87,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch
private AnimatorPlaybackController mOverviewResistYAnim;
// Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator.
- private ObjectAnimator mNormalToHintOverviewScrimAnimator;
+ private ValueAnimator mNormalToHintOverviewScrimAnimator;
private final QuickstepLauncher mLauncher;
private boolean mIsTrackpadSwipe;
@@ -134,7 +137,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch
float progressMultiplier = super.initCurrentAnimation();
if (mToState == HINT_STATE) {
// Track the drag across the entire height of the screen.
- progressMultiplier = -1f / mLauncher.getDeviceProfile().heightPx;
+ progressMultiplier = -1f / mLauncher.getDeviceProfile().getDeviceProperties().getHeightPx();
}
return progressMultiplier;
}
@@ -161,9 +164,10 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch
}
if (mFromState == NORMAL && mToState == HINT_STATE) {
- mNormalToHintOverviewScrimAnimator = ObjectAnimator.ofArgb(
+ mNormalToHintOverviewScrimAnimator = ObjectAnimator.ofObject(
mLauncher.getScrimView(),
- VIEW_BACKGROUND_COLOR,
+ SCRIM_COLORS,
+ ScrimColorsEvaluator.INSTANCE,
mFromState.getWorkspaceScrimColor(mLauncher),
mToState.getWorkspaceScrimColor(mLauncher));
}
@@ -172,6 +176,11 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch
mOverviewResistYAnim = null;
}
+ @Override
+ public String dump() {
+ return TAG;
+ }
+
@Override
protected void updateProgress(float fraction) {
super.updateProgress(fraction);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 58b274a47c..416c8ab4fd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -51,6 +51,7 @@ import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
+import static com.android.quickstep.views.RecentsView.DESKTOP_CAROUSEL_DETACH_PROGRESS;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -129,13 +130,13 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
mLauncher = launcher;
mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this);
mRecentsView = mLauncher.getOverviewPanel();
- mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
+ mXRange = mLauncher.getDeviceProfile().getDeviceProperties().getWidthPx() / 2f;
mYRange = LayoutUtils.getShelfTrackingDistance(
mLauncher,
mLauncher.getDeviceProfile(),
mRecentsView.getPagedOrientationHandler(),
- mRecentsView.getSizeStrategy());
- mMaxYProgress = mLauncher.getDeviceProfile().heightPx / mYRange;
+ mRecentsView.getContainerInterface());
+ mMaxYProgress = mLauncher.getDeviceProfile().getDeviceProperties().getHeightPx() / mYRange;
mMotionPauseDetector = new MotionPauseDetector(mLauncher);
mMotionPauseMinDisplacement = mLauncher.getResources().getDimension(
R.dimen.motion_pause_detector_min_displacement_from_app);
@@ -260,6 +261,8 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]);
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, 1f);
TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, fromState.showTaskThumbnailSplash() ? 1f : 0);
+ DESKTOP_CAROUSEL_DETACH_PROGRESS.set(mRecentsView,
+ fromState.detachDesktopCarousel() ? 1f : 0);
mRecentsView.setContentAlpha(1);
mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
mLauncher.getActionsView().getVisibilityAlpha().updateValue(
@@ -275,8 +278,9 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
xAnim.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1], LINEAR);
// Use QuickSwitchState instead of OverviewState to determine scrim color,
// since we need to take potential taskbar into account.
- xAnim.setViewBackgroundColor(mLauncher.getScrimView(),
- QUICK_SWITCH_FROM_HOME.getWorkspaceScrimColor(mLauncher), LINEAR);
+ xAnim.setScrimColors(mLauncher.getScrimView(),
+ QUICK_SWITCH_FROM_HOME.getWorkspaceScrimColor(mLauncher),
+ LINEAR);
if (!mRecentsView.hasTaskViews()) {
xAnim.addFloat(mRecentsView, CONTENT_ALPHA, 0f, 1f, LINEAR);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index a74b350a64..22055821fa 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -26,6 +26,7 @@ import android.view.MotionEvent;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.allapps.AllAppsTransitionController;
@@ -33,6 +34,7 @@ import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.LayoutUtils;
@@ -147,7 +149,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
mLauncher,
mLauncher.getDeviceProfile(),
recentsView.getPagedOrientationHandler(),
- recentsView.getSizeStrategy());
+ recentsView.getContainerInterface());
} else {
mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, config);
@@ -216,6 +218,11 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
@Override
protected void onReinitToState(LauncherState newToState) {
super.onReinitToState(newToState);
+ if (Flags.allAppsBlur() && mLauncher.isAllAppsBackgroundBlurEnabled()
+ && newToState == ALL_APPS) {
+ // About to start blurring during swipe to All Apps; prepare the renderer.
+ ((QuickstepLauncher) mLauncher).getDepthController().setEarlyWakeup(true);
+ }
if (newToState != ALL_APPS) {
InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index f582324f23..14896e53c7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -161,6 +161,6 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll
@Override
protected float getShiftRange() {
- return mLauncher.getDeviceProfile().widthPx / 2f;
+ return mLauncher.getDeviceProfile().getDeviceProperties().getWidthPx() / 2f;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 16647a90b7..9d501ed116 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -24,24 +24,25 @@ import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll;
+import static com.android.launcher3.Utilities.shouldEnableMouseInteractionChanges;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN;
import android.graphics.PointF;
import android.util.SparseArray;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowManager;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.SystemUiProxy;
-import java.io.PrintWriter;
+import java.util.function.Supplier;
import java.lang.reflect.InvocationTargetException;
import app.lawnchair.LawnchairAppKt;
@@ -56,29 +57,30 @@ public class StatusBarTouchController implements TouchController {
private static final String TAG = "StatusBarController";
- private final Launcher mLauncher;
+ private final BaseActivity mLauncher;
private final SystemUiProxy mSystemUiProxy;
private final float mTouchSlop;
private int mLastAction;
private final SparseArray mDownEvents;
+ private final Supplier mIsEnabledCheck;
/* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
private boolean mCanIntercept;
- public StatusBarTouchController(Launcher l) {
+ public StatusBarTouchController(BaseActivity l, Supplier isEnabledCheck) {
mLauncher = l;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(mLauncher);
// Guard against TAPs by increasing the touch slop.
mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop();
mDownEvents = new SparseArray<>();
+ mIsEnabledCheck = isEnabledCheck;
}
@Override
- public void dump(String prefix, PrintWriter writer) {
- writer.println(prefix + "mCanIntercept:" + mCanIntercept);
- writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction));
- writer.println(prefix + "mSysUiProxy available:"
- + SystemUiProxy.INSTANCE.get(mLauncher).isActive());
+ public String dump() {
+ return "mCanIntercept:" + mCanIntercept
+ + " , mLastAction:" + MotionEvent.actionToString(mLastAction)
+ + " , mSysUiProxy available:" + SystemUiProxy.INSTANCE.get(mLauncher).isActive();
}
private void dispatchTouchEvent(MotionEvent ev) {
@@ -165,9 +167,11 @@ public class StatusBarTouchController implements TouchController {
}
private boolean canInterceptTouch(MotionEvent ev) {
- if (isTrackpadScroll(ev) || !mLauncher.isInState(LauncherState.NORMAL)
+ if (isTrackpadScroll(ev) || !mIsEnabledCheck.get()
|| AbstractFloatingView.getTopOpenViewWithType(mLauncher,
- AbstractFloatingView.TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW) != null) {
+ AbstractFloatingView.TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW) != null || (
+ shouldEnableMouseInteractionChanges(mLauncher.asContext())
+ && ev.getSource() == InputDevice.SOURCE_MOUSE)) {
return false;
} else {
// For NORMAL state, only listen if the event originated above the navbar height
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
index 06e6734a37..a85f706f69 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
@@ -28,23 +28,41 @@ import com.android.launcher3.Utilities.boundToRange
import com.android.launcher3.Utilities.debugLog
import com.android.launcher3.Utilities.isRtl
import com.android.launcher3.Utilities.mapToRange
+import com.android.launcher3.statemanager.BaseState
+import com.android.launcher3.statemanager.StateManager.StateListener
+import com.android.launcher3.statemanager.StatefulContainer
import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.util.MSDLPlayerWrapper
import com.android.launcher3.util.TouchController
+import com.android.mechanics.spec.Breakpoint
+import com.android.mechanics.spec.Breakpoint.Companion.maxLimit
+import com.android.mechanics.spec.Breakpoint.Companion.minLimit
+import com.android.mechanics.spec.BreakpointKey
+import com.android.mechanics.spec.DirectionalMotionSpec
+import com.android.mechanics.spec.Guarantee
+import com.android.mechanics.spec.InputDirection
+import com.android.mechanics.spec.Mapping
+import com.android.mechanics.spec.MotionSpec
+import com.android.mechanics.spring.SpringParameters
+import com.android.mechanics.view.DistanceGestureContext
+import com.android.mechanics.view.ViewMotionValue
+import com.android.quickstep.views.RecentsDismissUtils
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskView
import com.google.android.msdl.data.model.MSDLToken
import kotlin.math.abs
+import kotlin.math.ceil
/** Touch controller for handling task view card dismiss swipes */
-class TaskViewDismissTouchController(
+class TaskViewDismissTouchController>(
private val container: CONTAINER,
private val taskViewRecentsTouchContext: TaskViewRecentsTouchContext,
) : TouchController, SingleAxisSwipeDetector.Listener where
CONTAINER : Context,
-CONTAINER : RecentsViewContainer {
+CONTAINER : RecentsViewContainer,
+CONTAINER : StatefulContainer {
private val recentsView: RecentsView<*, *> = container.getOverviewPanel()
private val detector: SingleAxisSwipeDetector =
SingleAxisSwipeDetector(
@@ -54,17 +72,37 @@ CONTAINER : RecentsViewContainer {
)
private val isRtl = isRtl(container.resources)
private val upDirection: Int = recentsView.pagedOrientationHandler.getUpDirection(isRtl)
-
+ private val maxUndershoot =
+ container.resources.getDimension(R.dimen.task_dismiss_max_undershoot)
+ private val detachThreshold =
+ container.resources.getDimension(R.dimen.task_dismiss_detach_threshold)
+ private val stateListener =
+ object : StateListener {
+ override fun onStateTransitionStart(toState: T) {
+ springAnimation?.cancel()
+ clearState()
+ }
+ }
private val tempTaskThumbnailBounds = Rect()
private var taskBeingDragged: TaskView? = null
- private var springAnimation: SpringAnimation? = null
+ private var taskDragDisplacementValue: ViewMotionValue? = null
+ private var springAnimation: RecentsDismissUtils.SpringSet? = null
private var dismissLength: Int = 0
private var verticalFactor: Int = 0
private var hasDismissThresholdHapticRun = false
private var initialDisplacement: Float = 0f
private var recentsScaleAnimation: SpringAnimation? = null
- private var isBlockedDuringDismissal = false
+ private var canInterceptTouch = false
+ private var isDismissing = false
+
+ init {
+ container.getStateManager().addStateListener(stateListener)
+ }
+
+ override fun onTouchControllerDestroyed() {
+ container.getStateManager().removeStateListener(stateListener)
+ }
private fun canInterceptTouch(ev: MotionEvent): Boolean =
when {
@@ -90,6 +128,12 @@ CONTAINER : RecentsViewContainer {
false
}
+ // Do not allow dismiss while recents is scrolling.
+ !recentsView.scroller.isFinished -> {
+ debugLog(TAG, "Not intercepting touch, recents scrolling.")
+ false
+ }
+
else ->
taskViewRecentsTouchContext.isRecentsInteractive.also { isRecentsInteractive ->
if (!isRecentsInteractive) {
@@ -99,15 +143,22 @@ CONTAINER : RecentsViewContainer {
}
override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
+ // On consecutive events, end animation early so user can dismiss next task.
+ springAnimation?.speedUpSpringsToEnd()
+
if ((ev.action == MotionEvent.ACTION_UP || ev.action == MotionEvent.ACTION_CANCEL)) {
clearState()
}
if (ev.action == MotionEvent.ACTION_DOWN) {
- if (!onActionDown(ev)) {
+ canInterceptTouch = onActionDown(ev)
+ if (!canInterceptTouch) {
return false
}
}
-
+ // Ignore other actions if touch intercepting has not been enabled in an ACTION_DOWN event.
+ if (!canInterceptTouch) {
+ return false
+ }
onControllerTouchEvent(ev)
val upDirectionIsPositive = upDirection == SingleAxisSwipeDetector.DIRECTION_POSITIVE
val wasInitialTouchUp =
@@ -119,42 +170,87 @@ CONTAINER : RecentsViewContainer {
override fun onControllerTouchEvent(ev: MotionEvent?): Boolean = detector.onTouchEvent(ev)
private fun onActionDown(ev: MotionEvent): Boolean {
- springAnimation?.cancel()
- recentsScaleAnimation?.cancel()
if (!canInterceptTouch(ev)) {
return false
}
taskBeingDragged =
- recentsView.taskViews
- .firstOrNull {
- recentsView.isTaskViewVisible(it) && container.dragLayer.isEventOverView(it, ev)
- }
- ?.also {
- val secondaryLayerDimension =
- recentsView.pagedOrientationHandler.getSecondaryDimension(
- container.dragLayer
- )
- // Dismiss length as bottom of task so it is fully off screen when dismissed.
- it.getThumbnailBounds(tempTaskThumbnailBounds, relativeToDragLayer = true)
- dismissLength =
- recentsView.pagedOrientationHandler.getTaskDismissLength(
- secondaryLayerDimension,
+ recentsView.taskViews.firstOrNull {
+ recentsView.isTaskViewVisible(it) && container.dragLayer.isEventOverView(it, ev)
+ }
+ // If event is not over a taskView, check if it would have been either over the
+ // currently dismissing task being dragged, or over where the next task will be.
+ ?: recentsView.taskViews.firstOrNull { taskView ->
+ if (!recentsView.isTaskViewVisible(taskView)) return@firstOrNull false
+ container.dragLayer.getDescendantRectRelativeToSelf(
+ taskView,
+ tempTaskThumbnailBounds,
+ )
+ if (taskView == taskBeingDragged && !isDismissing) {
+ val secondaryTranslation =
+ -taskView.secondaryDismissTranslationProperty.get(taskView).toInt()
+ recentsView.pagedOrientationHandler.extendRectForSecondaryTranslation(
tempTaskThumbnailBounds,
+ secondaryTranslation,
)
- verticalFactor =
- recentsView.pagedOrientationHandler.getTaskDismissVerticalDirection()
+ } else {
+ val primaryTranslation =
+ recentsView.taskViewsDismissPrimaryTranslations[taskView] ?: 0
+ recentsView.pagedOrientationHandler.extendRectForPrimaryTranslation(
+ tempTaskThumbnailBounds,
+ primaryTranslation,
+ )
+ }
+ tempTaskThumbnailBounds.contains(ev.x.toInt(), ev.y.toInt())
}
+
+ if (taskBeingDragged == null) {
+ debugLog(TAG, "Not intercepting touch, null dragged task.")
+ return false
+ }
+ val secondaryLayerDimension =
+ recentsView.pagedOrientationHandler.getSecondaryDimension(container.dragLayer)
+ // Dismiss length as bottom of task so it is fully off screen when dismissed.
+ // Take into account the recents scale when fully zoomed out on dismiss.
+ taskBeingDragged?.getThumbnailBounds(tempTaskThumbnailBounds, relativeToDragLayer = true)
+ dismissLength =
+ ceil(
+ recentsView.pagedOrientationHandler.getTaskDismissLength(
+ secondaryLayerDimension,
+ tempTaskThumbnailBounds,
+ ) / RECENTS_SCALE_ON_DISMISS_SUCCESS
+ )
+ .toInt()
+ verticalFactor = recentsView.pagedOrientationHandler.getTaskDismissVerticalDirection()
+ taskBeingDragged?.isBeingDraggedForDismissal = true
+
detector.setDetectableScrollConditions(upDirection, /* ignoreSlop= */ false)
return true
}
override fun onDragStart(start: Boolean, startDisplacement: Float) {
- if (isBlockedDuringDismissal) return
val taskBeingDragged = taskBeingDragged ?: return
debugLog(TAG, "Handling touch event.")
initialDisplacement =
taskBeingDragged.secondaryDismissTranslationProperty.get(taskBeingDragged)
+ taskDragDisplacementValue =
+ generateMotionValue(
+ initialDisplacement,
+ detachThreshold * verticalFactor,
+ container.asContext(),
+ ) { currentDisplacement ->
+ taskBeingDragged.secondaryDismissTranslationProperty.setValue(
+ taskBeingDragged,
+ currentDisplacement,
+ )
+ if (taskBeingDragged.isRunningTask && recentsView.enableDrawingLiveTile) {
+ recentsView.runActionOnRemoteHandles { remoteTargetHandle ->
+ remoteTargetHandle.taskViewSimulator.taskSecondaryTranslation.value =
+ currentDisplacement
+ }
+ recentsView.redrawLiveTile()
+ }
+ }
// Add a tiny bit of translation Z, so that it draws on top of other views. This is relevant
// (e.g.) when we dismiss a task by sliding it upward: if there is a row of icons above, we
@@ -163,36 +259,26 @@ CONTAINER : RecentsViewContainer {
}
override fun onDrag(displacement: Float): Boolean {
- if (isBlockedDuringDismissal) return true
- val taskBeingDragged = taskBeingDragged ?: return false
+ taskBeingDragged ?: return false
val currentDisplacement = displacement + initialDisplacement
val boundedDisplacement =
boundToRange(abs(currentDisplacement), 0f, dismissLength.toFloat())
// When swiping below origin, allow slight undershoot to simulate resisting the movement.
+ val isAboveOrigin =
+ recentsView.pagedOrientationHandler.isGoingUp(currentDisplacement, isRtl)
val totalDisplacement =
- if (recentsView.pagedOrientationHandler.isGoingUp(currentDisplacement, isRtl))
- boundedDisplacement * verticalFactor
+ if (isAboveOrigin) boundedDisplacement * verticalFactor
else
mapToRange(
boundedDisplacement,
0f,
dismissLength.toFloat(),
0f,
- container.resources.getDimension(R.dimen.task_dismiss_max_undershoot),
+ maxUndershoot,
DECELERATE,
) * -verticalFactor
- taskBeingDragged.secondaryDismissTranslationProperty.setValue(
- taskBeingDragged,
- totalDisplacement,
- )
- if (taskBeingDragged.isRunningTask && recentsView.enableDrawingLiveTile) {
- recentsView.runActionOnRemoteHandles { remoteTargetHandle ->
- remoteTargetHandle.taskViewSimulator.taskSecondaryTranslation.value =
- totalDisplacement
- }
- recentsView.redrawLiveTile()
- }
val dismissFraction = displacement / (dismissLength * verticalFactor).toFloat()
+ taskDragDisplacementValue?.input = totalDisplacement
RECENTS_SCALE_PROPERTY.setValue(recentsView, getRecentsScale(dismissFraction))
playDismissThresholdHaptic(displacement)
return true
@@ -219,8 +305,9 @@ CONTAINER : RecentsViewContainer {
}
override fun onDragEnd(velocity: Float) {
- if (isBlockedDuringDismissal) return
val taskBeingDragged = taskBeingDragged ?: return
+ taskDragDisplacementValue?.dispose()
+ taskBeingDragged.isBeingDraggedForDismissal = false
val currentDisplacement =
taskBeingDragged.secondaryDismissTranslationProperty.get(taskBeingDragged)
@@ -229,23 +316,23 @@ CONTAINER : RecentsViewContainer {
val velocityIsGoingUp = recentsView.pagedOrientationHandler.isGoingUp(velocity, isRtl)
val isFlingingTowardsDismiss = detector.isFling(velocity) && velocityIsGoingUp
val isFlingingTowardsRestState = detector.isFling(velocity) && !velocityIsGoingUp
- val isDismissing =
+ isDismissing =
isFlingingTowardsDismiss || (isBeyondDismissThreshold && !isFlingingTowardsRestState)
+ val dismissThreshold = (DISMISS_THRESHOLD_FRACTION * dismissLength * verticalFactor).toInt()
+ val finalPosition = if (isDismissing) (dismissLength * verticalFactor).toFloat() else 0f
springAnimation =
- recentsView
- .createTaskDismissSettlingSpringAnimation(
- taskBeingDragged,
- velocity,
- isDismissing,
- dismissLength,
- this::clearState,
- )
- .apply {
- animateToFinalPosition(
- if (isDismissing) (dismissLength * verticalFactor).toFloat() else 0f
- )
- }
- isBlockedDuringDismissal = true
+ recentsView.runTaskDismissSettlingSpringAnimation(
+ taskBeingDragged,
+ isDismissing,
+ RecentsDismissUtils.DismissedTaskData(
+ startVelocity = velocity,
+ dismissLength = dismissLength,
+ finalPosition = finalPosition,
+ dismissThreshold = dismissThreshold,
+ ),
+ /* shouldRemoveTaskView= */ isDismissing,
+ /* isSplitSelection= */ false,
+ )
recentsScaleAnimation =
recentsView.animateRecentsScale(RECENTS_SCALE_DEFAULT).addEndListener { _, _, _, _ ->
recentsScaleAnimation = null
@@ -255,10 +342,11 @@ CONTAINER : RecentsViewContainer {
private fun clearState() {
detector.finishedScrolling()
detector.setDetectableScrollConditions(0, false)
- taskBeingDragged?.translationZ = 0f
+ taskBeingDragged?.resetViewTransforms()
taskBeingDragged = null
springAnimation = null
- isBlockedDuringDismissal = false
+ taskDragDisplacementValue = null
+ isDismissing = false
}
private fun getRecentsScale(dismissFraction: Float): Float {
@@ -300,6 +388,43 @@ CONTAINER : RecentsViewContainer {
}
}
+ private fun generateMotionValue(
+ initialDisplacement: Float,
+ detachThreshold: Float,
+ context: Context,
+ updateCallback: (Float) -> Unit,
+ ): ViewMotionValue {
+ val direction = if (initialDisplacement < 0) InputDirection.Max else InputDirection.Min
+ val distanceGestureContext =
+ DistanceGestureContext.create(context, initialDisplacement, direction)
+ val viewMotionValue =
+ ViewMotionValue(
+ initialDisplacement,
+ distanceGestureContext,
+ generateMotionSpec(detachThreshold),
+ label = "taskDismiss::displacement",
+ )
+
+ viewMotionValue.addUpdateCallback { motionValue -> updateCallback(motionValue.output) }
+ return viewMotionValue
+ }
+
+ /** Motion spec for an initial magnetic detach. Track linearly otherwise. No reattach. */
+ private fun generateMotionSpec(detachThreshold: Float): MotionSpec {
+ val spring = SpringParameters(stiffness = 800f, dampingRatio = 0.95f)
+ val detachKey = BreakpointKey("TaskDismiss::Detach")
+ val breakpoints = mutableListOf()
+ val mappings = mutableListOf()
+
+ breakpoints.add(minLimit)
+ mappings.add(Mapping.Identity)
+ breakpoints.add(Breakpoint(detachKey, detachThreshold, spring, Guarantee.None))
+ mappings.add(Mapping.Linear(MAGNETIC_DETACH_INTERPOLATION_FRACTION))
+ breakpoints.add(maxLimit)
+
+ return MotionSpec(DirectionalMotionSpec(breakpoints, mappings))
+ }
+
companion object {
private const val TAG = "TaskViewDismissTouchController"
@@ -312,5 +437,7 @@ CONTAINER : RecentsViewContainer {
private const val RECENTS_SCALE_FIRST_THRESHOLD_FRACTION = 0.2f
private const val RECENTS_SCALE_DISMISS_THRESHOLD_FRACTION = 0.5f
private const val RECENTS_SCALE_SECOND_THRESHOLD_FRACTION = 0.575f
+
+ private const val MAGNETIC_DETACH_INTERPOLATION_FRACTION = 0.35f
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt
index fe9cae55f9..6225c47835 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt
@@ -60,6 +60,7 @@ CONTAINER : RecentsViewContainer {
private var launchEndDisplacement: Float = 0f
private var playbackController: AnimatorPlaybackController? = null
private var verticalFactor: Int = 0
+ private var canInterceptTouch = false
private fun canTaskLaunchTaskView(taskView: TaskView?) =
taskView != null &&
@@ -92,6 +93,12 @@ CONTAINER : RecentsViewContainer {
false
}
+ // Do not allow launch while recents is scrolling.
+ !recentsView.scroller.isFinished -> {
+ debugLog(TAG, "Not intercepting touch, recents scrolling.")
+ false
+ }
+
else ->
taskViewRecentsTouchContext.isRecentsInteractive.also { isRecentsInteractive ->
if (!isRecentsInteractive) {
@@ -108,11 +115,16 @@ CONTAINER : RecentsViewContainer {
clearState()
}
if (ev.action == MotionEvent.ACTION_DOWN) {
- if (!onActionDown(ev)) {
+ canInterceptTouch = onActionDown(ev)
+ if (!canInterceptTouch) {
clearState()
return false
}
}
+ // Ignore other actions if touch intercepting has not been enabled in an ACTION_DOWN event.
+ if (!canInterceptTouch) {
+ return false
+ }
onControllerTouchEvent(ev)
val downDirectionIsNegative = downDirection == SingleAxisSwipeDetector.DIRECTION_NEGATIVE
val wasInitialTouchDown =
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
index c9d68a5e37..d9b0366a40 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java
@@ -246,7 +246,7 @@ public class TaskViewTouchControllerDeprecated<
pa = new PendingAnimation(maxDuration);
mRecentsView.createTaskDismissAnimation(pa, mTaskBeingDragged,
true /* animateTaskView */, true /* removeTask */, maxDuration,
- false /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
+ false /* dismissingForSplitSelection*/, null /* gridEndData */);
mEndDisplacement = -secondaryTaskDimension;
} else {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
index b70cabe76d..e7d2cd23c6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
@@ -39,6 +39,6 @@ public class TransposedQuickSwitchTouchController extends QuickSwitchTouchContro
@Override
protected float getShiftRange() {
- return mLauncher.getDeviceProfile().heightPx / 2f;
+ return mLauncher.getDeviceProfile().getDeviceProperties().getHeightPx() / 2f;
}
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index d9b90f5c73..aa81afb759 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -30,7 +31,6 @@ import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
import static com.android.launcher3.BaseActivity.EVENT_STARTED;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
import static com.android.launcher3.Flags.enableGestureNavHorizontalTouchSlop;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.Flags.msdlFeedback;
@@ -98,6 +98,7 @@ import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowInsets;
import android.view.animation.Interpolator;
import android.widget.Toast;
+import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.PictureInPictureSurfaceTransaction;
import android.window.TransitionInfo;
@@ -136,7 +137,7 @@ import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
-import com.android.quickstep.fallback.window.RecentsWindowFlags;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -168,10 +169,10 @@ import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.wm.shell.Flags;
import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.pip.PipFlags;
import com.android.wm.shell.shared.startingsurface.SplashScreenExitAnimationUtils;
import com.google.android.msdl.data.model.MSDLToken;
@@ -215,6 +216,7 @@ public abstract class AbsSwipeUpHandler<
protected @Nullable RECENTS_CONTAINER mContainer;
protected @Nullable RECENTS_VIEW mRecentsView;
protected Runnable mGestureEndCallback;
+ protected Runnable mGestureAnimationEndCallback;
protected MultiStateCallback mStateCallback;
protected boolean mCanceled;
private boolean mRecentsViewScrollLinked = false;
@@ -347,15 +349,7 @@ public abstract class AbsSwipeUpHandler<
private final SwipePipToHomeAnimator[] mSwipePipToHomeAnimators =
new SwipePipToHomeAnimator[2];
- private final Runnable mLauncherOnDestroyCallback = () -> {
- ActiveGestureProtoLogProxy.logLauncherDestroyed();
- mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
- mRecentsView = null;
- mContainer = null;
- mStateCallback.clearState(STATE_LAUNCHER_PRESENT);
- mRecentsAnimationStartCallbacks.clear();
- mTaskAnimationManager.onLauncherDestroyed();
- };
+ private final Runnable mLauncherOnDestroyCallback;
// Interpolate RecentsView scale from start of quick switch scroll until this scroll threshold
private final float mQuickSwitchScaleScrollThreshold;
@@ -380,16 +374,30 @@ public abstract class AbsSwipeUpHandler<
private final MSDLPlayerWrapper mMSDLPlayerWrapper;
+ private final RotationTouchHelper mRotationTouchHelper;
+
public AbsSwipeUpHandler(Context context,
- TaskAnimationManager taskAnimationManager, GestureState gestureState,
+ TaskAnimationManager taskAnimationManager, RecentsAnimationDeviceState deviceState,
+ RotationTouchHelper rotationTouchHelper, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
InputConsumerController inputConsumer,
MSDLPlayerWrapper msdlPlayerWrapper) {
- super(context, gestureState);
- mDeviceState = RecentsAnimationDeviceState.INSTANCE.get(mContext);
+ super(context, gestureState, rotationTouchHelper);
mContainerInterface = gestureState.getContainerInterface();
mContextInitListener =
mContainerInterface.createActivityInitListener(this::onActivityInit);
+ mLauncherOnDestroyCallback = () -> {
+ ActiveGestureProtoLogProxy.logLauncherDestroyed();
+ mContextInitListener.unregister("AbsSwipeUpHandler.mLauncherOnDestroyCallback");
+ if (mRecentsView != null) {
+ mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
+ mRecentsView = null;
+ }
+ mContainer = null;
+ mStateCallback.clearState(STATE_LAUNCHER_PRESENT);
+ mRecentsAnimationStartCallbacks.clear();
+ mTaskAnimationManager.onLauncherDestroyed();
+ };
mInputConsumerProxy =
new InputConsumerProxy(context, /* rotationSupplier = */ () -> {
if (mRecentsView == null) {
@@ -401,8 +409,10 @@ public abstract class AbsSwipeUpHandler<
endLauncherTransitionController();
}, new InputProxyHandlerFactory(mContainerInterface, mGestureState));
mTaskAnimationManager = taskAnimationManager;
+ mDeviceState = deviceState;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
+ mRotationTouchHelper = rotationTouchHelper;
Resources res = context.getResources();
mQuickSwitchScaleScrollThreshold = res
@@ -588,7 +598,7 @@ public abstract class AbsSwipeUpHandler<
private void onLauncherStart() {
final RECENTS_CONTAINER container = mContainerInterface.getCreatedContainer();
- if (container == null || mContainer != container) {
+ if (container == null || mContainer != container || mRecentsView == null) {
return;
}
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
@@ -670,9 +680,9 @@ public abstract class AbsSwipeUpHandler<
mGestureState.getContainerInterface().setOnDeferredActivityLaunchCallback(
mOnDeferredActivityLaunch);
- mGestureState.runOnceAtState(STATE_END_TARGET_SET, () ->
- RotationTouchHelper.INSTANCE.get(mContext)
- .onEndTargetCalculated(mGestureState.getEndTarget(), mContainerInterface));
+ mGestureState.runOnceAtState(STATE_END_TARGET_SET,
+ () -> mRotationTouchHelper.onEndTargetCalculated(mGestureState.getEndTarget(),
+ mContainerInterface));
notifyGestureStarted();
}
@@ -902,7 +912,8 @@ public abstract class AbsSwipeUpHandler<
public Intent getLaunchIntent() {
// todo differentiate intent based on if we are on home or in app for overview in window
- boolean useHomeIntentForWindow = RecentsWindowFlags.getEnableOverviewInWindow();
+ boolean useHomeIntentForWindow =
+ mContainerInterface.getCreatedContainer() instanceof RecentsWindowManager;
return useHomeIntentForWindow ? getHomeIntent() : mGestureState.getOverviewIntent();
}
/**
@@ -962,7 +973,21 @@ public abstract class AbsSwipeUpHandler<
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets, @Nullable TransitionInfo transitionInfo) {
super.onRecentsAnimationStart(controller, targets, transitionInfo);
- if (targets.hasDesktopTasks(mContext)) {
+ boolean forDesktop;
+ if (DesktopModeStatus.enableMultipleDesktops(mContext)) {
+ GroupedTaskInfo groupedTaskInfo;
+ if (mGestureState.getRunningTask() != null
+ && (groupedTaskInfo =
+ mGestureState.getRunningTask().getPlaceholderGroupedTaskInfo(
+ /* splitTaskIds = */ null)) != null) {
+ forDesktop = groupedTaskInfo.isBaseType(GroupedTaskInfo.TYPE_DESK);
+ } else {
+ forDesktop = false;
+ }
+ } else {
+ forDesktop = targets.hasDesktopTasks(mContext);
+ }
+ if (forDesktop) {
mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets, transitionInfo);
} else {
int untrimmedAppCount = mRemoteTargetHandles.length;
@@ -993,9 +1018,11 @@ public abstract class AbsSwipeUpHandler<
.getOrientationState();
DeviceProfile dp = orientationState.getLauncherDeviceProfile(
mGestureState.getDisplayId());
- if (targets.minimizedHomeBounds != null && primaryTaskTarget != null) {
- Rect overviewStackBounds = mContainerInterface
- .getOverviewWindowBounds(targets.minimizedHomeBounds, primaryTaskTarget);
+ Rect overviewStackBounds = mContainerInterface.getOverviewWindowBounds(
+ targets.minimizedHomeBounds, primaryTaskTarget);
+ if (overviewStackBounds != null
+ && !overviewStackBounds.isEmpty()
+ && primaryTaskTarget != null) {
dp = dp.getMultiWindowProfile(mContext,
new WindowBounds(overviewStackBounds, targets.homeContentInsets));
} else {
@@ -1004,7 +1031,7 @@ public abstract class AbsSwipeUpHandler<
}
dp.updateInsets(targets.homeContentInsets);
initTransitionEndpoints(dp);
- orientationState.setMultiWindowMode(dp.isMultiWindowMode);
+ orientationState.setMultiWindowMode(dp.getDeviceProperties().isMultiWindowMode());
}
// Notify when the animation starts
@@ -1046,12 +1073,10 @@ public abstract class AbsSwipeUpHandler<
}
mHandled = true;
- InteractionJankMonitorWrapper.begin(mRecentsView, Cuj.CUJ_LAUNCHER_QUICK_SWITCH,
- 2000 /* ms timeout */);
- InteractionJankMonitorWrapper.begin(mRecentsView,
- Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
- InteractionJankMonitorWrapper.begin(mRecentsView,
- Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
+ InteractionJankMonitorWrapper.begin(
+ rv, Cuj.CUJ_LAUNCHER_QUICK_SWITCH, /* timeoutMs= */ 2000);
+ InteractionJankMonitorWrapper.begin(rv, Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
+ InteractionJankMonitorWrapper.begin(rv, Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
rv.post(() -> rv.getViewTreeObserver().removeOnDrawListener(this));
}
@@ -1519,7 +1544,7 @@ public abstract class AbsSwipeUpHandler<
}
private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTaskView) {
- if (mDp == null || !mDp.isGestureMode) {
+ if (mDp == null || !mDp.getDeviceProperties().isGestureMode()) {
// We probably never received an animation controller, skip logging.
return;
}
@@ -1662,21 +1687,26 @@ public abstract class AbsSwipeUpHandler<
boolean hasValidLeash = runningTaskTarget != null
&& runningTaskTarget.leash != null
&& runningTaskTarget.leash.isValid();
+ final boolean swipeUpInDesktopWindowing =
+ DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()
+ && runningTaskTarget != null
+ && runningTaskTarget.taskInfo.getWindowingMode()
+ == WINDOWING_MODE_FREEFORM;
boolean appCanEnterPip = !mDeviceState.isPipActive()
&& hasValidLeash
&& runningTaskTarget.allowEnterPip
&& runningTaskTarget.taskInfo.pictureInPictureParams != null
- && runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled();
+ && runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled()
+ && !swipeUpInDesktopWindowing;
HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(
cookies,
duration,
isTranslucent,
appCanEnterPip,
runningTaskTarget,
- !enableAdditionalHomeAnimations()
- || mRecentsView == null
+ mRecentsView == null
|| mRecentsView.getCurrentPage() == mRecentsView.getRunningTaskIndex()
- ? null : mRecentsView.getCurrentPageTaskView());
+ ? null : mRecentsView.getCurrentPageTaskView());
SwipePipToHomeAnimator swipePipToHomeAnimator = !mIsSwipeForSplit && appCanEnterPip
? createWindowAnimationToPip(homeAnimFactory, runningTaskTarget, start)
: null;
@@ -1753,8 +1783,10 @@ public abstract class AbsSwipeUpHandler<
mLauncherTransitionController = null;
if (mRecentsView != null) {
- mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget(),
- mRemoteTargetHandles);
+ AnimatorSet animatorSet = new AnimatorSet();
+ mRecentsView.onPrepareGestureEndAnimation(animatorSet, mGestureState.getEndTarget(),
+ mRemoteTargetHandles, /* isHandlingAtomicEvent= */ true);
+ animatorSet.setDuration(0).start();
}
} else {
AnimatorSet animatorSet = new AnimatorSet();
@@ -1796,9 +1828,10 @@ public abstract class AbsSwipeUpHandler<
animatorSet.play(windowAnim);
if (mRecentsView != null) {
mRecentsView.onPrepareGestureEndAnimation(
- mGestureState.isHandlingAtomicEvent() ? null : animatorSet,
+ animatorSet,
mGestureState.getEndTarget(),
- mRemoteTargetHandles);
+ mRemoteTargetHandles,
+ mGestureState.isHandlingAtomicEvent());
}
animatorSet.setDuration(duration).setInterpolator(interpolator);
animatorSet.start();
@@ -1950,24 +1983,26 @@ public abstract class AbsSwipeUpHandler<
private Rect getKeepClearAreaForHotseat() {
Rect keepClearArea;
+ final int heightPx = mDp.getDeviceProperties().getHeightPx();
+ final int widthPx = mDp.getDeviceProperties().getWidthPx();
// the keep clear area in global screen coordinates, in pixels
- if (mDp.isPhone) {
+ if (mDp.getDeviceProperties().isPhone()) {
if (mDp.isSeascape()) {
// in seascape the Hotseat is on the left edge of the screen
- keepClearArea = new Rect(0, 0, mDp.hotseatBarSizePx, mDp.heightPx);
- } else if (mDp.isLandscape) {
+ keepClearArea = new Rect(0, 0, mDp.hotseatBarSizePx, heightPx);
+ } else if (mDp.getDeviceProperties().isLandscape()) {
// in landscape the Hotseat is on the right edge of the screen
- keepClearArea = new Rect(mDp.widthPx - mDp.hotseatBarSizePx, 0,
- mDp.widthPx, mDp.heightPx);
+ keepClearArea = new Rect(widthPx - mDp.hotseatBarSizePx, 0,
+ widthPx, heightPx);
} else {
// in portrait mode the Hotseat is at the bottom of the screen
- keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx,
- mDp.widthPx, mDp.heightPx);
+ keepClearArea = new Rect(0, heightPx - mDp.hotseatBarSizePx,
+ widthPx, heightPx);
}
} else {
// large screens have Hotseat always at the bottom of the screen
- keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx,
- mDp.widthPx, mDp.heightPx);
+ keepClearArea = new Rect(0, heightPx - mDp.hotseatBarSizePx,
+ widthPx, heightPx);
}
return keepClearArea;
}
@@ -2145,6 +2180,9 @@ public abstract class AbsSwipeUpHandler<
if (mRecentsView != null) {
mRecentsView.onGestureAnimationEnd();
}
+ if (mGestureAnimationEndCallback != null) {
+ mGestureAnimationEndCallback.run();
+ }
resetLauncherListeners();
}
@@ -2305,7 +2343,7 @@ public abstract class AbsSwipeUpHandler<
mSwipePipToHomeAnimator.getFinishTransaction(),
mSwipePipToHomeAnimator.getContentOverlay());
mIsSwipingPipToHome = false;
- } else if (mIsSwipeForSplit && !Flags.enablePip2()) {
+ } else if (mIsSwipeForSplit && !PipFlags.isPip2ExperimentEnabled()) {
// Transaction to hide the task to avoid flicker for entering PiP from split-screen.
// Note: PiP2 handles entering differently, so skip if enable_pip2=true
PictureInPictureSurfaceTransaction tx =
@@ -2365,6 +2403,10 @@ public abstract class AbsSwipeUpHandler<
mGestureEndCallback = gestureEndCallback;
}
+ public void setGestureAnimationEndCallback(Runnable gestureAnimationEndCallback) {
+ mGestureAnimationEndCallback = gestureAnimationEndCallback;
+ }
+
protected void linkRecentsViewScroll() {
if (mRecentsView == null) {
return;
@@ -2680,7 +2722,8 @@ public abstract class AbsSwipeUpHandler<
transaction.setAlpha(app.leash, 1f - fadeProgress);
transaction.setPosition(app.leash,
/* x= */ app.startBounds.left
- + (mContainer.getDeviceProfile().overviewPageSpacing
+ + (
+ mContainer.getDeviceProfile().getOverviewProfile().getPageSpacing()
* (mRecentsView.isRtl() ? fadeProgress : -fadeProgress)),
/* y= */ 0f);
transaction.setScale(app.leash, 1f, 1f);
@@ -2731,7 +2774,7 @@ public abstract class AbsSwipeUpHandler<
// Scaling of RecentsView during quick switch based on amount of recents scroll
private float getScaleProgressDueToScroll() {
- if (mContainer == null || !mContainer.getDeviceProfile().isTablet || mRecentsView == null
+ if (mContainer == null || !mContainer.getDeviceProfile().getDeviceProperties().isTablet() || mRecentsView == null
|| !shouldLinkRecentsViewScroll()) {
return 0;
}
@@ -2766,7 +2809,11 @@ public abstract class AbsSwipeUpHandler<
*/
@Override
protected float overrideDisplacementForTransientTaskbar(float displacement) {
- if (!mIsTransientTaskbar) {
+ boolean shouldReturnDisplacement = mContainerInterface.getTaskbarController() == null
+ ? !mIsTransientTaskbar
+ : !mContainerInterface.getTaskbarController().shouldAllowTaskbarToAutoStash();
+
+ if (shouldReturnDisplacement) {
return displacement;
}
@@ -2797,6 +2844,7 @@ public abstract class AbsSwipeUpHandler<
}
public interface Factory {
- AbsSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs);
+ @Nullable
+ AbsSwipeUpHandler, ?, ?> newHandler(GestureState gestureState, long touchTimeMs);
}
}
diff --git a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
index b807a4bfe3..51a68d16da 100644
--- a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
+++ b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
@@ -27,6 +27,7 @@ import android.view.accessibility.AccessibilityManager
import com.android.launcher3.R
import com.android.launcher3.util.SettingsCache
import com.android.launcher3.util.SettingsCache.OnChangeListener
+import com.android.quickstep.input.QuickstepKeyGestureEventsManager
import java.util.concurrent.Executor
private val USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(USER_SETUP_COMPLETE)
@@ -41,6 +42,7 @@ private val USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(USER_SETUP_COMPL
class AllAppsActionManager(
private val context: Context,
private val bgExecutor: Executor,
+ private val quickstepKeyGestureEventsManager: QuickstepKeyGestureEventsManager,
private val createAllAppsPendingIntent: () -> PendingIntent,
) {
@@ -92,17 +94,22 @@ class AllAppsActionManager(
val accessibilityManager =
context.getSystemService(AccessibilityManager::class.java) ?: return@execute
if (shouldRegisterAction) {
+ val allAppsPendingIntent = createAllAppsPendingIntent()
accessibilityManager.registerSystemAction(
RemoteAction(
Icon.createWithResource(context, R.drawable.ic_apps),
context.getString(R.string.all_apps_label),
context.getString(R.string.all_apps_label),
- createAllAppsPendingIntent(),
+ allAppsPendingIntent,
),
GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS,
)
+ quickstepKeyGestureEventsManager.registerAllAppsKeyGestureEvent(
+ allAppsPendingIntent
+ )
} else {
accessibilityManager.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS)
+ quickstepKeyGestureEventsManager.unregisterAllAppsKeyGestureEvent()
}
}
}
@@ -112,6 +119,7 @@ class AllAppsActionManager(
context
.getSystemService(AccessibilityManager::class.java)
?.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS)
+ quickstepKeyGestureEventsManager.unregisterAllAppsKeyGestureEvent()
SettingsCache.INSTANCE[context].unregister(
USER_SETUP_COMPLETE_URI,
onSettingsChangeListener,
diff --git a/quickstep/src/com/android/quickstep/AspectRatioSystemShortcut.kt b/quickstep/src/com/android/quickstep/AspectRatioSystemShortcut.kt
index 68860ac91b..9013ff082b 100644
--- a/quickstep/src/com/android/quickstep/AspectRatioSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/AspectRatioSystemShortcut.kt
@@ -26,7 +26,7 @@ import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.popup.SystemShortcut
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskContainer
-import com.android.window.flags.Flags.universalResizableByDefault
+import com.android.window.flags2.Flags.universalResizableByDefault
/**
* System shortcut to change the application's aspect ratio compatibility mode.
@@ -58,8 +58,7 @@ class AspectRatioSystemShortcut(
}
mTarget.startActivitySafely(view, intent, mItemInfo)
- mTarget
- .statsLogManager
+ mTarget.statsLogManager
.logger()
.withItemInfo(mItemInfo)
.log(LauncherEvent.LAUNCHER_ASPECT_RATIO_SETTINGS_SYSTEM_SHORTCUT_TAP)
@@ -81,7 +80,7 @@ class AspectRatioSystemShortcut(
!universalResizableByDefault() -> null
// The option is only shown on sw600dp+ screens (checked by isTablet)
- !viewContainer.deviceProfile.isTablet -> null
+ !viewContainer.deviceProfile.deviceProperties.isTablet -> null
else -> {
listOf(
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 549c2f8e34..c387856245 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -18,9 +18,7 @@ package com.android.quickstep;
import static com.android.app.animation.Interpolators.ACCELERATE_2;
import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
-import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_ATTACHED_ALPHA_ANIM;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
@@ -33,14 +31,12 @@ import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.view.MotionEvent;
import androidx.annotation.Nullable;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarUIController;
@@ -71,37 +67,6 @@ public abstract class BaseActivityInterface Float = ::compu
companion object {
// computeCornerRadius is used as cornerRadiusProvider, so
// QuickStepContract::getWindowCornerRadius can be mocked properly.
- private fun computeCornerRadius(context: Context): Float =
+ fun computeCornerRadius(context: Context): Float =
context.resources.getDimension(R.dimen.desktop_windowing_freeform_rounded_corner_radius)
}
}
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index 4280baf21d..4fe3944258 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -16,12 +16,14 @@
package com.android.quickstep
+import android.view.Display
import android.view.View
import com.android.internal.jank.Cuj
import com.android.launcher3.AbstractFloatingViewHelper
import com.android.launcher3.R
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.popup.SystemShortcut
+import com.android.quickstep.fallback.window.RecentsWindowFlags.enableDesktopMenuOnSecondaryDisplay
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskContainer
@@ -65,8 +67,9 @@ class DesktopSystemShortcut(
@JvmOverloads
fun createFactory(
abstractFloatingViewHelper: AbstractFloatingViewHelper = AbstractFloatingViewHelper()
- ): TaskShortcutFactory {
- return object : TaskShortcutFactory {
+ ): TaskShortcutFactory =
+ object : TaskShortcutFactory {
+
override fun getShortcuts(
container: RecentsViewContainer,
taskContainer: TaskContainer,
@@ -74,20 +77,28 @@ class DesktopSystemShortcut(
val context = container.asContext()
val taskKey = taskContainer.task.key
val desktopModeCompatPolicy = DesktopModeCompatPolicy(context)
- return when {
- !DesktopModeStatus.canEnterDesktopMode(context) -> null
+ val isShortcutSupported =
+ enableDesktopMenuOnSecondaryDisplay ||
+ context.displayId == Display.DEFAULT_DISPLAY
- desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ return when {
+ !isShortcutSupported -> null
+
+ !DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ context.display,
+ ) -> null
+
+ desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(
taskKey.baseActivity?.packageName,
taskKey.numActivities,
taskKey.isTopActivityNoDisplay,
taskKey.isActivityStackTransparent,
- taskKey.userId,
) -> null
!taskContainer.task.isDockable -> null
- else -> {
+ else ->
listOf(
DesktopSystemShortcut(
container,
@@ -95,12 +106,10 @@ class DesktopSystemShortcut(
abstractFloatingViewHelper,
)
)
- }
}
}
override fun showForGroupedTask() = true
}
- }
}
}
diff --git a/quickstep/src/com/android/quickstep/DisplayModel.kt b/quickstep/src/com/android/quickstep/DisplayModel.kt
index 0b8af4045b..af2a43898a 100644
--- a/quickstep/src/com/android/quickstep/DisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/DisplayModel.kt
@@ -21,15 +21,21 @@ import android.hardware.display.DisplayManager
import android.util.Log
import android.util.SparseArray
import android.view.Display
+import android.window.DesktopExperienceFlags
import androidx.core.util.valueIterator
+import com.android.app.displaylib.DisplayDecorationListener
+import com.android.app.displaylib.DisplaysWithDecorationsRepositoryCompat
import com.android.quickstep.DisplayModel.DisplayResource
-import com.android.quickstep.SystemDecorationChangeObserver.Companion.INSTANCE
-import com.android.quickstep.SystemDecorationChangeObserver.DisplayDecorationListener
import java.io.PrintWriter
+import kotlinx.coroutines.CoroutineDispatcher
/** data model for managing resources with lifecycles that match that of the connected display */
-abstract class DisplayModel(val context: Context) :
- DisplayDecorationListener {
+abstract class DisplayModel(
+ val context: Context,
+ private val systemDecorationChangeObserver: SystemDecorationChangeObserver,
+ private val displaysWithDecorationsRepositoryCompat: DisplaysWithDecorationsRepositoryCompat,
+ private val dispatcher: CoroutineDispatcher,
+) : DisplayDecorationListener {
companion object {
private const val TAG = "DisplayModel"
@@ -37,8 +43,10 @@ abstract class DisplayModel(val context: Contex
}
private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
- private var systemDecorationChangeObserver: SystemDecorationChangeObserver? = null
- protected val displayResourceArray = SparseArray()
+ private val displayResourceArray = SparseArray()
+ private val useDisplayDecorationListener: Boolean =
+ DesktopExperienceFlags.ENABLE_SYS_DECORS_CALLBACKS_VIA_WM.isTrue() &&
+ DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()
override fun onDisplayAddSystemDecorations(displayId: Int) {
if (DEBUG) Log.d(TAG, "onDisplayAdded: displayId=$displayId")
@@ -58,16 +66,25 @@ abstract class DisplayModel(val context: Contex
protected abstract fun createDisplayResource(display: Display): RESOURCE_TYPE
protected fun initializeDisplays() {
- systemDecorationChangeObserver = INSTANCE[context]
- systemDecorationChangeObserver?.registerDisplayDecorationListener(this)
+ if (useDisplayDecorationListener) {
+ displaysWithDecorationsRepositoryCompat.registerDisplayDecorationListener(
+ this,
+ dispatcher,
+ )
+ } else {
+ systemDecorationChangeObserver.registerDisplayDecorationListener(this)
+ }
displayManager.displays
.filter { getDisplayResource(it.displayId) == null }
.forEach { storeDisplayResource(it.displayId) }
}
fun destroy() {
- systemDecorationChangeObserver?.unregisterDisplayDecorationListener(this)
- systemDecorationChangeObserver = null
+ if (useDisplayDecorationListener) {
+ displaysWithDecorationsRepositoryCompat.unregisterDisplayDecorationListener(this)
+ } else {
+ systemDecorationChangeObserver.unregisterDisplayDecorationListener(this)
+ }
displayResourceArray.valueIterator().forEach { displayResource ->
displayResource.cleanup()
}
diff --git a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
index 3b823f5e60..3b20ae18b1 100644
--- a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
@@ -24,7 +24,7 @@ import com.android.launcher3.popup.SystemShortcut
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskContainer
-import com.android.window.flags.Flags
+import com.android.window.flags2.Flags
import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -75,12 +75,11 @@ class ExternalDisplaySystemShortcut(
!Flags.moveToExternalDisplayShortcut() -> null
- desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ desktopModeCompatPolicy.shouldDisableDesktopEntryPoints(
taskKey.baseActivity?.packageName,
taskKey.numActivities,
taskKey.isTopActivityNoDisplay,
taskKey.isActivityStackTransparent,
- taskKey.userId,
) -> null
else -> {
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 7654471555..740e2d6647 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -27,12 +27,14 @@ import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.taskbar.FallbackTaskbarUIController;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.views.ScrimColors;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
@@ -63,9 +65,9 @@ public final class FallbackActivityInterface extends
RecentsPagedOrientationHandler orientationHandler) {
calculateTaskSize(context, dp, outRect, orientationHandler);
if (dp.isVerticalBarLayout() && DisplayController.getNavigationMode(context) != NO_BUTTON) {
- return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
+ return dp.isSeascape() ? outRect.left : (dp.getDeviceProperties().getWidthPx() - outRect.right);
} else {
- return dp.heightPx - outRect.bottom;
+ return dp.getDeviceProperties().getHeightPx() - outRect.bottom;
}
}
@@ -133,7 +135,8 @@ public final class FallbackActivityInterface extends
}
@Override
- public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+ public boolean deferStartingActivity(
+ @NonNull RecentsAnimationDeviceState deviceState, MotionEvent ev) {
// In non-gesture mode, user might be clicking on the home button which would directly
// start the home activity instead of going through recents. In that case, defer starting
// recents until we are sure it is a gesture.
@@ -225,7 +228,8 @@ public final class FallbackActivityInterface extends
}
@Override
- protected int getOverviewScrimColorForState(RecentsActivity activity, RecentsState state) {
+ protected ScrimColors getOverviewScrimColorForState(RecentsActivity activity,
+ RecentsState state) {
return state.getScrimColor(activity);
}
}
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index fc37091f86..d29a556155 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -20,6 +20,7 @@ import static android.content.Intent.EXTRA_COMPONENT_NAME;
import static android.content.Intent.EXTRA_USER;
import static com.android.app.animation.Interpolators.ACCELERATE;
+import static com.android.launcher3.GestureNavContract.EXTRA_ENABLE_GESTURE_CONTRACT;
import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT;
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION;
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
@@ -107,12 +108,13 @@ public class FallbackSwipeHandler extends
private boolean mAppCanEnterPip;
- public FallbackSwipeHandler(Context context,
- TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
+ public FallbackSwipeHandler(Context context, TaskAnimationManager taskAnimationManager,
+ RecentsAnimationDeviceState deviceState, RotationTouchHelper rotationTouchHelper,
+ GestureState gestureState, long touchTimeMs,
boolean continuingLastGesture, InputConsumerController inputConsumer,
MSDLPlayerWrapper msdlPlayerWrapper) {
- super(context, taskAnimationManager, gestureState, touchTimeMs,
- continuingLastGesture, inputConsumer, msdlPlayerWrapper);
+ super(context, taskAnimationManager, deviceState, rotationTouchHelper, gestureState,
+ touchTimeMs, continuingLastGesture, inputConsumer, msdlPlayerWrapper);
mRunningOverHome = mGestureState.getRunningTask() != null
&& mGestureState.getRunningTask().isHomeTask();
@@ -203,7 +205,9 @@ public class FallbackSwipeHandler extends
} else {
recentsCallback = callback;
}
- mRecentsView.cleanupRemoteTargets();
+ if (mRecentsView != null) {
+ mRecentsView.cleanupRemoteTargets();
+ }
mRecentsAnimationController.finish(
mAppCanEnterPip /* toRecents */, recentsCallback, true /* sendUserLeaveHint */);
}
@@ -236,7 +240,7 @@ public class FallbackSwipeHandler extends
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
// copied from {@link LauncherSwipeHandlerV2.LauncherHomeAnimationFactory}
- long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+ long accuracy = 2 * Math.max(mDp.getDeviceProperties().getWidthPx(), mDp.getDeviceProperties().getHeightPx());
return mContainer.getStateManager().createAnimationToNewWorkspace(
RecentsState.HOME, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS);
}
@@ -345,7 +349,7 @@ public class FallbackSwipeHandler extends
.setStartValue(mVerticalShiftForScale.value)
.setEndValue(0)
.setStartVelocity(-velocity / mTransitionDragLength)
- .setMinimumVisibleChange(1f / mDp.heightPx)
+ .setMinimumVisibleChange(1f / mDp.getDeviceProperties().getHeightPx())
.setDampingRatio(0.6f)
.setStiffness(800)
.build(mVerticalShiftForScale, AnimatedFloat.VALUE)
@@ -419,6 +423,7 @@ public class FallbackSwipeHandler extends
}
Bundle gestureNavContract = new Bundle();
+ gestureNavContract.putBoolean(EXTRA_ENABLE_GESTURE_CONTRACT, !mIsSwipeForSplit);
gestureNavContract.putParcelable(EXTRA_COMPONENT_NAME, key.getComponent());
gestureNavContract.putParcelable(EXTRA_USER, UserHandle.of(key.userId));
gestureNavContract.putParcelable(
diff --git a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
index 143da34ae8..330f7f1730 100644
--- a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
@@ -24,16 +24,19 @@ import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
-import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
+import com.android.app.displaylib.PerDisplayRepository;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.taskbar.TaskbarUIController;
+import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.views.ScrimColors;
import com.android.quickstep.GestureState.GestureEndTarget;
+import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
@@ -41,20 +44,31 @@ import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.ContextInitListener;
import com.android.quickstep.views.RecentsView;
+import dagger.assisted.AssistedInject;
+
import java.util.function.Consumer;
import java.util.function.Predicate;
+
/**
* {@link BaseWindowInterface} for recents when the default launcher is different than the
* currently running one and apps should interact with the {@link RecentsWindowManager} as opposed
* to the in-launcher one.
*/
-public final class FallbackWindowInterface extends BaseWindowInterface{
+public final class FallbackWindowInterface extends BaseWindowInterface {
- private final RecentsWindowManager mRecentsWindowManager;
+ public static final DaggerSingletonObject>
+ REPOSITORY_INSTANCE = new DaggerSingletonObject<>(
+ QuickstepBaseAppComponent::getFallbackWindowInterfaceRepository);
- public FallbackWindowInterface(RecentsWindowManager recentsWindowManager) {
+ @Nullable private RecentsWindowManager mRecentsWindowManager = null;
+
+ @AssistedInject
+ public FallbackWindowInterface() {
super(DEFAULT, BACKGROUND_APP);
+ }
+
+ public void setRecentsWindowManager(@Nullable RecentsWindowManager recentsWindowManager) {
mRecentsWindowManager = recentsWindowManager;
}
@@ -64,9 +78,9 @@ public final class FallbackWindowInterface extends BaseWindowInterface{
RecentsPagedOrientationHandler orientationHandler) {
calculateTaskSize(context, dp, outRect, orientationHandler);
if (dp.isVerticalBarLayout() && DisplayController.getNavigationMode(context) != NO_BUTTON) {
- return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
+ return dp.isSeascape() ? outRect.left : (dp.getDeviceProperties().getWidthPx() - outRect.right);
} else {
- return dp.heightPx - outRect.bottom;
+ return dp.getDeviceProperties().getHeightPx() - outRect.bottom;
}
}
@@ -119,8 +133,8 @@ public final class FallbackWindowInterface extends BaseWindowInterface{
@Override
public > T getVisibleRecentsView() {
RecentsWindowManager manager = getCreatedContainer();
- if(manager.isStarted() || isInLiveTileMode()){
- return getCreatedContainer().getOverviewPanel();
+ if (manager != null && (manager.isStarted() || isInLiveTileMode())) {
+ return manager.getOverviewPanel();
}
return null;
}
@@ -131,26 +145,17 @@ public final class FallbackWindowInterface extends BaseWindowInterface{
}
@Override
- protected int getOverviewScrimColorForState(RecentsWindowManager container,
- RecentsState state) {
+ protected ScrimColors getOverviewScrimColorForState(RecentsWindowManager container,
+ RecentsState state) {
return state.getScrimColor(container.asContext());
}
- @Override
- public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
- // In non-gesture mode, user might be clicking on the home button which would directly
- // start the home activity instead of going through recents. In that case, defer starting
- // recents until we are sure it is a gesture.
- return false;
-// return !deviceState.isFullyGesturalNavMode();
-// || super.deferStartingActivity(deviceState, ev);
- }
-
@Override
public void onExitOverview(Runnable exitRunnable) {
+ RecentsWindowManager windowManager = getCreatedContainer();
final StateManager stateManager =
- getCreatedContainer().getStateManager();
- if (stateManager.getState() == HOME) {
+ windowManager != null ? windowManager.getStateManager() : null;
+ if (stateManager == null || stateManager.getState() == HOME) {
exitRunnable.run();
notifyRecentsOfOrientation();
return;
@@ -203,8 +208,11 @@ public final class FallbackWindowInterface extends BaseWindowInterface{
}
private void notifyRecentsOfOrientation() {
- // reset layout on swipe to home
- ((RecentsView) getCreatedContainer().getOverviewPanel()).reapplyActiveRotation();
+ RecentsWindowManager recentsWindowManager = getCreatedContainer();
+ if (recentsWindowManager != null) {
+ // reset layout on swipe to home
+ ((RecentsView) recentsWindowManager.getOverviewPanel()).reapplyActiveRotation();
+ }
}
@Override
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index c4ba2d52bb..2492ac3aa8 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -41,7 +41,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulContainer;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
-import com.android.quickstep.fallback.window.RecentsWindowFlags;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -194,7 +194,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
public GestureState(OverviewComponentObserver componentObserver, int displayId, int gestureId) {
mDisplayId = displayId;
- mHomeIntent = componentObserver.getHomeIntent();
+ mHomeIntent = componentObserver.getHomeIntent(displayId);
mOverviewIntent = componentObserver.getOverviewIntent();
mContainerInterface = componentObserver.getContainerInterface(displayId);
mStateCallback = new MultiStateCallback(
@@ -323,7 +323,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
*/
public boolean useSyntheticRecentsTransition() {
return mRunningTask.isHomeTask()
- && RecentsWindowFlags.Companion.getEnableOverviewInWindow();
+ && mContainerInterface.getCreatedContainer() instanceof RecentsWindowManager;
}
/**
@@ -361,7 +361,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
* @return the single top-most running taskId for this gesture
*/
public int getTopRunningTaskId() {
- return getRunningTaskIds(false /*getMultipleTasks*/)[0];
+ var taskIds = getRunningTaskIds(/* getMultipleTasks = */ false);
+ return taskIds.length != 0 ? taskIds[0] : INVALID_TASK_ID;
}
/**
diff --git a/quickstep/src/com/android/quickstep/HighResLoadingState.kt b/quickstep/src/com/android/quickstep/HighResLoadingState.kt
index 8a21c4f613..aabaecfdfe 100644
--- a/quickstep/src/com/android/quickstep/HighResLoadingState.kt
+++ b/quickstep/src/com/android/quickstep/HighResLoadingState.kt
@@ -18,6 +18,7 @@ package com.android.quickstep
import android.content.res.Resources
import com.android.quickstep.recents.data.HighResLoadingStateNotifier
+import java.util.concurrent.CopyOnWriteArrayList
/** Determines when high res or low res thumbnails should be loaded. */
class HighResLoadingState : HighResLoadingStateNotifier {
@@ -38,7 +39,7 @@ class HighResLoadingState : HighResLoadingStateNotifier {
var isEnabled: Boolean = false
private set
- private val callbacks = ArrayList()
+ private val callbacks = CopyOnWriteArrayList()
interface HighResLoadingStateChangedCallback {
fun onHighResLoadingStateChanged(enabled: Boolean)
@@ -56,7 +57,7 @@ class HighResLoadingState : HighResLoadingStateNotifier {
val prevState = isEnabled
isEnabled = forceHighResThumbnails || (visible && !flingingFast)
if (prevState != isEnabled) {
- for (callback in callbacks.asReversed()) {
+ for (callback in callbacks) {
callback.onHighResLoadingStateChanged(isEnabled)
}
}
diff --git a/quickstep/src/com/android/quickstep/HomeVisibilityState.kt b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
index 020b9e2fab..253b434cb9 100644
--- a/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
+++ b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
@@ -20,7 +20,6 @@ import android.os.RemoteException
import android.util.Log
import android.view.InsetsState
import android.view.WindowInsets
-
import com.android.launcher3.Utilities
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.util.Executors
@@ -47,18 +46,25 @@ class HomeVisibilityState {
transitions?.setHomeTransitionListener(
object : Stub() {
override fun onHomeVisibilityChanged(isVisible: Boolean) {
- Utilities.postAsyncCallback(
- Executors.MAIN_EXECUTOR.handler,
- {
- isHomeVisible = isVisible
- listeners.forEach { it.onHomeVisibilityChanged(isVisible) }
- },
- )
+ Utilities.postAsyncCallback(Executors.MAIN_EXECUTOR.handler) {
+ isHomeVisible = isVisible
+ val copiedListeners = listeners.toSet()
+ copiedListeners.forEach { it.onHomeVisibilityChanged(isVisible) }
+ }
}
+
override fun onDisplayInsetsChanged(insetsState: InsetsState) {
- val bottomInset = insetsState.calculateInsets(insetsState.displayFrame,
- WindowInsets.Type.navigationBars(), false).bottom
- navbarInsetPosition = insetsState.displayFrame.bottom - bottomInset
+ val displayFrame = insetsState.displayFrame
+ val bottomInset =
+ insetsState
+ .calculateInsets(
+ displayFrame,
+ displayFrame,
+ WindowInsets.Type.navigationBars(),
+ false,
+ )
+ .bottom
+ navbarInsetPosition = displayFrame.bottom - bottomInset
}
}
)
diff --git a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
index 89b5b291c6..549fffdfa5 100644
--- a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
+++ b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
@@ -23,6 +23,7 @@ import com.android.launcher3.statemanager.BaseState
import com.android.launcher3.statemanager.StatefulContainer
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.util.LockedUserState.Companion.get
+import com.android.quickstep.fallback.window.RecentsWindowManager
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer
import com.android.quickstep.inputconsumers.AssistantInputConsumer
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer
@@ -46,6 +47,7 @@ import com.android.quickstep.views.RecentsViewContainer
import com.android.systemui.shared.system.InputChannelCompat
import com.android.systemui.shared.system.InputMonitorCompat
import com.android.wm.shell.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopState
import java.util.function.Consumer
import java.util.function.Function
@@ -71,8 +73,9 @@ object InputConsumerUtils {
swipeUpProxyProvider: Function,
overviewCommandHelper: OverviewCommandHelper,
event: MotionEvent,
+ rotationTouchHelper: RotationTouchHelper,
): InputConsumer where T : RecentsViewContainer, T : StatefulContainer {
- val tac = taskbarManager.currentActivityContext
+ val tac = taskbarManager.getCurrentActivityContext()
val bubbleControllers = tac?.bubbleControllers
if (bubbleControllers != null && BubbleBarInputConsumer.isEventOnBubbles(tac, event)) {
val consumer: InputConsumer =
@@ -129,6 +132,7 @@ object InputConsumerUtils {
taskAnimationManager,
inputMonitorCompat,
reasonString.append("%scan start system gesture", SUBSTRING_PREFIX),
+ rotationTouchHelper,
)
} else {
getDefaultInputConsumer(
@@ -170,6 +174,7 @@ object InputConsumerUtils {
inputEventReceiver,
event,
reasonString,
+ rotationTouchHelper,
)
} else {
reasonString =
@@ -389,6 +394,7 @@ object InputConsumerUtils {
deviceState,
base,
inputMonitorCompat,
+ rotationTouchHelper,
)
}
} else {
@@ -496,6 +502,7 @@ object InputConsumerUtils {
inputEventReceiver: InputChannelCompat.InputEventReceiver,
event: MotionEvent,
reasonString: CompoundString,
+ rotationTouchHelper: RotationTouchHelper,
): InputConsumer where T : RecentsViewContainer, T : StatefulContainer {
if (deviceState.isKeyguardShowingOccluded) {
// This handles apps showing over the lockscreen (e.g. camera)
@@ -512,15 +519,17 @@ object InputConsumerUtils {
"trying to use device locked input consumer",
SUBSTRING_PREFIX,
),
+ rotationTouchHelper,
)
}
reasonString.append("%skeyguard is not showing occluded", SUBSTRING_PREFIX)
val runningTask = gestureState.runningTask
+ val containerInterface = gestureState.getContainerInterface()
// Use overview input consumer for sharesheets on top of home.
val forceOverviewInputConsumer =
- gestureState.getContainerInterface().isStarted() &&
+ containerInterface.isStarted() &&
runningTask != null &&
runningTask.isRootChooseActivity
@@ -542,19 +551,21 @@ object InputConsumerUtils {
deviceState.isPredictiveBackToHomeInProgress)
// with shell-transitions, home is resumed during recents animation, so
// explicitly check against recents animation too.
+ // Home is always running and isn't resumed when home shows behind desktop.
val launcherResumedThroughShellTransition =
- (gestureState.getContainerInterface().isResumed() &&
- !previousGestureState.isRecentsAnimationRunning)
+ containerInterface.isResumed() &&
+ !previousGestureState.isRecentsAnimationRunning &&
+ !DesktopState.fromContext(context).shouldShowHomeBehindDesktop
+
// If a task fragment within Launcher is resumed
val launcherChildActivityResumed =
- (com.android.launcher3.Flags.useActivityOverlay() &&
- runningTask != null &&
+ runningTask != null &&
runningTask.isHomeTask &&
- overviewComponentObserver.isHomeAndOverviewSameActivity &&
- !launcherResumedThroughShellTransition &&
- !previousGestureState.isRecentsAnimationRunning)
+ !previousGestureState.isRecentsAnimationRunning &&
+ overviewComponentObserver.isHomeAndOverviewSame &&
+ containerInterface.isLauncherOverlayShowing
- return if (gestureState.getContainerInterface().isInLiveTileMode()) {
+ return if (containerInterface.isInLiveTileMode()) {
createOverviewInputConsumer(
userUnlocked,
taskAnimationManager,
@@ -591,19 +602,18 @@ object InputConsumerUtils {
previousGestureState,
gestureState,
event,
- reasonString.append(
- if (previousGestureAnimatedToLauncher)
- ("%sprevious gesture animated to launcher, " +
- "trying to use overview input consumer")
- else
- (if (launcherResumedThroughShellTransition)
- ("%slauncher resumed through a shell transition, " +
- "trying to use overview input consumer")
- else
- ("%sforceOverviewInputConsumer == true, " +
- "trying to use overview input consumer")),
- SUBSTRING_PREFIX,
- ),
+ reasonString
+ .append(
+ if (previousGestureAnimatedToLauncher)
+ (if (previousGestureState.isRunningAnimationToLauncher)
+ "%sprevious gesture is still animating to launcher"
+ else "%spredictive back animation is still in progress")
+ else if (launcherResumedThroughShellTransition)
+ "%slauncher resumed through a shell transition"
+ else "%sforceOverviewInputConsumer == true",
+ SUBSTRING_PREFIX,
+ )
+ .append(", trying to use overview input consumer"),
)
} else if (deviceState.isGestureBlockedTask(runningTask) || launcherChildActivityResumed) {
getDefaultInputConsumer(
@@ -631,6 +641,8 @@ object InputConsumerUtils {
inputEventReceiver,
gestureState,
event,
+ runningTask.isHomeTask,
+ rotationTouchHelper,
)
}
}
@@ -644,6 +656,7 @@ object InputConsumerUtils {
taskAnimationManager: TaskAnimationManager,
inputMonitorCompat: InputMonitorCompat,
reasonString: CompoundString,
+ rotationTouchHelper: RotationTouchHelper,
): InputConsumer {
return if (
(deviceState.isFullyGesturalNavMode || gestureState.isTrackpadGesture) &&
@@ -660,6 +673,7 @@ object InputConsumerUtils {
taskAnimationManager,
gestureState,
inputMonitorCompat,
+ rotationTouchHelper,
)
} else {
getDefaultInputConsumer(
@@ -690,8 +704,9 @@ object InputConsumerUtils {
event: MotionEvent,
reasonString: CompoundString,
): InputConsumer where T : RecentsViewContainer, T : StatefulContainer {
+ val containerInterface = gestureState.getContainerInterface()!!
val container: T =
- gestureState.getContainerInterface().getCreatedContainer()
+ containerInterface.getCreatedContainer()
?: return getDefaultInputConsumer(
gestureState.displayId,
userUnlocked,
@@ -708,16 +723,16 @@ object InputConsumerUtils {
val isPreviousGestureAnimatingToLauncher =
(previousGestureState.isRunningAnimationToLauncher ||
deviceState.isPredictiveBackToHomeInProgress)
- val isInLiveTileMode: Boolean =
- gestureState.getContainerInterface().isInLiveTileMode()
+ val isInLiveTileMode: Boolean = containerInterface.isInLiveTileMode()
reasonString.append(
if (hasWindowFocus) "%sactivity has window focus"
- else
- (if (isPreviousGestureAnimatingToLauncher)
+ else if (isPreviousGestureAnimatingToLauncher)
+ (if (previousGestureState.isRunningAnimationToLauncher)
"%sprevious gesture is still animating to launcher"
- else if (isInLiveTileMode) "%sdevice is in live mode"
- else "%sall overview focus conditions failed"),
+ else "%spredictive back animation is still in progress")
+ else if (isInLiveTileMode) "%sdevice is in live mode"
+ else "%sall overview focus conditions failed",
SUBSTRING_PREFIX,
)
return if (hasWindowFocus || isPreviousGestureAnimatingToLauncher || isInLiveTileMode) {
@@ -790,13 +805,16 @@ object InputConsumerUtils {
inputEventReceiver: InputChannelCompat.InputEventReceiver,
gestureState: GestureState,
event: MotionEvent,
+ isHomeTask: Boolean,
+ rotationTouchHelper: RotationTouchHelper,
): InputConsumer where T : RecentsViewContainer, T : StatefulContainer {
+ val containerInterface = gestureState.getContainerInterface()
val shouldDefer =
(!overviewComponentObserver.isHomeAndOverviewSame ||
- gestureState
- .getContainerInterface()
- .deferStartingActivity(deviceState, event))
- val disableHorizontalSwipe = deviceState.isInExclusionRegion(event)
+ containerInterface.deferStartingActivity(deviceState, event))
+ val disableHorizontalSwipe =
+ deviceState.isInExclusionRegion(event) &&
+ (containerInterface.getCreatedContainer() !is RecentsWindowManager || !isHomeTask)
return OtherActivityInputConsumer(
/* base= */ context,
deviceState,
@@ -808,6 +826,7 @@ object InputConsumerUtils {
inputEventReceiver,
disableHorizontalSwipe,
swipeUpHandlerFactory,
+ rotationTouchHelper,
)
}
diff --git a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
index 33a2366157..bb208c49c7 100644
--- a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
+++ b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
@@ -24,9 +24,12 @@ import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.InstantAppResolver;
+import javax.inject.Inject;
+
/**
* Implementation of InstantAppResolver using platform APIs
*/
@@ -38,7 +41,9 @@ public class InstantAppResolverImpl extends InstantAppResolver {
private final PackageManager mPM;
- public InstantAppResolverImpl(Context context) {
+ @Inject
+ public InstantAppResolverImpl(@ApplicationContext Context context) {
+ super();
mPM = context.getPackageManager();
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index fa8e4846b3..4badf1007c 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -27,7 +27,6 @@ import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
-import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
@@ -45,12 +44,14 @@ import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
+import com.android.launcher3.views.ScrimColors;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
+import com.android.wm.shell.shared.desktopmode.DesktopState;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -73,7 +74,7 @@ public final class LauncherActivityInterface extends
calculateTaskSize(context, dp, outRect, orientationHandler);
if (dp.isVerticalBarLayout()
&& DisplayController.getNavigationMode(context) != NavigationMode.NO_BUTTON) {
- return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
+ return dp.isSeascape() ? outRect.left : (dp.getDeviceProperties().getWidthPx() - outRect.right);
} else {
return LayoutUtils.getShelfTrackingDistance(context, dp, orientationHandler, this);
}
@@ -211,6 +212,12 @@ public final class LauncherActivityInterface extends
if (launcher == null) {
return false;
}
+ if (DesktopState.fromContext(launcher.asContext()).getShouldShowHomeBehindDesktop()
+ && !launcher.hasWindowFocus()) {
+ // Home is always shown behind desktop, but it is currently not the top task, so treat
+ // it as if it is not visible.
+ return false;
+ }
if (isInLiveTileMode()) {
RecentsView recentsView = getVisibleRecentsView();
if (recentsView == null) {
@@ -320,29 +327,11 @@ public final class LauncherActivityInterface extends
}
@Override
- protected int getOverviewScrimColorForState(QuickstepLauncher activity, LauncherState state) {
+ protected ScrimColors getOverviewScrimColorForState(QuickstepLauncher activity,
+ LauncherState state) {
return state.getWorkspaceScrimColor(activity);
}
- @Override
- public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
- LauncherTaskbarUIController uiController = getTaskbarController();
- if (uiController == null) {
- return super.deferStartingActivity(deviceState, ev);
- }
- return uiController.isEventOverAnyTaskbarItem(ev)
- || super.deferStartingActivity(deviceState, ev);
- }
-
- @Override
- public boolean shouldCancelCurrentGesture() {
- LauncherTaskbarUIController uiController = getTaskbarController();
- if (uiController == null) {
- return super.shouldCancelCurrentGesture();
- }
- return uiController.isDraggingItem();
- }
-
@Override
public LauncherState stateFromGestureEndTarget(GestureEndTarget endTarget) {
switch (endTarget) {
@@ -358,4 +347,11 @@ public final class LauncherActivityInterface extends
return NORMAL;
}
}
+
+ @Override
+ public boolean isLauncherOverlayShowing() {
+ Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedContext();
+
+ return launcher != null && launcher.getWorkspace().isOverlayShown();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 261ad9fb7c..d545c47361 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -25,8 +25,9 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
-import static com.android.window.flags.Flags.predictiveBackThreeButtonNav;
-import static com.android.window.flags.Flags.removeDepartTargetFromMotion;
+import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
+import static com.android.window.flags2.Flags.predictiveBackThreeButtonNav;
+import static com.android.window.flags2.Flags.removeDepartTargetFromMotion;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -98,7 +99,6 @@ public class LauncherBackAnimationController {
Flags.predictiveBackToHomePolish() ? 0.75f : 0.85f;
private static final float MAX_SCRIM_ALPHA_DARK = 0.8f;
private static final float MAX_SCRIM_ALPHA_LIGHT = 0.2f;
- private static final int MAX_BLUR_RADIUS = 20;
private static final int MIN_BLUR_RADIUS_PRE_COMMIT = 10;
private final QuickstepTransitionManager mQuickstepTransitionManager;
@@ -130,6 +130,7 @@ public class LauncherBackAnimationController {
private ValueAnimator mScrimAlphaAnimator;
private float mScrimAlpha;
private boolean mOverridingStatusBarFlags;
+ private int mMaxBlurRadius;
private int mLastBlurRadius = 0;
private final ComponentCallbacks mComponentCallbacks = new ComponentCallbacks() {
@@ -423,7 +424,7 @@ public class LauncherBackAnimationController {
final float[] colorComponents = new float[] { 0f, 0f, 0f };
mScrimAlpha = (isDarkTheme)
? MAX_SCRIM_ALPHA_DARK : MAX_SCRIM_ALPHA_LIGHT;
- setBlur(MAX_BLUR_RADIUS);
+ setBlur(mMaxBlurRadius);
mTransaction
.setColor(mScrimLayer, colorComponents)
.setAlpha(mScrimLayer, mScrimAlpha)
@@ -451,7 +452,7 @@ public class LauncherBackAnimationController {
// Scrim hasn't been attached yet. Let's attach it.
addScrimLayer();
} else {
- mLastBlurRadius = (int) lerp(MAX_BLUR_RADIUS, MIN_BLUR_RADIUS_PRE_COMMIT, progress);
+ mLastBlurRadius = (int) lerp(mMaxBlurRadius, MIN_BLUR_RADIUS_PRE_COMMIT, progress);
setBlur(mLastBlurRadius);
}
float screenWidth = mStartRect.width();
@@ -611,7 +612,7 @@ public class LauncherBackAnimationController {
// Scrim hasn't been attached yet. Let's attach it.
addScrimLayer();
}
- mScrimAlphaAnimator = new ValueAnimator().ofFloat(1, 0);
+ mScrimAlphaAnimator = ValueAnimator.ofFloat(1, 0);
mScrimAlphaAnimator.addUpdateListener(animation -> {
float value = (Float) animation.getAnimatedValue();
if (mScrimLayer != null && mScrimLayer.isValid()) {
@@ -627,7 +628,7 @@ public class LauncherBackAnimationController {
}
});
mScrimAlphaAnimator.setDuration(SCRIM_FADE_DURATION).start();
- backAnim.start();
+ backAnim.start(mLauncher.getStateManager());
}
private void loadResources() {
@@ -637,8 +638,14 @@ public class LauncherBackAnimationController {
R.dimen.swipe_back_window_corner_radius)
: 0;
mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
-// mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mLauncher);
- mStatusBarHeight = 24;
+ // pE-TODO(QPR1): mStatusBarHeight is 24
+ mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mLauncher);
+ if (Flags.allAppsBlur() || enableOverviewBackgroundWallpaperBlur()) {
+ mMaxBlurRadius = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.max_depth_blur_radius_enhanced);
+ } else {
+ mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
+ }
}
/**
diff --git a/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
index e61f3792b0..8bb6e17b4b 100644
--- a/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
+++ b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
@@ -10,13 +10,17 @@ import com.android.launcher3.Flags.enableLauncherBrMetricsFixed
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.Utilities
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger
+import com.android.launcher3.dagger.ApplicationContext
+import javax.inject.Inject
/**
* Concrete implementation for wrapper to log Restore event metrics for both success and failure to
* restore Launcher workspace from a backup. This implementation accesses SystemApis so is only
* available to QuickStep/NexusLauncher.
*/
-class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEventLogger() {
+class LauncherRestoreEventLoggerImpl
+@Inject
+constructor(@ApplicationContext private val context: Context) : LauncherRestoreEventLogger() {
companion object {
const val TAG = "LauncherRestoreEventLoggerImpl"
@@ -47,7 +51,7 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
override fun logLauncherItemsRestoreFailed(
@BackupRestoreDataType dataType: String,
count: Int,
- @BackupRestoreError error: String?
+ @BackupRestoreError error: String?,
) {
if (enableLauncherBrMetricsFixed()) {
restoreEventLogger?.logItemsRestoreFailed(dataType, count, error)
@@ -97,7 +101,7 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
*/
override fun logSingleFavoritesItemRestoreFailed(
favoritesId: Int,
- @BackupRestoreError error: String?
+ @BackupRestoreError error: String?,
) {
if (enableLauncherBrMetricsFixed()) {
restoreEventLogger?.logItemsRestoreFailed(favoritesIdToDataType(favoritesId), 1, error)
@@ -114,13 +118,13 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
override fun logFavoritesItemsRestoreFailed(
favoritesId: Int,
count: Int,
- @BackupRestoreError error: String?
+ @BackupRestoreError error: String?,
) {
if (enableLauncherBrMetricsFixed()) {
restoreEventLogger?.logItemsRestoreFailed(
favoritesIdToDataType(favoritesId),
count,
- error
+ error,
)
}
}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 4c56f35d90..85a677f18b 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -36,6 +36,7 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.model.data.ItemInfo;
@@ -69,10 +70,11 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler<
QuickstepLauncher, RecentsView, LauncherState> {
public LauncherSwipeHandlerV2(Context context, TaskAnimationManager taskAnimationManager,
+ RecentsAnimationDeviceState deviceState, RotationTouchHelper rotationTouchHelper,
GestureState gestureState, long touchTimeMs, boolean continuingLastGesture,
InputConsumerController inputConsumer, MSDLPlayerWrapper msdlPlayerWrapper) {
- super(context, taskAnimationManager, gestureState, touchTimeMs,
- continuingLastGesture, inputConsumer, msdlPlayerWrapper);
+ super(context, taskAnimationManager, deviceState, rotationTouchHelper, gestureState,
+ touchTimeMs, continuingLastGesture, inputConsumer, msdlPlayerWrapper);
}
@@ -85,8 +87,13 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler<
RemoteAnimationTarget runningTaskTarget,
@Nullable TaskView targetTaskView) {
if (mContainer == null) {
- mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
- isPresent -> mRecentsView.startHome());
+ mStateCallback.addChangeListener(
+ STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+ isPresent -> {
+ if (mRecentsView != null) {
+ mRecentsView.startHome();
+ }
+ });
return new HomeAnimationFactory() {
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
@@ -140,7 +147,7 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler<
true /* hideOriginal */, iconLocation, false /* isOpening */);
// We want the window alpha to be 0 once this threshold is met, so that the
- // FolderIconView can be seen morphing into the icon shape.
+ // FloatingIconView can be seen morphing into the icon shape.
float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
return new FloatingViewHomeAnimationFactory(floatingIconView) {
@@ -188,7 +195,11 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler<
float progress,
float radius,
int overlayAlpha) {
- floatingIconView.update(1f /* alpha */, currentRect, progress, windowAlphaThreshold,
+ // We want the icon alpha to be 1 once this threshold is met, so that it can be
+ // seen morphing into the icon shape. But before the threshold, we want to limit
+ // the alpha to reduce the blur effect behind the window.
+ float iconAlpha = Interpolators.clampToProgress(progress, 0f, windowAlphaThreshold);
+ floatingIconView.update(iconAlpha, currentRect, progress, windowAlphaThreshold,
radius, false, overlayAlpha);
}
@@ -316,7 +327,9 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler<
@Override
protected void finishRecentsControllerToHome(Runnable callback) {
- mRecentsView.cleanupRemoteTargets();
+ if (mRecentsView != null) {
+ mRecentsView.cleanupRemoteTargets();
+ }
mRecentsAnimationController.finish(
true /* toRecents */, callback, true /* sendUserLeaveHint */);
}
@@ -360,7 +373,7 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler<
public AnimatorPlaybackController createActivityAnimationToHome() {
// Return an empty APC here since we have an non-user controlled animation
// to home.
- long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+ long accuracy = 2 * Math.max(mDp.getDeviceProperties().getWidthPx(), mDp.getDeviceProperties().getHeightPx());
return mContainer.getStateManager().createAnimationToNewWorkspace(
NORMAL, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS);
}
diff --git a/quickstep/src/com/android/quickstep/OWNERS b/quickstep/src/com/android/quickstep/OWNERS
index 868e0abf56..f0d0b2b442 100644
--- a/quickstep/src/com/android/quickstep/OWNERS
+++ b/quickstep/src/com/android/quickstep/OWNERS
@@ -4,6 +4,5 @@ jonmiranda@google.com
jagrutdesai@google.com
randypfohl@google.com
saumyaprakash@google.com
-sukeshram@google.com
twickham@google.com
victortulias@google.com
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index b828903045..a010057324 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -20,7 +20,6 @@ import android.animation.AnimatorListenerAdapter
import android.content.Intent
import android.graphics.PointF
import android.os.SystemClock
-import android.os.Trace
import android.util.Log
import android.view.Display.DEFAULT_DISPLAY
import android.view.View
@@ -28,10 +27,10 @@ import android.window.TransitionInfo
import androidx.annotation.BinderThread
import androidx.annotation.UiThread
import androidx.annotation.VisibleForTesting
+import com.android.app.displaylib.DisplayRepository
+import com.android.app.displaylib.PerDisplayRepository
import com.android.internal.jank.Cuj
-import com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays
-import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
-import com.android.launcher3.Flags.enableOverviewCommandHelperTimeout
+import com.android.launcher3.DeviceProfile
import com.android.launcher3.PagedView
import com.android.launcher3.logger.LauncherAtom
import com.android.launcher3.logging.StatsLogManager
@@ -40,24 +39,29 @@ import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVER
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarUIController
-import com.android.launcher3.util.Executors
+import com.android.launcher3.util.OverviewCommandHelperProtoLogProxy
+import com.android.launcher3.util.OverviewReleaseFlags.enableGridOnlyOverview
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.coroutines.DispatcherProvider
import com.android.launcher3.util.coroutines.ProductionDispatchers
import com.android.quickstep.OverviewCommandHelper.CommandInfo.CommandStatus
-import com.android.quickstep.OverviewCommandHelper.CommandType.HIDE
+import com.android.quickstep.OverviewCommandHelper.CommandType.HIDE_ALT_TAB
import com.android.quickstep.OverviewCommandHelper.CommandType.HOME
-import com.android.quickstep.OverviewCommandHelper.CommandType.KEYBOARD_INPUT
-import com.android.quickstep.OverviewCommandHelper.CommandType.SHOW
+import com.android.quickstep.OverviewCommandHelper.CommandType.SHOW_ALT_TAB
+import com.android.quickstep.OverviewCommandHelper.CommandType.SHOW_WITH_FOCUS
import com.android.quickstep.OverviewCommandHelper.CommandType.TOGGLE
-import com.android.quickstep.fallback.window.RecentsDisplayModel
-import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
+import com.android.quickstep.OverviewCommandHelper.CommandType.TOGGLE_OVERVIEW_PREVIOUS
+import com.android.quickstep.fallback.window.RecentsWindowManager
+import com.android.quickstep.util.ActiveGestureLog
+import com.android.quickstep.util.ActiveGestureProtoLogProxy
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.TaskView
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.system.InteractionJankMonitorWrapper
+import com.android.wm.shell.Flags.enableShellTopTaskTracking
import java.io.PrintWriter
import java.util.concurrent.ConcurrentLinkedDeque
+import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
@@ -73,11 +77,13 @@ constructor(
private val touchInteractionService: TouchInteractionService,
private val overviewComponentObserver: OverviewComponentObserver,
private val dispatcherProvider: DispatcherProvider = ProductionDispatchers,
- private val recentsDisplayModel: RecentsDisplayModel,
- private val focusState: FocusState,
+ private val displayRepository: DisplayRepository,
private val taskbarManager: TaskbarManager,
+ private val taskAnimationManagerRepository: PerDisplayRepository,
+ private val elapsedRealtime: () -> Long = SystemClock::elapsedRealtime,
) {
- private val coroutineScope = CoroutineScope(SupervisorJob() + dispatcherProvider.background)
+ private val coroutineScope =
+ CoroutineScope(SupervisorJob() + dispatcherProvider.lightweightBackground)
private val commandQueue = ConcurrentLinkedDeque()
@@ -88,11 +94,13 @@ constructor(
*/
private var keyboardTaskFocusIndex = -1
+ private val lastToggleInfo = mutableMapOf()
+
private fun getContainerInterface(displayId: Int) =
overviewComponentObserver.getContainerInterface(displayId)
private fun getVisibleRecentsView(displayId: Int) =
- getContainerInterface(displayId).getVisibleRecentsView>()
+ getContainerInterface(displayId)?.getVisibleRecentsView>()
/**
* Adds a command to be executed next, after all pending tasks are completed. Max commands that
@@ -110,23 +118,25 @@ constructor(
isLastOfBatch: Boolean = true,
): CommandInfo? {
if (commandQueue.size >= MAX_QUEUE_SIZE) {
- Log.d(TAG, "command not added: $type - queue is full ($commandQueue).")
+ OverviewCommandHelperProtoLogProxy.logCommandQueueFull(type, commandQueue)
return null
}
- val command = CommandInfo(type, displayId = displayId, isLastOfBatch = isLastOfBatch)
+ val command =
+ CommandInfo(
+ type,
+ displayId = displayId,
+ createTime = elapsedRealtime(),
+ isLastOfBatch = isLastOfBatch,
+ )
commandQueue.add(command)
- Log.d(TAG, "command added: $command")
+ OverviewCommandHelperProtoLogProxy.logCommandAdded(command)
if (commandQueue.size == 1) {
- Log.d(TAG, "execute: $command - queue size: ${commandQueue.size}")
- if (enableOverviewCommandHelperTimeout()) {
- coroutineScope.launch(dispatcherProvider.main) { processNextCommand() }
- } else {
- Executors.MAIN_EXECUTOR.execute { processNextCommand() }
- }
+ OverviewCommandHelperProtoLogProxy.logCommandExecuted(command, commandQueue.size)
+ coroutineScope.launch(dispatcherProvider.main) { processNextCommand() }
} else {
- Log.d(TAG, "not executed: $command - queue size: ${commandQueue.size}")
+ OverviewCommandHelperProtoLogProxy.logCommandNotExecuted(command, commandQueue.size)
}
return command
@@ -144,28 +154,25 @@ constructor(
@BinderThread
fun addCommandsForAllDisplays(type: CommandType) =
- addCommandsForDisplays(
- type,
- recentsDisplayModel.activeDisplayResources
- .map { resource -> resource.displayId }
- .toIntArray(),
- )
+ addCommandsForDisplays(type, displayRepository.displayIds.value.toIntArray())
@BinderThread
fun addCommandsForDisplaysExcept(type: CommandType, excludedDisplayId: Int) =
addCommandsForDisplays(
type,
- recentsDisplayModel.activeDisplayResources
- .map { resource -> resource.displayId }
+ displayRepository.displayIds.value
.filter { displayId -> displayId != excludedDisplayId }
.toIntArray(),
)
- fun canStartHomeSafely(): Boolean = commandQueue.isEmpty() || commandQueue.first().type == HOME
+ fun canStartHomeSafely(): Boolean =
+ commandQueue.isEmpty() ||
+ commandQueue.first().type == HOME ||
+ commandQueue.first().type == TOGGLE_OVERVIEW_PREVIOUS
/** Clear pending or completed commands from the queue */
fun clearPendingCommands() {
- Log.d(TAG, "clearing pending commands: $commandQueue")
+ OverviewCommandHelperProtoLogProxy.logClearPendingCommands(commandQueue)
commandQueue.removeAll { it.status != CommandStatus.PROCESSING }
}
@@ -178,30 +185,19 @@ constructor(
private fun processNextCommand() {
val command: CommandInfo? = commandQueue.firstOrNull()
if (command == null) {
- Log.d(TAG, "no pending commands to be executed.")
+ OverviewCommandHelperProtoLogProxy.logNoPendingCommands()
return
}
command.status = CommandStatus.PROCESSING
- Log.d(TAG, "executing command: $command")
+ OverviewCommandHelperProtoLogProxy.logExecutingCommand(command)
- if (enableOverviewCommandHelperTimeout()) {
- coroutineScope.launch(dispatcherProvider.main) {
+ coroutineScope.launch(dispatcherProvider.main) {
withTimeout(QUEUE_WAIT_DURATION_IN_MS) {
executeCommandSuspended(command)
ensureActive()
onCommandFinished(command)
}
- }
- } else {
- val result =
- executeCommand(command, onCallbackResult = { onCommandFinished(command) })
- Log.d(TAG, "command executed: $command with result: $result")
- if (result) {
- onCommandFinished(command)
- } else {
- Log.d(TAG, "waiting for command callback: $command")
- }
}
}
@@ -212,7 +208,7 @@ constructor(
@VisibleForTesting
fun executeCommand(command: CommandInfo, onCallbackResult: () -> Unit): Boolean {
val recentsView = getVisibleRecentsView(command.displayId)
- Log.d(TAG, "executeCommand: $command - visibleRecentsView: $recentsView")
+ OverviewCommandHelperProtoLogProxy.logExecutingCommand(command, recentsView)
return if (recentsView != null) {
executeWhenRecentsIsVisible(command, recentsView, onCallbackResult)
} else {
@@ -227,11 +223,14 @@ constructor(
private suspend fun executeCommandSuspended(command: CommandInfo) =
suspendCancellableCoroutine { continuation ->
fun processResult(isCompleted: Boolean) {
- Log.d(TAG, "command executed: $command with result: $isCompleted")
+ OverviewCommandHelperProtoLogProxy.logExecutedCommandWithResult(
+ command,
+ isCompleted,
+ )
if (isCompleted) {
continuation.resume(Unit)
} else {
- Log.d(TAG, "waiting for command callback: $command")
+ OverviewCommandHelperProtoLogProxy.logWaitingForCommandCallback(command)
}
}
@@ -247,9 +246,9 @@ constructor(
onCallbackResult: () -> Unit,
): Boolean =
when (command.type) {
- SHOW -> true // already visible
- KEYBOARD_INPUT,
- HIDE -> {
+ SHOW_WITH_FOCUS -> true // already visible
+ SHOW_ALT_TAB,
+ HIDE_ALT_TAB -> {
if (recentsView.isHandlingTouch) {
true
} else {
@@ -261,38 +260,55 @@ constructor(
}
TOGGLE -> {
+ val runningTaskId = recentsView.runningTaskView?.taskIdSet
launchTask(
recentsView,
- getNextToggledTaskView(recentsView),
+ getNextToggledTaskView(recentsView, command.displayId),
command,
- onCallbackResult,
- )
+ ) {
+ if (enableGridOnlyOverview() && runningTaskId != null) {
+ lastToggleInfo[command.displayId] =
+ ToggleInfo(command.createTime, runningTaskId)
+ }
+ onCallbackResult()
+ }
+ }
+ TOGGLE_OVERVIEW_PREVIOUS -> {
+ val taskView = recentsView.runningTaskView
+ if (taskView == null) {
+ recentsView.startHome()
+ } else {
+ taskView.launchWithAnimation()
+ }
+ true
}
-
HOME -> {
recentsView.startHome()
true
}
}
- private fun getNextToggledTaskView(recentsView: RecentsView<*, *>): TaskView? {
- // When running task view is null we return last large taskView - typically focusView when
- // grid only is not enabled else last desktop task view.
- return if (recentsView.runningTaskView == null) {
- recentsView.lastLargeTaskView ?: recentsView.getFirstTaskView()
- } else {
+ private fun getNextToggledTaskView(recentsView: RecentsView<*, *>, displayId: Int): TaskView? {
+ val lastToggleInfo = lastToggleInfo[displayId]
+ val lastToggleTaskView =
if (
- enableLargeDesktopWindowingTile() &&
- recentsView.getTaskViewCount() == recentsView.largeTilesCount &&
- recentsView.runningTaskView === recentsView.lastLargeTaskView
+ enableGridOnlyOverview() &&
+ lastToggleInfo != null &&
+ elapsedRealtime() - lastToggleInfo.createTime < TOGGLE_PREVIOUS_TIMEOUT_MS
) {
- // Enables the toggle when only large tiles are in recents view.
- // We return previous because unlike small tiles, large tiles are always
- // on the right hand side.
- recentsView.previousTaskView ?: recentsView.runningTaskView
- } else {
- recentsView.nextTaskView ?: recentsView.runningTaskView
- }
+ recentsView.getTaskViewByTaskIds(lastToggleInfo.taskIds.toIntArray())
+ } else null
+ val runningTaskView = recentsView.runningTaskView
+ return when {
+ runningTaskView == null && !enableGridOnlyOverview() ->
+ // When running task view is null we return last large taskView - typically
+ // focusView or last desktop task view.
+ recentsView.lastLargeTaskView ?: recentsView.firstTaskView
+ runningTaskView == null ->
+ recentsView.firstNonDesktopTaskView ?: recentsView.lastDesktopTaskView
+ lastToggleTaskView != null && lastToggleTaskView != runningTaskView ->
+ lastToggleTaskView
+ else -> recentsView.nextTaskView ?: recentsView.previousTaskView ?: runningTaskView
}
}
@@ -310,10 +326,10 @@ constructor(
if (callbackList != null) {
callbackList.add {
- Log.d(TAG, "launching task callback: $command")
+ OverviewCommandHelperProtoLogProxy.logLaunchingTaskCallback(command)
onCallbackResult()
}
- Log.d(TAG, "launching task - waiting for callback: $command")
+ OverviewCommandHelperProtoLogProxy.logLaunchingTaskWaitingForCallback(command)
return false
} else {
recents.startHome()
@@ -321,85 +337,80 @@ constructor(
}
}
+ // Returns false if callbacks should be awaited, true otherwise.
private fun executeWhenRecentsIsNotVisible(
command: CommandInfo,
onCallbackResult: () -> Unit,
): Boolean {
- val containerInterface = getContainerInterface(command.displayId)
+ val containerInterface = getContainerInterface(command.displayId) ?: return true
val recentsViewContainer = containerInterface.getCreatedContainer()
val recentsView: RecentsView<*, *>? = recentsViewContainer?.getOverviewPanel()
val deviceProfile = recentsViewContainer?.getDeviceProfile()
- val uiController = containerInterface.getTaskbarController()
-
- val focusedDisplayId = focusState.focusedDisplayId
- val focusedDisplayUIController: TaskbarUIController? =
- if (enableOverviewInWindow) {
- Log.d(
- TAG,
- "Querying RecentsDisplayModel for TaskbarUIController for display: $focusedDisplayId",
- )
- recentsDisplayModel.getRecentsWindowManager(focusedDisplayId)?.taskbarUIController
+ val taskbarUIController: TaskbarUIController? =
+ if (
+ command.displayId != DEFAULT_DISPLAY &&
+ recentsViewContainer !is RecentsWindowManager
+ ) {
+ // When recentsViewContainer is not RecentsWindowManager, get TaskbarUiController
+ // from TaskbarManager as a workaround.
+ taskbarManager.getUIControllerForDisplay(command.displayId)
} else {
- Log.d(
- TAG,
- "Querying TaskbarManager for TaskbarUIController for display: $focusedDisplayId",
- )
- // TODO(b/395061396): Remove this path when overview in widow is enabled.
- taskbarManager.getUIControllerForDisplay(focusedDisplayId)
+ containerInterface.getTaskbarController()
}
- Log.d(
- TAG,
- "TaskbarUIController for display $focusedDisplayId was" +
- "${if (focusedDisplayUIController == null) " not" else ""} found",
- )
+
+ val taskAnimationManager = taskAnimationManagerRepository[command.displayId]
+ if (taskAnimationManager == null) {
+ Log.e(TAG, "No TaskAnimationManager found for display ${command.displayId}")
+ ActiveGestureProtoLogProxy.logOnTaskAnimationManagerNotAvailable(command.displayId)
+ return false
+ }
when (command.type) {
- HIDE -> {
- if (uiController == null || deviceProfile?.isTablet == false) return true
- keyboardTaskFocusIndex =
- if (
- enableAltTabKqsOnConnectedDisplays() && focusedDisplayUIController != null
- ) {
- focusedDisplayUIController.launchFocusedTask()
- } else {
- uiController.launchFocusedTask()
- }
+ HIDE_ALT_TAB -> {
+ if (
+ taskbarUIController == null ||
+ !shouldShowAltTabKqs(deviceProfile, command.displayId)
+ ) {
+ return true
+ }
+ keyboardTaskFocusIndex = taskbarUIController.launchFocusedTask()
if (keyboardTaskFocusIndex == -1) return true
}
- KEYBOARD_INPUT ->
- if (uiController != null && deviceProfile?.isTablet == true) {
- if (
- enableAltTabKqsOnConnectedDisplays() && focusedDisplayUIController != null
- ) {
- focusedDisplayUIController.openQuickSwitchView()
- } else {
- uiController.openQuickSwitchView()
- }
+ SHOW_ALT_TAB ->
+ if (
+ taskbarUIController != null &&
+ shouldShowAltTabKqs(deviceProfile, command.displayId)
+ ) {
+ taskbarUIController.openQuickSwitchView()
return true
} else {
keyboardTaskFocusIndex = 0
}
HOME -> {
- //ActiveGestureProtoLogProxy.logExecuteHomeCommand()
- // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
- // we should still call it on main thread because launcher is waiting for
- // ActivityTaskManager to resume it. Also calling startActivity() on bg thread
- // could potentially delay resuming launcher. See b/348668521 for more details.
- touchInteractionService.startActivity(overviewComponentObserver.homeIntent)
+ taskAnimationManager.maybeStartHomeAction {
+ // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
+ // we should still call it on main thread because launcher is waiting for
+ // ActivityTaskManager to resume it. Also calling startActivity() on bg thread
+ // could potentially delay resuming launcher. See b/348668521 for more details.
+ touchInteractionService.startActivity(
+ overviewComponentObserver.getHomeIntent(command.displayId)
+ )
+ }
return true
}
- SHOW ->
+ SHOW_WITH_FOCUS ->
// When Recents is not currently visible, the command's type is SHOW
// when overview is triggered via the keyboard overview button or Action+Tab
// keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button
// nav is TYPE_TOGGLE.
keyboardTaskFocusIndex = 0
- TOGGLE -> {}
+ TOGGLE,
+ TOGGLE_OVERVIEW_PREVIOUS -> {}
}
recentsView?.setKeyboardTaskFocusIndex(
@@ -411,44 +422,68 @@ constructor(
val animatorListener: Animator.AnimatorListener =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
- Log.d(TAG, "switching to Overview state - onAnimationStart: $command")
+ OverviewCommandHelperProtoLogProxy.logSwitchingToOverviewStateStart(command)
super.onAnimationStart(animation)
updateRecentsViewFocus(command)
logShowOverviewFrom(command)
}
override fun onAnimationEnd(animation: Animator) {
- Log.d(TAG, "switching to Overview state - onAnimationEnd: $command")
+ OverviewCommandHelperProtoLogProxy.logSwitchingToOverviewStateEnd(command)
super.onAnimationEnd(animation)
onRecentsViewFocusUpdated(command)
onCallbackResult()
}
}
if (containerInterface.switchToRecentsIfVisible(animatorListener)) {
- Log.d(TAG, "switching to Overview state - waiting: $command")
+ OverviewCommandHelperProtoLogProxy.logSwitchingToOverviewStateWaiting(command)
// If successfully switched, wait until animation finishes
return false
}
- if (!enableOverviewInWindow) {
- containerInterface.getCreatedContainer()?.rootView?.let { view ->
+ // If we get here then launcher is not the top visible task, so we should animate
+ // that task.
+
+ if (recentsViewContainer !is RecentsWindowManager) {
+ recentsViewContainer?.rootView?.let { view ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
}
}
val gestureState =
- touchInteractionService.createGestureState(
- command.displayId,
- GestureState.DEFAULT_STATE,
- GestureState.TrackpadGestureType.NONE,
- )
- gestureState.isHandlingAtomicEvent = true
+ touchInteractionService
+ .createGestureState(
+ command.displayId,
+ GestureState.DEFAULT_STATE,
+ GestureState.TrackpadGestureType.NONE,
+ )
+ .apply {
+ isHandlingAtomicEvent = true
+ if (!enableShellTopTaskTracking()) {
+ val runningTask = runningTask
+ // In the case where we are in an excluded, translucent overlay, ignore it
+ // and treat the running activity as the task behind the overlay.
+ val otherVisibleTask = runningTask?.visibleNonExcludedTask
+ if (otherVisibleTask != null) {
+ ActiveGestureProtoLogProxy.logUpdateGestureStateRunningTask(
+ otherVisibleTask.packageName ?: "MISSING",
+ runningTask.packageName ?: "MISSING",
+ )
+ updateRunningTask(otherVisibleTask)
+ }
+ }
+ }
val interactionHandler =
touchInteractionService
- // TODO(b/404757863): use command.displayId instead of focusedDisplayId.
- .getSwipeUpHandlerFactory(focusedDisplayId)
+ .getSwipeUpHandlerFactory(command.displayId)
.newHandler(gestureState, command.createTime)
- interactionHandler.setGestureEndCallback {
+ if (interactionHandler == null) {
+ // Can happen e.g. when a display is disconnected, so try to handle gracefully.
+ Log.d(TAG, "AbsSwipeUpHandler not available for displayId=${command.displayId})")
+ ActiveGestureProtoLogProxy.logOnAbsSwipeUpHandlerNotAvailable(command.displayId)
+ return true
+ }
+ interactionHandler.setGestureAnimationEndCallback {
onTransitionComplete(command, interactionHandler, onCallbackResult)
}
interactionHandler.initWhenReady("OverviewCommandHelper: command.type=${command.type}")
@@ -460,9 +495,9 @@ constructor(
targets: RecentsAnimationTargets,
transitionInfo: TransitionInfo?,
) {
- Log.d(TAG, "recents animation started: $command")
- if (enableOverviewInWindow) {
- containerInterface.getCreatedContainer()?.rootView?.let { view ->
+ OverviewCommandHelperProtoLogProxy.logRecentsAnimStarted(command)
+ if (recentsViewContainer is RecentsWindowManager) {
+ recentsViewContainer.rootView?.let { view ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
}
}
@@ -470,7 +505,7 @@ constructor(
updateRecentsViewFocus(command)
logShowOverviewFrom(command)
containerInterface.runOnInitBackgroundStateUI {
- Log.d(TAG, "recents animation started - onInitBackgroundStateUI: $command")
+ OverviewCommandHelperProtoLogProxy.logOnInitBackgroundStateUI(command)
interactionHandler.onGestureEnded(
0f,
PointF(),
@@ -483,7 +518,7 @@ constructor(
override fun onRecentsAnimationCanceled(
thumbnailDatas: HashMap
) {
- Log.d(TAG, "recents animation canceled: $command")
+ OverviewCommandHelperProtoLogProxy.logRecentsAnimCanceled(command)
interactionHandler.onGestureCancelled()
command.removeListener(this)
@@ -492,15 +527,6 @@ constructor(
}
}
- val taskAnimationManager =
- recentsDisplayModel.getTaskAnimationManager(command.displayId)
- ?: run {
- Log.e(TAG, "No TaskAnimationManager found for display ${command.displayId}")
- //ActiveGestureProtoLogProxy.logOnTaskAnimationManagerNotAvailable(
- // command.displayId
- //)
- return false
- }
if (taskAnimationManager.isRecentsAnimationRunning) {
command.setAnimationCallbacks(
taskAnimationManager.continueRecentsAnimation(gestureState)
@@ -512,29 +538,32 @@ constructor(
command.addListener(recentAnimListener)
taskAnimationManager.notifyRecentsAnimationState(recentAnimListener)
} else {
- // Lawnchair-TODO: What name to put in intent here?
val intent =
Intent(interactionHandler.getLaunchIntent())
- .putExtra("Something", gestureState.gestureId)
+ .putExtra(ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID, gestureState.gestureId)
command.setAnimationCallbacks(
taskAnimationManager.startRecentsAnimation(gestureState, intent, interactionHandler)
)
interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/)
command.addListener(recentAnimListener)
}
- //Trace.beginAsyncSection(TRANSITION_NAME, 0)
- Log.d(TAG, "switching via recents animation - onGestureStarted: $command")
+ OverviewCommandHelperProtoLogProxy.logSwitchingViaRecentsAnim(command)
return false
}
+ private fun shouldShowAltTabKqs(deviceProfile: DeviceProfile?, displayId: Int): Boolean =
+ // Alt+Tab KQS is always shown on tablets (large screen devices).
+ deviceProfile?.deviceProperties?.isTablet == true ||
+ // For small screen devices, it's only shown on connected displays.
+ displayId != DEFAULT_DISPLAY
+
private fun onTransitionComplete(
command: CommandInfo,
handler: AbsSwipeUpHandler<*, *, *>,
onCommandResult: () -> Unit,
) {
- Log.d(TAG, "switching via recents animation - onTransitionComplete: $command")
+ OverviewCommandHelperProtoLogProxy.logSwitchingViaRecentsAnimComplete(command)
command.removeListener(handler)
- Trace.endAsyncSection(TRANSITION_NAME, 0)
onRecentsViewFocusUpdated(command)
onCommandResult()
}
@@ -543,42 +572,54 @@ constructor(
private fun onCommandFinished(command: CommandInfo) {
command.status = CommandStatus.COMPLETED
if (commandQueue.firstOrNull() !== command) {
- Log.d(
- TAG,
- "next task not scheduled. First pending command type " +
- "is ${commandQueue.firstOrNull()} - command type is: $command",
+ OverviewCommandHelperProtoLogProxy.logCommandFinishedButNotScheduled(
+ commandQueue.firstOrNull(),
+ command,
)
return
}
- Log.d(TAG, "command executed successfully! $command")
+ OverviewCommandHelperProtoLogProxy.logCommandFinishedSuccessfully(command)
commandQueue.remove(command)
processNextCommand()
}
private fun cancelCommand(command: CommandInfo, throwable: Throwable?) {
command.status = CommandStatus.CANCELED
- Log.e(TAG, "command cancelled: $command - $throwable")
+ OverviewCommandHelperProtoLogProxy.logCommandCanceled(command, throwable)
commandQueue.remove(command)
processNextCommand()
}
private fun updateRecentsViewFocus(command: CommandInfo) {
val recentsView: RecentsView<*, *> = getVisibleRecentsView(command.displayId) ?: return
- if (command.type != KEYBOARD_INPUT && command.type != HIDE && command.type != SHOW) {
+ if (
+ command.type != SHOW_ALT_TAB &&
+ command.type != HIDE_ALT_TAB &&
+ command.type != SHOW_WITH_FOCUS
+ ) {
return
}
-
// When the overview is launched via alt tab (command type is TYPE_KEYBOARD_INPUT),
// the touch mode somehow is not change to false by the Android framework.
// The subsequent tab to go through tasks in overview can only be dispatched to
// focuses views, while focus can only be requested in
// {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
// here we launch overview with live tile.
- recentsView.viewRootImpl.touchModeChanged(false)
+ if (recentsView.isAttachedToWindow) {
+ recentsView.viewRootImpl.touchModeChanged(false)
+ } else {
+ recentsView.post { recentsView.viewRootImpl.touchModeChanged(false) }
+ }
// Ensure that recents view has focus so that it receives the followup key inputs
// Stops requesting focused after first view gets focused.
- recentsView.getTaskViewAt(keyboardTaskFocusIndex).requestFocus() ||
+ recentsView
+ .getTaskViewAt(
+ recentsView.indexOfChild(
+ recentsView.taskViews.elementAtOrNull(keyboardTaskFocusIndex)
+ )
+ )
+ .requestFocus() ||
recentsView.nextTaskView.requestFocus() ||
recentsView.firstTaskView.requestFocus() ||
recentsView.requestFocus()
@@ -586,11 +627,12 @@ constructor(
private fun onRecentsViewFocusUpdated(command: CommandInfo) {
val recentsView: RecentsView<*, *> = getVisibleRecentsView(command.displayId) ?: return
- if (command.type != HIDE || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) {
+ if (command.type != HIDE_ALT_TAB || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) {
return
}
recentsView.setKeyboardTaskFocusIndex(PagedView.INVALID_PAGE)
- recentsView.currentPage = keyboardTaskFocusIndex
+ recentsView.currentPage =
+ recentsView.indexOfChild(recentsView.taskViews.elementAtOrNull(keyboardTaskFocusIndex))
keyboardTaskFocusIndex = PagedView.INVALID_PAGE
}
@@ -604,12 +646,12 @@ constructor(
}
private fun logShowOverviewFrom(command: CommandInfo) {
- val containerInterface = getContainerInterface(command.displayId)
+ val containerInterface = getContainerInterface(command.displayId) ?: return
val container = containerInterface.getCreatedContainer() ?: return
val event =
when (command.type) {
- SHOW -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT
- HIDE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH
+ SHOW_WITH_FOCUS -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT
+ HIDE_ALT_TAB -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH
TOGGLE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON
else -> return
}
@@ -638,7 +680,7 @@ constructor(
data class CommandInfo(
val type: CommandType,
var status: CommandStatus = CommandStatus.IDLE,
- val createTime: Long = SystemClock.elapsedRealtime(),
+ val createTime: Long,
private var animationCallbacks: RecentsAnimationCallbacks? = null,
val displayId: Int = DEFAULT_DISPLAY,
val isLastOfBatch: Boolean = true,
@@ -664,13 +706,21 @@ constructor(
}
enum class CommandType {
- SHOW,
- KEYBOARD_INPUT,
- HIDE,
+ SHOW_WITH_FOCUS,
+ SHOW_ALT_TAB,
+ HIDE_ALT_TAB,
+ /** Toggle between overview and the next task */
TOGGLE, // Navigate to Overview
HOME, // Navigate to Home
+ /**
+ * Toggle between Overview and the previous screen before launching Overview, which can
+ * either be a task or the home screen.
+ */
+ TOGGLE_OVERVIEW_PREVIOUS,
}
+ data class ToggleInfo(val createTime: Long, val taskIds: Set)
+
companion object {
private const val TAG = "OverviewCommandHelper"
private const val TRANSITION_NAME = "Transition:toOverview"
@@ -681,5 +731,6 @@ constructor(
*/
private const val MAX_QUEUE_SIZE = 3
private const val QUEUE_WAIT_DURATION_IN_MS = 5000L
+ @VisibleForTesting val TOGGLE_PREVIOUS_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5)
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 1c2b1f01c7..2ae6a19164 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -21,11 +21,11 @@ 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.Flags.enableOverviewOnConnectedDisplays;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableFallbackOverviewInWindow;
import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableLauncherOverviewInWindow;
+import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableOverviewOnConnectedDisplays;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
import android.content.ActivityNotFoundException;
@@ -43,6 +43,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.app.displaylib.PerDisplayRepository;
import com.android.launcher3.R;
import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppComponent;
@@ -50,7 +51,7 @@ 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.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -77,10 +78,11 @@ public final class OverviewComponentObserver {
private final SimpleBroadcastReceiver mUserPreferenceChangeReceiver;
private final SimpleBroadcastReceiver mOtherHomeAppUpdateReceiver;
- private final RecentsDisplayModel mRecentsDisplayModel;
+ private final PerDisplayRepository mRecentsWindowManagerRepository;
- private final Intent mCurrentHomeIntent;
- private final Intent mMyHomeIntent;
+ private final Intent mCurrentPrimaryHomeIntent;
+ private final Intent mSecondaryHomeIntent;
+ private final Intent mMyPrimaryHomeIntent;
private final Intent mFallbackIntent;
private final SparseIntArray mConfigChangesMap = new SparseIntArray();
private final String mSetupWizardPkg;
@@ -98,19 +100,32 @@ public final class OverviewComponentObserver {
@Inject
public OverviewComponentObserver(
@ApplicationContext Context context,
- RecentsDisplayModel recentsDisplayModel,
+ PerDisplayRepository recentsWindowManagerRepository,
DaggerSingletonTracker lifecycleTracker) {
mUserPreferenceChangeReceiver =
new SimpleBroadcastReceiver(context, MAIN_EXECUTOR, this::updateOverviewTargets);
mOtherHomeAppUpdateReceiver =
new SimpleBroadcastReceiver(context, MAIN_EXECUTOR, this::updateOverviewTargets);
- mRecentsDisplayModel = recentsDisplayModel;
- mCurrentHomeIntent = createHomeIntent();
- mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(context.getPackageName());
- ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
+ mRecentsWindowManagerRepository = recentsWindowManagerRepository;
+ // Set up primary intents
+ mCurrentPrimaryHomeIntent = createHomeIntent();
+ mMyPrimaryHomeIntent = new Intent(mCurrentPrimaryHomeIntent).setPackage(
+ context.getPackageName());
+ ResolveInfo info = context.getPackageManager().resolveActivity(mMyPrimaryHomeIntent, 0);
ComponentName myHomeComponent =
new ComponentName(context.getPackageName(), info.activityInfo.name);
- mMyHomeIntent.setComponent(myHomeComponent);
+ mMyPrimaryHomeIntent.setComponent(myHomeComponent);
+ // Set up secondary home intent
+ mSecondaryHomeIntent = createSecondaryHomeIntent().setPackage(
+ context.getPackageName());
+ ResolveInfo secondaryInfo = context.getPackageManager().resolveActivity(
+ mSecondaryHomeIntent, 0);
+ if (secondaryInfo != null) {
+ ComponentName secondaryComponent = new ComponentName(context,
+ secondaryInfo.activityInfo.name);
+ mSecondaryHomeIntent.setComponent(secondaryComponent);
+ }
+
mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
mSetupWizardPkg = context.getString(R.string.setup_wizard_pkg);
@@ -172,7 +187,7 @@ public final class OverviewComponentObserver {
defaultHome = null;
}
- mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
+ mIsDefaultHome = Objects.equals(mMyPrimaryHomeIntent.getComponent(), defaultHome);
// Set assistant visibility to 0 from launcher's perspective, ensures any elements that
// launcher made invisible become visible again before the new activity control helper
@@ -184,7 +199,7 @@ public final class OverviewComponentObserver {
if (SEPARATE_RECENTS_ACTIVITY.get()) {
mIsDefaultHome = false;
if (defaultHome == null) {
- defaultHome = mMyHomeIntent.getComponent();
+ defaultHome = mMyPrimaryHomeIntent.getComponent();
}
}
@@ -196,28 +211,34 @@ public final class OverviewComponentObserver {
if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as our home app. Use Overview integrated in Launcher.
if (enableLauncherOverviewInWindow) {
+ RecentsWindowManager recentsWindowManager = mRecentsWindowManagerRepository.get(
+ DEFAULT_DISPLAY);
mDefaultDisplayContainerInterface =
- mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
+ recentsWindowManager != null ? recentsWindowManager.getContainerInterface()
+ : null;
} else {
mDefaultDisplayContainerInterface = LauncherActivityInterface.INSTANCE;
}
mIsHomeAndOverviewSame = true;
- mOverviewIntent = mMyHomeIntent;
- mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
+ mOverviewIntent = mMyPrimaryHomeIntent;
+ mCurrentPrimaryHomeIntent.setComponent(mMyPrimaryHomeIntent.getComponent());
// Remove any update listener as we don't care about other packages.
unregisterOtherHomeAppUpdateReceiver();
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
if (enableFallbackOverviewInWindow) {
+ RecentsWindowManager recentsWindowManager = mRecentsWindowManagerRepository.get(
+ DEFAULT_DISPLAY);
mDefaultDisplayContainerInterface =
- mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
+ recentsWindowManager != null ? recentsWindowManager.getContainerInterface()
+ : null;
} else {
mDefaultDisplayContainerInterface = FallbackActivityInterface.INSTANCE;
}
mIsHomeAndOverviewSame = false;
mOverviewIntent = mFallbackIntent;
- mCurrentHomeIntent.setComponent(defaultHome);
+ mCurrentPrimaryHomeIntent.setComponent(defaultHome);
// User's default home app can change as a result of package updates of this app (such
// as uninstalling the app or removing the "Launcher" feature in an update).
@@ -275,7 +296,7 @@ public final class OverviewComponentObserver {
* @return the overview intent
*/
public Intent getOverviewIntentIgnoreSysUiState() {
- return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent;
+ return mIsDefaultHome ? mMyPrimaryHomeIntent : mOverviewIntent;
}
/**
@@ -290,8 +311,12 @@ public final class OverviewComponentObserver {
/**
* Get the current intent for going to the home activity.
*/
- public Intent getHomeIntent() {
- return mCurrentHomeIntent;
+ public Intent getHomeIntent(int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ return mCurrentPrimaryHomeIntent;
+ } else {
+ return mSecondaryHomeIntent;
+ }
}
/**
@@ -301,10 +326,6 @@ public final class OverviewComponentObserver {
return mIsHomeAndOverviewSame;
}
- public boolean isHomeAndOverviewSameActivity() {
- return isHomeAndOverviewSame() && !enableLauncherOverviewInWindow;
- }
-
/**
* Get the current control helper for managing interactions to the overview container for
* the given displayId.
@@ -312,10 +333,16 @@ public final class OverviewComponentObserver {
* @param displayId The display id
* @return the control helper for the given display
*/
+ @Nullable
public BaseContainerInterface, ?> getContainerInterface(int displayId) {
- return (enableOverviewOnConnectedDisplays() && displayId != DEFAULT_DISPLAY)
- ? mRecentsDisplayModel.getFallbackWindowInterface(displayId)
- : mDefaultDisplayContainerInterface;
+ if (enableOverviewOnConnectedDisplays() && displayId != DEFAULT_DISPLAY) {
+ RecentsWindowManager recentsWindowManager = mRecentsWindowManagerRepository.get(
+ displayId);
+ return recentsWindowManager != null ? recentsWindowManager.getContainerInterface()
+ : null;
+ } else {
+ return mDefaultDisplayContainerInterface;
+ }
}
public void dump(PrintWriter pw) {
@@ -324,15 +351,16 @@ public final class OverviewComponentObserver {
pw.println(" isHomeDisabled=" + mIsHomeDisabled);
pw.println(" homeAndOverviewSame=" + mIsHomeAndOverviewSame);
pw.println(" overviewIntent=" + mOverviewIntent);
- pw.println(" homeIntent=" + mCurrentHomeIntent);
+ pw.println(" homeIntent=" + mCurrentPrimaryHomeIntent);
+ pw.println(" secondaryHomeIntent=" + mSecondaryHomeIntent);
}
/**
* Starts the intent for the current home activity.
*/
public static void startHomeIntentSafely(@NonNull Context context, @Nullable Bundle options,
- @NonNull String reason) {
- Intent intent = OverviewComponentObserver.INSTANCE.get(context).getHomeIntent();
+ @NonNull String reason, int displayId) {
+ Intent intent = OverviewComponentObserver.INSTANCE.get(context).getHomeIntent(displayId);
startHomeIntentSafely(context, intent, options, reason);
}
@@ -368,4 +396,9 @@ public final class OverviewComponentObserver {
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
+ private static Intent createSecondaryHomeIntent() {
+ return new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_SECONDARY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index e9f70242cd..0330d53452 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,268 +1 @@
-package com.android.quickstep;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.launcher3.taskbar.TaskbarThresholdUtils.getFromNavThreshold;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.view.WindowInsets;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.launcher3.testing.TestInformationHandler;
-import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.DisplayController;
-import com.android.quickstep.util.GroupTask;
-import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.TISBindHelper;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.RecentsViewContainer;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-public class QuickstepTestInformationHandler extends TestInformationHandler {
-
- protected final Context mContext;
-
- public QuickstepTestInformationHandler(Context context) {
- mContext = context;
- }
-
- @Override
- public Bundle call(String method, String arg, @Nullable Bundle extras) {
- final Bundle response = new Bundle();
- switch (method) {
- case TestProtocol.REQUEST_RECENT_TASKS_LIST: {
- ArrayList taskBaseIntentComponents = new ArrayList<>();
- CountDownLatch latch = new CountDownLatch(1);
- RecentsModel.INSTANCE.get(mContext).getTasks((taskGroups) -> {
- for (GroupTask group : taskGroups) {
- for (Task t : group.getTasks()) {
- taskBaseIntentComponents.add(
- t.key.baseIntent.getComponent().flattenToString());
- }
- }
- latch.countDown();
- });
- try {
- latch.await(2, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- response.putStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- taskBaseIntentComponents);
- return response;
- }
-
- case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
- final float swipeHeight =
- LayoutUtils.getDefaultSwipeHeight(mContext, mDeviceProfile);
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
- return response;
- }
-
- case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
- final float swipeHeight = mDeviceProfile.heightPx / 2f;
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
- return response;
- }
-
- case TestProtocol.REQUEST_GET_OVERVIEW_TASK_SIZE: {
- return getUIProperty(Bundle::putParcelable,
- recentsViewContainer ->
- recentsViewContainer.>getOverviewPanel()
- .getLastComputedTaskSize(),
- this::getRecentsViewContainer);
- }
-
- case TestProtocol.REQUEST_GET_OVERVIEW_GRID_TASK_SIZE: {
- return getUIProperty(Bundle::putParcelable,
- recentsViewContainer ->
- recentsViewContainer.>getOverviewPanel()
- .getLastComputedGridTaskSize(),
- this::getRecentsViewContainer);
- }
-
- case TestProtocol.REQUEST_GET_OVERVIEW_PAGE_SPACING: {
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- mDeviceProfile.overviewPageSpacing);
- return response;
- }
-
- case TestProtocol.REQUEST_GET_OVERVIEW_CURRENT_PAGE_INDEX: {
- return getLauncherUIProperty(Bundle::putInt,
- launcher -> launcher.getOverviewPanel().getCurrentPage());
- }
-
- case TestProtocol.REQUEST_HAS_TIS: {
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, true);
- return response;
- }
-
- case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
- runOnTISBinder(tisBinder -> {
- // Allow null-pointer to catch illegal states.
- tisBinder.getTaskbarManager().getCurrentActivityContext()
- .unstashTaskbarIfStashed();
- });
- return response;
-
- case TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD: {
- final Resources resources = mContext.getResources();
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- getFromNavThreshold(resources, mDeviceProfile));
- return response;
- }
-
- case TestProtocol.REQUEST_STASHED_TASKBAR_SCALE: {
- runOnTISBinder(tisBinder -> {
- response.putFloat(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- tisBinder.getTaskbarManager()
- .getCurrentActivityContext()
- .getStashedTaskbarScale());
- });
- return response;
- }
-
- case TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING: {
- return getTISBinderUIProperty(Bundle::putInt, tisBinder ->
- tisBinder.getTaskbarManager()
- .getCurrentActivityContext()
- .getTaskbarAllAppsTopPadding());
- }
-
- case TestProtocol.REQUEST_TASKBAR_APPS_LIST_SCROLL_Y: {
- return getTISBinderUIProperty(Bundle::putInt, tisBinder ->
- tisBinder.getTaskbarManager()
- .getCurrentActivityContext()
- .getTaskbarAllAppsScroll());
- }
-
- case TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT:
- runOnTISBinder(tisBinder -> {
- enableBlockingTimeout(tisBinder, true);
- });
- return response;
-
- case TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT:
- runOnTISBinder(tisBinder -> {
- enableBlockingTimeout(tisBinder, false);
- });
- return response;
-
- case TestProtocol.REQUEST_ENABLE_TRANSIENT_TASKBAR:
- enableTransientTaskbar(true);
- return response;
-
- case TestProtocol.REQUEST_DISABLE_TRANSIENT_TASKBAR:
- enableTransientTaskbar(false);
- return response;
-
- case TestProtocol.REQUEST_SHELL_DRAG_READY:
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- SystemUiProxy.INSTANCE.get(mContext).isDragAndDropReady());
- return response;
-
- case TestProtocol.REQUEST_REFRESH_OVERVIEW_TARGET:
- runOnTISBinder(TouchInteractionService.TISBinder::refreshOverviewTarget);
- return response;
-
- case TestProtocol.REQUEST_RECREATE_TASKBAR:
- // Allow null-pointer to catch illegal states.
- runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbars());
- return response;
- case TestProtocol.REQUEST_TASKBAR_IME_DOCKED:
- return getTISBinderUIProperty(Bundle::putBoolean, tisBinder ->
- tisBinder.getTaskbarManager()
- .getCurrentActivityContext().isImeDocked());
- case TestProtocol.REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED:
- runOnTISBinder(tisBinder -> {
- // Allow null-pointer to catch illegal states.
- tisBinder.getTaskbarManager().getCurrentActivityContext()
- .unstashBubbleBarIfStashed();
- });
- return response;
- case TestProtocol.REQUEST_INJECT_FAKE_TRACKPAD:
- runOnTISBinder(tisBinder -> tisBinder.injectFakeTrackpadForTesting());
- return response;
- case TestProtocol.REQUEST_EJECT_FAKE_TRACKPAD:
- runOnTISBinder(tisBinder -> tisBinder.ejectFakeTrackpadForTesting());
- return response;
- }
-
- return super.call(method, arg, extras);
- }
-
- @Override
- protected WindowInsets getWindowInsets() {
- RecentsViewContainer container = getRecentsViewContainer();
- WindowInsets insets = container == null
- ? null : container.getRootView().getRootWindowInsets();
- return insets == null ? super.getWindowInsets() : insets;
- }
-
- private RecentsViewContainer getRecentsViewContainer() {
- // TODO (b/400647896): support per-display container in e2e tests
- return OverviewComponentObserver.INSTANCE.get(mContext)
- .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer();
- }
-
- @Override
- protected boolean isLauncherInitialized() {
- return super.isLauncherInitialized() && SystemUiProxy.INSTANCE.get(mContext).isActive();
- }
-
- private void enableBlockingTimeout(
- TouchInteractionService.TISBinder tisBinder, boolean enable) {
- TaskbarActivityContext context = tisBinder.getTaskbarManager().getCurrentActivityContext();
- if (context == null) {
- return;
- }
- context.enableBlockingTimeoutDuringTests(enable);
- }
-
- private void enableTransientTaskbar(boolean enable) {
- DisplayController.INSTANCE.get(mContext).enableTransientTaskbarForTests(enable);
- }
-
- /**
- * Runs the given command on the UI thread, after ensuring we are connected to
- * TouchInteractionService.
- */
- protected void runOnTISBinder(Consumer connectionCallback) {
- try {
- CountDownLatch countDownLatch = new CountDownLatch(1);
- TISBindHelper helper = MAIN_EXECUTOR.submit(() ->
- new TISBindHelper(mContext, tisBinder -> {
- connectionCallback.accept(tisBinder);
- countDownLatch.countDown();
- })).get();
- countDownLatch.await();
- MAIN_EXECUTOR.execute(helper::onDestroy);
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- private Bundle getTISBinderUIProperty(
- BundleSetter bundleSetter, Function provider) {
- Bundle response = new Bundle();
-
- runOnTISBinder(tisBinder -> bundleSetter.set(
- response,
- TestProtocol.TEST_INFO_RESPONSE_FIELD,
- provider.apply(tisBinder)));
-
- return response;
- }
-}
+// LC: No-op
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index bbe2de0de1..50e929a221 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -18,9 +18,7 @@ package com.android.quickstep;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
-import static com.android.launcher3.Flags.enableSeparateExternalDisplayTasks;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_DESK;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT;
@@ -42,8 +40,6 @@ import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.ExternalDisplaysKt;
import com.android.quickstep.util.GroupTask;
@@ -67,6 +63,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -77,10 +74,7 @@ import app.lawnchair.compat.LawnchairQuickstepCompat;
/**
* Manages the recent task list from the system, caching it as necessary.
*/
-// TODO: b/401602554 - Consider letting [DesktopTasksController] notify [RecentTasksController] of
-// desk changes to trigger [IRecentTasksListener.onRecentTasksChanged()], instead of implementing
-// [DesktopVisibilityListener].
-public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityListener {
+public class RecentTasksList {
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
@@ -88,7 +82,6 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
private final KeyguardManager mKeyguardManager;
private final LooperExecutor mMainThreadExecutor;
private final SystemUiProxy mSysUiProxy;
- private final DesktopVisibilityController mDesktopVisibilityController;
// The list change id, increments as the task list changes in the system
private int mChangeId;
@@ -107,14 +100,12 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
public RecentTasksList(Context context, LooperExecutor mainThreadExecutor,
KeyguardManager keyguardManager, SystemUiProxy sysUiProxy,
TopTaskTracker topTaskTracker,
- DesktopVisibilityController desktopVisibilityController,
DaggerSingletonTracker tracker) {
mContext = context;
mMainThreadExecutor = mainThreadExecutor;
mKeyguardManager = keyguardManager;
mChangeId = 1;
mSysUiProxy = sysUiProxy;
- mDesktopVisibilityController = desktopVisibilityController;
if (LawnchairApp.isRecentsEnabled()) {
final IRecentTasksListener recentTasksListener = new IRecentTasksListener.Stub() {
@Override
@@ -167,13 +158,6 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
mSysUiProxy.registerRecentTasksListener(recentTasksListener);
tracker.addCloseable(
() -> mSysUiProxy.unregisterRecentTasksListener(recentTasksListener));
-
- if (DesktopModeStatus.enableMultipleDesktops(mContext)) {
- mDesktopVisibilityController.registerDesktopVisibilityListener(
- this);
- tracker.addCloseable(
- () -> mDesktopVisibilityController.unregisterDesktopVisibilityListener(this));
- }
}
// We may receive onRunningTaskAppeared events later for tasks which have already been
@@ -207,8 +191,27 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
* @param callback The callback to receive the list of recent tasks
* @return The change id of the current task list
*/
- public synchronized int getTasks(boolean loadKeysOnly,
- @Nullable Consumer> callback, Predicate filter) {
+ public synchronized int getTasks(
+ boolean loadKeysOnly,
+ @Nullable Consumer> callback,
+ Predicate filter) {
+ return getTasks(
+ loadKeysOnly,
+ callback == null ? null : (tasks, requestId) -> callback.accept(tasks),
+ filter);
+ }
+
+ /**
+ * Asynchronously fetches the list of recent tasks, reusing cached list if available.
+ *
+ * @param loadKeysOnly Whether to load other associated task data, or just the key
+ * @param callback The callback to receive the list of recent tasks
+ * @return The change id of the current task list
+ */
+ public synchronized int getTasks(
+ boolean loadKeysOnly,
+ @Nullable BiConsumer, Integer> callback,
+ Predicate filter) {
final int requestLoadId = mChangeId;
if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
// The list is up to date, send the callback on the next frame,
@@ -221,7 +224,7 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
.collect(Collectors.toCollection(ArrayList::new));
mMainThreadExecutor.post(() -> {
- callback.accept(result);
+ callback.accept(result, requestLoadId);
});
}
@@ -244,7 +247,7 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
.map(GroupTask::copy)
.collect(Collectors.toCollection(ArrayList::new));
- callback.accept(result);
+ callback.accept(result, requestLoadId);
}
});
});
@@ -314,27 +317,6 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
return mRunningTasks;
}
- @Override
- public void onDeskAdded(int displayId, int deskId) {
- onRecentTasksChanged();
- }
-
- @Override
- public void onDeskRemoved(int displayId, int deskId) {
- onRecentTasksChanged();
- }
-
- @Override
- public void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {
- // Should desk activation changes lead to the invalidation of the loaded tasks? The cases
- // are:
- // - Switching from one active desk to another.
- // - Switching from out of a desk session into an active desk.
- // - Switching from an active desk to a non-desk session.
- // These changes don't affect the list of desks, nor their contents, so let's ignore them
- // for now.
- }
-
private void onRunningTaskAppeared(RunningTaskInfo taskInfo) {
// Make sure this task is not already in the list
for (RunningTaskInfo existingTask : mRunningTasks) {
@@ -424,6 +406,7 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
continue;
}
+ // [getTaskInfo1] will not be null for types below beside [TYPE_DESK].
if (Flags.enableShellTopTaskTracking()) {
final TaskInfo taskInfo1 = rawTask.getBaseGroupedTask().getTaskInfo1();
final Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
@@ -435,10 +418,8 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
final Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
final Task task2 = Task.from(task2Key, taskInfo2,
tmpLockedUsers.get(task2Key.userId) /* isLocked */);
- final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
- convertShellSplitBoundsToLauncher(
- rawTask.getBaseGroupedTask().getSplitBounds());
- allTasks.add(new SplitTask(task1, task2, launcherSplitBounds));
+ allTasks.add(new SplitTask(task1, task2,
+ rawTask.getBaseGroupedTask().getSplitBounds()));
} else {
allTasks.add(new SingleTask(task1));
}
@@ -475,9 +456,7 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
}
if (task2 != null) {
Objects.requireNonNull(rawTask.getSplitBounds());
- final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
- convertShellSplitBoundsToLauncher(rawTask.getSplitBounds());
- allTasks.add(new SplitTask(task1, task2, launcherSplitBounds));
+ allTasks.add(new SplitTask(task1, task2, rawTask.getSplitBounds()));
} else {
allTasks.add(new SingleTask(task1));
}
@@ -502,8 +481,7 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
Set minimizedTaskIds = minimizedTaskIdArray != null
? CollectionsKt.toSet(ArraysKt.asIterable(minimizedTaskIdArray))
: Collections.emptySet();
- if (enableSeparateExternalDisplayTasks()
- && !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
// This code is not needed when the multiple desktop feature is enabled, since Shell
// will send a single `GroupedTaskInfo` for each desk with a unique `deskId` across
// all displays.
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index e1a5d7038e..269d3e8ce3 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -104,7 +104,7 @@ import app.lawnchair.compat.LawnchairQuickstepCompat;
* See {@link com.android.quickstep.views.RecentsView}.
*/
public final class RecentsActivity extends StatefulActivity implements
- RecentsViewContainer {
+ RecentsViewContainer, InvariantDeviceProfile.OnIDPChangeListener {
private static final String TAG = "RecentsActivity";
public static final ContextTracker.ActivityTracker ACTIVITY_TRACKER =
@@ -136,6 +136,7 @@ public final class RecentsActivity extends StatefulActivity implem
* Init drag layer and overview panel views.
*/
protected void setupViews() {
+ getTheme().applyStyle(getOverviewBlurStyleResId(), true);
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
// SplitSelectStateController needs to be created before setContentView()
mSplitSelectStateController =
@@ -238,6 +239,16 @@ public final class RecentsActivity extends StatefulActivity implem
return mScrimView;
}
+ @Override
+ public FallbackActivityInterface getContainerInterface() {
+ return FallbackActivityInterface.INSTANCE;
+ }
+
+ @Override
+ public SplitSelectStateController getSplitSelectStateController() {
+ return mSplitSelectStateController;
+ }
+
@Override
public FallbackRecentsView getOverviewPanel() {
return mFallbackRecentsView;
@@ -379,10 +390,10 @@ public final class RecentsActivity extends StatefulActivity implem
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setWallpaperDependentTheme(this);
-
mStateManager = new StateManager<>(this, RecentsState.BG_LAUNCHER);
initDeviceProfile();
+ InvariantDeviceProfile.INSTANCE.get(this).addOnChangeListener(this);
setupViews();
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
@@ -468,6 +479,7 @@ public final class RecentsActivity extends StatefulActivity implem
mActivityLaunchAnimationRunner = null;
mSplitSelectStateController.onDestroy();
mTISBindHelper.onDestroy();
+ InvariantDeviceProfile.INSTANCE.get(this).removeOnChangeListener(this);
}
@Override
@@ -491,7 +503,7 @@ public final class RecentsActivity extends StatefulActivity implem
this.getIApplicationThread(),
"StartHomeFromRecents"),
"Lawnchair");
- startHomeIntentSafely(this, options.toBundle(), TAG);
+ startHomeIntentSafely(this, options.toBundle(), TAG, getDisplayId());
}
private final RemoteAnimationFactory mAnimationToHomeFactory =
@@ -560,4 +572,14 @@ public final class RecentsActivity extends StatefulActivity implem
public boolean isRecentsViewVisible() {
return getStateManager().getState().isRecentsViewVisible();
}
+
+ @Override
+ public void onIdpChanged(boolean modelPropertiesChanged) {
+ onHandleConfigurationChanged();
+ }
+
+ @Override
+ public int getOverviewBlurStyleResId() {
+ return R.style.OverviewBlurFallbackStyle;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 2cf7f15898..cddfd29f5e 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -37,8 +37,9 @@ import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.fallback.window.RecentsWindowFlags;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
+import com.android.quickstep.views.RecentsViewContainer;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -58,15 +59,15 @@ public class RecentsAnimationCallbacks implements
com.android.systemui.shared.system.RecentsAnimationListener {
private final Set mListeners = new ArraySet<>();
- private final SystemUiProxy mSystemUiProxy;
+ private final RecentsViewContainer mContainer;
// TODO(141886704): Remove these references when they are no longer needed
private RecentsAnimationController mController;
private boolean mCancelled;
- public RecentsAnimationCallbacks(SystemUiProxy systemUiProxy) {
- mSystemUiProxy = systemUiProxy;
+ public RecentsAnimationCallbacks(RecentsViewContainer container) {
+ mContainer = container;
}
@UiThread
@@ -128,7 +129,7 @@ public class RecentsAnimationCallbacks implements
boolean isOpeningHome = Arrays.stream(appTargets).filter(app -> app.mode == MODE_OPENING
&& app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME)
.count() > 0;
- if (appCount == 0 && (!RecentsWindowFlags.Companion.getEnableOverviewInWindow()
+ if (appCount == 0 && (!(mContainer instanceof RecentsWindowManager)
|| isOpeningHome)) {
ActiveGestureProtoLogProxy.logOnRecentsAnimationStartCancelled();
// Edge case, if there are no closing app targets, then Launcher has nothing to handle
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 865cc47091..17c4405151 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -57,8 +57,6 @@ public class RecentsAnimationController {
private boolean mFinishRequested = false;
// Only valid when mFinishRequested == true.
private boolean mFinishTargetIsLauncher;
- // Only valid when mFinishRequested == true
- private boolean mLauncherIsVisibleAtFinish;
private RunnableList mPendingFinishCallbacks = new RunnableList();
public RecentsAnimationController(RecentsAnimationControllerCompat controller,
@@ -132,28 +130,14 @@ public class RecentsAnimationController {
finishController(toRecents, onFinishComplete, sendUserLeaveHint);
}
- @UiThread
- public void finish(boolean toRecents, boolean launcherIsVisibleAtFinish,
- Runnable onFinishComplete, boolean sendUserLeaveHint) {
- Preconditions.assertUIThread();
- finishController(toRecents, launcherIsVisibleAtFinish, onFinishComplete, sendUserLeaveHint,
- false);
- }
-
@UiThread
public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
- finishController(toRecents, false, callback, sendUserLeaveHint, false /* forceFinish */);
+ finishController(toRecents, callback, sendUserLeaveHint, false /* forceFinish */);
}
@UiThread
public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint,
boolean forceFinish) {
- finishController(toRecents, toRecents, callback, sendUserLeaveHint, forceFinish);
- }
-
- @UiThread
- public void finishController(boolean toRecents, boolean launcherIsVisibleAtFinish,
- Runnable callback, boolean sendUserLeaveHint, boolean forceFinish) {
mPendingFinishCallbacks.add(callback);
if (!forceFinish && mFinishRequested) {
// If finish has already been requested, then add the callback to the pending list.
@@ -165,7 +149,6 @@ public class RecentsAnimationController {
// Finish not yet requested
mFinishRequested = true;
mFinishTargetIsLauncher = toRecents;
- mLauncherIsVisibleAtFinish = launcherIsVisibleAtFinish;
mOnFinishedListener.accept(this);
Runnable finishCb = () -> {
mController.finish(toRecents, sendUserLeaveHint, new IResultReceiver.Stub() {
@@ -242,14 +225,6 @@ public class RecentsAnimationController {
return mFinishTargetIsLauncher;
}
- /**
- * RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether
- * the animation was finished to launcher vs an app.
- */
- public boolean getLauncherIsVisibleAtFinish() {
- return mLauncherIsVisibleAtFinish;
- }
-
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "RecentsAnimationController:");
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index ebb6268f32..a8ddbb4ed7 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -15,10 +15,9 @@
*/
package com.android.quickstep;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
@@ -53,7 +52,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_T
import android.app.ActivityTaskManager;
import android.content.Context;
import android.graphics.Region;
-import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -63,12 +61,10 @@ import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Utilities;
+import com.android.app.displaylib.PerDisplayRepository;
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.DisplayController;
@@ -77,6 +73,7 @@ import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SettingsCache;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
+import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ContextualSearchStateManager;
import com.android.quickstep.util.GestureExclusionManager;
@@ -89,19 +86,17 @@ import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
-import javax.inject.Inject;
+import java.io.PrintWriter;
import app.lawnchair.LawnchairApp;
/**
* Manages the state of the system during a swipe up gesture.
*/
-@LauncherAppSingleton
public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, ExclusionListener {
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
@@ -110,8 +105,19 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 1.414f;
- public static DaggerSingletonObject INSTANCE =
- new DaggerSingletonObject<>(LauncherAppComponent::getRecentsAnimationDeviceState);
+ public static final DaggerSingletonObject>
+ REPOSITORY_INSTANCE = new DaggerSingletonObject<>(
+ QuickstepBaseAppComponent::getRecentsAnimationDeviceStateRepository);
+
+ /** The SysUI state ignores trackpad, touch gestures, and keyboard shortcuts. */
+ private static final long GESTURE_OR_KB_SHORTCUT_DISABLING_STATES =
+ SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+ | SYSUI_STATE_QUICK_SETTINGS_EXPANDED
+ | SYSUI_STATE_MAGNIFICATION_OVERLAP
+ | SYSUI_STATE_DEVICE_DREAMING
+ | SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION
+ | SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING;
private final Context mContext;
private final DisplayController mDisplayController;
@@ -121,11 +127,8 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
private final RotationTouchHelper mRotationTouchHelper;
private final TaskStackChangeListener mPipListener;
- // Cache for better performance since it doesn't change at runtime.
- private final boolean mCanImeRenderGesturalNavButtons = isImeRenderingNavButtons();
private @SystemUiStateFlags long mSystemUiStateFlags = QuickStepContract.SYSUI_STATE_AWAKE;
- private final Map mSysUIStateFlagsPerDisplay = new ConcurrentHashMap<>();
private NavigationMode mMode = THREE_BUTTONS;
private NavBarPosition mNavBarPosition;
@@ -142,18 +145,20 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
private int mGestureBlockingTaskId = -1;
private @NonNull Region mExclusionRegion = GestureExclusionManager.EMPTY_REGION;
private boolean mExclusionListenerRegistered;
+ private final int mDisplayId;
- @VisibleForTesting
- @Inject
+ @AssistedInject
RecentsAnimationDeviceState(
@ApplicationContext Context context,
+ @Assisted int displayId,
+ @Assisted RotationTouchHelper rotationTouchHelper,
GestureExclusionManager exclusionManager,
DisplayController displayController,
ContextualSearchStateManager contextualSearchStateManager,
- RotationTouchHelper rotationTouchHelper,
SettingsCache settingsCache,
DaggerSingletonTracker lifeCycle) {
mContext = context;
+ mDisplayId = displayId;
mDisplayController = displayController;
mExclusionManager = exclusionManager;
mContextualSearchStateManager = contextualSearchStateManager;
@@ -166,10 +171,10 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
// Register for exclusion updates
lifeCycle.addCloseable(this::unregisterExclusionListener);
}
-
+
// Register for display changes changes
mDisplayController.addChangeListener(this);
- onDisplayInfoChanged(context, mDisplayController.getInfo(), CHANGE_ALL);
+ onDisplayInfoChanged(context, mDisplayController.getInfoForDisplay(mDisplayId), CHANGE_ALL);
lifeCycle.addCloseable(() -> mDisplayController.removeChangeListener(this));
if (mIsOneHandedModeSupported) {
@@ -226,16 +231,16 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
}
/**
- * Adds a listener for the nav mode change, guaranteed to be called after the device state's
+ * Adds a listener for the change flag, guaranteed to be called after the device state's
* mode has changed.
*
* @return Added {@link DisplayInfoChangeListener} so that caller is
* responsible for removing the listener from {@link DisplayController} to avoid memory leak.
*/
- public DisplayController.DisplayInfoChangeListener addNavigationModeChangedCallback(
- Runnable callback) {
+ public DisplayController.DisplayInfoChangeListener addDisplayInfoChangeCallback(
+ int changeFlag, Runnable callback) {
DisplayController.DisplayInfoChangeListener listener = (context, info, flags) -> {
- if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
+ if ((flags & changeFlag) != 0) {
callback.run();
}
};
@@ -246,7 +251,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
/**
* Remove the {DisplayController.DisplayInfoChangeListener} added from
- * {@link #addNavigationModeChangedCallback} when {@link TouchInteractionService} is destroyed.
+ * {@link #addDisplayInfoChangeCallback} when {@link TouchInteractionService} is destroyed.
*/
public void removeDisplayInfoChangeListener(
DisplayController.DisplayInfoChangeListener listener) {
@@ -365,47 +370,28 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
* Updates the system ui state flags from SystemUI for a specific display.
*
* @param stateFlags the current {@link SystemUiStateFlags} for the display.
- * @param displayId the display's ID.
*/
- public void setSysUIStateFlagsForDisplay(@SystemUiStateFlags long stateFlags,
- int displayId) {
- mSysUIStateFlagsPerDisplay.put(displayId, stateFlags);
+ public void setSysUIStateFlags(@SystemUiStateFlags long stateFlags) {
+ mSystemUiStateFlags = stateFlags;
}
/**
* Clears the system ui state flags for a specific display. This is called when the display is
* destroyed.
*
- * @param displayId the display's ID.
*/
- public void clearSysUIStateFlagsForDisplay(int displayId) {
- mSysUIStateFlagsPerDisplay.remove(displayId);
+ public void clearSysUIStateFlags() {
+ mSystemUiStateFlags = QuickStepContract.SYSUI_STATE_AWAKE;
}
/**
- * @return the system ui state flags for the default display.
- */
- // TODO(141886704): See if we can remove this
- @SystemUiStateFlags
- public long getSysuiStateFlag() {
- return getSystemUiStateFlags(DEFAULT_DISPLAY);
- }
-
- /**
- * @return the system ui state flags for a given display ID.
+ * @return the system ui state flags for this display.
*/
@SystemUiStateFlags
- public long getSystemUiStateFlags(int displayId) {
- return mSysUIStateFlagsPerDisplay.getOrDefault(displayId,
- QuickStepContract.SYSUI_STATE_AWAKE);
+ public long getSysuiStateFlags() {
+ return mSystemUiStateFlags;
}
- /**
- * @return the display ids that have sysui state.
- */
- public Set getDisplaysWithSysUIState() {
- return mSysUIStateFlagsPerDisplay.keySet();
- }
/**
* Sets the flag that indicates whether a predictive back-to-home animation is in progress
*/
@@ -424,8 +410,8 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
* @return whether SystemUI is in a state where we can start a system gesture.
*/
public boolean canStartSystemGesture() {
- boolean canStartWithNavHidden = (getSysuiStateFlag() & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
- || (getSysuiStateFlag() & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
+ boolean canStartWithNavHidden = (getSysuiStateFlags() & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+ || (getSysuiStateFlags() & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
|| mRotationTouchHelper.isTaskListFrozen();
return canStartWithNavHidden && canStartAnyGesture();
}
@@ -437,24 +423,27 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
*/
public boolean canStartTrackpadGesture() {
boolean trackpadGesturesEnabled =
- (getSysuiStateFlag() & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0;
+ (getSysuiStateFlags() & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0;
return trackpadGesturesEnabled && canStartAnyGesture();
}
+ /**
+ * @return whether SystemUI is in a state that allows the overview command from being started.
+ */
+ public boolean canStartOverviewCommand() {
+ final long sysUiStateFlags = getSysuiStateFlags();
+ final boolean overviewEnabled = !isOverviewDisabled();
+ return overviewEnabled && (sysUiStateFlags & GESTURE_OR_KB_SHORTCUT_DISABLING_STATES) == 0;
+ }
+
/**
* Common logic to determine if either trackpad or finger gesture can be started
*/
private boolean canStartAnyGesture() {
- boolean homeOrOverviewEnabled = (getSysuiStateFlag() & SYSUI_STATE_HOME_DISABLED) == 0
- || (getSysuiStateFlag() & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
- long gestureDisablingStates = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
- | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
- | SYSUI_STATE_QUICK_SETTINGS_EXPANDED
- | SYSUI_STATE_MAGNIFICATION_OVERLAP
- | SYSUI_STATE_DEVICE_DREAMING
- | SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION
- | SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING;
- return (gestureDisablingStates & getSysuiStateFlag()) == 0 && homeOrOverviewEnabled;
+ boolean homeOrOverviewEnabled = (getSysuiStateFlags() & SYSUI_STATE_HOME_DISABLED) == 0
+ || (getSysuiStateFlags() & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
+ return (GESTURE_OR_KB_SHORTCUT_DISABLING_STATES & getSysuiStateFlags()) == 0
+ && homeOrOverviewEnabled;
}
/**
@@ -462,35 +451,35 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
* (like camera or maps)
*/
public boolean isKeyguardShowingOccluded() {
- return (getSysuiStateFlag() & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0;
}
/**
* @return whether screen pinning is enabled and active
*/
public boolean isScreenPinningActive() {
- return (getSysuiStateFlag() & SYSUI_STATE_SCREEN_PINNING) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_SCREEN_PINNING) != 0;
}
/**
* @return whether assistant gesture is constraint
*/
public boolean isAssistantGestureIsConstrained() {
- return (getSysuiStateFlag() & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
}
/**
* @return whether the bubble stack is expanded
*/
public boolean isBubblesExpanded() {
- return (getSysuiStateFlag() & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
}
/**
* @return whether the global actions dialog is showing
*/
public boolean isSystemUiDialogShowing() {
- return (getSysuiStateFlag() & SYSUI_STATE_DIALOG_SHOWING) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_DIALOG_SHOWING) != 0;
}
/**
@@ -504,35 +493,35 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
* @return whether the accessibility menu is available.
*/
public boolean isAccessibilityMenuAvailable() {
- return (getSysuiStateFlag() & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
}
/**
* @return whether the accessibility menu shortcut is available.
*/
public boolean isAccessibilityMenuShortcutAvailable() {
- return (getSysuiStateFlag() & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
}
/**
* @return whether home is disabled (either by SUW/SysUI/device policy)
*/
public boolean isHomeDisabled() {
- return (getSysuiStateFlag() & SYSUI_STATE_HOME_DISABLED) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_HOME_DISABLED) != 0;
}
/**
* @return whether overview is disabled (either by SUW/SysUI/device policy)
*/
public boolean isOverviewDisabled() {
- return (getSysuiStateFlag() & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
}
/**
* @return whether one-handed mode is enabled and active
*/
public boolean isOneHandedModeActive() {
- return (getSysuiStateFlag() & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
+ return (getSysuiStateFlags() & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
}
/**
@@ -595,7 +584,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
*/
public boolean canTriggerAssistantAction(MotionEvent ev) {
return mAssistantAvailable
- && !QuickStepContract.isAssistantGestureDisabled(getSysuiStateFlag())
+ && !QuickStepContract.isAssistantGestureDisabled(getSysuiStateFlags())
&& mRotationTouchHelper.touchInAssistantRegion(ev)
&& !isTrackpadScroll(ev)
&& !isLockToAppActive();
@@ -613,7 +602,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
}
if (mIsOneHandedModeEnabled) {
- final Info displayInfo = mDisplayController.getInfo();
+ final Info displayInfo = mDisplayController.getInfoForDisplay(mDisplayId);
return (mRotationTouchHelper.touchInOneHandedModeRegion(ev)
&& (displayInfo.currentSize.x < displayInfo.currentSize.y));
}
@@ -634,8 +623,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
/** Returns whether IME is rendering nav buttons, and IME is currently showing. */
public boolean isImeRenderingNavButtons() {
- return mCanImeRenderGesturalNavButtons && mMode == NO_BUTTON
- && ((getSysuiStateFlag() & SYSUI_STATE_IME_VISIBLE) != 0);
+ return mMode == NO_BUTTON && ((getSysuiStateFlags() & SYSUI_STATE_IME_VISIBLE) != 0);
}
/**
@@ -669,7 +657,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
/** Returns a string representation of the system ui state flags for the default display. */
public String getSystemUiStateString() {
- return getSystemUiStateString(getSysuiStateFlag());
+ return getSystemUiStateString(getSysuiStateFlags());
}
/** Returns a string representation of the system ui state flags. */
@@ -680,24 +668,30 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
public void dump(PrintWriter pw) {
pw.println("DeviceState:");
pw.println(" canStartSystemGesture=" + canStartSystemGesture());
- pw.println(" systemUiFlagsForDefaultDisplay=" + getSysuiStateFlag());
+ pw.println(" systemUiFlagsForDefaultDisplay=" + getSysuiStateFlags());
pw.println(" systemUiFlagsDesc=" + getSystemUiStateString());
pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled="
- + QuickStepContract.isAssistantGestureDisabled(getSysuiStateFlag()));
+ + QuickStepContract.isAssistantGestureDisabled(getSysuiStateFlags()));
pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
pw.println(" deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
pw.println(" exclusionRegion=" + mExclusionRegion.getBounds());
pw.println(" pipIsActive=" + mPipIsActive);
pw.println(" predictiveBackToHomeInProgress=" + mIsPredictiveBackToHomeInProgress);
- for (int displayId : mSysUIStateFlagsPerDisplay.keySet()) {
- pw.println(" systemUiFlagsForDisplay" + displayId + "=" + getSystemUiStateFlags(
- displayId));
- pw.println(" systemUiFlagsForDisplay" + displayId + "Desc=" + getSystemUiStateString(
- getSystemUiStateFlags(displayId)));
- }
pw.println(" RotationTouchHelper:");
mRotationTouchHelper.dump(pw);
}
+
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @AssistedFactory
+ public interface Factory {
+ /** Creates a new instance of [RecentsAnimationDeviceState] for a given [displayId] and
+ * [rotationTouchHelper]. */
+ RecentsAnimationDeviceState create(int displayId, RotationTouchHelper rotationTouchHelper);
+ }
+
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 0deb1ca953..60b5337893 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -55,14 +55,11 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets {
*
* @return {@code true} if at least one target app is a desktop task
*/
+ // TODO: b/362720309 - Remove this function once multi-desks is fully launched.
public boolean hasDesktopTasks(Context context) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return false;
}
- // TODO: b/400866688 - Check if we need to update this such that for an empty desk, we
- // receive a list of apps that contain only the Launcher and the `DesktopWallpaperActivity`
- // and both are fullscreen windowing mode. A desk can also have transparent modals and
- // immersive apps which may not have a "freeform" windowing mode.
for (RemoteAnimationTarget target : apps) {
if (target.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
return true;
diff --git a/quickstep/src/com/android/quickstep/RecentsFilterState.java b/quickstep/src/com/android/quickstep/RecentsFilterState.java
index 1808a9769f..fa12d52032 100644
--- a/quickstep/src/com/android/quickstep/RecentsFilterState.java
+++ b/quickstep/src/com/android/quickstep/RecentsFilterState.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableOverviewOnConnectedDisplays;
+
import androidx.annotation.Nullable;
import com.android.quickstep.util.DesksUtils;
@@ -120,14 +122,17 @@ public class RecentsFilterState {
* @param packageName package name to filter GroupTasks by
* if null, Predicate filters out desktop tasks with no non-minimized tasks,
* unless the multiple desks feature is enabled, which allows empty desks.
+ * @param displayId filter out tasks not on this display
*/
- public static Predicate getFilter(@Nullable String packageName) {
- if (packageName == null) {
- return getDesktopTaskFilter();
+ public static Predicate getFilter(@Nullable String packageName, int displayId) {
+ Predicate filter = getDesktopTaskFilter();
+ if (packageName != null) {
+ filter = filter.and(groupTask -> groupTask.containsPackage(packageName));
}
-
- return (groupTask) -> (groupTask.containsPackage(packageName)
- && shouldKeepGroupTask(groupTask));
+ if (enableOverviewOnConnectedDisplays()) {
+ filter = filter.and(groupTask -> groupTask.matchesDisplayId(displayId));
+ }
+ return filter;
}
/**
@@ -135,7 +140,7 @@ public class RecentsFilterState {
* unless the multiple desks feature is enabled, which allows empty desks.
*/
public static Predicate getDesktopTaskFilter() {
- return (groupTask -> shouldKeepGroupTask(groupTask));
+ return RecentsFilterState::shouldKeepGroupTask;
}
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index eb032ffc7e..96689e2866 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -17,7 +17,7 @@ package com.android.quickstep;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
-import static com.android.launcher3.Flags.enableGridOnlyOverview;
+import static com.android.launcher3.util.OverviewReleaseFlags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
@@ -43,7 +43,6 @@ import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener;
import com.android.launcher3.icons.IconProvider;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.DisplayController;
@@ -70,6 +69,7 @@ import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -96,6 +96,17 @@ public class RecentsModel implements RecentTasksDataSource, TaskStackChangeListe
private final ConcurrentLinkedQueue mThumbnailChangeListeners =
new ConcurrentLinkedQueue<>();
+ private final ConcurrentLinkedQueue mRecentTasksChangedListeners =
+ new ConcurrentLinkedQueue<>();
+ private final RecentTasksChangedListener mRecentTasksListObserver =
+ new RecentTasksChangedListener() {
+ @Override
+ public void onRecentTasksChanged() {
+ mRecentTasksChangedListeners.forEach(
+ RecentTasksChangedListener::onRecentTasksChanged);
+ }
+ };
+
private final Context mContext;
private final RecentTasksList mTaskList;
private final TaskIconCache mIconCache;
@@ -108,14 +119,12 @@ public class RecentsModel implements RecentTasksDataSource, TaskStackChangeListe
DisplayController displayController,
LockedUserState lockedUserState,
Lazy themeManagerLazy,
- DesktopVisibilityController desktopVisibilityController,
DaggerSingletonTracker tracker
) {
// Lazily inject the ThemeManager and access themeManager once the device is
// unlocked. See b/393248495 for details.
this(context, new IconProvider(context), systemUiProxy, topTaskTracker,
- displayController, lockedUserState, themeManagerLazy, desktopVisibilityController,
- tracker);
+ displayController, lockedUserState, themeManagerLazy, tracker);
}
@SuppressLint("VisibleForTests")
@@ -126,7 +135,6 @@ public class RecentsModel implements RecentTasksDataSource, TaskStackChangeListe
DisplayController displayController,
LockedUserState lockedUserState,
Lazy themeManagerLazy,
- DesktopVisibilityController desktopVisibilityController,
DaggerSingletonTracker tracker) {
this(context,
new RecentTasksList(
@@ -134,7 +142,7 @@ public class RecentsModel implements RecentTasksDataSource, TaskStackChangeListe
MAIN_EXECUTOR,
context.getSystemService(KeyguardManager.class),
systemUiProxy,
- topTaskTracker, desktopVisibilityController, tracker),
+ topTaskTracker, tracker),
new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider, displayController),
new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR),
iconProvider,
@@ -156,6 +164,7 @@ public class RecentsModel implements RecentTasksDataSource, TaskStackChangeListe
DaggerSingletonTracker tracker) {
mContext = context;
mTaskList = taskList;
+ mTaskList.registerRecentTasksChangedListener(mRecentTasksListObserver);
mIconCache = iconCache;
mIconCache.registerTaskVisualsChangeListener(this);
mThumbnailCache = thumbnailCache;
@@ -183,10 +192,10 @@ public class RecentsModel implements RecentTasksDataSource, TaskStackChangeListe
Runnable unlockCallback = () -> themeManagerLazy.get().addChangeListener(this);
lockedUserState.runOnUserUnlocked(unlockCallback);
- // Lawnchair-TODO-Merge-High: taskStackChangeListeners run when isRecentsEnabled
tracker.addCloseable(() -> {
if (LawnchairApp.isRecentsEnabled()) {
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this);
+ mTaskList.unregisterRecentTasksChangedListener();
}
iconChangeCloseable.close();
mIconCache.removeTaskVisualsChangeListener();
@@ -223,14 +232,28 @@ public class RecentsModel implements RecentTasksDataSource, TaskStackChangeListe
/**
* Fetches the list of recent tasks, based on a filter
*
+ * @param filter Returns true if a GroupTask should be included into the list passed into
+ * callback.
* @param callback The callback to receive the task plan once its complete or null. This is
- * always called on the UI thread.
+ * always called on the UI thread.
+ * @return the request id associated with this call.
+ */
+ public int getTasks(Predicate filter, @Nullable Consumer> callback) {
+ return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
+ }
+
+ /**
+ * Fetches the list of recent tasks, based on a filter
+ *
+ * @param callback The callback to receive the task plan and request ID once its complete or
+ * null. This is always called on the UI thread.
* @param filter Returns true if a GroupTask should be included into the list passed into
* callback.
* @return the request id associated with this call.
*/
- public int getTasks(@Nullable Consumer> callback, Predicate filter) {
- return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
+ public int getTasks(
+ @Nullable BiConsumer, Integer> callback, Predicate filter) {
+ return mTaskList.getTasks(/* loadKeysOnly= */ false, callback, filter);
}
/**
@@ -390,14 +413,14 @@ public class RecentsModel implements RecentTasksDataSource, TaskStackChangeListe
* Registers a listener for recent tasks
*/
public void registerRecentTasksChangedListener(RecentTasksChangedListener listener) {
- mTaskList.registerRecentTasksChangedListener(listener);
+ mRecentTasksChangedListeners.add(listener);
}
/**
* Removes the previously registered running tasks listener
*/
- public void unregisterRecentTasksChangedListener() {
- mTaskList.unregisterRecentTasksChangedListener();
+ public void unregisterRecentTasksChangedListener(RecentTasksChangedListener listener) {
+ mRecentTasksChangedListeners.remove(listener);
}
/**
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 0bb825f317..60b1ff3e4d 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -20,10 +20,12 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import android.os.Bundle;
+import android.util.Log;
import android.view.RemoteAnimationTarget;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -31,6 +33,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public class RemoteAnimationTargets {
+ private static final String TAG = "RemoteAnimationTargets";
+
private final CopyOnWriteArrayList mReleaseChecks = new CopyOnWriteArrayList<>();
public final RemoteAnimationTarget[] unfilteredApps;
@@ -79,6 +83,8 @@ public class RemoteAnimationTargets {
return target;
}
}
+ Log.e(TAG, "taskId: " + taskId + " not found. apps contains: "
+ + Arrays.stream(apps).map(target -> target.taskId).toList());
return null;
}
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index e38b89aec6..e6e39e0a52 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -16,7 +16,7 @@
package com.android.quickstep;
-import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.enableMultipleDesktops;
import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
import static java.util.stream.Collectors.toList;
@@ -32,12 +32,14 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
-import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.split.SplitBounds;
+import kotlin.collections.CollectionsKt;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -52,56 +54,68 @@ public class RemoteTargetGluer {
// This is the default number of handles to create when we don't know how many tasks are running
// (e.g. if we're in split screen). Allocate extra for potential tasks overlaid, like volume.
- private static final int DEFAULT_NUM_HANDLES = 4;
-
+ private static final int DEFAULT_NUM_HANDLES = 10;
private RemoteTargetHandle[] mRemoteTargetHandles;
- private SplitConfigurationOptions.SplitBounds mSplitBounds;
+ private SplitBounds mSplitBounds;
/**
* Use this constructor if remote targets are split-screen independent
*/
public RemoteTargetGluer(Context context, BaseContainerInterface sizingStrategy,
RemoteAnimationTargets targets, boolean forDesktop) {
- init(context, sizingStrategy, targets.apps.length, forDesktop);
+ mRemoteTargetHandles = createHandles(context, sizingStrategy, forDesktop,
+ targets.apps.length);
}
/**
* Use this constructor if you want the number of handles created to match the number of active
* running tasks
*/
- public RemoteTargetGluer(Context context, BaseContainerInterface sizingStrategy) {
- // TODO: b/403344864 Make sure init with correct number of RemoteTargetHandle with
- // multi-desks feature enabled as well.
- int visibleTasksCount = DesktopVisibilityController.INSTANCE.get(context)
- .getVisibleDesktopTasksCountDeprecated();
- if (visibleTasksCount > 0) {
- // Allocate +1 to account for a new task added to the desktop mode
- int numHandles = visibleTasksCount + 1;
- init(context, sizingStrategy, numHandles, true /* forDesktop */);
- return;
+ public RemoteTargetGluer(Context context, BaseContainerInterface sizingStrategy,
+ @Nullable GroupedTaskInfo groupedTaskInfo) {
+ if (enableMultipleDesktops(context)) {
+ if (groupedTaskInfo != null && groupedTaskInfo.isBaseType(GroupedTaskInfo.TYPE_DESK)) {
+ // Allocate +1 to account for the DesktopWallpaperActivity added to the desk.
+ int numHandles = groupedTaskInfo.getTaskInfoList().size() + 1;
+ mRemoteTargetHandles = createHandles(context, sizingStrategy,
+ /* forDesktop = */ true, numHandles);
+ return;
+ }
+ } else {
+ int visibleTasksCount = DesktopVisibilityController.INSTANCE.get(context)
+ .getVisibleDesktopTasksCountDeprecated();
+ if (visibleTasksCount > 0) {
+ // Allocate +1 to account for the DesktopWallpaperActivity added to the desk.
+ int numHandles = visibleTasksCount + 1;
+ mRemoteTargetHandles = createHandles(context, sizingStrategy,
+ /* forDesktop = */ true, numHandles);
+ return;
+ }
}
// Assume 2 handles needed for split, scale down as needed later on when we actually
// get remote targets
- init(context, sizingStrategy, DEFAULT_NUM_HANDLES, false /* forDesktop */);
- }
-
- private void init(Context context, BaseContainerInterface sizingStrategy, int numHandles,
- boolean forDesktop) {
- mRemoteTargetHandles = createHandles(context, sizingStrategy, numHandles, forDesktop);
+ mRemoteTargetHandles = createHandles(context, sizingStrategy, /* forDesktop = */ false,
+ DEFAULT_NUM_HANDLES);
}
private RemoteTargetHandle[] createHandles(Context context,
- BaseContainerInterface sizingStrategy, int numHandles, boolean forDesktop) {
+ BaseContainerInterface sizingStrategy, boolean forDesktop, int numHandles) {
RemoteTargetHandle[] handles = new RemoteTargetHandle[numHandles];
for (int i = 0; i < numHandles; i++) {
- TaskViewSimulator tvs = new TaskViewSimulator(context, sizingStrategy, forDesktop , i);
- TransformParams transformParams = new TransformParams();
- handles[i] = new RemoteTargetHandle(tvs, transformParams);
+ handles[i] = createHandle(context, sizingStrategy, forDesktop, i);
}
return handles;
}
+ private RemoteTargetHandle createHandle(Context context,
+ BaseContainerInterface sizingStrategy, boolean forDesktop, int taskIndex) {
+ TaskViewSimulator tvs = new TaskViewSimulator(
+ context, sizingStrategy, forDesktop , taskIndex);
+ TransformParams transformParams = new TransformParams();
+ return new RemoteTargetHandle(tvs, transformParams);
+ }
+
/**
* Pairs together {@link TaskViewSimulator}s and {@link TransformParams} into a
* {@link RemoteTargetHandle}
@@ -126,7 +140,7 @@ public class RemoteTargetGluer {
* information specified.
*/
public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
- SplitConfigurationOptions.SplitBounds splitBounds) {
+ SplitBounds splitBounds) {
mSplitBounds = splitBounds;
return assignTargetsForSplitScreen(targets);
}
@@ -137,19 +151,13 @@ public class RemoteTargetGluer {
* the left/top task, index 1 right/bottom.
*/
public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
- resizeRemoteTargetHandles(targets);
-
// If we are in a true split screen case (2 apps running on screen), either:
// a) mSplitBounds was already set (from the clicked GroupedTaskView)
// b) A SplitBounds was passed up from shell (via AbsSwipeUpHandler)
// If both of these are null, we are in a 1-app or 1-app-plus-assistant case.
if (mSplitBounds == null && targets.extras != null
&& targets.extras.containsKey(KEY_EXTRA_SPLIT_BOUNDS)) {
- SplitBounds shellSplitBounds = targets.extras.getParcelable(KEY_EXTRA_SPLIT_BOUNDS,
- SplitBounds.class);
- if (shellSplitBounds != null) {
- mSplitBounds = convertShellSplitBoundsToLauncher(shellSplitBounds);
- }
+ mSplitBounds = targets.extras.getParcelable(KEY_EXTRA_SPLIT_BOUNDS, SplitBounds.class);
}
boolean containsSplitTargets = mSplitBounds != null;
@@ -157,66 +165,78 @@ public class RemoteTargetGluer {
mRemoteTargetHandles.length + " appsLength: " + targets.apps.length);
if (mRemoteTargetHandles.length == 1) {
+ resizeRemoteTargetHandles(targets);
// Single fullscreen app
// If we're not in split screen, the splitIds count doesn't really matter since we
// should always hit this case.
- mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
- if (targets.apps.length > 0) {
- // Unclear why/when target.apps length == 0, but it sure does happen :(
- mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(targets.apps[0], null);
- }
+ setRemoteTargetHandle(targets,
+ targets.apps.length > 0 ? targets.apps[0] : null,
+ /* targetsToExclude = */ null, /* transitionInfo = */ null,
+ /* splitBounds = */ null, /* taskIndex = */ 0);
} else if (!containsSplitTargets) {
+ resizeRemoteTargetHandles(targets);
// Single App + Assistant
for (int i = 0; i < mRemoteTargetHandles.length; i++) {
- mRemoteTargetHandles[i].mTransformParams.setTargetSet(targets);
- mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(targets.apps[i], null);
- }
- } else {
- // Split apps (+ maybe assistant)
- RemoteAnimationTarget topLeftTarget = targets.findTask(mSplitBounds.leftTopTaskId);
- RemoteAnimationTarget bottomRightTarget = targets.findTask(
- mSplitBounds.rightBottomTaskId);
- List overlayTargets = Arrays.stream(targets.apps).filter(
- target -> target.windowConfiguration.getWindowingMode()
- != WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW).collect(toList());
-
- // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
- // vice versa
- mRemoteTargetHandles[0].mTransformParams.setTargetSet(
- createRemoteAnimationTargetsForTarget(targets,
- Collections.singletonList(bottomRightTarget)));
- mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, mSplitBounds);
-
- mRemoteTargetHandles[1].mTransformParams.setTargetSet(
- createRemoteAnimationTargetsForTarget(targets,
- Collections.singletonList(topLeftTarget)));
- mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget, mSplitBounds);
-
- // Set the remaining overlay tasks to be their own TaskViewSimulator as fullscreen tasks
- if (!overlayTargets.isEmpty()) {
- ArrayList targetsToExclude = new ArrayList<>();
- targetsToExclude.add(topLeftTarget);
- targetsToExclude.add(bottomRightTarget);
- // Start i at 2 to account for top/left and bottom/right split handles already made
- for (int i = 2; i < targets.apps.length; i++) {
- if (i >= mRemoteTargetHandles.length) {
- Log.e(TAG, String.format("Attempting to animate an untracked target"
- + " (%d handles allocated, but %d want to animate)",
- mRemoteTargetHandles.length, targets.apps.length));
- break;
- }
- mRemoteTargetHandles[i].mTransformParams.setTargetSet(
- createRemoteAnimationTargetsForTarget(targets, targetsToExclude));
- mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(
- overlayTargets.get(i - 2));
- }
-
+ setRemoteTargetHandle(targets, targets.apps[i], /* targetsToExclude = */ null,
+ /* transitionInfo = */ null, /* splitBounds = */ null, /* taskIndex = */ i);
}
+ } else if (mSplitBounds != null) {
+ setSplitRemoteTargetHandles(targets);
}
return mRemoteTargetHandles;
}
+ private void setSplitRemoteTargetHandles(RemoteAnimationTargets targets) {
+ // Split apps (+ maybe assistant)
+ final List leftTopTargetIds = mSplitBounds.leftTopTaskIds;
+ final List rightBottomTargetIds = mSplitBounds.rightBottomTaskIds;
+ if (leftTopTargetIds.isEmpty() || rightBottomTargetIds.isEmpty()) {
+ throw new IllegalStateException("The target ids is invalid: mSplitBounds = "
+ + mSplitBounds);
+ }
+ final List leftTopTargets =
+ CollectionsKt.mapNotNull(leftTopTargetIds, targets::findTask);
+ final List rightBottomTargets =
+ CollectionsKt.mapNotNull(rightBottomTargetIds, targets::findTask);
+
+ final List overlayTargets = Arrays.stream(targets.apps).filter(
+ target -> isOverlayTarget(target, leftTopTargets,
+ rightBottomTargets)).toList();
+ final int handleCount = leftTopTargets.size() + rightBottomTargets.size()
+ + overlayTargets.size();
+ if (handleCount > targets.apps.length) {
+ throw new IllegalStateException("Attempting to animate app count:" + handleCount
+ + "but the total app count: " + targets.apps.length);
+ }
+ if (handleCount > mRemoteTargetHandles.length) {
+ throw new IllegalStateException("Attempting to animate app count:" + handleCount
+ + "but the max handle count: " + mRemoteTargetHandles.length);
+ }
+ if (handleCount < mRemoteTargetHandles.length) {
+ reduceRemoteTargetHandles(handleCount);
+ }
+
+ int taskIndex = 0;
+ for (final RemoteAnimationTarget target : leftTopTargets) {
+ setRemoteTargetHandle(targets, target, rightBottomTargets, /* transitionInfo = */ null,
+ mSplitBounds, taskIndex++);
+ }
+ for (final RemoteAnimationTarget target : rightBottomTargets) {
+ setRemoteTargetHandle(targets, target, leftTopTargets, /* transitionInfo = */ null,
+ mSplitBounds, taskIndex++);
+ }
+ // Set the remaining overlay tasks to be their own TaskViewSimulator as fullscreen tasks
+ if (!overlayTargets.isEmpty()) {
+ List targetsToExclude = new ArrayList<>(leftTopTargets);
+ targetsToExclude.addAll(rightBottomTargets);
+ for (final RemoteAnimationTarget target : overlayTargets) {
+ setRemoteTargetHandle(targets, target, targetsToExclude,
+ /* transitionInfo = */ null, /* splitBounds = */ null, taskIndex++);
+ }
+ }
+ }
+
/**
* Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this creates distinct
* transform params per app in {@code targets.apps} list.
@@ -229,28 +249,67 @@ public class RemoteTargetGluer {
RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
List excludeTargets = Arrays.stream(targets.apps)
.filter(target -> target.taskId != primaryTaskTarget.taskId).collect(toList());
- mRemoteTargetHandles[i].mTransformParams.setTargetSet(
- createRemoteAnimationTargetsForTarget(targets, excludeTargets));
- mRemoteTargetHandles[i].mTransformParams.setTransitionInfo(transitionInfo);
- mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
+ setRemoteTargetHandle(targets, primaryTaskTarget, excludeTargets, transitionInfo,
+ /* splitBounds = */ null, i);
}
return mRemoteTargetHandles;
}
+ private boolean isOverlayTarget(@NonNull RemoteAnimationTarget target,
+ @NonNull List leftTopTargets,
+ @NonNull List rightBottomTargets) {
+ return target.windowConfiguration.getWindowingMode()
+ != WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+ && !leftTopTargets.contains(target)
+ && !rightBottomTargets.contains(target);
+ }
+
/**
* Resize the `mRemoteTargetHandles` array since we assumed initial size, but
* `targets.apps` is the ultimate source of truth here
*/
private void resizeRemoteTargetHandles(RemoteAnimationTargets targets) {
- long appCount = Arrays.stream(targets.apps)
+ int handleCount = (int) Arrays.stream(targets.apps)
.filter(app -> app.mode == targets.targetMode)
.count();
- Log.d(TAG, "appCount: " + appCount + " handleLength: " + mRemoteTargetHandles.length);
- if (appCount < mRemoteTargetHandles.length) {
- Log.d(TAG, "resizing handles");
- RemoteTargetHandle[] newHandles = new RemoteTargetHandle[(int) appCount];
- System.arraycopy(mRemoteTargetHandles, 0/*src*/, newHandles, 0/*dst*/, (int) appCount);
- mRemoteTargetHandles = newHandles;
+ Log.d(TAG, "appCount: " + handleCount + " handleLength: " + mRemoteTargetHandles.length);
+ if (handleCount < mRemoteTargetHandles.length) {
+ reduceRemoteTargetHandles(handleCount);
+ }
+ }
+
+ /**
+ * Reduces the number of remote target handles to a specified count.
+ * The caller is responsible for ensuring that the target {@code handleCount}
+ * is always less than the current number of remote target handles
+ * ({@link #mRemoteTargetHandles}'s current size).
+ *
+ * @param handleCount The desired number of remote target handles after reduction.
+ * This value should be non-negative and less than the current size of the handle list.
+ */
+ private void reduceRemoteTargetHandles(int handleCount) {
+ Log.d(TAG, "Reduce handles, count: " + handleCount);
+ RemoteTargetHandle[] newHandles = new RemoteTargetHandle[(int) handleCount];
+ System.arraycopy(mRemoteTargetHandles, 0/*src*/, newHandles, 0/*dst*/, (int) handleCount);
+ mRemoteTargetHandles = newHandles;
+ }
+
+ private void setRemoteTargetHandle(@NonNull RemoteAnimationTargets targets,
+ @Nullable RemoteAnimationTarget target,
+ @Nullable List targetsToExclude,
+ @Nullable TransitionInfo transitionInfo,
+ @Nullable SplitBounds splitBounds, int taskIndex) {
+ if (targetsToExclude != null) {
+ mRemoteTargetHandles[taskIndex].mTransformParams.setTargetSet(
+ createRemoteAnimationTargetsForTarget(targets, targetsToExclude));
+ } else {
+ mRemoteTargetHandles[taskIndex].mTransformParams.setTargetSet(targets);
+ }
+ if (transitionInfo != null) {
+ mRemoteTargetHandles[taskIndex].mTransformParams.setTransitionInfo(transitionInfo);
+ }
+ if (target != null) {
+ mRemoteTargetHandles[taskIndex].mTaskViewSimulator.setPreview(target, splitBounds);
}
}
@@ -309,7 +368,7 @@ public class RemoteTargetGluer {
return mRemoteTargetHandles;
}
- public SplitConfigurationOptions.SplitBounds getSplitBounds() {
+ public SplitBounds getSplitBounds() {
return mSplitBounds;
}
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 0c3655f4ef..62ad26a1b6 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
@@ -33,9 +32,8 @@ import android.content.res.Resources;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
-import com.android.launcher3.dagger.ApplicationContext;
-import com.android.launcher3.dagger.LauncherAppComponent;
-import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.app.displaylib.PerDisplayRepository;
+import com.android.launcher3.dagger.WindowContext;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DaggerSingletonTracker;
@@ -43,12 +41,17 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.NavigationMode;
+import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.Flags;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.io.PrintWriter;
import javax.inject.Inject;
@@ -58,11 +61,11 @@ import app.lawnchair.util.LawnchairUtilsKt;
/**
* Helper class for transforming touch events
*/
-@LauncherAppSingleton
public class RotationTouchHelper implements DisplayInfoChangeListener {
- public static final DaggerSingletonObject INSTANCE =
- new DaggerSingletonObject<>(LauncherAppComponent::getRotationTouchHelper);
+ public static final DaggerSingletonObject>
+ REPOSITORY_INSTANCE = new DaggerSingletonObject<>(
+ QuickstepBaseAppComponent::getRotationTouchHelperRepository);
private final OrientationTouchTransformer mOrientationTouchTransformer;
private final DisplayController mDisplayController;
@@ -134,30 +137,29 @@ public class RotationTouchHelper implements DisplayInfoChangeListener {
*/
private boolean mInOverview;
private boolean mTaskListFrozen;
- private final Context mContext;
+ private final Context mWindowContext;
- @Inject
- RotationTouchHelper(@ApplicationContext Context context,
+ @AssistedInject
+ RotationTouchHelper(
+ @Assisted Context windowContext,
DisplayController displayController,
SystemUiProxy systemUiProxy,
DaggerSingletonTracker lifeCycle) {
- mContext = context;
+ mWindowContext = windowContext;
+ mDisplayId = windowContext.getDisplayId();
mDisplayController = displayController;
mSystemUiProxy = systemUiProxy;
- // TODO (b/398195845): this needs updating so non-default displays do not rotate with the
- // default display.
- mDisplayId = DEFAULT_DISPLAY;
- Resources resources = mContext.getResources();
+ Resources resources = mWindowContext.getResources();
mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
- () -> LawnchairUtilsKt.getWindowCornerRadius(mContext));
+ () -> LawnchairUtilsKt.getWindowCornerRadius(mWindowContext));
// Register for navigation mode and rotation changes
mDisplayController.addChangeListenerForDisplay(this, mDisplayId);
DisplayController.Info info = mDisplayController.getInfoForDisplay(mDisplayId);
- onDisplayInfoChanged(context, info, CHANGE_ALL);
+ onDisplayInfoChanged(mWindowContext, info, CHANGE_ALL);
- mOrientationListener = new OrientationEventListener(mContext) {
+ mOrientationListener = new OrientationEventListener(mWindowContext) {
@Override
public void onOrientationChanged(int degrees) {
int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
@@ -265,7 +267,7 @@ public class RotationTouchHelper implements DisplayInfoChangeListener {
NavigationMode newMode = info.getNavigationMode();
mOrientationTouchTransformer.setNavigationMode(newMode,
mDisplayController.getInfoForDisplay(mDisplayId),
- mContext.getResources());
+ mWindowContext.getResources());
TaskStackChangeListeners.getInstance()
.unregisterTaskStackListener(mFrozenTaskListener);
@@ -287,7 +289,7 @@ public class RotationTouchHelper implements DisplayInfoChangeListener {
void setGesturalHeight(int newGesturalHeight) {
mOrientationTouchTransformer.setGesturalHeight(
newGesturalHeight, mDisplayController.getInfoForDisplay(mDisplayId),
- mContext.getResources());
+ mWindowContext.getResources());
}
/**
@@ -394,4 +396,10 @@ public class RotationTouchHelper implements DisplayInfoChangeListener {
private boolean hasGestures(NavigationMode mode) {
return mode.hasGestures || (mode == THREE_BUTTONS && Flags.threeButtonCornerSwipe());
}
+
+ @AssistedFactory
+ public interface Factory {
+ /** Creates a new instance of [RotationTouchHelper] for a given [context]. */
+ RotationTouchHelper create(@WindowContext Context context);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 233f0a9e4f..39ba465714 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -17,7 +17,6 @@ package com.android.quickstep;
import static com.android.app.animation.Interpolators.ACCELERATE_1_5;
import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
import static com.android.launcher3.PagedView.INVALID_PAGE;
import android.animation.Animator;
@@ -53,6 +52,7 @@ import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -84,15 +84,22 @@ public abstract class SwipeUpAnimationLogic implements
protected boolean mIsSwipeForSplit;
- public SwipeUpAnimationLogic(Context context, GestureState gestureState) {
+ public SwipeUpAnimationLogic(Context context, GestureState gestureState,
+ RotationTouchHelper rotationTouchHelper) {
mContext = context;
mGestureState = gestureState;
updateIsGestureForSplit(TopTaskTracker.INSTANCE.get(context)
.getRunningSplitTaskIds().length);
- mTargetGluer = new RemoteTargetGluer(mContext, mGestureState.getContainerInterface());
+ GroupedTaskInfo groupedTaskInfo = null;
+ if (mGestureState.getRunningTask() != null) {
+ groupedTaskInfo =
+ mGestureState.getRunningTask().getPlaceholderGroupedTaskInfo(
+ /* splitTaskIds = */ null);
+ }
+ mTargetGluer = new RemoteTargetGluer(mContext, mGestureState.getContainerInterface(),
+ groupedTaskInfo);
mRemoteTargetHandles = mTargetGluer.getRemoteTargetHandles();
- RotationTouchHelper rotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().getOrientationState().update(
rotationTouchHelper.getCurrentActiveRotation(),
@@ -106,13 +113,14 @@ public abstract class SwipeUpAnimationLogic implements
.getSwipeUpDestinationAndLength(dp, mContext, TEMP_RECT,
mRemoteTargetHandles[0].getTaskViewSimulator().getOrientationState()
.getOrientationHandler());
- mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
+ mDragLengthFactor = (float) dp.getDeviceProperties().getHeightPx() / mTransitionDragLength;
for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
PendingAnimation pendingAnimation = new PendingAnimation(mTransitionDragLength * 2);
TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator();
taskViewSimulator.setDp(dp);
- taskViewSimulator.addAppToCarouselAnim(pendingAnimation, LINEAR);
+ taskViewSimulator.addAppToCarouselAnim(pendingAnimation, LINEAR,
+ mGestureState.isHandlingAtomicEvent());
AnimatorPlaybackController playbackController =
pendingAnimation.createPlaybackController();
@@ -178,10 +186,14 @@ public abstract class SwipeUpAnimationLogic implements
PagedOrientationHandler orientationHandler = getOrientationHandler();
DeviceProfile dp = mDp;
final int halfIconSize = dp.iconSizePx / 2;
- float primaryDimension = orientationHandler
- .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
- float secondaryDimension = orientationHandler
- .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
+ float primaryDimension = orientationHandler.getPrimaryValue(
+ dp.getDeviceProperties().getAvailableWidthPx(),
+ dp.getDeviceProperties().getAvailableHeightPx()
+ );
+ float secondaryDimension = orientationHandler.getSecondaryValue(
+ dp.getDeviceProperties().getAvailableWidthPx(),
+ dp.getDeviceProperties().getAvailableHeightPx()
+ );
final float targetX = primaryDimension / 2f;
final float targetY = secondaryDimension - dp.hotseatBarSizePx;
// Fallback to animate to center of screen.
@@ -252,7 +264,7 @@ public abstract class SwipeUpAnimationLogic implements
}
public boolean isPortrait() {
- return !mDp.isLandscape && !mDp.isSeascape();
+ return !mDp.getDeviceProperties().isLandscape() && !mDp.isSeascape();
}
}
@@ -442,7 +454,7 @@ public abstract class SwipeUpAnimationLogic implements
// to end up offscreen.
mRunningTaskViewScrollOffset = factory.isRtl()
? (Math.min(0, -invariantStartRect.right))
- : (Math.max(0, mDp.widthPx - invariantStartRect.left));
+ : (Math.max(0, mDp.getDeviceProperties().getWidthPx() - invariantStartRect.left));
}
@Override
@@ -451,7 +463,7 @@ public abstract class SwipeUpAnimationLogic implements
float alpha = mAnimationFactory.getWindowAlpha(progress);
mHomeAnim.setPlayFraction(progress);
- if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
+ if (mTargetTaskView == null) {
mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect);
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
mLocalTransformParams
@@ -471,10 +483,9 @@ public abstract class SwipeUpAnimationLogic implements
currentRect,
progress,
mMatrix.mapRadius(cornerRadius),
- !enableAdditionalHomeAnimations() || mTargetTaskView == null
- ? 0 : (int) (alpha * 255));
+ mTargetTaskView == null ? 0 : (int) (alpha * 255));
- if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
+ if (mTargetTaskView == null) {
return;
}
if (mAnimationFactory.isAnimatingIntoIcon() && mAnimationFactory.isAnimationReady()) {
@@ -534,7 +545,7 @@ public abstract class SwipeUpAnimationLogic implements
public void onAnimationStart(Animator animation) {
setUp();
mHomeAnim.dispatchOnStart();
- if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
+ if (mTargetTaskView == null) {
return;
}
Rect thumbnailBounds = new Rect();
@@ -549,7 +560,7 @@ public abstract class SwipeUpAnimationLogic implements
}
private void setUp() {
- if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
+ if (mTargetTaskView == null) {
return;
}
RecentsView recentsView = mTargetTaskView.getRecentsView();
@@ -570,7 +581,7 @@ public abstract class SwipeUpAnimationLogic implements
}
private void cleanUp() {
- if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) {
+ if (mTargetTaskView == null) {
return;
}
RecentsView recentsView = mTargetTaskView.getRecentsView();
diff --git a/quickstep/src/com/android/quickstep/SystemDecorationChangeObserver.kt b/quickstep/src/com/android/quickstep/SystemDecorationChangeObserver.kt
index 45594786aa..d93f5f5e5d 100644
--- a/quickstep/src/com/android/quickstep/SystemDecorationChangeObserver.kt
+++ b/quickstep/src/com/android/quickstep/SystemDecorationChangeObserver.kt
@@ -18,6 +18,7 @@ package com.android.quickstep
import android.content.Context
import android.util.Log
+import com.android.app.displaylib.DisplayDecorationListener
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.DaggerSingletonObject
@@ -38,14 +39,6 @@ class SystemDecorationChangeObserver @Inject constructor(@ApplicationContext con
)
}
- interface DisplayDecorationListener {
- fun onDisplayAddSystemDecorations(displayId: Int)
-
- fun onDisplayRemoved(displayId: Int)
-
- fun onDisplayRemoveSystemDecorations(displayId: Int)
- }
-
fun notifyAddSystemDecorations(displayId: Int) {
if (DEBUG) Log.d(TAG, "SystemDecorationAdded: $displayId")
for (listener in mDisplayDecorationListeners) {
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
index 1598cd4d7c..6d8c50389e 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -18,6 +18,7 @@ package com.android.quickstep
import android.app.ActivityManager
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
@@ -30,6 +31,9 @@ import android.os.Handler
import android.os.IBinder
import android.os.Message
import android.os.RemoteException
+import android.os.Trace
+import android.os.Trace.traceBegin
+import android.os.Trace.traceEnd
import android.os.UserHandle
import android.util.Log
import android.view.IRemoteAnimationRunner
@@ -44,6 +48,7 @@ import android.window.RemoteTransition
import android.window.TaskSnapshot
import android.window.TransitionFilter
import android.window.TransitionInfo
+import android.window.WindowContainerTransaction
import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
@@ -143,6 +148,10 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
private var desktopTaskListener: IDesktopTaskListener? = null
private val remoteTransitions = LinkedHashMap()
+ // Save bubble bar state in case service is not bound yet when it is updated. SysUI relies on
+ // this to suppress the floating bubbles UI.
+ private var hasBubbleBar = false
+
private val stateChangeCallbacks: MutableList = ArrayList()
private var originalTransactionToken: IBinder? = null
@@ -167,11 +176,11 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
@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.
+ * This returns a 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 by lazy {
+ private fun getRecentsPendingIntent(displayId: Int) =
PendingIntent.getActivity(
context,
0,
@@ -183,9 +192,9 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
.setPendingIntentCreatorBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
)
+ .setLaunchDisplayId(displayId)
.toBundle(),
)
- }
val unfoldTransitionProvider: ProxyUnfoldTransitionProvider? =
if ((Flags.enableUnfoldStateAnimation() && ResourceUnfoldTransitionConfig().isEnabled))
@@ -264,6 +273,7 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
this.unfoldAnimation = if (Flags.enableUnfoldStateAnimation()) null else unfoldAnimation
this.dragAndDrop = dragAndDrop
linkToDeath()
+ setHasBubbleBar(hasBubbleBar)
// re-attach the listeners once missing due to setProxy has not been initialized yet.
setPipAnimationListener(pipAnimationListener)
setBubblesListener(bubblesListener)
@@ -417,6 +427,14 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
//systemUiProxy?.notifyTaskbarAutohideSuspend(suspend)
}
+ fun notifyRecentsButtonPositionChanged(bounds: Rect) {
+ executeWithErrorLog({
+ "Failed call notifyRecentsButtonPositionChanged with arg: $bounds"
+ }) {
+ systemUiProxy?.notifyRecentsButtonPositionChanged(bounds)
+ }
+ }
+
fun takeScreenshot(request: ScreenshotRequest) =
executeWithErrorLog({ "Failed call takeScreenshot" }) {
// LC-Ignored
@@ -564,6 +582,14 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
//
// Bubbles
//
+ /** Tells SysUI whether bubble bar is used or not. */
+ fun setHasBubbleBar(hasBubbleBar: Boolean) {
+ executeWithErrorLog({ "Failed call setHasBubbleBar" }) {
+ bubbles?.setHasBubbleBar(hasBubbleBar)
+ }
+ this.hasBubbleBar = hasBubbleBar
+ }
+
/** Sets the listener to be notified of bubble state changes. */
fun setBubblesListener(listener: IBubblesListener?) {
executeWithErrorLog({ "Failed call registerBubblesListener" }) {
@@ -579,10 +605,13 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
* 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
+ * @param bubbleBarTopToScreenBottom distance between the top coordinate of bubble bar and the
+ * bottom of the screen
*/
- fun showBubble(key: String?, top: Int) =
- executeWithErrorLog({ "Failed call showBubble" }) { bubbles?.showBubble(key, top) }
+ fun showBubble(key: String?, bubbleBarTopToScreenBottom: Int) =
+ executeWithErrorLog({ "Failed call showBubble" }) {
+ bubbles?.showBubble(key, bubbleBarTopToScreenBottom)
+ }
/** Tells SysUI to remove all bubbles. */
fun removeAllBubbles() =
@@ -608,11 +637,12 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
* expanded.
*
* @param location location of the bubble bar
- * @param top new top coordinate for bubble bar on screen
+ * @param bubbleBarTopToScreenBottom distance between the new top coordinate for bubble bar and
+ * the bottom of the screen
*/
- fun stopBubbleDrag(location: BubbleBarLocation?, top: Int) =
+ fun stopBubbleDrag(location: BubbleBarLocation?, bubbleBarTopToScreenBottom: Int) =
executeWithErrorLog({ "Failed call stopBubbleDrag" }) {
- bubbles?.stopBubbleDrag(location, top)
+ bubbles?.stopBubbleDrag(location, bubbleBarTopToScreenBottom)
}
/**
@@ -648,13 +678,12 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
}
/**
- * Tells SysUI the top coordinate of bubble bar on screen
- *
- * @param topOnScreen top coordinate for bubble bar on screen
+ * Tells SysUI the distance between the top coordinate of the bubble bar and the bottom of the
+ * screen
*/
- fun updateBubbleBarTopOnScreen(topOnScreen: Int) =
- executeWithErrorLog({ "Failed call updateBubbleBarTopOnScreen" }) {
- bubbles?.updateBubbleBarTopOnScreen(topOnScreen)
+ fun updateBubbleBarTopToScreenBottom(bubbleBarTopToScreenBottom: Int) =
+ executeWithErrorLog({ "Failed call updateBubbleBarTopToScreenBottom" }) {
+ bubbles?.updateBubbleBarTopToScreenBottom(bubbleBarTopToScreenBottom)
}
/**
@@ -689,16 +718,9 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
fun showExpandedView() =
executeWithErrorLog({ "Failed call showExpandedView" }) { bubbles?.showExpandedView() }
- /** Tells SysUI to show the bubble drop target. */
- @JvmOverloads
- fun showBubbleDropTarget(show: Boolean, bubbleBarLocation: BubbleBarLocation? = null) =
- executeWithErrorLog({ "Failed call showDropTarget" }) {
- bubbles?.showDropTarget(show, bubbleBarLocation)
- }
-
/** Tells SysUI to move the dragged bubble to full screen. */
fun moveDraggedBubbleToFullscreen(key: String, dropLocation: Point) {
- executeWithErrorLog({ "Failed to call moveDraggedBubbleToFullscreen"}) {
+ executeWithErrorLog({ "Failed to call moveDraggedBubbleToFullscreen" }) {
bubbles?.moveDraggedBubbleToFullscreen(key, dropLocation)
}
}
@@ -1058,6 +1080,7 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
throw GetRecentTasksException("null mRecentTasks")
}
try {
+ traceBegin(Trace.TRACE_TAG_APP, "getRecentTasks")
val rawTasks =
recentTasks?.getRecentTasks(
numTasks,
@@ -1068,6 +1091,8 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
} catch (e: RemoteException) {
Log.e(TAG, "Failed call getRecentTasks", e)
throw GetRecentTasksException("Failed call getRecentTasks", e)
+ } finally {
+ traceEnd(Trace.TRACE_TAG_APP)
}
}
@@ -1109,11 +1134,18 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
/**
* Calls shell to activate the desk whose ID is `deskId` on whatever display it exists on. This
- * will bring all tasks on this desk to the front.
+ * will show all tasks on this desk and bring [taskIdToReorderToFront] to the front if it's
+ * provided and already on the given desk. If the provided [taskIdToReorderToFront]'s value is
+ * null, do not change the windows' activation on the desk.
*/
- fun activateDesk(deskId: Int, transition: RemoteTransition?) =
+ @JvmOverloads
+ fun activateDesk(
+ deskId: Int,
+ transition: RemoteTransition?,
+ taskIdToReorderToFront: Int? = null,
+ ) =
executeWithErrorLog({ "Failed call activateDesk" }) {
- desktopMode?.activateDesk(deskId, transition)
+ desktopMode?.activateDesk(deskId, transition, taskIdToReorderToFront ?: INVALID_TASK_ID)
}
/** Calls shell to remove the desk whose ID is `deskId`. */
@@ -1124,10 +1156,23 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
fun removeAllDesks() =
executeWithErrorLog({ "Failed call removeAllDesks" }) { desktopMode?.removeAllDesks() }
- /** Call shell to show all apps active on the desktop */
- fun showDesktopApps(displayId: Int, transition: RemoteTransition?) =
+ /**
+ * Call shell to show all apps active on the desktop and bring [taskIdToReorderToFront] to front
+ * if it's valid on the default desk on the given display. If the provided
+ * [taskIdToReorderToFront]'s value is null, do not change the windows' activation on the desk.
+ */
+ @JvmOverloads
+ fun showDesktopApps(
+ displayId: Int,
+ transition: RemoteTransition? = null,
+ taskIdToReorderToFront: Int? = null,
+ ) =
executeWithErrorLog({ "Failed call showDesktopApps" }) {
- desktopMode?.showDesktopApps(displayId, transition)
+ desktopMode?.showDesktopApps(
+ displayId,
+ transition,
+ taskIdToReorderToFront ?: INVALID_TASK_ID,
+ )
}
/** If task with the given id is on the desktop, bring it to front */
@@ -1140,6 +1185,17 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
desktopMode?.showDesktopApp(taskId, transition, toFrontReason)
}
+ /** Call shell to move to an existing fullscreen task (given by [taskId]) from desktop. */
+ @JvmOverloads
+ fun moveToFullscreen(
+ taskId: Int,
+ desktopModeTransitionSource: DesktopModeTransitionSource,
+ remoteTransition: RemoteTransition? = null,
+ ) =
+ executeWithErrorLog({ "Failed call moveToFullscreen" }) {
+ desktopMode?.moveToFullscreen(taskId, desktopModeTransitionSource, remoteTransition)
+ }
+
/** Set a listener on shell to get updates about desktop task state */
fun setDesktopTaskListener(listener: IDesktopTaskListener?) {
desktopTaskListener = listener
@@ -1206,16 +1262,19 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
options: ActivityOptions,
listener: RecentsAnimationListener,
useSyntheticRecentsTransition: Boolean,
+ wct: WindowContainerTransaction? = null,
+ displayId: Int,
): Boolean {
executeWithErrorLog({ "Error starting recents via shell" }) {
recentTasks?.startRecentsTransition(
- recentsPendingIntent,
+ getRecentsPendingIntent(displayId),
intent,
options.toBundle().apply {
if (useSyntheticRecentsTransition) {
putBoolean("is_synthetic_recents_transition", true)
}
},
+ wct,
context.iApplicationThread,
RecentsAnimationListenerStub(listener),
)
@@ -1254,8 +1313,9 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
transitionInfo,
)
- override fun onAnimationCanceled(taskIds: IntArray?, taskSnapshots: Array