diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index a64936d287..4b43bd2c4d 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1353,8 +1353,13 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ? null : mLauncher.getTaskbarUIController().findMatchingView(launcherView), true /* hideOriginal */, targetRect, false /* isOpening */); - isInHotseat = launcherView.getTag() instanceof ItemInfo - && ((ItemInfo) launcherView.getTag()).isInHotseat(); + if (launcherView.getTag() instanceof ItemInfo itemInfo) { + isInHotseat = itemInfo.isInHotseat(); + if (isInHotseat) { + int dx = mLauncher.getHotseatItemTranslationX(itemInfo); + targetRect.offset(dx, 0); + } + } } else { targetRect.set(getDefaultWindowTargetRect()); } diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index 1bebfbdb9c..de7a2d8f9e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -51,6 +51,7 @@ import com.android.quickstep.util.GroupTask; import com.android.quickstep.util.TISBindHelper; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import java.io.PrintWriter; @@ -487,6 +488,18 @@ public class LauncherTaskbarUIController extends TaskbarUIController { return "LauncherTaskbarUIController"; } + @Override + public void onBubbleBarLocationAnimated(BubbleBarLocation location) { + mTaskbarLauncherStateController.onBubbleBarLocationChanged(location, /* animate = */ true); + mLauncher.setBubbleBarLocation(location); + } + + @Override + public void onBubbleBarLocationUpdated(BubbleBarLocation location) { + mTaskbarLauncherStateController.onBubbleBarLocationChanged(location, /* animate = */ false); + mLauncher.setBubbleBarLocation(location); + } + @Override public void onSwipeToUnstashTaskbar() { // Once taskbar is unstashed, the user cannot return back to the overlay. We can diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index 2ac579361e..a4a93f7d56 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -201,6 +201,8 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT private final Rect mFloatingRotationButtonBounds = new Rect(); + private @Nullable BubbleBarLocation mBubbleBarLocation; + // Initialized in init. private TaskbarControllers mControllers; private boolean mIsImeRenderingNavButtons; @@ -1174,6 +1176,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT /** Adjusts navigation buttons layout accordingly to the bubble bar position. */ @Override public void onBubbleBarLocationUpdated(BubbleBarLocation location) { + mBubbleBarLocation = location; mNavButtonContainer.setTranslationX(getNavBarTranslationX(location)); } @@ -1181,6 +1184,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT @Override public void onBubbleBarLocationAnimated(BubbleBarLocation location) { // TODO(b/346381754) add the teleport animation similarly to the bubble bar + mBubbleBarLocation = location; mNavButtonContainer.setTranslationX(getNavBarTranslationX(location)); } @@ -1221,9 +1225,12 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT public void onTaskbarLayoutChange() { if (com.android.wm.shell.Flags.enableBubbleBarInPersistentTaskBar() && mControllers.bubbleControllers.isPresent()) { - BubbleBarLocation bubblesLocation = mControllers.bubbleControllers.get() - .bubbleBarViewController.getBubbleBarLocation(); - onBubbleBarLocationUpdated(bubblesLocation); + if (mBubbleBarLocation == null) { + // only set bubble bar location if it was not set before, e.g. at device boot + mBubbleBarLocation = mControllers.bubbleControllers.get() + .bubbleBarViewController.getBubbleBarLocation(); + } + onBubbleBarLocationUpdated(mBubbleBarLocation); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java index 56fd2bb66d..59746751a5 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java @@ -26,6 +26,7 @@ import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController; import com.android.launcher3.taskbar.bubbles.BubbleControllers; import com.android.launcher3.taskbar.overlay.TaskbarOverlayController; import com.android.systemui.shared.rotation.RotationButtonController; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import java.io.PrintWriter; import java.util.ArrayList; @@ -219,7 +220,11 @@ public class TaskbarControllers { uiController = newUiController; uiController.init(this); uiController.updateStateForSysuiFlags(mSharedState.sysuiStateFlags); - + bubbleControllers.ifPresent(bubbleControllers -> { + BubbleBarLocation location = + bubbleControllers.bubbleBarViewController.getBubbleBarLocation(); + uiController.onBubbleBarLocationUpdated(location); + }); // Notify that the ui controller has changed navbarButtonsViewController.onUiControllerChanged(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 876221b1d7..065646ad39 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -19,11 +19,14 @@ import static com.android.app.animation.Interpolators.EMPHASIZED; import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_ALIGNMENT; import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_STASH; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; +import static com.android.launcher3.Utilities.isRtl; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_FOR_BUBBLES; import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME; +import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_IN_ANIM_ALPHA_DURATION_MS; +import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_OUT_ANIM_POSITION_DURATION_MS; import static com.android.launcher3.util.FlagDebugUtils.appendFlag; import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE; @@ -42,8 +45,10 @@ import android.util.Log; 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.Hotseat; import com.android.launcher3.Hotseat.HotseatQsbAlphaId; import com.android.launcher3.LauncherState; import com.android.launcher3.QuickstepTransitionManager; @@ -62,6 +67,7 @@ import com.android.quickstep.views.RecentsView; import com.android.systemui.animation.ViewRootSync; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import java.io.PrintWriter; import java.util.HashMap; @@ -151,6 +157,7 @@ public class TaskbarLauncherStateController { private AnimatedFloat mTaskbarAlpha; private AnimatedFloat mTaskbarCornerRoundness; private MultiProperty mTaskbarAlphaForHome; + private @Nullable Animator mHotseatTranslationXAnimation; private QuickstepLauncher mLauncher; private boolean mIsDestroyed = false; @@ -168,6 +175,8 @@ public class TaskbarLauncherStateController { private boolean mShouldDelayLauncherStateAnim; + private @Nullable BubbleBarLocation mBubbleBarLocation; + // We skip any view synchronizations during init/destroy. private boolean mCanSyncViews; @@ -185,6 +194,8 @@ public class TaskbarLauncherStateController { mIsQsbInline = dp.isQsbInline; TaskbarLauncherStateController.this.updateIconAlphaForHome( mTaskbarAlphaForHome.getValue(), ALPHA_CHANNEL_TASKBAR_ALIGNMENT); + TaskbarLauncherStateController.this.onBubbleBarLocationChanged( + mBubbleBarLocation, /* animate = */ false); } }; @@ -833,6 +844,74 @@ public class TaskbarLauncherStateController { } } + /** Updates launcher home screen appearance accordingly to the bubble bar location. */ + public void onBubbleBarLocationChanged(BubbleBarLocation location, boolean animate) { + DeviceProfile deviceProfile = mLauncher.getDeviceProfile(); + if (mBubbleBarLocation == location) return; + mBubbleBarLocation = location; + if (!deviceProfile.shouldAdjustHotseatOnBubblesLocationUpdate( + mControllers.taskbarActivityContext)) { + return; + } + int targetX = 0; + if (mBubbleBarLocation != null) { + boolean isBubblesOnLeft = location.isOnLeft(isRtl(mLauncher.getResources())); + targetX = deviceProfile.getHotseatTranslationXForBubbleBar(/* isNavbarOnRight= */ + isBubblesOnLeft); + } + updateHotseatAndQsbTranslationX(targetX, animate); + } + + private void updateHotseatAndQsbTranslationX(float targetValue, boolean animate) { + // cancel existing animation + if (mHotseatTranslationXAnimation != null) { + mHotseatTranslationXAnimation.cancel(); + } + Runnable alignTaskbar = new Runnable() { + @Override + public void run() { + // We only need to align the task bar when on launcher home screen + if (mControllers.taskbarStashController.isOnHome()) { + DeviceProfile dp = mLauncher.getDeviceProfile(); + mControllers.taskbarViewController + .setLauncherIconAlignment(/* alignmentRatio = */ 1, dp); + } + } + }; + Hotseat hotseat = mLauncher.getHotseat(); + AnimatorSet translationXAnimation = new AnimatorSet(); + MultiProperty iconsTranslationX = hotseat.getIconsTranslationX( + Hotseat.ICONS_TRANSLATION_X_NAV_BAR_ALIGNMENT); + if (animate) { + translationXAnimation.playTogether(iconsTranslationX.animateToValue(targetValue)); + } else { + iconsTranslationX.setValue(targetValue); + } + float qsbTargetX = 0; + if (mIsQsbInline) { + qsbTargetX = targetValue; + } + MultiProperty qsbTranslationX = hotseat.getQsbTranslationX(); + if (qsbTranslationX != null) { + if (animate) { + translationXAnimation.playTogether(qsbTranslationX.animateToValue(qsbTargetX)); + } else { + qsbTranslationX.setValue(qsbTargetX); + } + } + if (!animate) { + alignTaskbar.run(); + return; + } + mHotseatTranslationXAnimation = translationXAnimation; + translationXAnimation.setStartDelay(FADE_OUT_ANIM_POSITION_DURATION_MS); + translationXAnimation.setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS); + translationXAnimation.setInterpolator(Interpolators.EMPHASIZED); + translationXAnimation.addListener(AnimatorListeners.forEndCallback(alignTaskbar)); + translationXAnimation.start(); + } + + private final class TaskBarRecentsAnimationListener implements RecentsAnimationCallbacks.RecentsAnimationListener { private final RecentsAnimationCallbacks mCallbacks; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 7624afb440..532ee1e5ca 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -446,6 +446,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba return hasAnyFlag(FLAG_IN_OVERVIEW); } + /** Returns whether the taskbar is currently on launcher home screen. */ + public boolean isOnHome() { + return !isInOverview() && !isInApp(); + } + /** Returns whether taskbar is hidden for bubbles. */ public boolean isHiddenForBubbles() { return hasAnyFlag(FLAG_STASHED_FOR_BUBBLES); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 9c8c2a9fda..b80aaf8840 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -36,6 +36,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.popup.SystemShortcut; +import com.android.launcher3.taskbar.bubbles.BubbleBarController; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.OverviewCommandHelper; @@ -47,6 +48,7 @@ import com.android.quickstep.views.TaskContainer; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import java.io.PrintWriter; import java.util.Collections; @@ -55,7 +57,7 @@ import java.util.stream.Stream; /** * Base class for providing different taskbar UI */ -public class TaskbarUIController { +public class TaskbarUIController implements BubbleBarController.BubbleBarLocationListener { public static final TaskbarUIController DEFAULT = new TaskbarUIController(); // Initialized in init. @@ -433,6 +435,14 @@ public class TaskbarUIController { public void stashHotseat(boolean stash) { } + @Override + public void onBubbleBarLocationAnimated(BubbleBarLocation location) { + } + + @Override + public void onBubbleBarLocationUpdated(BubbleBarLocation location) { + } + /** Un-stash the hotseat instantly */ public void unStashHotseatInstantly() { } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 0a67ac2f1d..1f3fdeb737 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -827,6 +827,14 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar : mPersistentTaskbarDp.taskbarBottomMargin; int firstRecentTaskIndex = -1; + int hotseatNavBarTranslationX = 0; + if (mCurrentBubbleBarLocation != null + && taskbarDp.shouldAdjustHotseatOnBubblesLocationUpdate(mActivity)) { + boolean isBubblesOnLeft = mCurrentBubbleBarLocation.isOnLeft( + mTaskbarView.isLayoutRtl()); + hotseatNavBarTranslationX = taskbarDp + .getHotseatTranslationXForBubbleBar(/* isNavbarOnRight = */ isBubblesOnLeft); + } for (int i = 0; i < mTaskbarView.getChildCount(); i++) { View child = mTaskbarView.getChildAt(i); boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonContainer(); @@ -860,16 +868,20 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f)); } } - if (child == mTaskbarView.getQsb()) { boolean isRtl = Utilities.isRtl(child.getResources()); float hotseatIconCenter = isRtl ? launcherDp.widthPx - hotseatPadding.right + borderSpacing + launcherDp.hotseatQsbWidth / 2f : hotseatPadding.left - borderSpacing - launcherDp.hotseatQsbWidth / 2f; + if (taskbarDp.isQsbInline) { + hotseatIconCenter += hotseatNavBarTranslationX; + } float childCenter = (child.getLeft() + child.getRight()) / 2f; - childCenter += ((Reorderable) child).getTranslateDelegate().getTranslationX( - INDEX_TASKBAR_PINNING_ANIM).getValue(); + if (child instanceof Reorderable reorderableChild) { + childCenter += reorderableChild.getTranslateDelegate().getTranslationX( + INDEX_TASKBAR_PINNING_ANIM).getValue(); + } float halfQsbIconWidthDiff = (launcherDp.hotseatQsbWidth - taskbarDp.taskbarIconSize) / 2f; float scale = ((float) taskbarDp.taskbarIconSize) @@ -878,8 +890,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar float fromX = isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff; float toX = hotseatIconCenter - childCenter; - if (child instanceof Reorderable) { - MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate(); + if (child instanceof Reorderable reorderableChild) { + MultiTranslateDelegate mtd = reorderableChild.getTranslateDelegate(); setter.addFloat(mtd.getTranslationX(INDEX_TASKBAR_ALIGNMENT_ANIM), MULTI_PROPERTY_VALUE, fromX, toX, interpolator); @@ -926,6 +938,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar + (hotseatCellSize + borderSpacing) * positionInHotseat + hotseatCellSize / 2f; } + hotseatIconCenter += hotseatNavBarTranslationX; float childCenter = (child.getLeft() + child.getRight()) / 2f; childCenter += ((Reorderable) child).getTranslateDelegate().getTranslationX( INDEX_TASKBAR_PINNING_ANIM).getValue(); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java index 6860004133..51e09abc65 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java @@ -196,7 +196,8 @@ public class BubbleBarController extends IBubblesListener.Stub { mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse( key -> setSelectedBubbleInternal(mBubbles.get(key))); mBubbleBarViewController.setBoundsChangeListener(this::onBubbleBarBoundsChanged); - + mBubbleBarLocationListener.onBubbleBarLocationUpdated( + mBubbleBarViewController.getBubbleBarLocation()); if (sBubbleBarEnabled) { mSystemUiProxy.setBubblesListener(this); } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java index a66df4c357..d3ac324ac6 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java @@ -22,6 +22,7 @@ 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.BubbleBarLocationOnDemandListener; import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.RunnableList; @@ -76,11 +77,11 @@ public class BubbleControllers { * in constructors for now, as some controllers may still be waiting for init(). */ public void init(TaskbarControllers taskbarControllers) { - // TODO(b/346381754) add TaskbarLauncherStateController implementation to adjust the hotseat BubbleBarLocationCompositeListener bubbleBarLocationListeners = new BubbleBarLocationCompositeListener( taskbarControllers.navbarButtonsViewController, - taskbarControllers.taskbarViewController + taskbarControllers.taskbarViewController, + new BubbleBarLocationOnDemandListener(() -> taskbarControllers.uiController) ); bubbleBarController.init(this, bubbleBarLocationListeners, diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleBarLocationOnDemandListener.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleBarLocationOnDemandListener.kt new file mode 100644 index 0000000000..ffe7c4473b --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleBarLocationOnDemandListener.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.taskbar.bubbles.stashing + +import com.android.launcher3.taskbar.bubbles.BubbleBarController.BubbleBarLocationListener +import com.android.wm.shell.shared.bubbles.BubbleBarLocation + +/** On demand implementation of [BubbleBarLocationListener]. */ +class BubbleBarLocationOnDemandListener( + private val listenerProvider: () -> BubbleBarLocationListener +) : BubbleBarLocationListener { + + override fun onBubbleBarLocationAnimated(location: BubbleBarLocation) { + listenerProvider().onBubbleBarLocationAnimated(location) + } + + override fun onBubbleBarLocationUpdated(location: BubbleBarLocation) { + listenerProvider().onBubbleBarLocationUpdated(location) + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index e80e838e90..3a34f4e7a3 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -37,6 +37,7 @@ import static com.android.launcher3.LauncherState.NO_OFFSET; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; +import static com.android.launcher3.Utilities.isRtl; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; import static com.android.launcher3.config.FeatureFlags.enableSplitContextually; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; @@ -65,6 +66,8 @@ import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent 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.Flags.enableBubbleAnything; +import static com.android.wm.shell.Flags.enableBubbleBarInPersistentTaskBar; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50; import android.animation.Animator; @@ -198,6 +201,7 @@ import com.android.systemui.unfold.config.UnfoldTransitionConfig; import com.android.systemui.unfold.dagger.UnfoldMain; import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver; import com.android.systemui.unfold.updates.RotationChangeProvider; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import kotlin.Unit; @@ -239,6 +243,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, private SplitSelectStateController mSplitSelectStateController; private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController; private SplitToWorkspaceController mSplitToWorkspaceController; + private BubbleBarLocation mBubbleBarLocation; /** * If Launcher restarted while in the middle of an Overview split select, it needs this data to @@ -463,7 +468,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, if (Flags.enablePrivateSpace()) { shortcuts.add(UNINSTALL_APP); } - if (com.android.wm.shell.Flags.enableBubbleAnything()) { + if (enableBubbleAnything()) { shortcuts.add(BUBBLE_SHORTCUT); } return shortcuts.stream(); @@ -1105,6 +1110,29 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, return mTaskbarUIController; } + /** Provides the translation X for the hotseat item. */ + public int getHotseatItemTranslationX(ItemInfo itemInfo) { + int translationX = 0; + if (isBubbleBarEnabled() + && enableBubbleBarInPersistentTaskBar() + && mBubbleBarLocation != null) { + boolean isBubblesOnLeft = mBubbleBarLocation.isOnLeft(isRtl(getResources())); + translationX += mDeviceProfile + .getHotseatTranslationXForBubbleBar(/* isNavbarOnRight = */ isBubblesOnLeft); + } + if (isBubbleBarEnabled() && hasBubbles()) { + // TODO(368379159) : create a class to reuse computation logic + float adjustedBorderSpace = + mDeviceProfile.getHotseatAdjustedBorderSpaceForBubbleBar(this); + if (Float.compare(adjustedBorderSpace, 0f) != 0) { + float borderSpaceDelta = adjustedBorderSpace - mDeviceProfile.hotseatBorderSpace; + translationX += + (int) (mDeviceProfile.iconSizePx + itemInfo.cellX * borderSpaceDelta); + } + } + return translationX; + } + public SplitToWorkspaceController getSplitToWorkspaceController() { return mSplitToWorkspaceController; } @@ -1410,6 +1438,11 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, SystemUiProxy.INSTANCE.get(this).showAppBubble(intent); } + /** Sets the location of the bubble bar */ + public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { + mBubbleBarLocation = bubbleBarLocation; + } + private static final class LauncherTaskViewController extends TaskViewTouchController { diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index cc5baea07e..b7fcd980b2 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -31,6 +31,8 @@ import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTO import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE; import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp; import static com.android.launcher3.testing.shared.ResourceUtils.roundPxValueFromFloat; +import static com.android.wm.shell.Flags.enableBubbleBar; +import static com.android.wm.shell.Flags.enableBubbleBarInPersistentTaskBar; import static com.android.wm.shell.Flags.enableTinyTaskbar; import android.annotation.SuppressLint; @@ -64,8 +66,10 @@ import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecT import com.android.launcher3.responsive.ResponsiveSpec.DimensionType; import com.android.launcher3.responsive.ResponsiveSpecsProvider; import com.android.launcher3.util.CellContentDimensions; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.IconSizeSteps; +import com.android.launcher3.util.NavigationMode; import com.android.launcher3.util.ResourceHelper; import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.window.WindowManagerProxy; @@ -219,6 +223,8 @@ public class DeviceProfile { public int hotseatBarBottomSpacePx; public int hotseatBarEndOffset; public int hotseatQsbSpace; + public int inlineNavButtonsEndSpacingPx; + public int navButtonsLayoutWidthPx; public int springLoadedHotseatBarTopMarginPx; // These 2 values are only used for isVerticalBar // Padding between edge of screen and hotseat @@ -233,7 +239,6 @@ public class DeviceProfile { private final int mMinHotseatIconSpacePx; private final int mMinHotseatQsbWidthPx; private final int mMaxHotseatIconSpacePx; - public final int inlineNavButtonsEndSpacingPx; // Space required for the bubble bar between the hotseat and the edge of the screen. If there's // not enough space, the hotseat will adjust itself for the bubble bar. private final int mBubbleBarSpaceThresholdPx; @@ -692,17 +697,12 @@ public class DeviceProfile { if (areNavButtonsInline && !isPhone) { inlineNavButtonsEndSpacingPx = res.getDimensionPixelSize(inv.inlineNavButtonsEndSpacing); - /* - * 3 nav buttons + - * Spacing between nav buttons + - * Space at the end for contextual buttons - */ - hotseatBarEndOffset = 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) - + 2 * res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween) - + inlineNavButtonsEndSpacingPx; - } else { - inlineNavButtonsEndSpacingPx = 0; - hotseatBarEndOffset = 0; + /* 3 nav buttons + Spacing between nav buttons */ + navButtonsLayoutWidthPx = 3 * res.getDimensionPixelSize( + R.dimen.taskbar_nav_buttons_size) + + 2 * res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween); + /* nav buttons layout width + Space at the end for contextual buttons */ + hotseatBarEndOffset = navButtonsLayoutWidthPx + inlineNavButtonsEndSpacingPx; } mBubbleBarSpaceThresholdPx = @@ -2326,6 +2326,25 @@ public class DeviceProfile { return c.createConfigurationContext(config); } + /** + * Returns whether Taskbar and Hotseat should adjust horizontally on bubble bar location update. + */ + public boolean shouldAdjustHotseatOnBubblesLocationUpdate(Context context) { + return enableBubbleBar() + && enableBubbleBarInPersistentTaskBar() + && DisplayController.getNavigationMode(context) + == NavigationMode.THREE_BUTTONS; + } + + /** Returns hotseat translation X for the bubble bar position. */ + public int getHotseatTranslationXForBubbleBar(boolean isNavbarOnRight) { + if (isNavbarOnRight) { + return 0; + } else { + return navButtonsLayoutWidthPx; + } + } + /** * Callback when a component changes the DeviceProfile associated with it, as a result of * configuration change diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 024dde477f..ae4c122703 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -34,8 +34,10 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.IntDef; +import androidx.annotation.Nullable; import com.android.launcher3.util.HorizontalInsettableView; +import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.util.MultiTranslateDelegate; import com.android.launcher3.util.MultiValueAlpha; @@ -61,6 +63,14 @@ public class Hotseat extends CellLayout implements Insettable { public @interface HotseatQsbAlphaId { } + public static final int ICONS_TRANSLATION_X_NAV_BAR_ALIGNMENT = 0; + public static final int ICONS_TRANSLATION_X_CHANNELS_COUNT = 1; + + @Retention(RetentionPolicy.RUNTIME) + @IntDef({ICONS_TRANSLATION_X_NAV_BAR_ALIGNMENT}) + public @interface IconsTranslationX { + } + // Ratio of empty space, qsb should take up to appear visually centered. public static final float QSB_CENTER_FACTOR = .325f; private static final int BUBBLE_BAR_ADJUSTMENT_ANIMATION_DURATION_MS = 250; @@ -72,6 +82,10 @@ public class Hotseat extends CellLayout implements Insettable { private final MultiValueAlpha mIconsAlphaChannels; private final MultiValueAlpha mQsbAlphaChannels; + private @Nullable MultiProperty mQsbTranslationX; + + private final MultiPropertyFactory mIconsTranslationXFactory; + private final View mQsb; public Hotseat(Context context) { @@ -88,9 +102,26 @@ public class Hotseat extends CellLayout implements Insettable { addView(mQsb); mIconsAlphaChannels = new MultiValueAlpha(getShortcutsAndWidgets(), ALPHA_CHANNEL_CHANNELS_COUNT); + if (mQsb instanceof Reorderable qsbReorderable) { + mQsbTranslationX = qsbReorderable.getTranslateDelegate() + .getTranslationX(MultiTranslateDelegate.INDEX_NAV_BAR_ANIM); + } + mIconsTranslationXFactory = new MultiPropertyFactory<>(getShortcutsAndWidgets(), + VIEW_TRANSLATE_X, ICONS_TRANSLATION_X_CHANNELS_COUNT, Float::sum); mQsbAlphaChannels = new MultiValueAlpha(mQsb, ALPHA_CHANNEL_CHANNELS_COUNT); } + /** Provides translation X for hotseat icons for the channel. */ + public MultiProperty getIconsTranslationX(@IconsTranslationX int channelId) { + return mIconsTranslationXFactory.get(channelId); + } + + /** Provides translation X for hotseat Qsb. */ + @Nullable + public MultiProperty getQsbTranslationX() { + return mQsbTranslationX; + } + /** * Returns orientation specific cell X given invariant order in the hotseat */