Merge "Reland "Update Split button visibility based on DeviceProfile change"" into main
diff --git a/Android.bp b/Android.bp
index 877f7bb..c4ea48a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -75,7 +75,7 @@
"androidx.test.uiautomator_uiautomator",
"androidx.preference_preference",
"SystemUISharedLib",
- "animationlib",
+ "//frameworks/libs/systemui:animationlib",
"launcher-testing-shared",
],
srcs: [
@@ -150,9 +150,9 @@
"androidx.cardview_cardview",
"androidx.window_window",
"com.google.android.material_material",
- "iconloader_base",
- "view_capture",
- "animationlib",
+ "//frameworks/libs/systemui:iconloader_base",
+ "//frameworks/libs/systemui:view_capture",
+ "//frameworks/libs/systemui:animationlib",
"SystemUI-statsd",
"launcher-testing-shared",
"androidx.lifecycle_lifecycle-common-java8",
diff --git a/quickstep/src/com/android/launcher3/HomeTransitionController.java b/quickstep/src/com/android/launcher3/HomeTransitionController.java
deleted file mode 100644
index c4a2e9e..0000000
--- a/quickstep/src/com/android/launcher3/HomeTransitionController.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.quickstep.SystemUiProxy;
-import com.android.wm.shell.shared.IHomeTransitionListener;
-
-/**
- * Controls launcher response to home activity visibility changing.
- */
-public class HomeTransitionController {
-
- @Nullable private QuickstepLauncher mLauncher;
- @Nullable private IHomeTransitionListener mHomeTransitionListener;
-
- public void registerHomeTransitionListener(QuickstepLauncher launcher) {
- mLauncher = launcher;
- mHomeTransitionListener = new IHomeTransitionListener.Stub() {
- @Override
- public void onHomeVisibilityChanged(boolean isVisible) {
- MAIN_EXECUTOR.execute(() -> {
- if (mLauncher != null && mLauncher.getTaskbarUIController() != null) {
- mLauncher.getTaskbarUIController().onLauncherVisibilityChanged(isVisible);
- }
- });
- }
- };
-
- SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(mHomeTransitionListener);
- }
-
- public void unregisterHomeTransitionListener() {
- SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(null);
- mHomeTransitionListener = null;
- mLauncher = null;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index a59aead..def6287 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -35,6 +35,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
@@ -49,8 +50,10 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.OnboardingPrefs;
+import com.android.quickstep.HomeVisibilityState;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.RecentsView;
@@ -79,6 +82,7 @@
AnimatedFloat.VALUE, DISPLAY_PROGRESS_COUNT, Float::max);
private final QuickstepLauncher mLauncher;
+ private final HomeVisibilityState mHomeState;
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
dp -> {
@@ -87,6 +91,8 @@
mControllers.taskbarViewController.onRotationChanged(dp);
}
};
+ private final HomeVisibilityState.VisibilityChangeListener mVisibilityChangeListener =
+ this::onLauncherVisibilityChanged;
// Initialized in init.
private final TaskbarLauncherStateController
@@ -94,6 +100,7 @@
public LauncherTaskbarUIController(QuickstepLauncher launcher) {
mLauncher = launcher;
+ mHomeState = SystemUiProxy.INSTANCE.get(mLauncher).getHomeVisibilityState();
}
@Override
@@ -104,8 +111,11 @@
mControllers.getSharedState().sysuiStateFlags);
mLauncher.setTaskbarUIController(this);
-
- onLauncherVisibilityChanged(mLauncher.hasBeenResumed(), true /* fromInit */);
+ mHomeState.addListener(mVisibilityChangeListener);
+ onLauncherVisibilityChanged(
+ Flags.useActivityOverlay()
+ ? mHomeState.isHomeVisible() : mLauncher.hasBeenResumed(),
+ true /* fromInit */);
onStashedInAppChanged(mLauncher.getDeviceProfile());
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
@@ -129,6 +139,7 @@
mLauncher.setTaskbarUIController(null);
mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+ mHomeState.removeListener(mVisibilityChangeListener);
updateTaskTransitionSpec(true);
}
@@ -234,7 +245,8 @@
@Override
public void refreshResumedState() {
- onLauncherVisibilityChanged(mLauncher.hasBeenResumed());
+ onLauncherVisibilityChanged(Flags.useActivityOverlay()
+ ? mHomeState.isHomeVisible() : mLauncher.hasBeenResumed());
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 8d48154..1a120da 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -656,7 +656,8 @@
* Returns if the current Launcher state has hotseat on top of other elemnets.
*/
public boolean isInHotseatOnTopStates() {
- return mLauncherState != LauncherState.ALL_APPS;
+ return mLauncherState != LauncherState.ALL_APPS
+ && !mLauncher.getWorkspace().isOverlayShown();
}
boolean isInOverview() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 1f3c483..c1c76d0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -220,7 +220,7 @@
mBubbleStashedHandleViewController.setHiddenForBubbles(
!sBubbleBarEnabled || mBubbles.isEmpty());
mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse(
- key -> setSelectedBubble(mBubbles.get(key)));
+ key -> setSelectedBubbleInternal(mBubbles.get(key)));
});
}
@@ -390,7 +390,7 @@
}
}
if (bubbleToSelect != null) {
- setSelectedBubble(bubbleToSelect);
+ setSelectedBubbleInternal(bubbleToSelect);
if (previouslySelectedBubble == null) {
mBubbleStashController.animateToInitialState(update.expanded);
}
@@ -409,8 +409,7 @@
if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) {
// Animate when receiving updates. Skip it if we received the initial state.
boolean animate = !update.initialState;
- mBubbleBarViewController.setBubbleBarLocation(update.bubbleBarLocation, animate);
- mBubbleStashController.setBubbleBarLocation(update.bubbleBarLocation);
+ updateBubbleBarLocationInternal(update.bubbleBarLocation, animate);
}
}
}
@@ -436,7 +435,7 @@
/** Updates the currently selected bubble for launcher views and tells WMShell to show it. */
public void showAndSelectBubble(BubbleBarItem b) {
if (DEBUG) Log.w(TAG, "showingSelectedBubble: " + b.getKey());
- setSelectedBubble(b);
+ setSelectedBubbleInternal(b);
showSelectedBubble();
}
@@ -445,7 +444,7 @@
* WMShell that the selection has changed, that should go through either
* {@link #showSelectedBubble()} or {@link #showAndSelectBubble(BubbleBarItem)}.
*/
- private void setSelectedBubble(BubbleBarItem b) {
+ private void setSelectedBubbleInternal(BubbleBarItem b) {
if (!Objects.equals(b, mSelectedBubble)) {
if (DEBUG) Log.w(TAG, "selectingBubble: " + b.getKey());
mSelectedBubble = b;
@@ -464,6 +463,21 @@
return null;
}
+ /**
+ * Set a new bubble bar location.
+ * <p>
+ * Updates the value locally in Launcher and in WMShell.
+ */
+ public void updateBubbleBarLocation(BubbleBarLocation location) {
+ updateBubbleBarLocationInternal(location, false /* animate */);
+ mSystemUiProxy.setBubbleBarLocation(location);
+ }
+
+ private void updateBubbleBarLocationInternal(BubbleBarLocation location, boolean animate) {
+ mBubbleBarViewController.setBubbleBarLocation(location, animate);
+ mBubbleStashController.setBubbleBarLocation(location);
+ }
+
//
// Loading data for the bubbles
//
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 4ca7c89..c27e9f1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -24,7 +24,9 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.LayoutDirection;
@@ -108,6 +110,7 @@
private final float mIconSize;
// The elevation of the bubbles within the bar
private final float mBubbleElevation;
+ private final float mDragElevation;
private final int mPointerSize;
// Whether the bar is expanded (i.e. the bubble activity is being displayed).
@@ -138,11 +141,15 @@
@Nullable
private Consumer<String> mUpdateSelectedBubbleAfterCollapse;
+ private boolean mDragging;
+
@Nullable
private BubbleView mDraggedBubbleView;
private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
+ private boolean mLocationChangePending;
+
public BubbleBarView(Context context) {
this(context, null);
}
@@ -163,6 +170,7 @@
mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
mIconSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation);
+ mDragElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_drag_elevation);
mPointerSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_pointer_size);
setClipToPadding(false);
@@ -237,12 +245,13 @@
}
}
+ @SuppressLint("RtlHardcoded")
private void onBubbleBarLocationChanged() {
+ mLocationChangePending = false;
final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
mBubbleBarBackground.setAnchorLeft(onLeft);
mRelativePivotX = onLeft ? 0f : 1f;
- ViewGroup.LayoutParams layoutParams = getLayoutParams();
- if (layoutParams instanceof LayoutParams lp) {
+ if (getLayoutParams() instanceof LayoutParams lp) {
lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
setLayoutParams(lp);
}
@@ -267,11 +276,82 @@
}
}
+ /**
+ * Set whether this view is being currently being dragged
+ */
+ public void setIsDragging(boolean dragging) {
+ if (mDragging == dragging) {
+ return;
+ }
+ mDragging = dragging;
+ setElevation(dragging ? mDragElevation : mBubbleElevation);
+ if (!dragging && mLocationChangePending) {
+ // During drag finish animation we may update the translation x value to shift the
+ // bubble to the new drop target. Clear the translation here.
+ setTranslationX(0f);
+ onBubbleBarLocationChanged();
+ }
+ }
+
+ /**
+ * Adjust resting position for the bubble bar while it is being dragged.
+ * <p>
+ * Bubble bar is laid out on left or right side of the screen. When it is being dragged to
+ * the opposite side, the resting position should be on that side. Calculate any additional
+ * translation that may be required to move the bubble bar to the new side.
+ *
+ * @param restingPosition relative resting position of the bubble bar from the laid out position
+ */
+ @SuppressLint("RtlHardcoded")
+ void adjustRelativeRestingPosition(PointF restingPosition) {
+ final boolean locationOnLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
+ // Bubble bar is placed left or right with gravity. Check where it is currently.
+ final int absoluteGravity = Gravity.getAbsoluteGravity(
+ ((LayoutParams) getLayoutParams()).gravity, getLayoutDirection());
+ final boolean gravityOnLeft =
+ (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT;
+
+ // Bubble bar is pinned to the same side per gravity and the desired location.
+ // Resting translation does not need to be adjusted.
+ if (locationOnLeft == gravityOnLeft) {
+ return;
+ }
+
+ // Bubble bar is laid out on left or right side of the screen. And the desired new
+ // location is on the other side. Calculate x translation value required to shift the
+ // bubble bar from one side to the other.
+ float x = getDistanceFromOtherSide();
+ if (locationOnLeft) {
+ // New location is on the left, shift left
+ // before -> |......ooo.| after -> |.ooo......|
+ restingPosition.x = -x;
+ } else {
+ // New location is on the right, shift right
+ // before -> |.ooo......| after -> |......ooo.|
+ restingPosition.x = x;
+ }
+ }
+
+ private float getDistanceFromOtherSide() {
+ // Calculate the shift needed to position the bubble bar on the other side
+ int displayWidth = getResources().getDisplayMetrics().widthPixels;
+ int margin = 0;
+ if (getLayoutParams() instanceof MarginLayoutParams lp) {
+ margin += lp.leftMargin;
+ margin += lp.rightMargin;
+ }
+ return (float) (displayWidth - getWidth() - margin);
+ }
+
private void setBubbleBarLocationInternal(BubbleBarLocation bubbleBarLocation) {
if (bubbleBarLocation != mBubbleBarLocation) {
mBubbleBarLocation = bubbleBarLocation;
- onBubbleBarLocationChanged();
- invalidate();
+ if (mDragging) {
+ mLocationChangePending = true;
+ } else {
+ onBubbleBarLocationChanged();
+ invalidate();
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index dab7d9d..5ffc6d8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -25,7 +25,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.R;
import com.android.launcher3.taskbar.TaskbarActivityContext;
/**
@@ -55,9 +54,8 @@
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleDismissController = bubbleControllers.bubbleDismissController;
mBubbleBarPinController = bubbleControllers.bubbleBarPinController;
- mBubbleBarPinController.setListener(location -> {
- // TODO(b/330585397): update bubble bar location in shell
- });
+ mBubbleBarPinController.setListener(
+ bubbleControllers.bubbleBarController::updateBubbleBarLocation);
mBubbleDismissController.setListener(
stuck -> mBubbleBarPinController.setDropTargetHidden(stuck));
}
@@ -96,11 +94,8 @@
@SuppressLint("ClickableViewAccessibility")
public void setupBubbleBarView(@NonNull BubbleBarView bubbleBarView) {
PointF initialRelativePivot = new PointF();
- final int restingElevation = bubbleBarView.getResources().getDimensionPixelSize(
- R.dimen.bubblebar_elevation);
- final int dragElevation = bubbleBarView.getResources().getDimensionPixelSize(
- R.dimen.bubblebar_drag_elevation);
bubbleBarView.setOnTouchListener(new BubbleTouchListener() {
+
@Override
protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) {
if (bubbleBarView.isExpanded()) return false;
@@ -114,7 +109,7 @@
// By default the bubble bar view pivot is in bottom right corner, while dragging
// it should be centered in order to align it with the dismiss target view
bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f);
- bubbleBarView.setElevation(dragElevation);
+ bubbleBarView.setIsDragging(true);
mBubbleBarPinController.onDragStart(
bubbleBarView.getBubbleBarLocation().isOnLeft(bubbleBarView.isLayoutRtl()));
}
@@ -138,7 +133,14 @@
void onDragEnd() {
// Restoring the initial pivot for the bubble bar view
bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
- bubbleBarView.setElevation(restingElevation);
+ bubbleBarView.setIsDragging(false);
+ }
+
+ @Override
+ protected PointF getRestingPosition() {
+ PointF restingPosition = super.getRestingPosition();
+ bubbleBarView.adjustRelativeRestingPosition(restingPosition);
+ return restingPosition;
}
});
}
@@ -226,6 +228,13 @@
protected void onDragDismiss() {
}
+ /**
+ * Get the resting position of the view when drag is released
+ */
+ protected PointF getRestingPosition() {
+ return mViewInitialPosition;
+ }
+
@Override
@SuppressLint("ClickableViewAccessibility")
public boolean onTouch(@NonNull View view, @NonNull MotionEvent event) {
@@ -353,7 +362,7 @@
mAnimator.animateDismiss(mViewInitialPosition, onComplete);
} else {
onDragRelease();
- mAnimator.animateToInitialState(mViewInitialPosition, getCurrentVelocity(),
+ mAnimator.animateToInitialState(getRestingPosition(), getCurrentVelocity(),
onComplete);
}
mBubbleDismissController.hideDismissView();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index b49c752..8b923ad 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -62,8 +62,8 @@
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.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import android.animation.Animator;
@@ -105,7 +105,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
-import com.android.launcher3.HomeTransitionController;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -245,8 +244,6 @@
private boolean mIsPredictiveBackToHomeInProgress;
- private HomeTransitionController mHomeTransitionController;
-
@Override
protected void setupViews() {
super.setupViews();
@@ -277,11 +274,6 @@
mAppTransitionManager.registerRemoteAnimations();
mAppTransitionManager.registerRemoteTransitions();
- if (FeatureFlags.enableHomeTransitionListener()) {
- mHomeTransitionController = new HomeTransitionController();
- mHomeTransitionController.registerHomeTransitionListener(this);
- }
-
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
mDesktopVisibilityController = new DesktopVisibilityController(this);
@@ -523,10 +515,6 @@
mLauncherUnfoldAnimationController.onDestroy();
}
- if (mHomeTransitionController != null) {
- mHomeTransitionController.unregisterHomeTransitionListener();
- }
-
if (mDesktopVisibilityController != null) {
mDesktopVisibilityController.unregisterSystemUiListener();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 7875dae..2625919 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -68,10 +68,13 @@
@Override
public void onBackInvoked(Launcher launcher) {
// In predictive back swipe, onBackInvoked() will be called after onBackStarted().
- // Because the 2nd InteractionJankMonitor.begin() will be ignore within timeout, it's safe
- // to call InteractionJankMonitorWrapper.begin here.
- InteractionJankMonitorWrapper.begin(launcher.getAppsView(),
- Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
+ // In 3 button mode, onBackStarted() is not called but onBackInvoked() will be called.
+ // Thus In onBackInvoked(), we should only begin instrumenting if we didn't call
+ // onBackStarted() to start instrumenting CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK.
+ if (!InteractionJankMonitorWrapper.isInstrumenting(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK)) {
+ InteractionJankMonitorWrapper.begin(
+ launcher.getAppsView(), Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
+ }
super.onBackInvoked(launcher);
}
diff --git a/quickstep/src/com/android/quickstep/HomeVisibilityState.kt b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
new file mode 100644
index 0000000..241e16d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.os.RemoteException
+import android.util.Log
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.util.Executors
+import com.android.wm.shell.shared.IHomeTransitionListener.Stub
+import com.android.wm.shell.shared.IShellTransitions
+
+/** Class to track visibility state of Launcher */
+class HomeVisibilityState {
+
+ var isHomeVisible = true
+ private set
+
+ private var listeners = mutableSetOf<VisibilityChangeListener>()
+
+ fun addListener(l: VisibilityChangeListener) = listeners.add(l)
+
+ fun removeListener(l: VisibilityChangeListener) = listeners.remove(l)
+
+ fun init(transitions: IShellTransitions?) {
+ if (!FeatureFlags.enableHomeTransitionListener()) return
+ try {
+ transitions?.setHomeTransitionListener(
+ object : Stub() {
+ override fun onHomeVisibilityChanged(isVisible: Boolean) {
+ Executors.MAIN_EXECUTOR.execute {
+ isHomeVisible = isVisible
+ listeners.forEach { it.onHomeVisibilityChanged(isVisible) }
+ }
+ }
+ }
+ )
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Failed call setHomeTransitionListener", e)
+ }
+ }
+
+ interface VisibilityChangeListener {
+ fun onHomeVisibilityChanged(isVisible: Boolean)
+ }
+
+ override fun toString() = "{HomeVisibilityState isHomeVisible=$isHomeVisible}"
+
+ companion object {
+
+ private const val TAG = "HomeVisibilityState"
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 97c48e6..7c17e4e 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -35,6 +35,7 @@
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherInitListener;
@@ -206,8 +207,17 @@
@UiThread
private Launcher getVisibleLauncher() {
Launcher launcher = getCreatedActivity();
- return (launcher != null) && launcher.isStarted()
- && (isInLiveTileMode() || launcher.hasBeenResumed()) ? launcher : null;
+ if (launcher == null) {
+ return null;
+ }
+ if (launcher.isStarted() && (isInLiveTileMode() || launcher.hasBeenResumed())) {
+ return launcher;
+ }
+ if (Flags.useActivityOverlay()
+ && SystemUiProxy.INSTANCE.get(launcher).getHomeVisibilityState().isHomeVisible()) {
+ return launcher;
+ }
+ return null;
}
@Override
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 2d25295..fa425f1 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -39,6 +39,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
+import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationTarget;
@@ -302,7 +303,7 @@
if (mScrimLayer == null) {
addScrimLayer();
}
- mTransaction.apply();
+ applyTransaction();
}
private void setLauncherTargetViewVisible(boolean isVisible) {
@@ -342,7 +343,8 @@
return;
}
if (mScrimLayer.isValid()) {
- mTransaction.remove(mScrimLayer).apply();
+ mTransaction.remove(mScrimLayer);
+ applyTransaction();
}
mScrimLayer = null;
}
@@ -396,7 +398,11 @@
mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
}
+ applyTransaction();
+ }
+ private void applyTransaction() {
+ mTransaction.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
mTransaction.apply();
}
@@ -511,7 +517,7 @@
float value = (Float) animation.getAnimatedValue();
if (mScrimLayer != null && mScrimLayer.isValid()) {
mTransaction.setAlpha(mScrimLayer, value * mScrimAlpha);
- mTransaction.apply();
+ applyTransaction();
}
});
mScrimAlphaAnimator.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index b6272da..4183842 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -63,7 +63,6 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.util.ScreenshotRequest;
import com.android.internal.view.AppearanceRegion;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.ActiveGestureLog;
@@ -82,6 +81,7 @@
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.bubbles.IBubbles;
import com.android.wm.shell.bubbles.IBubblesListener;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import com.android.wm.shell.common.pip.IPip;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
@@ -91,7 +91,6 @@
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.recents.IRecentTasks;
import com.android.wm.shell.recents.IRecentTasksListener;
-import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.IShellTransitions;
import com.android.wm.shell.splitscreen.ISplitScreen;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
@@ -157,7 +156,7 @@
private IOnBackInvokedCallback mBackToLauncherCallback;
private IRemoteAnimationRunner mBackToLauncherRunner;
private IDragAndDrop mDragAndDrop;
- private IHomeTransitionListener mHomeTransitionListener;
+ private final HomeVisibilityState mHomeVisibilityState = new HomeVisibilityState();
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
@@ -269,7 +268,7 @@
setBubblesListener(mBubblesListener);
registerSplitScreenListener(mSplitScreenListener);
registerSplitSelectListener(mSplitSelectListener);
- setHomeTransitionListener(mHomeTransitionListener);
+ mHomeVisibilityState.init(mShellTransitions);
setStartingWindowListener(mStartingWindowListener);
setLauncherUnlockAnimationController(
mLauncherActivityClass, mLauncherUnlockAnimationController);
@@ -808,6 +807,18 @@
}
}
+ /**
+ * Tells SysUI to update the bubble bar location to the new location.
+ * @param location new location for the bubble bar
+ */
+ public void setBubbleBarLocation(BubbleBarLocation location) {
+ try {
+ mBubbles.setBubbleBarLocation(location);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setBubbleBarLocation");
+ }
+ }
+
//
// Splitscreen
//
@@ -1102,22 +1113,8 @@
mRemoteTransitions.remove(remoteTransition);
}
- public void setHomeTransitionListener(IHomeTransitionListener listener) {
- if (!FeatureFlags.enableHomeTransitionListener()) {
- return;
- }
-
- mHomeTransitionListener = listener;
-
- if (mShellTransitions != null) {
- try {
- mShellTransitions.setHomeTransitionListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setHomeTransitionListener", e);
- }
- } else {
- Log.w(TAG, "Unable to call setHomeTransitionListener because ShellTransitions is null");
- }
+ public HomeVisibilityState getHomeVisibilityState() {
+ return mHomeVisibilityState;
}
/**
@@ -1558,7 +1555,7 @@
pw.println("\tmSplitSelectListener=" + mSplitSelectListener);
pw.println("\tmOneHanded=" + mOneHanded);
pw.println("\tmShellTransitions=" + mShellTransitions);
- pw.println("\tmHomeTransitionListener=" + mHomeTransitionListener);
+ pw.println("\tmHomeVisibilityState=" + mHomeVisibilityState);
pw.println("\tmStartingWindow=" + mStartingWindow);
pw.println("\tmStartingWindowListener=" + mStartingWindowListener);
pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController);
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d8cbbf9..4089498 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -70,6 +70,7 @@
import android.window.TransitionInfo;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.launcher3.Launcher;
@@ -132,6 +133,7 @@
private final StatsLogManager mStatsLogManager;
private final SystemUiProxy mSystemUiProxy;
private final StateManager mStateManager;
+ @Nullable
private SplitFromDesktopController mSplitFromDesktopController;
@Nullable
private DepthController mDepthController;
@@ -208,6 +210,9 @@
mActivityBackCallback = null;
mAppPairsController.onDestroy();
mSplitSelectDataHolder.onDestroy();
+ if (mSplitFromDesktopController != null) {
+ mSplitFromDesktopController.onDestroy();
+ }
}
/**
@@ -643,7 +648,12 @@
}
public void initSplitFromDesktopController(Launcher launcher) {
- mSplitFromDesktopController = new SplitFromDesktopController(launcher);
+ initSplitFromDesktopController(new SplitFromDesktopController(launcher));
+ }
+
+ @VisibleForTesting
+ void initSplitFromDesktopController(SplitFromDesktopController controller) {
+ mSplitFromDesktopController = controller;
}
private RemoteTransition getShellRemoteTransition(int firstTaskId, int secondTaskId,
@@ -977,6 +987,11 @@
SystemUiProxy.INSTANCE.get(mLauncher).registerSplitSelectListener(mSplitSelectListener);
}
+ void onDestroy() {
+ SystemUiProxy.INSTANCE.get(mLauncher).unregisterSplitSelectListener(
+ mSplitSelectListener);
+ }
+
/**
* Enter split select from desktop mode.
* @param taskInfo the desktop task to move to split stage
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index 18b1ea0..a7ed8a7 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -36,6 +36,7 @@
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.quickstep.RecentsModel
import com.android.quickstep.SystemUiProxy
+import com.android.quickstep.util.SplitSelectStateController.SplitFromDesktopController
import com.android.systemui.shared.recents.model.Task
import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
import java.util.function.Consumer
@@ -65,6 +66,7 @@
private val context: StatefulActivity<*> = mock()
private val recentsModel: RecentsModel = mock()
private val pendingIntent: PendingIntent = mock()
+ private val splitFromDesktopController: SplitFromDesktopController = mock()
private lateinit var splitSelectStateController: SplitSelectStateController
@@ -607,6 +609,18 @@
assertTrue(splitSelectStateController.isBothSplitAppsConfirmed)
}
+ @Test
+ fun splitSelectStateControllerDestroyed_SplitFromDesktopControllerAlsoDestroyed() {
+ // Initiate split from desktop controller
+ splitSelectStateController.initSplitFromDesktopController(splitFromDesktopController)
+
+ // Simulate default controller being destroyed
+ splitSelectStateController.onDestroy()
+
+ // Verify desktop controller is also destroyed
+ verify(splitFromDesktopController).onDestroy()
+ }
+
// Generate GroupTask with default userId.
private fun generateGroupTask(
task1ComponentName: ComponentName,
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
index df73e09..a9ff161 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
import android.graphics.Rect;
@@ -23,6 +25,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
@@ -45,6 +48,7 @@
@Test
@NavigationModeSwitch(mode = NavigationModeSwitchRule.Mode.THREE_BUTTON)
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
public void testThreeButtonsTaskbarBoundsAfterConfigChangeDuringIme() {
Rect taskbarBoundsBefore = getTaskbar().getVisibleBounds();
// Go home and to an IME activity (any configuration change would do, as long as it
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index cfa8967..3273f27 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -680,7 +680,7 @@
@Override
public void onBackCancelled() {
- mStateManager.getState().onBackCancelled(Launcher.this);
+ Launcher.this.onBackCancelled();
}
};
}
@@ -2086,6 +2086,10 @@
mStateManager.getState().onBackInvoked(this);
}
+ protected void onBackCancelled() {
+ mStateManager.getState().onBackCancelled(this);
+ }
+
protected void onScreenOnChanged(boolean isOn) {
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 4b65b73..799b67b 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -85,6 +85,7 @@
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.recyclerview.AllAppsRecyclerViewPool;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -1366,6 +1367,18 @@
invalidateHeader();
}
+ /**
+ * Set {@link Animator.AnimatorListener} on {@link mAllAppsTransitionController} to observe
+ * animation of backing out of all apps search view to all apps view.
+ */
+ public void setAllAppsSearchBackAnimatorListener(Animator.AnimatorListener listener) {
+ Preconditions.assertNotNull(mAllAppsTransitionController);
+ if (mAllAppsTransitionController == null) {
+ return;
+ }
+ mAllAppsTransitionController.setAllAppsSearchBackAnimationListener(listener);
+ }
+
public void setScrimView(ScrimView scrimView) {
mScrimView = scrimView;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 63f6227..a4d1dc1 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -44,6 +44,7 @@
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
+import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
@@ -167,6 +168,8 @@
private final AnimatedFloat mAllAppScale = new AnimatedFloat(this::onScaleProgressChanged);
private final int mNavScrimFlag;
+ @Nullable private Animator.AnimatorListener mAllAppsSearchBackAnimationListener;
+
private boolean mIsVerticalLayout;
// Animation in this class is controlled by a single variable {@link mProgress}.
@@ -312,11 +315,25 @@
}
}
- /** Animate all apps view to 1f scale. */
+ /** Set {@link Animator.AnimatorListener} for scaling all apps scale to 1 animation. */
+ public void setAllAppsSearchBackAnimationListener(Animator.AnimatorListener listener) {
+ mAllAppsSearchBackAnimationListener = listener;
+ }
+
+ /**
+ * Animate all apps view to 1f scale. This is called when backing (exiting) from all apps
+ * search view to all apps view.
+ */
public void animateAllAppsToNoScale() {
- mAllAppScale.animateToValue(1f)
- .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
- .start();
+ if (mAllAppScale.isAnimating()) {
+ return;
+ }
+ Animator animator = mAllAppScale.animateToValue(1f)
+ .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS);
+ if (mAllAppsSearchBackAnimationListener != null) {
+ animator.addListener(mAllAppsSearchBackAnimationListener);
+ }
+ animator.start();
}
/**
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index aa3c5ba..f01e7e4 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1432,6 +1432,11 @@
updateTextViewFocus();
}
+ @Override
+ public void onTitleChanged(CharSequence title) {
+ mFolderName.setText(title);
+ }
+
/**
* Utility methods to iterate over items of the view
*/
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 62ce311..175452e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -718,6 +718,7 @@
requestLayout();
}
+ @Override
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title);
setContentDescription(getAccessiblityTitle(title));
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 1bbb2fe..39d9cd9 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -174,6 +174,8 @@
void onAdd(WorkspaceItemInfo item, int rank);
void onRemove(List<WorkspaceItemInfo> item);
void onItemsChanged(boolean animate);
+ void onTitleChanged(CharSequence title);
+
}
public boolean hasOption(int optionFlag) {
@@ -246,6 +248,10 @@
if (modelWriter != null) {
modelWriter.updateItemInDatabase(this);
}
+
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onTitleChanged(title);
+ }
}
/**
diff --git a/tests/src/com/android/launcher3/LauncherIntentTest.java b/tests/src/com/android/launcher3/LauncherIntentTest.java
index e8822c3..a3013c7 100644
--- a/tests/src/com/android/launcher3/LauncherIntentTest.java
+++ b/tests/src/com/android/launcher3/LauncherIntentTest.java
@@ -29,7 +29,6 @@
import com.android.launcher3.allapps.SearchRecyclerView;
import com.android.launcher3.ui.AbstractLauncherUiTest;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,7 +39,6 @@
public final Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
@Test
- @Ignore("b/329152799")
public void testAllAppsIntent() {
// setup by moving to home
mLauncher.goHome();
@@ -66,8 +64,6 @@
// Highlights the search bar, then fills text to display the SearchView.
private void moveToSearchView() {
- mLauncher.goHome().switchToAllApps();
-
// All Apps view should be loaded
assertTrue("Launcher internal state is not All Apps",
isInState(() -> LauncherState.ALL_APPS));
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
index 43fc8ff..913dfa2 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
@@ -37,6 +37,7 @@
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.After;
import org.junit.Before;
@@ -240,6 +241,7 @@
});
}
+ @ScreenRecordRule.ScreenRecord // b/329935119
@Test
@PortraitLandscape
public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {