diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java index 6c7fe5be90..644705b292 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java @@ -25,6 +25,7 @@ import android.os.Debug; import android.util.Log; import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.Launcher; @@ -38,6 +39,7 @@ import com.android.wm.shell.desktopmode.IDesktopTaskListener; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.Executor; /** * Controls the visibility of the workspace and the resumed / paused state when desktop mode @@ -56,7 +58,7 @@ public class DesktopVisibilityController { private boolean mGestureInProgress; @Nullable - private IDesktopTaskListener mDesktopTaskListener; + private DesktopTaskListenerImpl mDesktopTaskListener; public DesktopVisibilityController(Launcher launcher) { mLauncher = launcher; @@ -66,24 +68,7 @@ public class DesktopVisibilityController { * Register a listener with System UI to receive updates about desktop tasks state */ public void registerSystemUiListener() { - mDesktopTaskListener = new IDesktopTaskListener.Stub() { - @Override - public void onTasksVisibilityChanged(int displayId, int visibleTasksCount) { - MAIN_EXECUTOR.execute(() -> { - if (displayId == mLauncher.getDisplayId()) { - if (DEBUG) { - Log.d(TAG, "desktop visible tasks count changed=" + visibleTasksCount); - } - setVisibleDesktopTasksCount(visibleTasksCount); - } - }); - } - - @Override - public void onStashedChanged(int displayId, boolean stashed) { - Log.w(TAG, "IDesktopTaskListener: onStashedChanged is deprecated"); - } - }; + mDesktopTaskListener = new DesktopTaskListenerImpl(this, mLauncher.getDisplayId()); SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(mDesktopTaskListener); } @@ -92,6 +77,7 @@ public class DesktopVisibilityController { */ public void unregisterSystemUiListener() { SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(null); + mDesktopTaskListener.release(); mDesktopTaskListener = null; } @@ -355,4 +341,43 @@ public class DesktopVisibilityController { */ void onDesktopVisibilityChanged(boolean visible); } + + /** + * Wrapper for the IDesktopTaskListener stub to prevent lingering references to the launcher + * activity via the controller. + */ + private static class DesktopTaskListenerImpl extends IDesktopTaskListener.Stub { + + private DesktopVisibilityController mController; + private final int mDisplayId; + + DesktopTaskListenerImpl(@NonNull DesktopVisibilityController controller, int displayId) { + mController = controller; + mDisplayId = displayId; + } + + /** + * Clears any references to the controller. + */ + void release() { + mController = null; + } + + @Override + public void onTasksVisibilityChanged(int displayId, int visibleTasksCount) { + MAIN_EXECUTOR.execute(() -> { + if (mController != null && displayId == mDisplayId) { + if (DEBUG) { + Log.d(TAG, "desktop visible tasks count changed=" + visibleTasksCount); + } + mController.setVisibleDesktopTasksCount(visibleTasksCount); + } + }); + } + + @Override + public void onStashedChanged(int displayId, boolean stashed) { + Log.w(TAG, "IDesktopTaskListener: onStashedChanged is deprecated"); + } + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java index c201236586..a833ccf9c4 100644 --- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java @@ -16,19 +16,24 @@ package com.android.launcher3.taskbar; import android.content.Context; +import android.content.Intent; +import android.content.pm.ShortcutInfo; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; +import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; +import com.android.quickstep.SystemUiProxy; import java.util.ArrayList; import java.util.List; // TODO(b/218912746): Share more behavior to avoid all apps context depending directly on taskbar. /** Base for common behavior between taskbar window contexts. */ -public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext { +public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext, + SystemShortcut.BubbleActivityStarter { protected final LayoutInflater mLayoutInflater; private final List mDPChangeListeners = new ArrayList<>(); @@ -48,6 +53,18 @@ public abstract class BaseTaskbarContext extends ContextThemeWrapper implements return mDPChangeListeners; } + @Override + public void showShortcutBubble(ShortcutInfo info) { + if (info == null) return; + SystemUiProxy.INSTANCE.get(this).showShortcutBubble(info); + } + + @Override + public void showAppBubble(Intent intent) { + if (intent == null || intent.getPackage() == null) return; + SystemUiProxy.INSTANCE.get(this).showAppBubble(intent); + } + /** Callback invoked when a drag is initiated within this context. */ public abstract void onDragStart(); diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index 3981e43c66..e5edd518b8 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -225,9 +225,12 @@ public class LauncherTaskbarUIController extends TaskbarUIController { // Launcher is resumed during the swipe-to-overview gesture under shell-transitions, so // avoid updating taskbar state in that situation (when it's non-interactive -- or // "background") to avoid premature animations. - if (ENABLE_SHELL_TRANSITIONS && isVisible - && mLauncher.getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE) - && !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) { + LauncherState state = mLauncher.getStateManager().getState(); + boolean nonInteractiveState = state.hasFlag(FLAG_NON_INTERACTIVE) + && !state.isTaskbarAlignedWithHotseat(mLauncher); + if ((ENABLE_SHELL_TRANSITIONS + && isVisible + && (nonInteractiveState || mSkipLauncherVisibilityChange))) { return null; } diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java index 266d0b9c98..475b51646b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java @@ -212,7 +212,7 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT * stashed handle to wrap around the hotseat items. */ public Animator createRevealAnimToIsStashed(boolean isStashed, Rect taskbarToHotseatOffsets) { - Rect visualBounds = new Rect(mControllers.taskbarViewController.getIconLayoutBounds()); + Rect visualBounds = mControllers.taskbarViewController.getIconLayoutVisualBounds(); float startRadius = mStashedHandleRadius; if (DisplayController.isTransientTaskbar(mActivity)) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 9d394a81d7..9c954d13bb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -927,7 +927,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags, fromInit); boolean isShadeVisible = (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0; - onNotificationShadeExpandChanged(isShadeVisible, fromInit); + onNotificationShadeExpandChanged(isShadeVisible, fromInit || isPhoneMode()); mControllers.taskbarViewController.setRecentsButtonDisabled( mControllers.navbarButtonsViewController.isRecentsDisabled() || isNavBarKidsModeActive()); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java index 6ac862e9e0..8a86402e7b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java @@ -85,6 +85,9 @@ public class TaskbarForceVisibleImmersiveController implements TouchController { /** Update values tracked via sysui flags. */ public void updateSysuiFlags(@SystemUiStateFlags long sysuiFlags) { + if (mContext.isPhoneMode()) { + return; + } mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) == 0; if (mContext.isNavBarForceVisible()) { if (mIsImmersiveMode) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java index b69759039a..e80ad7a8a1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java @@ -53,6 +53,7 @@ import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.LogUtils; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Objects; @@ -69,6 +70,9 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba private static final SystemShortcut.Factory APP_INFO = SystemShortcut.AppInfo::new; + private static final SystemShortcut.Factory + BUBBLE = SystemShortcut.BubbleShortcut::new; + private final TaskbarActivityContext mContext; private final PopupDataProvider mPopupDataProvider; @@ -182,10 +186,13 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba // Create a Stream of all applicable system shortcuts private Stream getSystemShortcuts() { // append split options to APP_INFO shortcut, the order here will reflect in the popup - return Stream.concat( - Stream.of(APP_INFO), - mControllers.uiController.getSplitMenuOptions() - ); + ArrayList shortcuts = new ArrayList<>(); + shortcuts.add(APP_INFO); + shortcuts.addAll(mControllers.uiController.getSplitMenuOptions().toList()); + if (com.android.wm.shell.Flags.enableBubbleAnything()) { + shortcuts.add(BUBBLE); + } + return shortcuts.stream(); } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java index 5e7c7cedf4..4df0223e9a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java @@ -28,6 +28,7 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.launcher3.anim.AnimatedFloat; +import com.android.launcher3.taskbar.bubbles.BubbleControllers; import com.android.launcher3.util.DisplayController; import com.android.quickstep.SystemUiProxy; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; @@ -86,6 +87,10 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa * Updates the scrim state based on the flags. */ public void updateStateForSysuiFlags(@SystemUiStateFlags long stateFlags, boolean skipAnim) { + if (mActivity.isPhoneMode()) { + // There is no scrim for the bar in the phone mode. + return; + } if (isBubbleBarEnabled() && DisplayController.isTransientTaskbar(mActivity)) { // These scrims aren't used if bubble bar & transient taskbar are active. return; @@ -97,10 +102,20 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa private boolean shouldShowScrim() { final boolean bubblesExpanded = (mSysUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0; boolean isShadeVisible = (mSysUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0; + BubbleControllers bubbleControllers = mActivity.getBubbleControllers(); + boolean isBubbleControllersPresented = bubbleControllers != null; + // when the taskbar is in persistent mode, we hide the task bar icons on bubble bar expand, + // which makes the taskbar invisible, so need to check if the bubble bar is not on home + // to show the scrim view + boolean showScrimForBubbles = bubblesExpanded + && !mTaskbarVisible + && isBubbleControllersPresented + && !DisplayController.isTransientTaskbar(mActivity) + && !bubbleControllers.bubbleStashController.isBubblesShowingOnHome(); return bubblesExpanded && !mControllers.navbarButtonsViewController.isImeVisible() && !isShadeVisible && !mControllers.taskbarStashController.isStashed() - && mTaskbarVisible; + && (mTaskbarVisible || showScrimForBubbles); } private float getScrimAlpha() { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 5d6fdc145d..e33e29335b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -1022,6 +1022,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba /** Called when some system ui state has changed. (See SYSUI_STATE_... in QuickstepContract) */ public void updateStateForSysuiFlags(long systemUiStateFlags, boolean skipAnim) { + if (mActivity.isPhoneMode()) { + return; + } + long animDuration = TASKBAR_STASH_DURATION; long startDelay = 0; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 2ada5bafd2..42bf8db7df 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -61,6 +61,8 @@ public class TaskbarUIController { // Initialized in init. protected TaskbarControllers mControllers; + protected boolean mSkipLauncherVisibilityChange; + @CallSuper protected void init(TaskbarControllers taskbarControllers) { mControllers = taskbarControllers; @@ -418,4 +420,12 @@ public class TaskbarUIController { public void setUserIsNotGoingHome(boolean isNotGoingHome) { mControllers.taskbarStashController.setUserIsNotGoingHome(isNotGoingHome); } + + /** + * Sets whether to prevent taskbar from reacting to launcher visibility during the recents + * transition animation. + */ + public void setSkipLauncherVisibilityChange(boolean skip) { + mSkipLauncherVisibilityChange = skip; + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index e58069a7e4..fc76972105 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -669,8 +669,20 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar return isShown() && mIconLayoutBounds.contains(xInOurCoordinates, yInOurCoorindates); } + /** + * Gets visual bounds of the taskbar view. The visual bounds correspond to the taskbar touch + * area, rather than layout placement in the parent view. + */ + public Rect getIconLayoutVisualBounds() { + return new Rect(mIconLayoutBounds); + } + + /** Gets taskbar layout bounds in parent view. */ public Rect getIconLayoutBounds() { - return mIconLayoutBounds; + Rect actualBounds = new Rect(mIconLayoutBounds); + actualBounds.top = getTop(); + actualBounds.bottom = getBottom(); + return actualBounds; } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index ffa481988f..b8b85d120a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -96,7 +96,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 4; public static final int ALPHA_INDEX_ASSISTANT_INVOKED = 5; public static final int ALPHA_INDEX_SMALL_SCREEN = 6; - private static final int NUM_ALPHA_CHANNELS = 7; + + public static final int ALPHA_INDEX_BUBBLE_BAR = 7; + private static final int NUM_ALPHA_CHANNELS = 8; private static boolean sEnableModelLoadingForTests = true; @@ -272,6 +274,10 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar OneShotPreDrawListener.add(mTaskbarView, listener); } + public Rect getIconLayoutVisualBounds() { + return mTaskbarView.getIconLayoutVisualBounds(); + } + public Rect getIconLayoutBounds() { return mTaskbarView.getIconLayoutBounds(); } @@ -462,14 +468,14 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar if (mControllers.getSharedState().startTaskbarVariantIsTransient) { float transY = mTransientTaskbarDp.taskbarBottomMargin + (mTransientTaskbarDp.taskbarHeight - - mTaskbarView.getIconLayoutBounds().bottom) + - mTaskbarView.getIconLayoutVisualBounds().bottom) - (mPersistentTaskbarDp.taskbarHeight - mTransientTaskbarDp.taskbarIconSize) / 2f; taskbarIconTranslationYForPinningValue = mapRange(scale, 0f, transY); } else { float transY = -mTransientTaskbarDp.taskbarBottomMargin + (mPersistentTaskbarDp.taskbarHeight - - mTaskbarView.getIconLayoutBounds().bottom) + - mTaskbarView.getIconLayoutVisualBounds().bottom) - (mTransientTaskbarDp.taskbarHeight - mTransientTaskbarDp.taskbarIconSize) / 2f; taskbarIconTranslationYForPinningValue = mapRange(scale, transY, 0f); diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt index 619c9c4f6a..c380c8d6f1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt @@ -110,7 +110,7 @@ class VoiceInteractionWindowController(val context: TaskbarActivityContext) : } fun setIsVoiceInteractionWindowVisible(visible: Boolean, skipAnim: Boolean) { - if (isVoiceInteractionWindowVisible == visible) { + if (isVoiceInteractionWindowVisible == visible || context.isPhoneMode) { return } isVoiceInteractionWindowVisible = visible diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index 4d0cad228e..a2746df3d9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -661,13 +661,25 @@ public class BubbleBarView extends FrameLayout { return displayHeight - bubbleBarHeight + (int) mController.getBubbleBarTranslationY(); } - /** - * Updates the bounds with translation that may have been applied and returns the result. - */ + /** Returns the bounds with translation that may have been applied. */ public Rect getBubbleBarBounds() { - mBubbleBarBounds.top = getTop() + (int) getTranslationY() + mPointerSize; - mBubbleBarBounds.bottom = getBottom() + (int) getTranslationY(); - return mBubbleBarBounds; + Rect bounds = new Rect(mBubbleBarBounds); + bounds.top = getTop() + (int) getTranslationY() + mPointerSize; + bounds.bottom = getBottom() + (int) getTranslationY(); + return bounds; + } + + /** Returns the expanded bounds with translation that may have been applied. */ + public Rect getBubbleBarExpandedBounds() { + Rect expandedBounds = getBubbleBarBounds(); + if (!isExpanded() || isExpanding()) { + if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) { + expandedBounds.right = expandedBounds.left + (int) expandedWidth(); + } else { + expandedBounds.left = expandedBounds.right - (int) expandedWidth(); + } + } + return expandedBounds; } /** @@ -1279,6 +1291,13 @@ public class BubbleBarView extends FrameLayout { return mIsBarExpanded; } + /** + * Returns whether the bubble bar is expanding. + */ + public boolean isExpanding() { + return mWidthAnimator.isRunning() && mIsBarExpanded; + } + /** * Get width of the bubble bar as if it would be expanded. * diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 83123b507b..df33df85e2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -72,6 +72,7 @@ public class BubbleBarViewController { private BubbleDragController mBubbleDragController; private TaskbarStashController mTaskbarStashController; private TaskbarInsetsController mTaskbarInsetsController; + private TaskbarViewPropertiesProvider mTaskbarViewPropertiesProvider; private View.OnClickListener mBubbleClickListener; private View.OnClickListener mBubbleBarClickListener; private BubbleView.Controller mBubbleViewController; @@ -110,13 +111,16 @@ public class BubbleBarViewController { R.dimen.bubblebar_icon_size); } - public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) { + /** Initializes controller. */ + public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers, + TaskbarViewPropertiesProvider taskbarViewPropertiesProvider) { mBubbleStashController = bubbleControllers.bubbleStashController; mBubbleBarController = bubbleControllers.bubbleBarController; mBubbleDragController = bubbleControllers.bubbleDragController; mTaskbarStashController = controllers.taskbarStashController; mTaskbarInsetsController = controllers.taskbarInsetsController; mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); + mTaskbarViewPropertiesProvider = taskbarViewPropertiesProvider; onBubbleBarConfigurationChanged(/* animate= */ false); mActivity.addOnDeviceProfileChangeListener( dp -> onBubbleBarConfigurationChanged(/* animate= */ true)); @@ -348,6 +352,7 @@ public class BubbleBarViewController { if (hidden) { mBarView.setAlpha(0); mBarView.setExpanded(false); + updatePersistentTaskbar(/* isBubbleBarExpanded = */ false); } mActivity.bubbleBarVisibilityChanged(!hidden); } @@ -612,6 +617,7 @@ public class BubbleBarViewController { public void setExpanded(boolean isExpanded) { if (isExpanded != mBarView.isExpanded()) { mBarView.setExpanded(isExpanded); + updatePersistentTaskbar(isExpanded); if (!isExpanded) { mSystemUiProxy.collapseBubbles(); } else { @@ -622,6 +628,25 @@ public class BubbleBarViewController { } } + private void updatePersistentTaskbar(boolean isBubbleBarExpanded) { + if (mBubbleStashController.isTransientTaskBar()) return; + boolean hideTaskbar = isBubbleBarExpanded && isIntersectingTaskbar(); + mTaskbarViewPropertiesProvider + .getIconsAlpha() + .animateToValue(hideTaskbar ? 0 : 1) + .start(); + } + + /** Return {@code true} if expanded bubble bar would intersect the taskbar. */ + public boolean isIntersectingTaskbar() { + if (mBarView.isExpanding() || mBarView.isExpanded()) { + Rect taskbarViewBounds = mTaskbarViewPropertiesProvider.getTaskbarViewBounds(); + return mBarView.getBubbleBarExpandedBounds().intersect(taskbarViewBounds); + } else { + return false; + } + } + /** * Sets whether the bubble bar should be expanded. This method is used in response to UI events * from SystemUI. @@ -755,4 +780,14 @@ public class BubbleBarViewController { pw.println(" Bubble bar view is null!"); } } + + /** Interface for BubbleBarViewController to get the taskbar view properties. */ + public interface TaskbarViewPropertiesProvider { + + /** Returns the bounds of the taskbar. */ + Rect getTaskbarViewBounds(); + + /** Returns taskbar icons alpha */ + MultiPropertyFactory.MultiProperty getIconsAlpha(); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java index 8478dc2171..e00916af6c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java @@ -15,8 +15,15 @@ */ package com.android.launcher3.taskbar.bubbles; +import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_BUBBLE_BAR; + +import android.graphics.Rect; +import android.view.View; + import com.android.launcher3.taskbar.TaskbarControllers; +import com.android.launcher3.taskbar.bubbles.BubbleBarViewController.TaskbarViewPropertiesProvider; import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController; +import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.RunnableList; import java.io.PrintWriter; @@ -79,7 +86,20 @@ public class BubbleControllers { bubbleStashedHandleViewController.orElse(null), taskbarControllers::runAfterInit ); - bubbleBarViewController.init(taskbarControllers, /* bubbleControllers = */ this); + bubbleBarViewController.init(taskbarControllers, /* bubbleControllers = */ this, + new TaskbarViewPropertiesProvider() { + @Override + public Rect getTaskbarViewBounds() { + return taskbarControllers.taskbarViewController.getIconLayoutBounds(); + } + + @Override + public MultiPropertyFactory.MultiProperty getIconsAlpha() { + return taskbarControllers.taskbarViewController + .getTaskbarIconAlpha() + .get(ALPHA_INDEX_BUBBLE_BAR); + } + }); bubbleDragController.init(/* bubbleControllers = */ this); bubbleDismissController.init(/* bubbleControllers = */ this); bubbleBarPinController.init(this); diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index b2cc369376..5a288b0336 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -45,6 +45,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition; import static com.android.launcher3.popup.SystemShortcut.APP_INFO; +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; @@ -75,6 +76,7 @@ import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.ShortcutInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.RectF; @@ -215,7 +217,8 @@ import java.util.function.BiConsumer; import java.util.function.Predicate; import java.util.stream.Stream; -public class QuickstepLauncher extends Launcher implements RecentsViewContainer { +public class QuickstepLauncher extends Launcher implements RecentsViewContainer, + SystemShortcut.BubbleActivityStarter { private static final boolean TRACE_LAYOUTS = SystemProperties.getBoolean("persist.debug.trace_layouts", false); private static final String TRACE_RELAYOUT_CLASS = @@ -466,6 +469,9 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer if (Flags.enablePrivateSpace()) { shortcuts.add(UNINSTALL_APP); } + if (com.android.wm.shell.Flags.enableBubbleAnything()) { + shortcuts.add(BUBBLE_SHORTCUT); + } return shortcuts.stream(); } @@ -1436,6 +1442,18 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer return true; } + @Override + public void showShortcutBubble(ShortcutInfo info) { + if (info == null) return; + SystemUiProxy.INSTANCE.get(this).showShortcutBubble(info); + } + + @Override + public void showAppBubble(Intent intent) { + if (intent == null || intent.getPackage() == null) return; + SystemUiProxy.INSTANCE.get(this).showAppBubble(intent); + } + private static final class LauncherTaskViewController extends TaskViewTouchController { diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 0c2f29b8db..59e9f054c8 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -47,7 +47,6 @@ import android.view.IRecentsAnimationController; import android.view.IRecentsAnimationRunner; import android.view.IRemoteAnimationRunner; import android.view.MotionEvent; -import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.window.IOnBackInvokedCallback; @@ -894,6 +893,36 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle, SafeCloseable { } } + /** + * Tells SysUI to show a shortcut bubble. + * + * @param info the shortcut info used to create or identify the bubble. + */ + public void showShortcutBubble(ShortcutInfo info) { + try { + if (mBubbles != null) { + mBubbles.showShortcutBubble(info); + } + } catch (RemoteException e) { + Log.w(TAG, "Failed call show bubble for shortcut"); + } + } + + /** + * Tells SysUI to show a bubble of an app. + * + * @param intent the intent used to create the bubble. + */ + public void showAppBubble(Intent intent) { + try { + if (mBubbles != null) { + mBubbles.showAppBubble(intent); + } + } catch (RemoteException e) { + Log.w(TAG, "Failed call show bubble for app"); + } + } + // // Splitscreen // diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index f414399a0b..ab80a8c1b5 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -18,6 +18,7 @@ package com.android.quickstep; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static com.android.launcher3.Flags.enableHandleDelayedGestureCallbacks; +import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.NavigationMode.NO_BUTTON; @@ -44,6 +45,7 @@ import androidx.annotation.UiThread; import com.android.internal.util.ArrayUtils; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.taskbar.TaskbarUIController; import com.android.launcher3.util.DisplayController; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.SystemUiFlagUtils; @@ -334,6 +336,28 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn options.setTransientLaunch(); } options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime); + + // Notify taskbar that we should skip reacting to launcher visibility change to + // avoid a jumping taskbar. + TaskbarUIController taskbarUIController = containerInterface.getTaskbarController(); + if (enableScalingRevealHomeAnimation() && taskbarUIController != null) { + taskbarUIController.setSkipLauncherVisibilityChange(true); + + mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() { + @Override + public void onRecentsAnimationCanceled( + @NonNull HashMap thumbnailDatas) { + taskbarUIController.setSkipLauncherVisibilityChange(false); + } + + @Override + public void onRecentsAnimationFinished( + @NonNull RecentsAnimationController controller) { + taskbarUIController.setSkipLauncherVisibilityChange(false); + } + }); + } + mRecentsAnimationStartPending = getSystemUiProxy() .startRecentsActivity(intent, options, mCallbacks); if (enableHandleDelayedGestureCallbacks()) { diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index 54f2dd3529..770ec5a919 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -836,7 +836,7 @@ public class SplitSelectStateController { private final int mSplitPlaceholderSize; private final int mSplitPlaceholderInset; private ActivityManager.RunningTaskInfo mTaskInfo; - private ISplitSelectListener mSplitSelectListener; + private DesktopSplitSelectListenerImpl mSplitSelectListener; private Drawable mAppIcon; public SplitFromDesktopController(QuickstepLauncher launcher, @@ -847,21 +847,14 @@ public class SplitSelectStateController { R.dimen.split_placeholder_size); mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize( R.dimen.split_placeholder_inset); - mSplitSelectListener = new ISplitSelectListener.Stub() { - @Override - public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo, - int splitPosition, Rect taskBounds) { - MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo, splitPosition, - taskBounds)); - return true; - } - }; + mSplitSelectListener = new DesktopSplitSelectListenerImpl(this); SystemUiProxy.INSTANCE.get(mLauncher).registerSplitSelectListener(mSplitSelectListener); } void onDestroy() { SystemUiProxy.INSTANCE.get(mLauncher).unregisterSplitSelectListener( mSplitSelectListener); + mSplitSelectListener.release(); mSplitSelectListener = null; } @@ -954,4 +947,35 @@ public class SplitSelectStateController { } } } + + /** + * Wrapper for the ISplitSelectListener stub to prevent lingering references to the launcher + * activity via the controller. + */ + private static class DesktopSplitSelectListenerImpl extends ISplitSelectListener.Stub { + + private SplitFromDesktopController mController; + + DesktopSplitSelectListenerImpl(@NonNull SplitFromDesktopController controller) { + mController = controller; + } + + /** + * Clears any references to the controller. + */ + void release() { + mController = null; + } + + @Override + public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo, + int splitPosition, Rect taskBounds) { + MAIN_EXECUTOR.execute(() -> { + if (mController != null) { + mController.enterSplitSelect(taskInfo, splitPosition, taskBounds); + } + }); + return true; + } + } } diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt index 694a3822fe..2122d9aff7 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt @@ -24,6 +24,9 @@ import com.android.launcher3.BuildConfig import com.android.launcher3.ui.AbstractLauncherUiTest import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape import com.android.launcher3.uioverrides.QuickstepLauncher +import com.android.launcher3.util.rule.TestStabilityRule +import com.android.launcher3.util.rule.TestStabilityRule.LOCAL +import com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT import com.google.common.truth.Truth.assertWithMessage import org.junit.Before import org.junit.Test @@ -42,6 +45,7 @@ class TaplTestsOverviewDesktop : AbstractLauncherUiTest() { mLauncher.goHome() } + @TestStabilityRule.Stability(flavors = LOCAL or PLATFORM_POSTSUBMIT) @Test @PortraitLandscape fun enterDesktopViaOverviewMenu() { diff --git a/res/drawable/ic_bubble_button.xml b/res/drawable/ic_bubble_button.xml new file mode 100644 index 0000000000..1ed212e09a --- /dev/null +++ b/res/drawable/ic_bubble_button.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 3b458c2cff..fd724a5553 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -215,7 +215,8 @@ Don\'t suggest app Pin Prediction - + + Bubble diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java index 40e3813fe3..f31bf1e5fe 100644 --- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java +++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java @@ -25,6 +25,7 @@ import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.launcher3.Flags; import com.android.launcher3.LauncherSettings; @@ -97,6 +98,8 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { public int options; + @Nullable + private ShortcutInfo mShortcutInfo = null; public WorkspaceItemInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; @@ -175,6 +178,9 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { public void updateFromDeepShortcutInfo(@NonNull final ShortcutInfo shortcutInfo, @NonNull final Context context) { + if (com.android.wm.shell.Flags.enableBubbleAnything()) { + mShortcutInfo = shortcutInfo; + } // {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent intent = ShortcutKey.makeIntent(shortcutInfo); title = shortcutInfo.getShortLabel(); @@ -204,6 +210,11 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new); } + @Nullable + public ShortcutInfo getDeepShortcutInfo() { + return mShortcutInfo; + } + /** * {@code true} if the shortcut is disabled due to its app being a lower version. */ diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index f7e116819a..0c90eb904a 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -11,9 +11,11 @@ import android.app.ActivityOptions; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ShortcutInfo; import android.graphics.Rect; import android.os.Process; import android.os.UserHandle; +import android.util.Log; import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; @@ -25,6 +27,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.AbstractFloatingViewHelper; import com.android.launcher3.Flags; +import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.SecondaryDropTarget; import com.android.launcher3.Utilities; @@ -53,6 +56,7 @@ import java.util.Arrays; */ public abstract class SystemShortcut extends ItemInfo implements View.OnClickListener { + private static final String TAG = "SystemShortcut"; private final int mIconResId; protected final int mLabelResId; @@ -383,4 +387,63 @@ public abstract class SystemShortcut extends ItemInfo mAbstractFloatingViewHelper.closeOpenViews(mTarget, true, AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); } + + public static final Factory BUBBLE_SHORTCUT = + (activity, itemInfo, originalView) -> { + if ((itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) + && (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) + && !(itemInfo instanceof WorkspaceItemInfo)) { + return null; + } + return new BubbleShortcut(activity, itemInfo, originalView); + }; + + public interface BubbleActivityStarter { + /** Tell SysUI to show the provided shortcut in a bubble. */ + void showShortcutBubble(ShortcutInfo info); + + /** Tell SysUI to show the provided intent in a bubble. */ + void showAppBubble(Intent intent); + } + + public static class BubbleShortcut extends SystemShortcut { + + private BubbleActivityStarter mStarter; + + public BubbleShortcut(T target, ItemInfo itemInfo, View originalView) { + super(R.drawable.ic_bubble_button, R.string.bubble, target, + itemInfo, originalView); + if (target instanceof BubbleActivityStarter) { + mStarter = (BubbleActivityStarter) target; + } + } + + @Override + public void onClick(View view) { + dismissTaskMenuView(); + if (mStarter == null) { + Log.w(TAG, "starter null!"); + return; + } + // TODO: handle GroupTask (single) items so that recent items in taskbar work + if (mItemInfo instanceof WorkspaceItemInfo) { + WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo; + ShortcutInfo shortcutInfo = workspaceItemInfo.getDeepShortcutInfo(); + if (shortcutInfo != null) { + mStarter.showShortcutBubble(shortcutInfo); + return; + } + } + // If we're here check for an intent + Intent intent = mItemInfo.getIntent(); + if (intent != null) { + if (intent.getPackage() == null) { + intent.setPackage(mItemInfo.getTargetPackage()); + } + mStarter.showAppBubble(intent); + } else { + Log.w(TAG, "unable to bubble, no intent: " + mItemInfo); + } + } + } }