From 23e6f38eb55c8fba296b96e0bb07d7e23902c9ac Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Tue, 27 Aug 2024 11:34:49 -0700 Subject: [PATCH 01/23] Skip updating taskbar background alpha in phone mode Based on https://b.corp.google.com/issues/361593564#comment4 Bug: 361593564 Test: manual Flag: EXEMPT bugfix Change-Id: I1b23847af707e6fa208345b87953b53f79543bfe --- .../android/launcher3/taskbar/TaskbarDragLayerController.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java index acf976fe9a..2845ceee7a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java @@ -171,6 +171,10 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa } private void updateBackgroundAlpha() { + if (mActivity.isPhoneMode()) { + return; + } + final float bgNavbar = mBgNavbar.value; final float bgTaskbar = mBgTaskbar.value * mKeyguardBgTaskbar.value * mNotificationShadeBgTaskbar.value * mImeBgTaskbar.value From 9bcdb1cbe9a27d1adfe8afccf0f499fb47c7c94c Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 28 Aug 2024 08:29:14 -0700 Subject: [PATCH 02/23] Fix bug where PredictedIcon circle/dot flicker. The issue is that the PredictedIcon in the Taskbar would draw right before we swap to the matching icon in the Hotseat. Fixes: 362677520 Test: launch predicted app icon from hotseat, swipe to go home Flag: EXEMPT bugfix Change-Id: Iab8b707edf846b257c7fb7cacc9008e5c66353a7 --- .../launcher3/uioverrides/PredictedAppIcon.java | 9 ++------- src/com/android/launcher3/BubbleTextView.java | 4 ++-- .../android/launcher3/folder/FolderIcon.java | 4 ++-- .../launcher3/views/BubbleTextHolder.java | 2 +- .../launcher3/views/FloatingIconView.java | 14 +++++++++++--- ...View.java => FloatingIconViewCompanion.java} | 17 +++++++++++------ .../launcher3/views/FloatingSurfaceView.java | 4 ++-- 7 files changed, 31 insertions(+), 23 deletions(-) rename src/com/android/launcher3/views/{IconLabelDotView.java => FloatingIconViewCompanion.java} (56%) diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 37e00341e6..4590efefb2 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -379,12 +379,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { } @Override - public void setIconVisible(boolean visible) { - setForceHideRing(!visible); - super.setIconVisible(visible); - } - - private void setForceHideRing(boolean forceHideRing) { + public void setForceHideRing(boolean forceHideRing) { if (mForceHideRing == forceHideRing) { return; } @@ -417,7 +412,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { private void drawEffect(Canvas canvas) { // Don't draw ring effect if item is about to be dragged or if the icon is not visible. - if (mDrawForDrag || !mIsIconVisible) { + if (mDrawForDrag || !mIsIconVisible || mForceHideRing) { return; } mIconRingPaint.setColor(RING_SHADOW_COLOR); diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 26e900d0aa..8121e53542 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -89,7 +89,7 @@ import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; -import com.android.launcher3.views.IconLabelDotView; +import com.android.launcher3.views.FloatingIconViewCompanion; import java.text.NumberFormat; import java.util.HashMap; @@ -101,7 +101,7 @@ import java.util.Locale; * too aggressive. */ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, - IconLabelDotView, DraggableView, Reorderable { + FloatingIconViewCompanion, DraggableView, Reorderable { public static final String TAG = "BubbleTextView"; diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index a0b695a9bc..de1bcc34db 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -83,7 +83,7 @@ import com.android.launcher3.util.Executors; import com.android.launcher3.util.MultiTranslateDelegate; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.ActivityContext; -import com.android.launcher3.views.IconLabelDotView; +import com.android.launcher3.views.FloatingIconViewCompanion; import com.android.launcher3.widget.PendingAddShortcutInfo; import java.util.ArrayList; @@ -93,7 +93,7 @@ import java.util.function.Predicate; /** * An icon that can appear on in the workspace representing an {@link Folder}. */ -public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView, +public class FolderIcon extends FrameLayout implements FolderListener, FloatingIconViewCompanion, DraggableView, Reorderable { private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this); diff --git a/src/com/android/launcher3/views/BubbleTextHolder.java b/src/com/android/launcher3/views/BubbleTextHolder.java index 84f8049ded..d2ae93b5be 100644 --- a/src/com/android/launcher3/views/BubbleTextHolder.java +++ b/src/com/android/launcher3/views/BubbleTextHolder.java @@ -20,7 +20,7 @@ import com.android.launcher3.BubbleTextView; /** * Views that contain {@link BubbleTextView} should implement this interface. */ -public interface BubbleTextHolder extends IconLabelDotView { +public interface BubbleTextHolder extends FloatingIconViewCompanion { BubbleTextView getBubbleText(); @Override diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 37482ac44d..4ee6affcb2 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -22,7 +22,7 @@ import static com.android.launcher3.Flags.enableAdditionalHomeAnimations; import static com.android.launcher3.Utilities.getFullDrawable; import static com.android.launcher3.Utilities.mapToRange; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; -import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible; +import static com.android.launcher3.views.FloatingIconViewCompanion.setPropertiesVisible; import android.animation.Animator; import android.content.Context; @@ -516,6 +516,10 @@ public class FloatingIconView extends FrameLayout implements // When closing an app, we want the item on the workspace to be invisible immediately updateViewsVisibility(false /* isVisible */); } + if (mFadeOutView instanceof FloatingIconViewCompanion fivc) { + fivc.setForceHideDot(true); + fivc.setForceHideRing(true); + } } @Override @@ -653,6 +657,10 @@ public class FloatingIconView extends FrameLayout implements if (view.mFadeOutView != null) { view.mFadeOutView.setAlpha(1f); } + if (view.mFadeOutView instanceof FloatingIconViewCompanion fivc) { + fivc.setForceHideDot(false); + fivc.setForceHideRing(false); + } if (hideOriginal) { view.updateViewsVisibility(true /* isVisible */); @@ -674,10 +682,10 @@ public class FloatingIconView extends FrameLayout implements private void updateViewsVisibility(boolean isVisible) { if (mOriginalIcon != null) { - setIconAndDotVisible(mOriginalIcon, isVisible); + setPropertiesVisible(mOriginalIcon, isVisible); } if (mMatchVisibilityView != null) { - setIconAndDotVisible(mMatchVisibilityView, isVisible); + setPropertiesVisible(mMatchVisibilityView, isVisible); } } diff --git a/src/com/android/launcher3/views/IconLabelDotView.java b/src/com/android/launcher3/views/FloatingIconViewCompanion.java similarity index 56% rename from src/com/android/launcher3/views/IconLabelDotView.java rename to src/com/android/launcher3/views/FloatingIconViewCompanion.java index e9113cf874..fc23903662 100644 --- a/src/com/android/launcher3/views/IconLabelDotView.java +++ b/src/com/android/launcher3/views/FloatingIconViewCompanion.java @@ -18,19 +18,24 @@ package com.android.launcher3.views; import android.view.View; /** - * A view that has an icon, label, and notification dot. + * A view that can be drawn (in some capacity) via) {@link FloatingIconView}. + * This interface allows us to hide certain properties of the view that the FloatingIconView + * cannot draw, which allows us to make a seamless handoff between the FloatingIconView and + * the companion view. */ -public interface IconLabelDotView { +public interface FloatingIconViewCompanion { void setIconVisible(boolean visible); void setForceHideDot(boolean hide); + default void setForceHideRing(boolean hide) {} /** * Sets the visibility of icon and dot of the view */ - static void setIconAndDotVisible(View view, boolean visible) { - if (view instanceof IconLabelDotView) { - ((IconLabelDotView) view).setIconVisible(visible); - ((IconLabelDotView) view).setForceHideDot(!visible); + static void setPropertiesVisible(View view, boolean visible) { + if (view instanceof FloatingIconViewCompanion) { + ((FloatingIconViewCompanion) view).setIconVisible(visible); + ((FloatingIconViewCompanion) view).setForceHideDot(!visible); + ((FloatingIconViewCompanion) view).setForceHideRing(!visible); } else { view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); } diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java index cab798215c..7fa751772e 100644 --- a/src/com/android/launcher3/views/FloatingSurfaceView.java +++ b/src/com/android/launcher3/views/FloatingSurfaceView.java @@ -17,7 +17,7 @@ package com.android.launcher3.views; import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.views.FloatingIconView.getLocationBoundsForView; -import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible; +import static com.android.launcher3.views.FloatingIconViewCompanion.setPropertiesVisible; import android.content.Context; import android.graphics.Canvas; @@ -237,7 +237,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements private void setCurrentIconVisible(boolean isVisible) { if (mIcon != null) { - setIconAndDotVisible(mIcon, isVisible); + setPropertiesVisible(mIcon, isVisible); } } } From e7ae4e31ae94f35064bf31024d143912b4be8e2a Mon Sep 17 00:00:00 2001 From: Jordan Silva Date: Wed, 28 Aug 2024 13:45:59 +0100 Subject: [PATCH 03/23] Refactoring OverviewCommandHelper (1/3) - Updates pendingCommands to be a ConcurrentLinkedDeque and make the list safe across multiple threads. - Introduces CommandStatus to clear only IDLE and COMPLETED commands. - Adds CommandType enum to prevent adding an invalid command value into the queue and having an unexpected behavior. - Log messages improved Bug: 352046797 Bug: 351122926 Flag: EXEMPT bugfix. Test: Manual. Change-Id: I80705dca0be579e62cb9e2bd923808dd33c4d633 --- .../taskbar/TaskbarUIController.java | 4 +- .../quickstep/OverviewCommandHelper.kt | 224 +++++++++--------- .../quickstep/TouchInteractionService.java | 14 +- .../TaskbarUnstashInputConsumer.java | 3 +- 4 files changed, 122 insertions(+), 123 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 43960a1c7f..6b1173a516 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -20,7 +20,6 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP; -import static com.android.quickstep.OverviewCommandHelper.TYPE_HIDE; import android.content.Intent; import android.graphics.drawable.BitmapDrawable; @@ -40,6 +39,7 @@ import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.OverviewCommandHelper; +import com.android.quickstep.OverviewCommandHelper.CommandType; import com.android.quickstep.util.GroupTask; import com.android.quickstep.util.TISBindHelper; import com.android.quickstep.views.RecentsView; @@ -394,7 +394,7 @@ public class TaskbarUIController { if (overviewCommandHelper == null) { return; } - overviewCommandHelper.addCommand(TYPE_HIDE); + overviewCommandHelper.addCommand(CommandType.HIDE); } /** diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt index f6b9e4e26f..56153a94c0 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt @@ -33,6 +33,8 @@ import com.android.launcher3.logging.StatsLogManager import com.android.launcher3.logging.StatsLogManager.LauncherEvent.* import com.android.launcher3.util.Executors import com.android.launcher3.util.RunnableList +import com.android.quickstep.OverviewCommandHelper.CommandInfo.CommandStatus +import com.android.quickstep.OverviewCommandHelper.CommandType.* import com.android.quickstep.util.ActiveGestureLog import com.android.quickstep.views.RecentsView import com.android.quickstep.views.RecentsViewContainer @@ -40,6 +42,7 @@ import com.android.quickstep.views.TaskView import com.android.systemui.shared.recents.model.ThumbnailData import com.android.systemui.shared.system.InteractionJankMonitorWrapper import java.io.PrintWriter +import java.util.concurrent.ConcurrentLinkedDeque /** Helper class to handle various atomic commands for switching between Overview. */ class OverviewCommandHelper( @@ -47,7 +50,7 @@ class OverviewCommandHelper( private val overviewComponentObserver: OverviewComponentObserver, private val taskAnimationManager: TaskAnimationManager ) { - private val pendingCommands = mutableListOf() + private val commandQueue = ConcurrentLinkedDeque() /** * Index of the TaskView that should be focused when launching Overview. Persisted so that we do @@ -64,22 +67,26 @@ class OverviewCommandHelper( */ private var waitForToggleCommandComplete = false + private val activityInterface: BaseActivityInterface<*, *> + get() = overviewComponentObserver.activityInterface + + private val visibleRecentsView: RecentsView<*, *>? + get() = activityInterface.getVisibleRecentsView>() + /** Called when the command finishes execution. */ - private fun scheduleNextTask(command: CommandInfo) { - if (pendingCommands.isEmpty()) { - Log.d(TAG, "no pending commands to schedule") - return - } - if (pendingCommands.first() !== command) { + private fun onCommandFinished(command: CommandInfo) { + command.status = CommandStatus.COMPLETED + if (commandQueue.first() !== command) { Log.d( TAG, "next task not scheduled. First pending command type " + - "is ${pendingCommands.first()} - command type is: $command" + "is ${commandQueue.first()} - command type is: $command" ) return } - Log.d(TAG, "scheduleNextTask called: $command") - pendingCommands.removeFirst() + + Log.d(TAG, "command executed successfully! $command") + commandQueue.remove(command) executeNext() } @@ -90,24 +97,21 @@ class OverviewCommandHelper( */ @UiThread private fun executeNext() { - if (pendingCommands.isEmpty()) { - Log.d(TAG, "executeNext - pendingCommands is empty") - return - } - val command = pendingCommands.first() - val result = executeCommand(command) - Log.d(TAG, "executeNext command type: $command, result: $result") - if (result) { - scheduleNextTask(command) - } - } + val command: CommandInfo = + commandQueue.firstOrNull() + ?: run { + Log.d(TAG, "no pending commands to be executed.") + return + } - @UiThread - private fun addCommand(command: CommandInfo) { - val wasEmpty = pendingCommands.isEmpty() - pendingCommands.add(command) - if (wasEmpty) { - executeNext() + Log.d(TAG, "executing command: $command") + val result = executeCommand(command) + + Log.d(TAG, "command executed: $command with result: $result") + if (result) { + onCommandFinished(command) + } else { + Log.d(TAG, "waiting for command callback: $command") } } @@ -117,29 +121,29 @@ class OverviewCommandHelper( * dropped. */ @BinderThread - fun addCommand(type: Int) { - if (pendingCommands.size >= MAX_QUEUE_SIZE) { - Log.d( - TAG, - "the pending command queue is full (${pendingCommands.size}). command not added: $type" - ) + fun addCommand(type: CommandType) { + if (commandQueue.size >= MAX_QUEUE_SIZE) { + Log.d(TAG, "commands queue is full ($commandQueue). command not added: $type") return } - Log.d(TAG, "adding command type: $type") + val command = CommandInfo(type) - Executors.MAIN_EXECUTOR.execute { addCommand(command) } + commandQueue.add(command) + Log.d(TAG, "command added: $command") + + if (commandQueue.size == 1) { + Executors.MAIN_EXECUTOR.execute { executeNext() } + } } - @UiThread + fun canStartHomeSafely(): Boolean = commandQueue.isEmpty() || commandQueue.first().type == HOME + + /** Clear commands from the queue */ fun clearPendingCommands() { - Log.d(TAG, "clearing pending commands - size: ${pendingCommands.size}") - pendingCommands.clear() + Log.d(TAG, "clearing pending commands: $commandQueue") + commandQueue.clear() } - @UiThread - fun canStartHomeSafely(): Boolean = - pendingCommands.isEmpty() || pendingCommands.first().type == TYPE_HOME - private fun getNextTask(view: RecentsView<*, *>): TaskView? { val runningTaskView = view.runningTaskView @@ -166,7 +170,7 @@ class OverviewCommandHelper( if (callbackList != null) { callbackList.add { Log.d(TAG, "launching task callback: $command") - scheduleNextTask(command) + onCommandFinished(command) waitForToggleCommandComplete = false } Log.d(TAG, "launching task - waiting for callback: $command") @@ -183,21 +187,18 @@ class OverviewCommandHelper( * is deferred until [.scheduleNextTask] is called */ private fun executeCommand(command: CommandInfo): Boolean { - if (waitForToggleCommandComplete && command.type == TYPE_TOGGLE) { + command.status = CommandStatus.PROCESSING + + if (waitForToggleCommandComplete && command.type == TOGGLE) { Log.d(TAG, "executeCommand: $command - waiting for toggle command complete") return true } - val activityInterface: BaseActivityInterface<*, *> = - overviewComponentObserver.activityInterface - val visibleRecentsView: RecentsView<*, *>? = - activityInterface.getVisibleRecentsView>() - val createdRecentsView: RecentsView<*, *>? - - Log.d(TAG, "executeCommand: $command - visibleRecentsView: $visibleRecentsView") - if (visibleRecentsView == null) { + var recentsView = visibleRecentsView + Log.d(TAG, "executeCommand: $command - visibleRecentsView: $recentsView") + if (recentsView == null) { val activity = activityInterface.getCreatedContainer() as? RecentsViewContainer - createdRecentsView = activity?.getOverviewPanel() + recentsView = activity?.getOverviewPanel() val deviceProfile = activity?.getDeviceProfile() val uiController = activityInterface.getTaskbarController() val allowQuickSwitch = @@ -207,22 +208,20 @@ class OverviewCommandHelper( (deviceProfile.isTablet || deviceProfile.isTwoPanels) when (command.type) { - TYPE_HIDE -> { + HIDE -> { if (!allowQuickSwitch) return true keyboardTaskFocusIndex = uiController!!.launchFocusedTask() if (keyboardTaskFocusIndex == -1) return true } - TYPE_KEYBOARD_INPUT -> + KEYBOARD_INPUT -> if (allowQuickSwitch) { uiController!!.openQuickSwitchView() return true } else { keyboardTaskFocusIndex = 0 } - TYPE_HOME -> { - ActiveGestureLog.INSTANCE.addLog( - "OverviewCommandHelper.executeCommand(TYPE_HOME)" - ) + HOME -> { + ActiveGestureLog.INSTANCE.addLog("OverviewCommandHelper.executeCommand(HOME)") // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call, // we should still call it on main thread because launcher is waiting for // ActivityTaskManager to resume it. Also calling startActivity() on bg thread @@ -230,38 +229,37 @@ class OverviewCommandHelper( touchInteractionService.startActivity(overviewComponentObserver.homeIntent) return true } - TYPE_SHOW -> - // When Recents is not currently visible, the command's type is - // TYPE_SHOW + SHOW -> + // When Recents is not currently visible, the command's type is SHOW // when overview is triggered via the keyboard overview button or Action+Tab // keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button // nav is TYPE_TOGGLE. keyboardTaskFocusIndex = 0 - else -> {} + TOGGLE -> {} } } else { - createdRecentsView = visibleRecentsView - when (command.type) { - TYPE_SHOW -> return true // already visible - TYPE_KEYBOARD_INPUT, - TYPE_HIDE -> { - if (visibleRecentsView.isHandlingTouch) return true - - keyboardTaskFocusIndex = PagedView.INVALID_PAGE - val currentPage = visibleRecentsView.nextPage - val taskView = visibleRecentsView.getTaskViewAt(currentPage) - return launchTask(visibleRecentsView, taskView, command) + return when (command.type) { + SHOW -> true // already visible + KEYBOARD_INPUT, + HIDE -> { + if (recentsView.isHandlingTouch) { + true + } else { + keyboardTaskFocusIndex = PagedView.INVALID_PAGE + val currentPage = recentsView.nextPage + val taskView = recentsView.getTaskViewAt(currentPage) + launchTask(recentsView, taskView, command) + } } - TYPE_TOGGLE -> - return launchTask(visibleRecentsView, getNextTask(visibleRecentsView), command) - TYPE_HOME -> { - visibleRecentsView.startHome() - return true + TOGGLE -> launchTask(recentsView, getNextTask(recentsView), command) + HOME -> { + recentsView.startHome() + true } } } - createdRecentsView?.setKeyboardTaskFocusIndex(keyboardTaskFocusIndex) + recentsView?.setKeyboardTaskFocusIndex(keyboardTaskFocusIndex) // Handle recents view focus when launching from home val animatorListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() { @@ -275,7 +273,7 @@ class OverviewCommandHelper( Log.d(TAG, "switching to Overview state - onAnimationEnd: $command") super.onAnimationEnd(animation) onRecentsViewFocusUpdated(command) - scheduleNextTask(command) + onCommandFinished(command) } } if (activityInterface.switchToRecentsIfVisible(animatorListener)) { @@ -326,7 +324,7 @@ class OverviewCommandHelper( command.removeListener(this) activityInterface.getCreatedContainer() ?: return - createdRecentsView?.onRecentsAnimationComplete() + recentsView?.onRecentsAnimationComplete() } } @@ -365,17 +363,12 @@ class OverviewCommandHelper( command.removeListener(handler) Trace.endAsyncSection(TRANSITION_NAME, 0) onRecentsViewFocusUpdated(command) - scheduleNextTask(command) + onCommandFinished(command) } private fun updateRecentsViewFocus(command: CommandInfo) { - val recentsView: RecentsView<*, *> = - overviewComponentObserver.activityInterface.getVisibleRecentsView() ?: return - if ( - command.type != TYPE_KEYBOARD_INPUT && - command.type != TYPE_HIDE && - command.type != TYPE_SHOW - ) { + val recentsView: RecentsView<*, *> = visibleRecentsView ?: return + if (command.type != KEYBOARD_INPUT && command.type != HIDE && command.type != SHOW) { return } @@ -394,9 +387,8 @@ class OverviewCommandHelper( } private fun onRecentsViewFocusUpdated(command: CommandInfo) { - val recentsView: RecentsView<*, *> = - overviewComponentObserver.activityInterface.getVisibleRecentsView() ?: return - if (command.type != TYPE_HIDE || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) { + val recentsView: RecentsView<*, *> = visibleRecentsView ?: return + if (command.type != HIDE || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) { return } recentsView.setKeyboardTaskFocusIndex(PagedView.INVALID_PAGE) @@ -413,15 +405,13 @@ class OverviewCommandHelper( return true } - private fun logShowOverviewFrom(commandType: Int) { - val activityInterface: BaseActivityInterface<*, *> = - overviewComponentObserver.activityInterface + private fun logShowOverviewFrom(commandType: CommandType) { val container = activityInterface.getCreatedContainer() as? RecentsViewContainer ?: return val event = when (commandType) { - TYPE_SHOW -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT - TYPE_HIDE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH - TYPE_TOGGLE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON + SHOW -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT + HIDE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH + TOGGLE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON else -> return } StatsLogManager.newInstance(container.asContext()) @@ -438,16 +428,17 @@ class OverviewCommandHelper( fun dump(pw: PrintWriter) { pw.println("OverviewCommandHelper:") - pw.println(" pendingCommands=${pendingCommands.size}") - if (pendingCommands.isNotEmpty()) { - pw.println(" pendingCommandType=${pendingCommands.first().type}") + pw.println(" pendingCommands=${commandQueue.size}") + if (commandQueue.isNotEmpty()) { + pw.println(" pendingCommandType=${commandQueue.first().type}") } - pw.println(" mKeyboardTaskFocusIndex=$keyboardTaskFocusIndex") - pw.println(" mWaitForToggleCommandComplete=$waitForToggleCommandComplete") + pw.println(" keyboardTaskFocusIndex=$keyboardTaskFocusIndex") + pw.println(" waitForToggleCommandComplete=$waitForToggleCommandComplete") } private data class CommandInfo( - val type: Int, + val type: CommandType, + var status: CommandStatus = CommandStatus.IDLE, val createTime: Long = SystemClock.elapsedRealtime(), private var animationCallbacks: RecentsAnimationCallbacks? = null ) { @@ -462,23 +453,30 @@ class OverviewCommandHelper( fun removeListener(listener: RecentsAnimationCallbacks.RecentsAnimationListener?) { animationCallbacks?.removeListener(listener) } + + enum class CommandStatus { + IDLE, + PROCESSING, + COMPLETED + } + } + + enum class CommandType { + SHOW, + KEYBOARD_INPUT, + HIDE, + TOGGLE, // Navigate to Overview + HOME, // Navigate to Home } companion object { private const val TAG = "OverviewCommandHelper" - - const val TYPE_SHOW: Int = 1 - const val TYPE_KEYBOARD_INPUT: Int = 2 - const val TYPE_HIDE: Int = 3 - const val TYPE_TOGGLE: Int = 4 - const val TYPE_HOME: Int = 5 + private const val TRANSITION_NAME = "Transition:toOverview" /** * Use case for needing a queue is double tapping recents button in 3 button nav. Size of 2 * should be enough. We'll toss in one more because we're kind hearted. */ private const val MAX_QUEUE_SIZE = 3 - - private const val TRANSITION_NAME = "Transition:toOverview" } } diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 2b5aa71125..69d1c35bdb 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -108,6 +108,7 @@ import com.android.launcher3.util.PluginManagerWrapper; import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.ScreenOnTracker; import com.android.launcher3.util.TraceHelper; +import com.android.quickstep.OverviewCommandHelper.CommandType; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantInputConsumer; import com.android.quickstep.inputconsumers.BubbleBarInputConsumer; @@ -245,7 +246,7 @@ public class TouchInteractionService extends Service { return; } TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); - tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE); + tis.mOverviewCommandHelper.addCommand(CommandType.TOGGLE); }); } @@ -255,10 +256,9 @@ public class TouchInteractionService extends Service { executeForTouchInteractionService(tis -> { if (triggeredFromAltTab) { TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); - tis.mOverviewCommandHelper.addCommand( - OverviewCommandHelper.TYPE_KEYBOARD_INPUT); + tis.mOverviewCommandHelper.addCommand(CommandType.KEYBOARD_INPUT); } else { - tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW); + tis.mOverviewCommandHelper.addCommand(CommandType.SHOW); } }); } @@ -269,7 +269,7 @@ public class TouchInteractionService extends Service { executeForTouchInteractionService(tis -> { if (triggeredFromAltTab && !triggeredFromHomeKey) { // onOverviewShownFromAltTab hides the overview and ends at the target app - tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HIDE); + tis.mOverviewCommandHelper.addCommand(CommandType.HIDE); } }); } @@ -594,12 +594,12 @@ public class TouchInteractionService extends Service { private final TaskbarNavButtonCallbacks mNavCallbacks = new TaskbarNavButtonCallbacks() { @Override public void onNavigateHome() { - mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HOME); + mOverviewCommandHelper.addCommand(CommandType.HOME); } @Override public void onToggleOverview() { - mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE); + mOverviewCommandHelper.addCommand(CommandType.TOGGLE); } }; diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java index 17a97fae21..9284e138af 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java @@ -46,6 +46,7 @@ import com.android.launcher3.util.DisplayController; import com.android.quickstep.GestureState; import com.android.quickstep.InputConsumer; import com.android.quickstep.OverviewCommandHelper; +import com.android.quickstep.OverviewCommandHelper.CommandType; import com.android.systemui.shared.system.InputMonitorCompat; /** @@ -200,7 +201,7 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { break; case MotionEvent.ACTION_BUTTON_RELEASE: if (isStashedTaskbarHovered) { - mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HOME); + mOverviewCommandHelper.addCommand(CommandType.HOME); } break; } From dd737b590c95942735e073a6731e467905a79d13 Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Wed, 28 Aug 2024 09:54:36 -0700 Subject: [PATCH 04/23] Refactor TaskbarIconSpecs and TaskbarSpecsEvaluator This cl includes - Because of IconNormalizer bug we are inflating iconsizes to match the specs. - specs changed from row x column to column X row. Test: Presubmit Bug: 356465292 Flag: com.android.launcher3.enable_taskbar_customization Change-Id: I4b4856da92053149267e66a187f744c9c00ea3d9 --- .../taskbar/customization/TaskbarIconSpecs.kt | 9 +++++---- .../taskbar/customization/TaskbarSpecsEvaluator.kt | 12 ++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt index 887eb01f3d..6be0828e0f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt @@ -19,10 +19,11 @@ package com.android.launcher3.taskbar.customization /** Taskbar Icon Specs */ object TaskbarIconSpecs { - val iconSize40dp = TaskbarIconSize(40) - val iconSize44dp = TaskbarIconSize(44) - val iconSize48dp = TaskbarIconSize(48) - val iconSize52dp = TaskbarIconSize(52) + // Mapping of visual icon size to icon specs value http://b/235886078 + val iconSize40dp = TaskbarIconSize(44) + val iconSize44dp = TaskbarIconSize(48) + val iconSize48dp = TaskbarIconSize(52) + val iconSize52dp = TaskbarIconSize(57) val transientTaskbarIconSizes = arrayOf(iconSize44dp, iconSize48dp, iconSize52dp) diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt index 761b47e179..f37b2c15bf 100644 --- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt @@ -25,14 +25,14 @@ class TaskbarSpecsEvaluator( numRows: Int = taskbarActivityContext.deviceProfile.inv.numRows, numColumns: Int = taskbarActivityContext.deviceProfile.inv.numColumns, ) { - var taskbarIconSize: TaskbarIconSize = getIconSizeByGrid(numRows, numColumns) + var taskbarIconSize: TaskbarIconSize = getIconSizeByGrid(numColumns, numRows) // TODO(b/341146605) : initialize it to taskbar container in later cl. private var taskbarContainer: List = emptyList() val taskbarIconPadding: Int = - if (TaskbarIconSpecs.minimumTaskbarIconTouchSize.size > taskbarIconSize.size) { - (TaskbarIconSpecs.minimumTaskbarIconTouchSize.size - taskbarIconSize.size) / 2 + if (TaskbarIconSpecs.iconSize52dp.size > taskbarIconSize.size) { + (TaskbarIconSpecs.iconSize52dp.size - taskbarIconSize.size) / 2 } else { 0 } @@ -44,10 +44,10 @@ class TaskbarSpecsEvaluator( TaskbarIconSpecs.defaultPersistentIconMargin } - fun getIconSizeByGrid(row: Int, column: Int): TaskbarIconSize { + fun getIconSizeByGrid(columns: Int, rows: Int): TaskbarIconSize { return if (taskbarFeatureEvaluator.isTransient) { TaskbarIconSpecs.transientTaskbarIconSizeByGridSize.getOrDefault( - TransientTaskbarIconSizeKey(row, column, taskbarFeatureEvaluator.isLandscape), + TransientTaskbarIconSizeKey(columns, rows, taskbarFeatureEvaluator.isLandscape), TaskbarIconSpecs.defaultTransientIconSize, ) } else { @@ -102,6 +102,6 @@ class TaskbarSpecsEvaluator( data class TaskbarIconSize(val size: Int) -data class TransientTaskbarIconSizeKey(val row: Int, val column: Int, val isLandscape: Boolean) +data class TransientTaskbarIconSizeKey(val columns: Int, val rows: Int, val isLandscape: Boolean) data class TaskbarIconMarginSize(val size: Int) From ef0b042f5c1a468baf0ed208796ad515e0480b8e Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Tue, 27 Aug 2024 11:00:45 -0700 Subject: [PATCH 05/23] Add waiting to test to wait for the Workspace to be in Edit mode I thought I wouldn't need to wait for the Edit mode to start but sometimes the test goes too fast and drops the item before we get to the edit mode and the drop doesn't have the correct coordinates or get's cancelled. Fix: 352569050 Test: IntegrationReorderWidgetsTest Flag: TEST_ONLY Change-Id: I744579c28267189f85b9ca297e1d170d6120bb64 --- src/com/android/launcher3/debug/TestEventsEmitterProduction.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt b/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt index e218b4df0c..52b454f2b7 100644 --- a/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt +++ b/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt @@ -27,6 +27,8 @@ enum class TestEvent(val event: String) { WORKSPACE_ON_DROP("WORKSPACE_ON_DROP"), RESIZE_FRAME_SHOWING("RESIZE_FRAME_SHOWING"), WORKSPACE_FINISH_LOADING("WORKSPACE_FINISH_LOADING"), + SPRING_LOADED_STATE_STARTED("SPRING_LOADED_STATE_STARTED"), + SPRING_LOADED_STATE_COMPLETED("SPRING_LOADED_STATE_COMPLETED"), } /** Interface to create TestEventEmitters. */ From 7f9985af96d2e54029c530aaaee1cd4289076008 Mon Sep 17 00:00:00 2001 From: Zak Cohen Date: Wed, 28 Aug 2024 16:27:16 -0700 Subject: [PATCH 06/23] FloatingWidgetView - avoid calling position callback when nothing changes. Previously, the position changed callback for the transition animation was called on every layout change, whether or not the position has changed. Now only call the callback and the layout changes if the targets don't match the current state. Bug: 327696338 Flag: EXEMPT bug fix Test: manual Change-Id: I3007a1c8d0a869866c66eb14d75f5b48d977f863 --- .../quickstep/views/FloatingWidgetView.java | 57 ++++++++++++++----- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java index 4dde635da7..bdca596a92 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java @@ -123,8 +123,8 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, @Override public void onGlobalLayout() { if (isUninitialized()) return; - positionViews(); - if (mOnTargetChangeRunnable != null) { + boolean positionsChanged = positionViews(); + if (mOnTargetChangeRunnable != null && positionsChanged) { mOnTargetChangeRunnable.run(); } } @@ -212,21 +212,43 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, onGlobalLayout(); } - /** Sets the layout parameters of the floating view and its background view child. */ - private void positionViews() { + /** + * Sets the layout parameters of the floating view and its background view child. + * @return true if any of the views positions change due to this call. + */ + private boolean positionViews() { + boolean positionsChanged = false; + LayoutParams layoutParams = (LayoutParams) getLayoutParams(); - layoutParams.setMargins(0, 0, 0, 0); - setLayoutParams(layoutParams); + + if (layoutParams.topMargin != 0 || layoutParams.bottomMargin != 0 + || layoutParams.rightMargin != 0 || layoutParams.leftMargin != 0) { + positionsChanged = true; + layoutParams.setMargins(0, 0, 0, 0); + setLayoutParams(layoutParams); + } // FloatingWidgetView layout is forced LTR - mBackgroundView.setTranslationX(mBackgroundPosition.left); - mBackgroundView.setTranslationY(mBackgroundPosition.top + mIconOffsetY); + float targetY = mBackgroundPosition.top + mIconOffsetY; + if (mBackgroundView.getTranslationX() != mBackgroundPosition.left + || mBackgroundView.getTranslationY() != targetY) { + positionsChanged = true; + mBackgroundView.setTranslationX(mBackgroundPosition.left); + mBackgroundView.setTranslationY(targetY); + } + LayoutParams backgroundParams = (LayoutParams) mBackgroundView.getLayoutParams(); - backgroundParams.leftMargin = 0; - backgroundParams.topMargin = 0; - backgroundParams.width = (int) mBackgroundPosition.width(); - backgroundParams.height = (int) mBackgroundPosition.height(); - mBackgroundView.setLayoutParams(backgroundParams); + if (backgroundParams.leftMargin != 0 || backgroundParams.topMargin != 0 + || backgroundParams.width != Math.round(mBackgroundPosition.width()) + || backgroundParams.height != Math.round(mBackgroundPosition.height())) { + positionsChanged = true; + + backgroundParams.leftMargin = 0; + backgroundParams.topMargin = 0; + backgroundParams.width = Math.round(mBackgroundPosition.width()); + backgroundParams.height = Math.round(mBackgroundPosition.height()); + mBackgroundView.setLayoutParams(backgroundParams); + } if (mForegroundOverlayView != null) { sTmpMatrix.reset(); @@ -237,8 +259,15 @@ public class FloatingWidgetView extends FrameLayout implements AnimatorListener, sTmpMatrix.postScale(foregroundScale, foregroundScale); sTmpMatrix.postTranslate(mBackgroundPosition.left, mBackgroundPosition.top + mIconOffsetY); - mForegroundOverlayView.setMatrix(sTmpMatrix); + + // We use the animation matrix here, because calling setMatrix on the GhostView + // actually sets the animation matrix, not the regular one. + if (!sTmpMatrix.equals(mForegroundOverlayView.getAnimationMatrix())) { + positionsChanged = true; + mForegroundOverlayView.setMatrix(sTmpMatrix); + } } + return positionsChanged; } private void finish(DragLayer dragLayer) { From 4baa30d151ed4184088f6047da360f34cbf9cf61 Mon Sep 17 00:00:00 2001 From: Mark Fasheh Date: Wed, 31 Jul 2024 22:39:45 +0000 Subject: [PATCH 07/23] Update launcher cold startup trace Bug: 341061764 Flag: EXEMPT bugfix Test: Boot phone, take trace Change-Id: I29b5be5525206ac83bc36596395a1f71b43715c6 --- src/com/android/launcher3/Launcher.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 3ca60997bc..bafb528dd1 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -435,6 +435,10 @@ public class Launcher extends StatefulActivity mIsColdStartupAfterReboot = sIsNewProcess && !LockedUserState.get(this).isUserUnlockedAtLauncherStartup(); if (mIsColdStartupAfterReboot) { + /* + * This trace is used to calculate the time from create to the point that icons are + * visible. + */ Trace.beginAsyncSection( COLD_STARTUP_TRACE_METHOD_NAME, COLD_STARTUP_TRACE_COOKIE); } @@ -2384,10 +2388,6 @@ public class Launcher extends StatefulActivity .logEnd(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION) .log() .reset(); - if (mIsColdStartupAfterReboot) { - Trace.endAsyncSection(COLD_STARTUP_TRACE_METHOD_NAME, - COLD_STARTUP_TRACE_COOKIE); - } }); } @@ -2396,6 +2396,10 @@ public class Launcher extends StatefulActivity RunnableList onCompleteSignal, int workspaceItemCount, boolean isBindSync) { mModelCallbacks.onInitialBindComplete(boundPages, pendingTasks, onCompleteSignal, workspaceItemCount, isBindSync); + if (mIsColdStartupAfterReboot) { + Trace.endAsyncSection(COLD_STARTUP_TRACE_METHOD_NAME, + COLD_STARTUP_TRACE_COOKIE); + } } /** From 728096835a0c1b3305128bf9aad95a05f052ebff Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Thu, 29 Aug 2024 09:26:32 -0700 Subject: [PATCH 08/23] Promote TaplAddWidgetTest#testResizeWidget Test: Presubmit Bug: 316910614 Flag: TEST_ONLY Change-Id: Ic4b927f523df7ac531fdbd88670adb12fc43df13 --- .../com/android/launcher3/ui/widget/TaplAddWidgetTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java index 9b184ae664..9c916fa638 100644 --- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java @@ -15,9 +15,6 @@ */ package com.android.launcher3.ui.widget; -import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; -import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; - import static org.junit.Assert.assertNotNull; import android.platform.test.annotations.PlatinumTest; @@ -33,7 +30,6 @@ import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.rule.ShellCommandRule; -import com.android.launcher3.util.rule.TestStabilityRule.Stability; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import org.junit.Assume; @@ -102,7 +98,6 @@ public class TaplAddWidgetTest extends AbstractLauncherUiTest { /** * Test dragging a widget to the workspace and resize it. */ - @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/316910614 @PlatinumTest(focusArea = "launcher") @Test public void testResizeWidget() throws Throwable { From 6060db4970a667472132e578a6df13975d678b9c Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Wed, 31 Jul 2024 16:31:44 -0700 Subject: [PATCH 09/23] Use TaskbarAllAppsButtonContainer and TaskbarDividerContainer in TaskbarView This cl includes - Usage of TaskbarAllAppsButtonContainer and TaskbarDividerContainer in TaskbarView. This can be seen as a refactor. - Animation refactor related to all apps button container refactor Test: Presubmit Bug: 356465292 Flag: com.android.launcher3.enable_taskbar_customization Change-Id: I5b7c4f56aacac18fc6872f77b15e2e9ae896799e --- .../taskbar/TaskbarActivityContext.java | 18 +- .../taskbar/TaskbarDividerPopupView.kt | 5 +- .../launcher3/taskbar/TaskbarView.java | 166 ++++-------------- .../taskbar/TaskbarViewCallbacks.java | 9 +- .../taskbar/TaskbarViewController.java | 19 +- .../TaskbarAllAppsButtonContainer.kt | 97 ++++++++-- .../customization/TaskbarDividerContainer.kt | 27 +-- 7 files changed, 148 insertions(+), 193 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 487bc54278..6058395692 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -29,7 +29,6 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ON_BOARD_POPUP; import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY; import static com.android.launcher3.Flags.enableCursorHoverStates; -import static com.android.launcher3.Flags.enableTaskbarCustomization; import static com.android.launcher3.Utilities.calculateTextHeight; import static com.android.launcher3.Utilities.isRunningInTestHarness; import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; @@ -230,15 +229,12 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mNavigationBarPanelContext = navigationBarPanelContext; applyDeviceProfile(launcherDp); final Resources resources = getResources(); - - if (enableTaskbarCustomization()) { - mTaskbarFeatureEvaluator = TaskbarFeatureEvaluator.getInstance(this); - mTaskbarSpecsEvaluator = new TaskbarSpecsEvaluator( - this, - mTaskbarFeatureEvaluator, - mDeviceProfile.inv.numRows, - mDeviceProfile.inv.numColumns); - } + mTaskbarFeatureEvaluator = TaskbarFeatureEvaluator.getInstance(this); + mTaskbarSpecsEvaluator = new TaskbarSpecsEvaluator( + this, + mTaskbarFeatureEvaluator, + mDeviceProfile.inv.numRows, + mDeviceProfile.inv.numColumns); mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false); mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", @@ -1760,12 +1756,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mControllers.taskbarStashController.isInStashedLauncherState(); } - @Nullable public TaskbarFeatureEvaluator getTaskbarFeatureEvaluator() { return mTaskbarFeatureEvaluator; } - @Nullable public TaskbarSpecsEvaluator getTaskbarSpecsEvaluator() { return mTaskbarSpecsEvaluator; } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt index a635537907..b5a33147ce 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt @@ -169,8 +169,11 @@ constructor( override fun addArrow() { super.addArrow() + val location = IntArray(2) + popupContainer.getLocationInDragLayer(dividerView, location) + val dividerViewX = location[0].toFloat() // Change arrow location to the middle of popup. - mArrow.x = (dividerView.x + dividerView.width / 2) - (mArrowWidth / 2) + mArrow.x = (dividerViewX + dividerView.width / 2) - (mArrowWidth / 2) } override fun updateArrowColor() { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index 54a7fdc613..8b8b4da27a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -22,10 +22,8 @@ import static com.android.launcher3.Flags.enableCursorHoverStates; import static com.android.launcher3.Flags.enableRecentsInTaskbar; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER; -import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR; import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning; import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.content.Context; import android.content.res.Resources; @@ -39,12 +37,9 @@ import android.view.InputDevice; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; -import androidx.annotation.DimenRes; -import androidx.annotation.DrawableRes; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -62,13 +57,12 @@ import com.android.launcher3.model.data.CollectionInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.taskbar.customization.TaskbarAllAppsButtonContainer; +import com.android.launcher3.taskbar.customization.TaskbarDividerContainer; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.LauncherBindableItemsContainer; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; -import com.android.launcher3.views.IconButtonView; -import com.android.quickstep.DeviceConfigWrapper; -import com.android.quickstep.util.AssistStateManager; import com.android.quickstep.util.DesktopTask; import com.android.quickstep.util.GroupTask; import com.android.systemui.shared.recents.model.Task; @@ -100,16 +94,13 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar private View.OnLongClickListener mIconLongClickListener; // Only non-null when the corresponding Folder is open. - private @Nullable FolderIcon mLeaveBehindFolderIcon; + @Nullable private FolderIcon mLeaveBehindFolderIcon; // Only non-null when device supports having an All Apps button. - private @Nullable IconButtonView mAllAppsButton; - private Runnable mAllAppsTouchRunnable; - private long mAllAppsButtonTouchDelayMs; - private boolean mAllAppsTouchTriggered; + @Nullable private final TaskbarAllAppsButtonContainer mAllAppsButtonContainer; - // Only non-null when device supports having an All Apps button, or Recent Apps. - private @Nullable IconButtonView mTaskbarDivider; + // Only non-null when device supports having an All Apps button. + @Nullable private TaskbarDividerContainer mTaskbarDividerContainer; /** * Whether the divider is between Hotseat icons and Recents, @@ -173,55 +164,14 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar // Needed to draw folder leave-behind when opening one. setWillNotDraw(false); - mAllAppsButton = (IconButtonView) LayoutInflater.from(context) - .inflate(R.layout.taskbar_all_apps_button, this, false); - mAllAppsButton.setIconDrawable(resources.getDrawable( - getAllAppsButton(isTransientTaskbar))); - mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding); - mAllAppsButton.setForegroundTint( - mActivityContext.getColor(R.color.all_apps_button_color)); + mAllAppsButtonContainer = new TaskbarAllAppsButtonContainer(context); if (enableTaskbarPinning() || enableRecentsInTaskbar()) { - mTaskbarDivider = (IconButtonView) LayoutInflater.from(context).inflate( - R.layout.taskbar_divider, - this, false); - mTaskbarDivider.setIconDrawable( - resources.getDrawable(R.drawable.taskbar_divider_button)); - mTaskbarDivider.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding); + mTaskbarDividerContainer = new TaskbarDividerContainer(context); } // TODO: Disable touch events on QSB otherwise it can crash. mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false); - - // Default long press (touch) delay = 400ms - mAllAppsButtonTouchDelayMs = ViewConfiguration.getLongPressTimeout(); - } - - @DrawableRes - private int getAllAppsButton(boolean isTransientTaskbar) { - boolean shouldSelectTransientIcon = - (isTransientTaskbar || enableTaskbarPinning()) - && !mActivityContext.isThreeButtonNav(); - if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { - return shouldSelectTransientIcon - ? R.drawable.ic_transient_taskbar_all_apps_search_button - : R.drawable.ic_taskbar_all_apps_search_button; - } else { - return shouldSelectTransientIcon - ? R.drawable.ic_transient_taskbar_all_apps_button - : R.drawable.ic_taskbar_all_apps_button; - } - } - - @DimenRes - public int getAllAppsButtonTranslationXOffset(boolean isTransientTaskbar) { - if (isTransientTaskbar) { - return R.dimen.transient_taskbar_all_apps_button_translation_x_offset; - } else { - return ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get() - ? R.dimen.taskbar_all_apps_search_button_translation_x_offset - : R.dimen.taskbar_all_apps_button_translation_x_offset; - } } @Override @@ -306,27 +256,11 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar mIconClickListener = mControllerCallbacks.getIconOnClickListener(); mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener(); - if (mAllAppsButton != null) { - mAllAppsButton.setOnClickListener(this::onAllAppsButtonClick); - mAllAppsButton.setOnLongClickListener(this::onAllAppsButtonLongClick); - mAllAppsButton.setOnTouchListener(this::onAllAppsButtonTouch); - mAllAppsButton.setHapticFeedbackEnabled( - mControllerCallbacks.isAllAppsButtonHapticFeedbackEnabled()); - mAllAppsTouchRunnable = () -> { - mControllerCallbacks.triggerAllAppsButtonLongClick(); - mAllAppsTouchTriggered = true; - }; - AssistStateManager assistStateManager = AssistStateManager.INSTANCE.get(mContext); - if (DeviceConfigWrapper.get().getCustomLpaaThresholds() - && assistStateManager.getLPNHDurationMillis().isPresent()) { - mAllAppsButtonTouchDelayMs = assistStateManager.getLPNHDurationMillis().get(); - } + if (mAllAppsButtonContainer != null) { + mAllAppsButtonContainer.setUpCallbacks(callbacks); } - if (mTaskbarDivider != null && !mActivityContext.isThreeButtonNav()) { - mTaskbarDivider.setOnLongClickListener( - mControllerCallbacks.getTaskbarDividerLongClickListener()); - mTaskbarDivider.setOnTouchListener( - mControllerCallbacks.getTaskbarDividerRightClickListener()); + if (mTaskbarDividerContainer != null && callbacks.supportsDividerLongPress()) { + mTaskbarDividerContainer.setUpCallbacks(callbacks); } } @@ -348,11 +282,11 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar int numViewsAnimated = 0; mAddedDividerForRecents = false; - if (mAllAppsButton != null) { - removeView(mAllAppsButton); + if (mAllAppsButtonContainer != null) { + removeView(mAllAppsButtonContainer); - if (mTaskbarDivider != null) { - removeView(mTaskbarDivider); + if (mTaskbarDividerContainer != null) { + removeView(mTaskbarDividerContainer); } } removeView(mQsb); @@ -439,8 +373,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar nextViewIndex++; } - if (mTaskbarDivider != null && !recentTasks.isEmpty()) { - addView(mTaskbarDivider, nextViewIndex++); + if (mTaskbarDividerContainer != null && !recentTasks.isEmpty()) { + addView(mTaskbarDividerContainer, nextViewIndex++); mAddedDividerForRecents = true; } @@ -504,12 +438,14 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar removeAndRecycle(getChildAt(nextViewIndex)); } - if (mAllAppsButton != null) { - addView(mAllAppsButton, mIsRtl ? hotseatItemInfos.length : 0); + if (mAllAppsButtonContainer != null) { + addView(mAllAppsButtonContainer, mIsRtl ? hotseatItemInfos.length : 0); // If there are no recent tasks, add divider after All Apps (unless it's the only view). - if (!mAddedDividerForRecents && mTaskbarDivider != null && getChildCount() > 1) { - addView(mTaskbarDivider, mIsRtl ? (getChildCount() - 1) : 1); + if (!mAddedDividerForRecents + && mTaskbarDividerContainer != null + && getChildCount() > 1) { + addView(mTaskbarDividerContainer, mIsRtl ? (getChildCount() - 1) : 1); } } if (mActivityContext.getDeviceProfile().isQsbInline) { @@ -637,7 +573,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar int qsbTop = (bottom - top - deviceProfile.hotseatQsbHeight) / 2; int qsbBottom = qsbTop + deviceProfile.hotseatQsbHeight; child.layout(qsbStart, qsbTop, qsbEnd, qsbBottom); - } else if (child == mTaskbarDivider) { + } else if (child == mTaskbarDividerContainer) { iconEnd += mItemMarginLeftRight; int iconStart = iconEnd - mIconTouchSize; child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom); @@ -727,16 +663,16 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar * Returns the all apps button in the taskbar. */ @Nullable - public View getAllAppsButtonView() { - return mAllAppsButton; + public TaskbarAllAppsButtonContainer getAllAppsButtonContainer() { + return mAllAppsButtonContainer; } /** * Returns the taskbar divider in the taskbar. */ @Nullable - public View getTaskbarDividerView() { - return mTaskbarDivider; + public TaskbarDividerContainer getTaskbarDividerViewContainer() { + return mTaskbarDividerContainer; } /** @@ -832,48 +768,6 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } } } - return mAllAppsButton; - } - - private boolean onAllAppsButtonTouch(View view, MotionEvent ev) { - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - mAllAppsTouchTriggered = false; - MAIN_EXECUTOR.getHandler().postDelayed( - mAllAppsTouchRunnable, mAllAppsButtonTouchDelayMs); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - cancelAllAppsButtonTouch(); - } - return false; - } - - private void cancelAllAppsButtonTouch() { - MAIN_EXECUTOR.getHandler().removeCallbacks(mAllAppsTouchRunnable); - // ACTION_UP is first triggered, then click listener / long-click listener is triggered on - // the next frame, so we need to post twice and delay the reset. - if (mAllAppsButton != null) { - mAllAppsButton.post(() -> { - mAllAppsButton.post(() -> { - mAllAppsTouchTriggered = false; - }); - }); - } - } - - private void onAllAppsButtonClick(View view) { - if (!mAllAppsTouchTriggered) { - mControllerCallbacks.triggerAllAppsButtonClick(view); - } - } - - // Handle long click from Switch Access and Voice Access - private boolean onAllAppsButtonLongClick(View view) { - if (!MAIN_EXECUTOR.getHandler().hasCallbacks(mAllAppsTouchRunnable) - && !mAllAppsTouchTriggered) { - mControllerCallbacks.triggerAllAppsButtonLongClick(); - } - return true; + return mAllAppsButtonContainer; } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java index e6cac2f9db..296d3792c7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java @@ -51,7 +51,7 @@ public class TaskbarViewCallbacks { } /** Trigger All Apps button click action. */ - protected void triggerAllAppsButtonClick(View v) { + public void triggerAllAppsButtonClick(View v) { InteractionJankMonitorWrapper.begin(v, Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS, /* tag= */ "TASKBAR_BUTTON"); mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP); @@ -59,7 +59,7 @@ public class TaskbarViewCallbacks { } /** Trigger All Apps button long click action. */ - protected void triggerAllAppsButtonLongClick() { + public void triggerAllAppsButtonLongClick() { mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS); } @@ -74,6 +74,11 @@ public class TaskbarViewCallbacks { }; } + /** Check to see if we support long press on taskbar divider */ + public boolean supportsDividerLongPress() { + return !mActivity.isThreeButtonNav(); + } + public View.OnTouchListener getTaskbarDividerRightClickListener() { return (v, event) -> { if (event.isFromSource(InputDevice.SOURCE_MOUSE) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index aef21aa6d1..85cc1420d9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -72,7 +72,6 @@ import com.android.launcher3.util.LauncherBindableItemsContainer; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiTranslateDelegate; import com.android.launcher3.util.MultiValueAlpha; -import com.android.launcher3.views.IconButtonView; import com.android.quickstep.util.GroupTask; import com.android.systemui.shared.recents.model.Task; @@ -299,7 +298,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar @Nullable public View getAllAppsButtonView() { - return mTaskbarView.getAllAppsButtonView(); + return mTaskbarView.getAllAppsButtonContainer(); } public AnimatedFloat getTaskbarIconScaleForStash() { @@ -366,9 +365,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar View[] iconViews = mTaskbarView.getIconViews(); float scale = mTaskbarIconTranslationXForPinning.value; float transientTaskbarAllAppsOffset = mActivity.getResources().getDimension( - mTaskbarView.getAllAppsButtonTranslationXOffset(true)); + mTaskbarView.getAllAppsButtonContainer().getAllAppsButtonTranslationXOffset(true)); float persistentTaskbarAllAppsOffset = mActivity.getResources().getDimension( - mTaskbarView.getAllAppsButtonTranslationXOffset(false)); + mTaskbarView.getAllAppsButtonContainer().getAllAppsButtonTranslationXOffset(false)); float allAppIconTranslateRange = mapRange(scale, transientTaskbarAllAppsOffset, persistentTaskbarAllAppsOffset); @@ -383,7 +382,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar } if (mActivity.isThreeButtonNav()) { - ((IconButtonView) mTaskbarView.getAllAppsButtonView()) + mTaskbarView.getAllAppsButtonContainer() .setTranslationXForTaskbarAllAppsIcon(allAppIconTranslateRange); return; } @@ -408,8 +407,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar -finalMarginScale * (iconIndex - halfIconCount)); } - if (iconView.equals(mTaskbarView.getAllAppsButtonView())) { - ((IconButtonView) iconView).setTranslationXForTaskbarAllAppsIcon( + if (iconView.equals(mTaskbarView.getAllAppsButtonContainer())) { + mTaskbarView.getAllAppsButtonContainer().setTranslationXForTaskbarAllAppsIcon( allAppIconTranslateRange); } } @@ -537,7 +536,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar } public View getTaskbarDividerView() { - return mTaskbarView.getTaskbarDividerView(); + return mTaskbarView.getTaskbarDividerViewContainer(); } /** @@ -753,8 +752,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar int firstRecentTaskIndex = -1; for (int i = 0; i < mTaskbarView.getChildCount(); i++) { View child = mTaskbarView.getChildAt(i); - boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonView(); - boolean isTaskbarDividerView = child == mTaskbarView.getTaskbarDividerView(); + boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonContainer(); + boolean isTaskbarDividerView = child == mTaskbarView.getTaskbarDividerViewContainer(); boolean isRecentTask = child.getTag() instanceof GroupTask; // TODO(b/343522351): show recents on the home screen. final boolean isRecentsInHotseat = false; diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt index 7d2d36d756..726800c2da 100644 --- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt @@ -18,18 +18,27 @@ package com.android.launcher3.taskbar.customization import android.annotation.SuppressLint import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Color.TRANSPARENT import android.util.AttributeSet import android.view.LayoutInflater -import android.widget.LinearLayout +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration import androidx.annotation.DimenRes import androidx.annotation.DrawableRes import androidx.core.view.setPadding import com.android.launcher3.R import com.android.launcher3.Utilities.dpToPx -import com.android.launcher3.config.FeatureFlags +import com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR +import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning import com.android.launcher3.taskbar.TaskbarActivityContext +import com.android.launcher3.taskbar.TaskbarViewCallbacks +import com.android.launcher3.util.Executors.MAIN_EXECUTOR import com.android.launcher3.views.ActivityContext import com.android.launcher3.views.IconButtonView +import com.android.quickstep.DeviceConfigWrapper +import com.android.quickstep.util.AssistStateManager /** Taskbar all apps button container for customizable taskbar. */ class TaskbarAllAppsButtonContainer @@ -38,19 +47,21 @@ constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, -) : LinearLayout(context, attrs), TaskbarContainer { +) : IconButtonView(context, attrs), TaskbarContainer { - private val allAppsButton: IconButtonView = - LayoutInflater.from(context).inflate(R.layout.taskbar_all_apps_button, this, false) - as IconButtonView private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context) + private var allAppsTouchTriggered = false + private var allAppsTouchRunnable: Runnable? = null + private var allAppsButtonTouchDelayMs: Long = ViewConfiguration.getLongPressTimeout().toLong() + private lateinit var taskbarViewCallbacks: TaskbarViewCallbacks override val spaceNeeded: Int get() { - return dpToPx(activityContext.taskbarSpecsEvaluator!!.taskbarIconSize.size.toFloat()) + return dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat()) } init { + LayoutInflater.from(context).inflate(R.layout.taskbar_all_apps_button, null, false) setUpIcon() } @@ -58,24 +69,39 @@ constructor( private fun setUpIcon() { val drawable = resources.getDrawable( - getAllAppsButton(activityContext.taskbarFeatureEvaluator!!.isTransient) + getAllAppsButton(activityContext.taskbarFeatureEvaluator.isTransient) ) - val padding = activityContext.taskbarSpecsEvaluator!!.taskbarIconPadding + backgroundTintList = ColorStateList.valueOf(TRANSPARENT) + setIconDrawable(drawable) + setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat())) + setForegroundTint(activityContext.getColor(R.color.all_apps_button_color)) + } - allAppsButton.setIconDrawable(drawable) - allAppsButton.setPadding(padding) - allAppsButton.setForegroundTint(activityContext.getColor(R.color.all_apps_button_color)) - - // TODO(b/356465292) : add click listeners in future cl - addView(allAppsButton) + @SuppressLint("ClickableViewAccessibility") + fun setUpCallbacks(callbacks: TaskbarViewCallbacks) { + taskbarViewCallbacks = callbacks + setOnClickListener(this::onAllAppsButtonClick) + setOnLongClickListener(this::onAllAppsButtonLongClick) + setOnTouchListener(this::onAllAppsButtonTouch) + isHapticFeedbackEnabled = taskbarViewCallbacks.isAllAppsButtonHapticFeedbackEnabled() + allAppsTouchRunnable = Runnable { + taskbarViewCallbacks.triggerAllAppsButtonLongClick() + allAppsTouchTriggered = true + } + val assistStateManager = AssistStateManager.INSTANCE[mContext] + if ( + DeviceConfigWrapper.get().customLpaaThresholds && + assistStateManager.lpnhDurationMillis.isPresent + ) { + allAppsButtonTouchDelayMs = assistStateManager.lpnhDurationMillis.get() + } } @DrawableRes private fun getAllAppsButton(isTransientTaskbar: Boolean): Int { val shouldSelectTransientIcon = - isTransientTaskbar || - (FeatureFlags.enableTaskbarPinning() && !activityContext.isThreeButtonNav) - return if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { + isTransientTaskbar || (enableTaskbarPinning() && !activityContext.isThreeButtonNav) + return if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { if (shouldSelectTransientIcon) R.drawable.ic_transient_taskbar_all_apps_search_button else R.drawable.ic_taskbar_all_apps_search_button } else { @@ -88,10 +114,43 @@ constructor( fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int { return if (isTransientTaskbar) { R.dimen.transient_taskbar_all_apps_button_translation_x_offset - } else if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { + } else if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { R.dimen.taskbar_all_apps_search_button_translation_x_offset } else { R.dimen.taskbar_all_apps_button_translation_x_offset } } + + private fun onAllAppsButtonTouch(view: View, ev: MotionEvent): Boolean { + when (ev.action) { + MotionEvent.ACTION_DOWN -> { + allAppsTouchTriggered = false + MAIN_EXECUTOR.handler.postDelayed(allAppsTouchRunnable!!, allAppsButtonTouchDelayMs) + } + MotionEvent.ACTION_UP, + MotionEvent.ACTION_CANCEL -> cancelAllAppsButtonTouch() + } + return false + } + + private fun cancelAllAppsButtonTouch() { + MAIN_EXECUTOR.handler.removeCallbacks(allAppsTouchRunnable!!) + // ACTION_UP is first triggered, then click listener / long-click listener is triggered on + // the next frame, so we need to post twice and delay the reset. + this.post { this.post { allAppsTouchTriggered = false } } + } + + private fun onAllAppsButtonClick(view: View) { + if (!allAppsTouchTriggered) { + taskbarViewCallbacks.triggerAllAppsButtonClick(view) + } + } + + // Handle long click from Switch Access and Voice Access + private fun onAllAppsButtonLongClick(view: View): Boolean { + if (!MAIN_EXECUTOR.handler.hasCallbacks(allAppsTouchRunnable!!) && !allAppsTouchTriggered) { + taskbarViewCallbacks.triggerAllAppsButtonLongClick() + } + return true + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt index 26e71f7f08..1fb835ab33 100644 --- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt @@ -18,13 +18,15 @@ package com.android.launcher3.taskbar.customization import android.annotation.SuppressLint import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Color.TRANSPARENT import android.util.AttributeSet import android.view.LayoutInflater -import android.widget.LinearLayout import androidx.core.view.setPadding import com.android.launcher3.R import com.android.launcher3.Utilities.dpToPx import com.android.launcher3.taskbar.TaskbarActivityContext +import com.android.launcher3.taskbar.TaskbarViewCallbacks import com.android.launcher3.views.ActivityContext import com.android.launcher3.views.IconButtonView @@ -35,31 +37,30 @@ constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, -) : LinearLayout(context, attrs), TaskbarContainer { - - private val taskbarDivider: IconButtonView = - LayoutInflater.from(context).inflate(R.layout.taskbar_divider, this, false) - as IconButtonView +) : IconButtonView(context, attrs), TaskbarContainer { private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context) override val spaceNeeded: Int get() { - return dpToPx(activityContext.taskbarSpecsEvaluator!!.taskbarIconSize.size.toFloat()) + return dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat()) } init { + LayoutInflater.from(context).inflate(R.layout.taskbar_divider, null, false) setUpIcon() } @SuppressLint("UseCompatLoadingForDrawables") fun setUpIcon() { + backgroundTintList = ColorStateList.valueOf(TRANSPARENT) val drawable = resources.getDrawable(R.drawable.taskbar_divider_button) - val padding = activityContext.taskbarSpecsEvaluator!!.taskbarIconPadding + setIconDrawable(drawable) + setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat())) + } - taskbarDivider.setIconDrawable(drawable) - taskbarDivider.setPadding(padding) - - // TODO(b/356465292):: add click listeners in future cl - addView(taskbarDivider) + @SuppressLint("ClickableViewAccessibility") + fun setUpCallbacks(callbacks: TaskbarViewCallbacks) { + setOnLongClickListener(callbacks.taskbarDividerLongClickListener) + setOnTouchListener(callbacks.taskbarDividerRightClickListener) } } From 8104ad3321ae234657fc8b3bdcef6e579e42478a Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Tue, 27 Aug 2024 09:20:07 -0700 Subject: [PATCH 10/23] Follow RegionSamplingHelper being moved into shell - update imports for usages in launcher Flag: EXEMPT updating imports & bp file for class moving to sysui Test: make; gradle build for launcher; treehugger Bug: 353160491 Change-Id: I6d9d004e8ab6f67f494927424d2651c5b848e967 --- .../android/launcher3/taskbar/StashedHandleViewController.java | 2 +- .../taskbar/bubbles/BubbleStashedHandleViewController.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java index 475b51646b..ec710c5229 100644 --- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java @@ -41,8 +41,8 @@ import com.android.launcher3.util.Executors; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiValueAlpha; import com.android.quickstep.NavHandle; -import com.android.systemui.shared.navigationbar.RegionSamplingHelper; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; +import com.android.wm.shell.shared.handles.RegionSamplingHelper; import java.io.PrintWriter; diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java index 6bfe8f40ae..00cc269e9f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java @@ -37,9 +37,9 @@ import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController; import com.android.launcher3.util.Executors; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiValueAlpha; -import com.android.systemui.shared.navigationbar.RegionSamplingHelper; import com.android.wm.shell.common.bubbles.BubbleBarLocation; import com.android.wm.shell.shared.animation.PhysicsAnimator; +import com.android.wm.shell.shared.handles.RegionSamplingHelper; /** * Handles properties/data collection, then passes the results to our stashed handle View to render. From 4290d3cab1cbc990840c1a75d5c7b539601e71cf Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Thu, 29 Aug 2024 11:33:11 -0700 Subject: [PATCH 11/23] Only handle taps on bubble bar when it was collapsed/stashed on down When bubbles are expanded and you tap on the currently opened bubble, the stack collapses. In the input consumer code, we also get that tap & we handle it because at the "up" event at that time, we're collapsed (or stashed in some cases). To fix this, only handle the "up" event for the bar being collapsed / stashed, if it was in that state when the gesture started (i.e. on down) Flag: com.android.wm.shell.enable_bubble_bar Bug: 362165243 Test: manual - expand bubble bar in home / overview - tap on the currently expanded bubble => observe the animation is smooth - expand bubble bar in app - tap on currently expanded bubble => observe the animation is smooth Change-Id: I1861af15c88bc0cbf2514faac7464de2f85aad1e --- .../inputconsumers/BubbleBarInputConsumer.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java index dbe20685d5..92031c5429 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java @@ -45,6 +45,7 @@ public class BubbleBarInputConsumer implements InputConsumer { private boolean mSwipeUpOnBubbleHandle; private boolean mPassedTouchSlop; + private boolean mStashedOrCollapsedOnDown; private final int mTouchSlop; private final PointF mDownPos = new PointF(); @@ -69,13 +70,13 @@ public class BubbleBarInputConsumer implements InputConsumer { @Override public void onMotionEvent(MotionEvent ev) { - final boolean isStashed = mBubbleStashController.isStashed(); final int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = ev.getPointerId(0); mDownPos.set(ev.getX(), ev.getY()); mLastPos.set(mDownPos); + mStashedOrCollapsedOnDown = mBubbleStashController.isStashed() || isCollapsed(); break; case MotionEvent.ACTION_MOVE: int pointerIndex = ev.findPointerIndex(mActivePointerId); @@ -89,7 +90,7 @@ public class BubbleBarInputConsumer implements InputConsumer { if (!mPassedTouchSlop) { mPassedTouchSlop = Math.abs(dY) > mTouchSlop || Math.abs(dX) > mTouchSlop; } - if ((isCollapsed() || isStashed) && !mSwipeUpOnBubbleHandle && mPassedTouchSlop) { + if (mStashedOrCollapsedOnDown && !mSwipeUpOnBubbleHandle && mPassedTouchSlop) { boolean verticalGesture = Math.abs(dY) > Math.abs(dX); if (verticalGesture && !mBubbleDragController.isDragging()) { mSwipeUpOnBubbleHandle = true; @@ -102,11 +103,10 @@ public class BubbleBarInputConsumer implements InputConsumer { break; case MotionEvent.ACTION_UP: boolean isWithinTapTime = ev.getEventTime() - ev.getDownTime() <= mTimeForTap; - if (isWithinTapTime && !mSwipeUpOnBubbleHandle && !mPassedTouchSlop) { + if (isWithinTapTime && !mSwipeUpOnBubbleHandle && !mPassedTouchSlop + && mStashedOrCollapsedOnDown) { // Taps on the handle / collapsed state should open the bar - if (isStashed || isCollapsed()) { - mBubbleStashController.showBubbleBar(/* expandBubbles= */ true); - } + mBubbleStashController.showBubbleBar(/* expandBubbles= */ true); } break; } From 1f25f6405c29979995f8c28f5eb5fff70aa1d6f4 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Wed, 21 Aug 2024 16:31:16 -0700 Subject: [PATCH 12/23] [Predictive Back] Clean up flags Fix: 270614790 Flag: NONE - clean up flag Test: presubmit Change-Id: I956ce6067dca5d2517b45046ae8a392df1023e49 --- .../launcher3/WidgetPickerActivity.java | 6 ----- .../allapps/TaskbarAllAppsSlideInView.java | 27 ++++++++----------- .../uioverrides/QuickstepLauncher.java | 12 ++------- 3 files changed, 13 insertions(+), 32 deletions(-) diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java index e925af6cda..50e8e5e077 100644 --- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java +++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java @@ -20,7 +20,6 @@ import static android.content.ClipDescription.MIMETYPE_TEXT_INTENT; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; -import static com.android.launcher3.Flags.enablePredictiveBackGesture; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; @@ -170,11 +169,6 @@ public class WidgetPickerActivity extends BaseActivity { @Override protected void registerBackDispatcher() { - if (!enablePredictiveBackGesture()) { - super.registerBackDispatcher(); - return; - } - getOnBackInvokedDispatcher().registerOnBackInvokedCallback( OnBackInvokedDispatcher.PRIORITY_DEFAULT, new BackAnimationCallback()); diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java index 90ac87202b..f5ac66f776 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java @@ -16,7 +16,6 @@ package com.android.launcher3.taskbar.allapps; import static com.android.app.animation.Interpolators.EMPHASIZED; -import static com.android.launcher3.Flags.enablePredictiveBackGesture; import static com.android.launcher3.touch.AllAppsSwipeController.ALL_APPS_FADE_MANUAL; import static com.android.launcher3.touch.AllAppsSwipeController.SCRIM_FADE_MANUAL; @@ -193,14 +192,12 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView Date: Thu, 29 Aug 2024 14:20:16 -0700 Subject: [PATCH 13/23] Add some info about the overflow in bubble bar dump Flag: com.android.wm.shell.enable_bubble_bar Test: manual - add some bubbles to bubble bar, take a BR and look at the dump Bug: 358304168 Change-Id: I91e50591df166e928d06ba9a29f69f923218de71 --- .../com/android/launcher3/taskbar/bubbles/BubbleBarView.java | 5 +++-- .../launcher3/taskbar/bubbles/BubbleBarViewController.java | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index c458936c80..71867fe54b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -1473,8 +1473,9 @@ public class BubbleBarView extends FrameLayout { pw.println("BubbleBarView state:"); pw.println(" visibility: " + getVisibility()); pw.println(" alpha: " + getAlpha()); - pw.println(" translation Y: " + getTranslationY()); - pw.println(" bubbles in bar (childCount = " + getChildCount() + ")"); + pw.println(" translationY: " + getTranslationY()); + pw.println(" childCount: " + getChildCount()); + pw.println(" hasOverflow: " + hasOverflow()); for (BubbleView bubbleView: getBubbles()) { BubbleBarItem bubble = bubbleView.getBubble(); String key = bubble == null ? "null" : bubble.getKey(); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 5c1a5467d8..65f857eb16 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -793,6 +793,7 @@ public class BubbleBarViewController { pw.println(" mShouldShowEducation: " + mShouldShowEducation); pw.println(" mBubbleBarTranslationY.value: " + mBubbleBarTranslationY.value); pw.println(" mBubbleBarSwipeUpTranslationY: " + mBubbleBarSwipeUpTranslationY); + pw.println(" mOverflowAdded: " + mOverflowAdded); if (mBarView != null) { mBarView.dump(pw); } else { From d159e61166ac0da6e2ac7f063b5313d42eb5256c Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 26 Aug 2024 21:03:03 +0000 Subject: [PATCH 14/23] 5/ Remove unused recents code - The shell transit flag is already hardcoded to true in Launcher so we can remove the non-shell transit paths - The minimized split was from legacy splitscreen where launcher would show in a minimized state, which is no longer the case Flag: EXEMPT bugfix Bug: 359608026 Test: atest NexusLauncherTests Change-Id: Idea46fc065485233b9e508dae3ebc4ee87c29ba6 Signed-off-by: Winson Chung --- .../launcher3/QuickstepTransitionManager.java | 9 ++--- .../taskbar/LauncherTaskbarUIController.java | 5 +-- .../uioverrides/QuickstepLauncher.java | 8 +---- .../states/BackgroundAppState.java | 4 +-- .../android/quickstep/AbsSwipeUpHandler.java | 4 +-- .../quickstep/BaseContainerInterface.java | 2 -- .../quickstep/FallbackActivityInterface.java | 6 ---- .../quickstep/LauncherActivityInterface.java | 5 --- .../quickstep/LauncherSwipeHandlerV2.java | 3 -- .../quickstep/RecentsAnimationCallbacks.java | 8 ++--- .../quickstep/RecentsAnimationController.java | 34 +++---------------- .../quickstep/TaskAnimationManager.java | 10 +++--- .../DeviceLockedInputConsumer.java | 17 ++++------ .../util/SplitSelectStateController.java | 3 +- .../SplitWithKeyboardShortcutController.java | 3 +- .../quickstep/util/TaskViewSimulator.java | 22 ++++-------- .../com/android/quickstep/views/TaskView.kt | 14 ++++---- .../quickstep/TaskAnimationManagerTest.java | 2 -- src/com/android/launcher3/BaseActivity.java | 6 ---- 19 files changed, 38 insertions(+), 127 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 4a1035fd56..e51c956016 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -65,7 +65,6 @@ import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VAL import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import static com.android.launcher3.views.FloatingIconView.getFloatingIconView; -import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; import static com.android.quickstep.util.AnimUtils.clampToDuration; import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback; @@ -1228,9 +1227,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * Registers remote animations used when closing apps to home screen. */ public void registerRemoteTransitions() { - if (ENABLE_SHELL_TRANSITIONS) { - SystemUiProxy.INSTANCE.get(mLauncher).shareTransactionQueue(); - } + SystemUiProxy.INSTANCE.get(mLauncher).shareTransactionQueue(); if (SEPARATE_RECENTS_ACTIVITY.get()) { return; } @@ -1294,9 +1291,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } protected void unregisterRemoteTransitions() { - if (ENABLE_SHELL_TRANSITIONS) { - SystemUiProxy.INSTANCE.get(mLauncher).unshareTransactionQueue(); - } + SystemUiProxy.INSTANCE.get(mLauncher).unshareTransactionQueue(); if (SEPARATE_RECENTS_ACTIVITY.get()) { return; } diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index b63b9ddab2..02ece8e2d5 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -19,7 +19,6 @@ import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE; import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES; import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE; -import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY; import android.animation.Animator; @@ -232,9 +231,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { LauncherState state = mLauncher.getStateManager().getState(); boolean nonInteractiveState = state.hasFlag(FLAG_NON_INTERACTIVE) && !state.isTaskbarAlignedWithHotseat(mLauncher); - if ((ENABLE_SHELL_TRANSITIONS - && isVisible - && (nonInteractiveState || mSkipLauncherVisibilityChange))) { + if (isVisible && (nonInteractiveState || mSkipLauncherVisibilityChange)) { return null; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 17735e10dc..f67d49aa9c 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -499,10 +499,8 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0; if (started) { DeviceProfile profile = getDeviceProfile(); - boolean willUserBeActive = - (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0; boolean visible = (state == NORMAL || state == OVERVIEW) - && (willUserBeActive || isUserActive()) + && isUserActive() && !profile.isVerticalBarLayout() && !mIsOverlayVisible; SystemUiProxy.INSTANCE.get(this) @@ -1279,10 +1277,6 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, return ObjectWrapper.wrap(new Integer(info.id)); } - public void setHintUserWillBeActive() { - addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); - } - @Override public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) { super.onDisplayInfoChanged(context, info, flags); diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 262564613a..1ba784bb6e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -17,7 +17,6 @@ package com.android.launcher3.uioverrides.states; import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; -import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import android.content.Context; import android.graphics.Color; @@ -107,8 +106,7 @@ public class BackgroundAppState extends OverviewState { @Override public boolean isTaskbarAlignedWithHotseat(Launcher launcher) { - if (ENABLE_SHELL_TRANSITIONS) return false; - return super.isTaskbarAlignedWithHotseat(launcher); + return false; } @Override diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 5392b70703..3d8c1f984f 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -939,7 +939,6 @@ public abstract class AbsSwipeUpHandler mListeners = new ArraySet<>(); private final SystemUiProxy mSystemUiProxy; - private final boolean mAllowMinimizeSplitScreen; // TODO(141886704): Remove these references when they are no longer needed private RecentsAnimationController mController; private boolean mCancelled; - public RecentsAnimationCallbacks(SystemUiProxy systemUiProxy, - boolean allowMinimizeSplitScreen) { + public RecentsAnimationCallbacks(SystemUiProxy systemUiProxy) { mSystemUiProxy = systemUiProxy; - mAllowMinimizeSplitScreen = allowMinimizeSplitScreen; } @UiThread @@ -122,7 +119,7 @@ public class RecentsAnimationCallbacks implements } mController = new RecentsAnimationController(animationController, - mAllowMinimizeSplitScreen, this::onAnimationFinished); + this::onAnimationFinished); if (mCancelled) { Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), mController::finishAnimationToApp); @@ -219,7 +216,6 @@ public class RecentsAnimationCallbacks implements public void dump(String prefix, PrintWriter pw) { pw.println(prefix + "RecentsAnimationCallbacks:"); - pw.println(prefix + "\tmAllowMinimizeSplitScreen=" + mAllowMinimizeSplitScreen); pw.println(prefix + "\tmCancelled=" + mCancelled); } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 1b05e284be..adcf4ef6cd 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -17,7 +17,6 @@ package com.android.quickstep; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; -import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION; import android.content.Context; @@ -52,21 +51,17 @@ public class RecentsAnimationController { private static final String TAG = "RecentsAnimationController"; private final RecentsAnimationControllerCompat mController; private final Consumer mOnFinishedListener; - private final boolean mAllowMinimizeSplitScreen; private boolean mUseLauncherSysBarFlags = false; - private boolean mSplitScreenMinimized = false; private boolean mFinishRequested = false; // Only valid when mFinishRequested == true. private boolean mFinishTargetIsLauncher; private RunnableList mPendingFinishCallbacks = new RunnableList(); public RecentsAnimationController(RecentsAnimationControllerCompat controller, - boolean allowMinimizeSplitScreen, Consumer onFinishedListener) { mController = controller; mOnFinishedListener = onFinishedListener; - mAllowMinimizeSplitScreen = allowMinimizeSplitScreen; } /** @@ -85,33 +80,16 @@ public class RecentsAnimationController { if (mUseLauncherSysBarFlags != useLauncherSysBarFlags) { mUseLauncherSysBarFlags = useLauncherSysBarFlags; UI_HELPER_EXECUTOR.execute(() -> { - if (!ENABLE_SHELL_TRANSITIONS) { - mController.setAnimationTargetsBehindSystemBars(!useLauncherSysBarFlags); - } else { - try { - WindowManagerGlobal.getWindowManagerService().setRecentsAppBehindSystemBars( - useLauncherSysBarFlags); - } catch (RemoteException e) { - Log.e(TAG, "Unable to reach window manager", e); - } + try { + WindowManagerGlobal.getWindowManagerService().setRecentsAppBehindSystemBars( + useLauncherSysBarFlags); + } catch (RemoteException e) { + Log.e(TAG, "Unable to reach window manager", e); } }); } } - /** - * Indicates that the gesture has crossed the window boundary threshold and we should minimize - * if we are in splitscreen. - */ - public void setSplitScreenMinimized(Context context, boolean splitScreenMinimized) { - if (!mAllowMinimizeSplitScreen) { - return; - } - if (mSplitScreenMinimized != splitScreenMinimized) { - mSplitScreenMinimized = splitScreenMinimized; - } - } - /** * Remove task remote animation target from * {@link RecentsAnimationCallbacks#onTasksAppeared}}. @@ -272,9 +250,7 @@ public class RecentsAnimationController { public void dump(String prefix, PrintWriter pw) { pw.println(prefix + "RecentsAnimationController:"); - pw.println(prefix + "\tmAllowMinimizeSplitScreen=" + mAllowMinimizeSplitScreen); pw.println(prefix + "\tmUseLauncherSysBarFlags=" + mUseLauncherSysBarFlags); - pw.println(prefix + "\tmSplitScreenMinimized=" + mSplitScreenMinimized); pw.println(prefix + "\tmFinishRequested=" + mFinishRequested); pw.println(prefix + "\tmFinishTargetIsLauncher=" + mFinishTargetIsLauncher); } diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 85eea3b00b..49ec5973e1 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -60,9 +60,8 @@ import java.io.PrintWriter; import java.util.HashMap; public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener { - public static final boolean ENABLE_SHELL_TRANSITIONS = true; - public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS - && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false); + public static final boolean SHELL_TRANSITIONS_ROTATION = + SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false); private final Context mCtx; private RecentsAnimationController mController; @@ -160,8 +159,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn final BaseContainerInterface containerInterface = gestureState.getContainerInterface(); mLastGestureState = gestureState; - RecentsAnimationCallbacks newCallbacks = new RecentsAnimationCallbacks( - getSystemUiProxy(), containerInterface.allowMinimizeSplitScreen()); + RecentsAnimationCallbacks newCallbacks = new RecentsAnimationCallbacks(getSystemUiProxy()); mCallbacks = newCallbacks; mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() { @Override @@ -191,7 +189,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn } mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets); - if (ENABLE_SHELL_TRANSITIONS && mTargets.hasRecents + if (mTargets.hasRecents // The filtered (MODE_CLOSING) targets only contain 1 home activity. && mTargets.apps.length == 1 && mTargets.apps[0].windowConfiguration.getActivityType() diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java index 14f47d13a7..b66d4cba33 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java @@ -24,7 +24,6 @@ import static com.android.launcher3.util.VelocityUtils.PX_PER_MS; import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely; -import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID; import android.animation.Animator; @@ -212,15 +211,13 @@ public class DeviceLockedInputConsumer implements InputConsumer, // This will come back and cancel the interaction. startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null, TAG); mHomeLaunched = true; - } else if (ENABLE_SHELL_TRANSITIONS) { - if (mTaskAnimationManager.getCurrentCallbacks() != null) { - if (mRecentsAnimationController != null) { - finishRecentsAnimationForShell(dismissTask); - } else { - // the transition of recents animation hasn't started, wait for it - mCancelWhenRecentsStart = true; - mDismissTask = dismissTask; - } + } else if (mTaskAnimationManager.getCurrentCallbacks() != null) { + if (mRecentsAnimationController != null) { + finishRecentsAnimationForShell(dismissTask); + } else { + // the transition of recents animation hasn't started, wait for it + mCancelWhenRecentsStart = true; + mDismissTask = dismissTask; } } mStateCallback.setState(STATE_HANDLER_INVALIDATED); diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index ae6757f17d..1af12f1ac1 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -886,8 +886,7 @@ public class SplitSelectStateController { Log.w(TAG, "Package not found: " + packageName, e); } RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks( - SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()), - false /* allowMinimizeSplitScreen */); + SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext())); DesktopSplitRecentsAnimationListener listener = new DesktopSplitRecentsAnimationListener(splitPosition, taskBounds); diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java index 1c417eb9d9..4c6e4ff359 100644 --- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java +++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java @@ -84,8 +84,7 @@ public class SplitWithKeyboardShortcutController { return; } RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks( - SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()), - false /* allowMinimizeSplitScreen */); + SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext())); SplitWithKeyboardShortcutRecentsAnimationListener listener = new SplitWithKeyboardShortcutRecentsAnimationListener(leftOrTop); diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index e5c54bb3d7..c7777d86a5 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -24,7 +24,6 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED; import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition; -import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation; import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation; @@ -535,21 +534,12 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { // If mDrawsBelowRecents is unset, no reordering will be enforced. if (mDrawsBelowRecents != null) { - // In legacy transitions, the animation leashes remain in same hierarchy in the - // TaskDisplayArea, so we don't want to bump the layer too high otherwise it will - // conflict with layers that WM core positions (ie. the input consumers). For shell - // transitions, the animation leashes are reparented to an animation container so we - // can bump layers as needed. - if (ENABLE_SHELL_TRANSITIONS) { - builder.setLayer(mDrawsBelowRecents - ? Integer.MIN_VALUE + app.prefixOrderIndex - // 1000 is an arbitrary number to give room for multiple layers. - : Integer.MAX_VALUE - 1000 + app.prefixOrderIndex); - } else { - builder.setLayer(mDrawsBelowRecents - ? Integer.MIN_VALUE + app.prefixOrderIndex - : 0); - } + // In shell transitions, the animation leashes are reparented to an animation container + // so we can bump layers as needed. + builder.setLayer(mDrawsBelowRecents + ? Integer.MIN_VALUE + app.prefixOrderIndex + // 1000 is an arbitrary number to give room for multiple layers. + : Integer.MAX_VALUE - 1000 + app.prefixOrderIndex); } } diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt index e43d7b4fc0..3c40602ba0 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.kt +++ b/quickstep/src/com/android/quickstep/views/TaskView.kt @@ -1032,14 +1032,12 @@ constructor( // triggered by QuickstepTransitionManager.AppLaunchAnimationRunner. return RunnableList().also { recentsView.addSideTaskLaunchCallback(it) } } - if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { - // If the recents transition is running (ie. in live tile mode), then the start - // of a new task will merge into the existing transition and it currently will - // not be run independently, so we need to rely on the onTaskAppeared() call - // for the new task to trigger the side launch callback to flush this runnable - // list (which is usually flushed when the app launch animation finishes) - recentsView.addSideTaskLaunchCallback(opts.onEndCallback) - } + // If the recents transition is running (ie. in live tile mode), then the start + // of a new task will merge into the existing transition and it currently will + // not be run independently, so we need to rely on the onTaskAppeared() call + // for the new task to trigger the side launch callback to flush this runnable + // list (which is usually flushed when the app launch animation finishes) + recentsView.addSideTaskLaunchCallback(opts.onEndCallback) return opts.onEndCallback } else { notifyTaskLaunchFailed() diff --git a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java index 3a83ae34c5..28c8a4ac18 100644 --- a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java +++ b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java @@ -59,8 +59,6 @@ public class TaskAnimationManagerTest { @Test public void startRecentsActivity_allowBackgroundLaunch() { - assumeTrue(TaskAnimationManager.ENABLE_SHELL_TRANSITIONS); - final LauncherActivityInterface activityInterface = mock(LauncherActivityInterface.class); final GestureState gestureState = mock(GestureState.class); final RecentsAnimationCallbacks.RecentsAnimationListener listener = diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java index 633091d87b..fec94fe8d7 100644 --- a/src/com/android/launcher3/BaseActivity.java +++ b/src/com/android/launcher3/BaseActivity.java @@ -109,11 +109,6 @@ public abstract class BaseActivity extends Activity implements ActivityContext { */ public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4; - /** - * State flag indicating if the user will be active shortly. - */ - public static final int ACTIVITY_STATE_USER_WILL_BE_ACTIVE = 1 << 5; - /** * State flag indicating that a state transition is in progress */ @@ -316,7 +311,6 @@ public abstract class BaseActivity extends Activity implements ActivityContext { */ public void setResumed() { addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE); - removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); } public boolean isUserActive() { From 9f8e4b6b6282360997038de0f73163fbc73e073a Mon Sep 17 00:00:00 2001 From: Jihoon Kang Date: Fri, 30 Aug 2024 00:29:41 +0000 Subject: [PATCH 15/23] Make java_sdk_library dependencies explicit modules should specify the submodule of java_sdk_library that the module actually depends on Test: CI Bug: 358613520 Change-Id: If1b96a7ffa7ddde50c44817e0d96257ae2967a0e --- Android.bp | 6 +++--- tests/Android.bp | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Android.bp b/Android.bp index 78db013d3a..8bd1bf5264 100644 --- a/Android.bp +++ b/Android.bp @@ -94,7 +94,7 @@ filegroup { android_library { name: "launcher-aosp-tapl", libs: [ - "framework-statsd", + "framework-statsd.stubs.module_lib", ], static_libs: [ "androidx.annotation_annotation", @@ -269,7 +269,7 @@ android_library { "quickstep/res", ], libs: [ - "framework-statsd", + "framework-statsd.stubs.module_lib", ], static_libs: [ "Launcher3ResLib", @@ -326,7 +326,7 @@ android_library { ], resource_dirs: [], libs: [ - "framework-statsd", + "framework-statsd.stubs.module_lib", ], // Note the ordering here is important when it comes to resource // overriding. We want the most specific resource overrides defined diff --git a/tests/Android.bp b/tests/Android.bp index 1e2caa7aa8..8ce95072ff 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -153,9 +153,9 @@ android_test { "com_android_launcher3_flags_lib", ], libs: [ - "android.test.base", - "android.test.runner", - "android.test.mock", + "android.test.base.stubs.system", + "android.test.runner.stubs.system", + "android.test.mock.stubs.system", ], // Libraries used by mockito inline extended jni_libs: [ @@ -260,9 +260,9 @@ android_robolectric_test { "launcher-testing-shared", ], libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", + "android.test.runner.stubs.system", + "android.test.base.stubs.system", + "android.test.mock.stubs.system", "truth", ], instrumentation_for: "Launcher3", From 9ab2b8c785d0f7e119369eb0610454621b3b0f89 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Thu, 29 Aug 2024 17:48:03 -0700 Subject: [PATCH 16/23] Update bubble bar stash and unstash animation Bubble bar should stash and unstash from the handle. Update the animation to match what is used for the taskbar animation. Fix the stash handle reveal animation to animate the stash bounds to bubble bar bounds and back. This gives the effect of the handle morphing into the bubble bar and bubble bar morphing back into the handle. Bug: 345680453 Flag: com.android.wm.shell.enable_bubble_bar Test: atest TransientBubbleStashControllerTest Test: have bubbles in bubble bar and be in app, swipe up for taskbar and observe the bubble bar handle transforming into the bubble bar Test: with taskbar and bubbles expanded, check that bubble bar transforms into the handle Change-Id: I8508e9207092774cbdb990e65f7f84e56a924d0c --- quickstep/res/values/dimens.xml | 1 + .../taskbar/TaskbarActivityContext.java | 2 +- .../bubbles/BubbleBarViewController.java | 52 +++-- .../BubbleStashedHandleViewController.java | 119 +++++++---- .../bubbles/stashing/BubbleStashController.kt | 6 +- .../PersistentBubbleStashController.kt | 2 +- .../TransientBubbleStashController.kt | 192 +++++++++++------- .../PersistentBubbleStashControllerTest.kt | 2 +- .../TransientBubbleStashControllerTest.kt | 15 +- 9 files changed, 248 insertions(+), 143 deletions(-) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index d9818828c9..9996367c80 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -437,6 +437,7 @@ 55dp @dimen/transient_taskbar_stashed_height @dimen/taskbar_stashed_handle_height + @dimen/transient_taskbar_stash_spring_velocity_dp_per_s 9dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index e19042e8a4..47ae741908 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -283,7 +283,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { TaskbarHotseatDimensionsProvider dimensionsProvider = new DeviceProfileDimensionsProviderAdapter(this); BubbleStashController bubbleStashController = isTransientTaskbar - ? new TransientBubbleStashController(dimensionsProvider, getResources()) + ? new TransientBubbleStashController(dimensionsProvider, this) : new PersistentBubbleStashController(dimensionsProvider); bubbleControllersOptional = Optional.of(new BubbleControllers( new BubbleBarController(this, bubbleBarView), diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 5c1a5467d8..8b3bb4e736 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -80,12 +80,14 @@ public class BubbleBarViewController { // These are exposed to {@link BubbleStashController} to animate for stashing/un-stashing private final MultiValueAlpha mBubbleBarAlpha; - private final AnimatedFloat mBubbleBarScale = new AnimatedFloat(this::updateScale); + private final AnimatedFloat mBubbleBarScaleY = new AnimatedFloat(this::updateScaleY); private final AnimatedFloat mBubbleBarTranslationY = new AnimatedFloat( this::updateTranslationY); // Modified when swipe up is happening on the bubble bar or task bar. private float mBubbleBarSwipeUpTranslationY; + // Modified when bubble bar is springing back into the stash handle. + private float mBubbleBarStashTranslationY; // Whether the bar is hidden for a sysui state. private boolean mHiddenForSysui; @@ -125,7 +127,7 @@ public class BubbleBarViewController { onBubbleBarConfigurationChanged(/* animate= */ false); mActivity.addOnDeviceProfileChangeListener( dp -> onBubbleBarConfigurationChanged(/* animate= */ true)); - mBubbleBarScale.updateValue(1f); + mBubbleBarScaleY.updateValue(1f); mBubbleClickListener = v -> onBubbleClicked((BubbleView) v); mBubbleBarClickListener = v -> expandBubbleBar(); mBubbleDragController.setupBubbleBarView(mBarView); @@ -255,8 +257,8 @@ public class BubbleBarViewController { return mBubbleBarAlpha; } - public AnimatedFloat getBubbleBarScale() { - return mBubbleBarScale; + public AnimatedFloat getBubbleBarScaleY() { + return mBubbleBarScaleY; } public AnimatedFloat getBubbleBarTranslationY() { @@ -267,6 +269,27 @@ public class BubbleBarViewController { return mBarView.getBubbleBarCollapsedHeight(); } + /** + * @see BubbleBarView#getRelativePivotX() + */ + public float getBubbleBarRelativePivotX() { + return mBarView.getRelativePivotX(); + } + + /** + * @see BubbleBarView#getRelativePivotY() + */ + public float getBubbleBarRelativePivotY() { + return mBarView.getRelativePivotY(); + } + + /** + * @see BubbleBarView#setRelativePivot(float, float) + */ + public void setBubbleBarRelativePivot(float x, float y) { + mBarView.setRelativePivot(x, y); + } + /** * Whether the bubble bar is visible or not. */ @@ -474,17 +497,20 @@ public class BubbleBarViewController { updateTranslationY(); } - private void updateTranslationY() { - mBarView.setTranslationY(mBubbleBarTranslationY.value - + mBubbleBarSwipeUpTranslationY); + /** + * Sets the translation of the bubble bar during the stash animation. + */ + public void setTranslationYForStash(float transY) { + mBubbleBarStashTranslationY = transY; + updateTranslationY(); } - /** - * Applies scale properties for the entire bubble bar. - */ - private void updateScale() { - float scale = mBubbleBarScale.value; - mBarView.setScaleX(scale); + private void updateTranslationY() { + mBarView.setTranslationY(mBubbleBarTranslationY.value + mBubbleBarSwipeUpTranslationY + + mBubbleBarStashTranslationY); + } + + private void updateScaleY(float scale) { mBarView.setScaleY(scale); } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java index 6bfe8f40ae..5c4428c92d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java @@ -21,6 +21,7 @@ import static android.view.View.VISIBLE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.annotation.Nullable; import android.content.res.Resources; import android.graphics.Outline; import android.graphics.Rect; @@ -28,6 +29,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewOutlineProvider; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.anim.RevealOutlineAnimation; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; @@ -49,23 +51,29 @@ public class BubbleStashedHandleViewController { private final TaskbarActivityContext mActivity; private final StashedHandleView mStashedHandleView; private final MultiValueAlpha mStashedHandleAlpha; + private float mTranslationForSwipeY; + private float mTranslationForStashY; // Initialized in init. private BubbleBarViewController mBarViewController; private BubbleStashController mBubbleStashController; private RegionSamplingHelper mRegionSamplingHelper; - private int mBarSize; - private int mStashedTaskbarHeight; + // Height of the area for the stash handle. Handle will be drawn in the center of this. + // This is also the area where touch is handled on the handle. + private int mStashedBubbleBarHeight; private int mStashedHandleWidth; private int mStashedHandleHeight; - // The bounds we want to clip to in the settled state when showing the stashed handle. + // The bounds of the stashed handle in settled state. private final Rect mStashedHandleBounds = new Rect(); + private float mStashedHandleRadius; // When the reveal animation is cancelled, we can assume it's about to create a new animation, // which should start off at the same point the cancelled one left off. private float mStartProgressForNextRevealAnim; - private boolean mWasLastRevealAnimReversed; + // Use a nullable boolean to handle initial case where the last animation direction is not known + @Nullable + private Boolean mWasLastRevealAnimReversed = null; // XXX: if there are more of these maybe do state flags instead private boolean mHiddenForSysui; @@ -77,6 +85,7 @@ public class BubbleStashedHandleViewController { mActivity = activity; mStashedHandleView = stashedHandleView; mStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1); + mStashedHandleAlpha.setUpdateVisibility(true); } /** Initialize controller. */ @@ -84,26 +93,31 @@ public class BubbleStashedHandleViewController { mBarViewController = bubbleControllers.bubbleBarViewController; mBubbleStashController = bubbleControllers.bubbleStashController; + DeviceProfile deviceProfile = mActivity.getDeviceProfile(); Resources resources = mActivity.getResources(); mStashedHandleHeight = resources.getDimensionPixelSize( R.dimen.bubblebar_stashed_handle_height); mStashedHandleWidth = resources.getDimensionPixelSize( R.dimen.bubblebar_stashed_handle_width); - mBarSize = resources.getDimensionPixelSize(R.dimen.bubblebar_size); - final int bottomMargin = resources.getDimensionPixelSize( - R.dimen.transient_taskbar_bottom_margin); - mStashedHandleView.getLayoutParams().height = mBarSize + bottomMargin; + int barSize = resources.getDimensionPixelSize(R.dimen.bubblebar_size); + // Use the max translation for bubble bar whether it is on the home screen or in app. + // Use values directly from device profile to avoid referencing other bubble controllers + // during init flow. + int maxTy = Math.max(deviceProfile.hotseatBarBottomSpacePx, + deviceProfile.taskbarBottomMargin); + // Adjust handle view size to accommodate the handle morphing into the bubble bar + mStashedHandleView.getLayoutParams().height = barSize + maxTy; mStashedHandleAlpha.get(0).setValue(0); - mStashedTaskbarHeight = resources.getDimensionPixelSize( + mStashedBubbleBarHeight = resources.getDimensionPixelSize( R.dimen.bubblebar_stashed_size); mStashedHandleView.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - float stashedHandleRadius = view.getHeight() / 2f; - outline.setRoundRect(mStashedHandleBounds, stashedHandleRadius); + mStashedHandleRadius = view.getHeight() / 2f; + outline.setRoundRect(mStashedHandleBounds, mStashedHandleRadius); } }); @@ -132,28 +146,25 @@ public class BubbleStashedHandleViewController { private void updateBounds(BubbleBarLocation bubbleBarLocation) { // As more bubbles get added, the icon bounds become larger. To ensure a consistent // handle bar position, we pin it to the edge of the screen. - final int stashedCenterY = mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2; + final int stashedCenterY = mStashedHandleView.getHeight() - mStashedBubbleBarHeight / 2; + final int stashedCenterX; if (bubbleBarLocation.isOnLeft(mStashedHandleView.isLayoutRtl())) { final int left = mBarViewController.getHorizontalMargin(); - mStashedHandleBounds.set( - left, - stashedCenterY - mStashedHandleHeight / 2, - left + mStashedHandleWidth, - stashedCenterY + mStashedHandleHeight / 2); - mStashedHandleView.setPivotX(0); + stashedCenterX = left + mStashedHandleWidth / 2; } else { final int right = - mActivity.getDeviceProfile().widthPx - mBarViewController.getHorizontalMargin(); - mStashedHandleBounds.set( - right - mStashedHandleWidth, - stashedCenterY - mStashedHandleHeight / 2, - right, - stashedCenterY + mStashedHandleHeight / 2); - mStashedHandleView.setPivotX(mStashedHandleView.getWidth()); + mStashedHandleView.getRight() - mBarViewController.getHorizontalMargin(); + stashedCenterX = right - mStashedHandleWidth / 2; } - + mStashedHandleBounds.set( + stashedCenterX - mStashedHandleWidth / 2, + stashedCenterY - mStashedHandleHeight / 2, + stashedCenterX + mStashedHandleWidth / 2, + stashedCenterY + mStashedHandleHeight / 2 + ); mStashedHandleView.updateSampledRegion(mStashedHandleBounds); - mStashedHandleView.setPivotY(mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2f); + mStashedHandleView.setPivotX(stashedCenterX); + mStashedHandleView.setPivotY(stashedCenterY); } public void onDestroy() { @@ -168,13 +179,6 @@ public class BubbleStashedHandleViewController { return mStashedHandleHeight; } - /** - * Returns the height when the bubble bar is unstashed (so the height of the bubble bar). - */ - public int getUnstashedHeight() { - return mBarSize; - } - /** * Called when system ui state changes. Bubbles don't show when the device is locked. */ @@ -242,7 +246,20 @@ public class BubbleStashedHandleViewController { * Sets the translation of the stashed handle during the swipe up gesture. */ public void setTranslationYForSwipe(float transY) { - mStashedHandleView.setTranslationY(transY); + mTranslationForSwipeY = transY; + updateTranslationY(); + } + + /** + * Sets the translation of the stashed handle during the spring on stash animation. + */ + public void setTranslationYForStash(float transY) { + mTranslationForStashY = transY; + updateTranslationY(); + } + + private void updateTranslationY() { + mStashedHandleView.setTranslationY(mTranslationForSwipeY + mTranslationForStashY); } /** Returns the translation of the stashed handle. */ @@ -263,18 +280,17 @@ public class BubbleStashedHandleViewController { * the size of where the bubble bar icons will be. */ public Animator createRevealAnimToIsStashed(boolean isStashed) { - Rect bubbleBarBounds = new Rect(mBarViewController.getBubbleBarBounds()); + Rect bubbleBarBounds = getLocalBubbleBarBounds(); - // Account for the full visual height of the bubble bar - int heightDiff = (mBarSize - bubbleBarBounds.height()) / 2; - bubbleBarBounds.top -= heightDiff; - bubbleBarBounds.bottom += heightDiff; - float stashedHandleRadius = mStashedHandleView.getHeight() / 2f; + float bubbleBarRadius = bubbleBarBounds.height() / 2f; final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider( - stashedHandleRadius, stashedHandleRadius, bubbleBarBounds, mStashedHandleBounds); + bubbleBarRadius, mStashedHandleRadius, bubbleBarBounds, mStashedHandleBounds); boolean isReversed = !isStashed; - boolean changingDirection = mWasLastRevealAnimReversed != isReversed; + // We are only changing direction when mWasLastRevealAnimReversed is set at least once + boolean changingDirection = + mWasLastRevealAnimReversed != null && mWasLastRevealAnimReversed != isReversed; + mWasLastRevealAnimReversed = isReversed; if (changingDirection) { mStartProgressForNextRevealAnim = 1f - mStartProgressForNextRevealAnim; @@ -291,6 +307,21 @@ public class BubbleStashedHandleViewController { return revealAnim; } + /** + * Get bounds for the bubble bar in the space of the handle view + */ + private Rect getLocalBubbleBarBounds() { + // Position the bubble bar bounds to the space of handle view + Rect bubbleBarBounds = new Rect(mBarViewController.getBubbleBarBounds()); + // Start by moving bubble bar bounds to the bottom of handle view + int height = bubbleBarBounds.height(); + bubbleBarBounds.bottom = mStashedHandleView.getHeight(); + bubbleBarBounds.top = bubbleBarBounds.bottom - height; + // Then apply translation that is applied to the bubble bar + bubbleBarBounds.offset(0, (int) mBubbleStashController.getBubbleBarTranslationY()); + return bubbleBarBounds; + } + /** Checks that the stash handle is visible and that the motion event is within bounds. */ public boolean isEventOverHandle(MotionEvent ev) { if (mStashedHandleView.getVisibility() != VISIBLE) { @@ -299,7 +330,7 @@ public class BubbleStashedHandleViewController { // the bounds of the handle only include the visible part, so we check that the Y coordinate // is anywhere within the stashed height of bubble bar (same as taskbar stashed height). - final int top = mActivity.getDeviceProfile().heightPx - mStashedTaskbarHeight; + final int top = mActivity.getDeviceProfile().heightPx - mStashedBubbleBarHeight; final float x = ev.getRawX(); return ev.getRawY() >= top && x >= mStashedHandleBounds.left && x <= mStashedHandleBounds.right; diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt index 48eb7dea15..b2a88ac919 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt @@ -179,10 +179,10 @@ interface BubbleStashController { /** How long to stash/unstash. */ const val BAR_STASH_DURATION = InsetsController.ANIMATION_DURATION_RESIZE.toLong() + const val BAR_STASH_ALPHA_DURATION = 50L + const val BAR_STASH_ALPHA_DELAY = 33L + /** How long to translate Y coordinate of the BubbleBar. */ const val BAR_TRANSLATION_DURATION = 300L - - /** The scale bubble bar animates to when being stashed. */ - const val STASHED_BAR_SCALE = 0.5f } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt index 1b650195ef..3ebd97e315 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt @@ -116,7 +116,7 @@ class PersistentBubbleStashController( bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY // bubble bar has only alpha property, getting it at index 0 bubbleBarAlphaAnimator = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0) - bubbleBarScaleAnimator = bubbleBarViewController.bubbleBarScale + bubbleBarScaleAnimator = bubbleBarViewController.bubbleBarScaleY } private fun animateAfterUnlock() { diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt index 1a4b9823ef..b8fd1cb1ed 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt @@ -17,30 +17,36 @@ package com.android.launcher3.taskbar.bubbles.stashing import android.animation.Animator -import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet -import android.content.res.Resources +import android.content.Context import android.view.MotionEvent import android.view.View import androidx.annotation.VisibleForTesting +import androidx.core.animation.doOnEnd +import androidx.core.animation.doOnStart +import androidx.dynamicanimation.animation.SpringForce +import com.android.app.animation.Interpolators.EMPHASIZED +import com.android.app.animation.Interpolators.LINEAR import com.android.launcher3.R import com.android.launcher3.anim.AnimatedFloat -import com.android.launcher3.taskbar.StashedHandleViewController +import com.android.launcher3.anim.SpringAnimationBuilder import com.android.launcher3.taskbar.TaskbarInsetsController import com.android.launcher3.taskbar.bubbles.BubbleBarViewController import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController +import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_ALPHA_DELAY +import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_ALPHA_DURATION import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION -import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider import com.android.launcher3.util.MultiPropertyFactory import com.android.wm.shell.common.bubbles.BubbleBarLocation import com.android.wm.shell.shared.animation.PhysicsAnimator +import kotlin.math.max class TransientBubbleStashController( private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider, - resources: Resources + private val context: Context ) : BubbleStashController { private lateinit var bubbleBarViewController: BubbleBarViewController @@ -50,14 +56,20 @@ class TransientBubbleStashController( // stash view properties private var bubbleStashedHandleViewController: BubbleStashedHandleViewController? = null private var stashHandleViewAlpha: MultiPropertyFactory.MultiProperty? = null + private var translationYDuringStash = AnimatedFloat { transY -> + bubbleStashedHandleViewController?.setTranslationYForStash(transY) + bubbleBarViewController.setTranslationYForStash(transY) + } + private val stashHandleStashVelocity = + context.resources.getDimension(R.dimen.bubblebar_stashed_handle_spring_velocity_dp_per_s) private var stashedHeight: Int = 0 // bubble bar properties private lateinit var bubbleBarAlpha: MultiPropertyFactory.MultiProperty private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat private lateinit var bubbleBarScale: AnimatedFloat - private val mHandleCenterFromScreenBottom = - resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f + private val handleCenterFromScreenBottom = + context.resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f private var animator: AnimatorSet? = null @@ -136,12 +148,9 @@ class TransientBubbleStashController( bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY // bubble bar has only alpha property, getting it at index 0 bubbleBarAlpha = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0) - bubbleBarScale = bubbleBarViewController.bubbleBarScale + bubbleBarScale = bubbleBarViewController.bubbleBarScaleY stashedHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0 - stashHandleViewAlpha = - bubbleStashedHandleViewController - ?.stashedHandleAlpha - ?.get(StashedHandleViewController.ALPHA_INDEX_STASHED) + stashHandleViewAlpha = bubbleStashedHandleViewController?.stashedHandleAlpha?.get(0) } private fun animateAfterUnlock() { @@ -179,7 +188,7 @@ class TransientBubbleStashController( stashHandleViewAlpha?.value = 1f this.bubbleBarTranslationYAnimator.updateValue(getStashTranslation()) bubbleBarAlpha.setValue(0f) - bubbleBarScale.updateValue(STASHED_BAR_SCALE) + bubbleBarScale.updateValue(getStashScale()) isStashed = true onIsStashedChanged() } @@ -223,11 +232,11 @@ class TransientBubbleStashController( // the difference between the centers of the handle and the bubble bar is the difference // between their distance from the bottom of the screen. val barCenter: Float = bubbleBarViewController.bubbleBarCollapsedHeight / 2f - return mHandleCenterFromScreenBottom - barCenter + return handleCenterFromScreenBottom - barCenter } override fun getStashedHandleTranslationForNewBubbleAnimation(): Float { - return -mHandleCenterFromScreenBottom + return -handleCenterFromScreenBottom } override fun getStashedHandlePhysicsAnimator(): PhysicsAnimator? { @@ -245,7 +254,13 @@ class TransientBubbleStashController( override fun getHandleTranslationY(): Float? = bubbleStashedHandleViewController?.translationY private fun getStashTranslation(): Float { - return (bubbleBarViewController.bubbleBarCollapsedHeight - stashedHeight) / 2f + return bubbleBarTranslationY / 2f + } + + @VisibleForTesting + fun getStashScale(): Float { + val handleHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0 + return handleHeight / bubbleBarViewController.bubbleBarCollapsedHeight } /** @@ -258,61 +273,84 @@ class TransientBubbleStashController( @Suppress("SameParameterValue") private fun createStashAnimator(isStashed: Boolean, duration: Long): AnimatorSet { val animatorSet = AnimatorSet() - val fullLengthAnimatorSet = AnimatorSet() - // Not exactly half and may overlap. See [first|second]HalfDurationScale below. - val firstHalfAnimatorSet = AnimatorSet() - val secondHalfAnimatorSet = AnimatorSet() - val firstHalfDurationScale: Float - val secondHalfDurationScale: Float - val stashHandleAlphaValue: Float - if (isStashed) { - firstHalfDurationScale = 0.75f - secondHalfDurationScale = 0.5f - stashHandleAlphaValue = 1f - fullLengthAnimatorSet.play( - bubbleBarTranslationYAnimator.animateToValue(getStashTranslation()) - ) - firstHalfAnimatorSet.playTogether( - bubbleBarAlpha.animateToValue(0f), - bubbleBarScale.animateToValue(STASHED_BAR_SCALE) - ) - } else { - firstHalfDurationScale = 0.5f - secondHalfDurationScale = 0.75f - stashHandleAlphaValue = 0f - fullLengthAnimatorSet.playTogether( - bubbleBarScale.animateToValue(1f), - bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY) - ) - secondHalfAnimatorSet.playTogether(bubbleBarAlpha.animateToValue(1f)) - } - stashHandleViewAlpha?.let { - secondHalfAnimatorSet.playTogether(it.animateToValue(stashHandleAlphaValue)) - } - bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.let { - fullLengthAnimatorSet.play(it) - } - fullLengthAnimatorSet.setDuration(duration) - firstHalfAnimatorSet.setDuration((duration * firstHalfDurationScale).toLong()) - secondHalfAnimatorSet.setDuration((duration * secondHalfDurationScale).toLong()) - secondHalfAnimatorSet.startDelay = (duration * (1 - secondHalfDurationScale)).toLong() - animatorSet.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet, secondHalfAnimatorSet) - animatorSet.addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - animator = null - controllersAfterInitAction.runAfterInit { - if (isStashed) { - bubbleBarViewController.isExpanded = false - } - taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() - } - } + + val alphaDuration = if (isStashed) duration else BAR_STASH_ALPHA_DURATION + val alphaDelay = if (isStashed) BAR_STASH_ALPHA_DELAY else 0L + animatorSet.play( + createStashAlphaAnimator(isStashed).apply { + this.duration = max(0L, alphaDuration - alphaDelay) + this.startDelay = alphaDelay + this.interpolator = LINEAR } ) + + animatorSet.play( + createSpringOnStashAnimator(isStashed).apply { + this.duration = duration + this.interpolator = LINEAR + } + ) + + animatorSet.play( + bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.apply { + this.duration = duration + this.interpolator = EMPHASIZED + } + ) + + val scaleTarget = if (isStashed) getStashScale() else 1f + animatorSet.play( + bubbleBarScale.animateToValue(scaleTarget).apply { + this.duration = duration + this.interpolator = EMPHASIZED + this.setBubbleBarPivotDuringAnim(0.5f, 1f) + } + ) + + val translationYTarget = if (isStashed) getStashTranslation() else bubbleBarTranslationY + animatorSet.play( + bubbleBarTranslationYAnimator.animateToValue(translationYTarget).apply { + this.duration = duration + this.interpolator = EMPHASIZED + } + ) + + animatorSet.doOnEnd { + animator = null + controllersAfterInitAction.runAfterInit { + if (isStashed) { + bubbleBarViewController.isExpanded = false + } + taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() + } + } return animatorSet } + private fun createStashAlphaAnimator(isStashed: Boolean): AnimatorSet { + val stashHandleAlphaTarget = if (isStashed) 1f else 0f + val barAlphaTarget = if (isStashed) 0f else 1f + return AnimatorSet().apply { + play(bubbleBarAlpha.animateToValue(barAlphaTarget)) + play(stashHandleViewAlpha?.animateToValue(stashHandleAlphaTarget)) + } + } + + private fun createSpringOnStashAnimator(isStashed: Boolean): Animator { + if (!isStashed) { + // Animate the stash translation back to 0 + return translationYDuringStash.animateToValue(0f) + } + // Apply a spring to the handle + return SpringAnimationBuilder(context) + .setStartValue(translationYDuringStash.value) + .setEndValue(0f) + .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY) + .setStiffness(SpringForce.STIFFNESS_LOW) + .setStartVelocity(stashHandleStashVelocity) + .build(translationYDuringStash, AnimatedFloat.VALUE) + } + private fun onIsStashedChanged() { controllersAfterInitAction.runAfterInit { taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() @@ -363,13 +401,23 @@ class TransientBubbleStashController( } private fun Animator.updateTouchRegionOnAnimationEnd(): Animator { - this.addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - onIsStashedChanged() - } + doOnEnd { onIsStashedChanged() } + return this + } + + private fun Animator.setBubbleBarPivotDuringAnim(pivotX: Float, pivotY: Float): Animator { + var initialPivotX = Float.NaN + var initialPivotY = Float.NaN + doOnStart { + initialPivotX = bubbleBarViewController.bubbleBarRelativePivotX + initialPivotY = bubbleBarViewController.bubbleBarRelativePivotY + bubbleBarViewController.setBubbleBarRelativePivot(pivotX, pivotY) + } + doOnEnd { + if (!initialPivotX.isNaN() && !initialPivotY.isNaN()) { + bubbleBarViewController.setBubbleBarRelativePivot(initialPivotX, initialPivotY) } - ) + } return this } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt index c0a5dfaf7b..4106a2c9a6 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt @@ -251,7 +251,7 @@ class PersistentBubbleStashControllerTest { whenever(bubbleBarViewController.hasBubbles()).thenReturn(true) whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(translationY) - whenever(bubbleBarViewController.bubbleBarScale).thenReturn(scale) + whenever(bubbleBarViewController.bubbleBarScaleY).thenReturn(scale) whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(alpha) whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT) } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt index b5809c2b78..63c21979c2 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt @@ -31,7 +31,6 @@ import com.android.launcher3.taskbar.TaskbarInsetsController import com.android.launcher3.taskbar.bubbles.BubbleBarView import com.android.launcher3.taskbar.bubbles.BubbleBarViewController import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController -import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE import com.android.launcher3.util.MultiValueAlpha import com.android.wm.shell.shared.animation.PhysicsAnimator import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils @@ -59,7 +58,7 @@ class TransientBubbleStashControllerTest { const val HOTSEAT_TRANSLATION_Y = -45f const val TASK_BAR_TRANSLATION_Y = -TASKBAR_BOTTOM_SPACE const val HANDLE_VIEW_HEIGHT = 4 - const val BUBBLE_BAR_STASHED_TRANSLATION_Y = 48 + const val BUBBLE_BAR_STASHED_TRANSLATION_Y = -2.5f } @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this) @@ -90,7 +89,7 @@ class TransientBubbleStashControllerTest { val taskbarHotseatDimensionsProvider = DefaultDimensionsProvider(taskBarBottomSpace = TASKBAR_BOTTOM_SPACE) mTransientBubbleStashController = - TransientBubbleStashController(taskbarHotseatDimensionsProvider, context.resources) + TransientBubbleStashController(taskbarHotseatDimensionsProvider, context) setUpBubbleBarView() setUpBubbleBarController() setUpStashedHandleView() @@ -174,8 +173,8 @@ class TransientBubbleStashControllerTest { assertThat(mTransientBubbleStashController.isStashed).isTrue() assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y) assertThat(bubbleBarView.alpha).isEqualTo(0f) - assertThat(bubbleBarView.scaleX).isEqualTo(STASHED_BAR_SCALE) - assertThat(bubbleBarView.scaleY).isEqualTo(STASHED_BAR_SCALE) + assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScale()) + assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScale()) // Handle view is visible assertThat(stashedHandleView.translationY).isEqualTo(0) assertThat(stashedHandleView.alpha).isEqualTo(1) @@ -243,8 +242,8 @@ class TransientBubbleStashControllerTest { // Then all property values are updated assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y) assertThat(bubbleBarView.alpha).isEqualTo(0) - assertThat(bubbleBarView.scaleX).isEqualTo(STASHED_BAR_SCALE) - assertThat(bubbleBarView.scaleY).isEqualTo(STASHED_BAR_SCALE) + assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScale()) + assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScale()) // Handle is visible at correct Y position assertThat(stashedHandleView.alpha).isEqualTo(1) assertThat(stashedHandleView.translationY).isEqualTo(0) @@ -306,7 +305,7 @@ class TransientBubbleStashControllerTest { whenever(bubbleBarViewController.hasBubbles()).thenReturn(true) whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(barTranslationY) - whenever(bubbleBarViewController.bubbleBarScale).thenReturn(barScale) + whenever(bubbleBarViewController.bubbleBarScaleY).thenReturn(barScale) whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(barAlpha) whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT) } From ebfae1852997480546dfbc7e54079d24b147c080 Mon Sep 17 00:00:00 2001 From: Jordan Silva Date: Wed, 28 Aug 2024 15:20:05 +0100 Subject: [PATCH 17/23] Refactoring OverviewCommandHelper (2/3) This CL should not affect the behavior of OverviewCommandHelper. - It splits executeCommand into smaller functions - Rearrange public/private functions - Adds extra logs. Bug: 352046797 Bug: 351122926 Flag: EXEMPT bugfix. Test: Manual. Change-Id: I283f179794af812973a559db6291febe19e3c6d7 --- .../quickstep/OverviewCommandHelper.kt | 267 +++++++++--------- 1 file changed, 139 insertions(+), 128 deletions(-) diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt index 56153a94c0..5e29139e5b 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt @@ -73,48 +73,6 @@ class OverviewCommandHelper( private val visibleRecentsView: RecentsView<*, *>? get() = activityInterface.getVisibleRecentsView>() - /** Called when the command finishes execution. */ - private fun onCommandFinished(command: CommandInfo) { - command.status = CommandStatus.COMPLETED - if (commandQueue.first() !== command) { - Log.d( - TAG, - "next task not scheduled. First pending command type " + - "is ${commandQueue.first()} - command type is: $command" - ) - return - } - - Log.d(TAG, "command executed successfully! $command") - commandQueue.remove(command) - executeNext() - } - - /** - * Executes the next command from the queue. If the command finishes immediately (returns true), - * it continues to execute the next command, until the queue is empty of a command defer's its - * completion (returns false). - */ - @UiThread - private fun executeNext() { - val command: CommandInfo = - commandQueue.firstOrNull() - ?: run { - Log.d(TAG, "no pending commands to be executed.") - return - } - - Log.d(TAG, "executing command: $command") - val result = executeCommand(command) - - Log.d(TAG, "command executed: $command with result: $result") - if (result) { - onCommandFinished(command) - } else { - Log.d(TAG, "waiting for command callback: $command") - } - } - /** * Adds a command to be executed next, after all pending tasks are completed. Max commands that * can be queued is [.MAX_QUEUE_SIZE]. Requests after reaching that limit will be silently @@ -138,23 +96,89 @@ class OverviewCommandHelper( fun canStartHomeSafely(): Boolean = commandQueue.isEmpty() || commandQueue.first().type == HOME - /** Clear commands from the queue */ + /** Clear pending commands from the queue */ fun clearPendingCommands() { Log.d(TAG, "clearing pending commands: $commandQueue") commandQueue.clear() } - private fun getNextTask(view: RecentsView<*, *>): TaskView? { - val runningTaskView = view.runningTaskView + /** + * Executes the next command from the queue. If the command finishes immediately (returns true), + * it continues to execute the next command, until the queue is empty of a command defer's its + * completion (returns false). + */ + @UiThread + private fun executeNext() { + val command: CommandInfo = + commandQueue.firstOrNull() + ?: run { + Log.d(TAG, "no pending commands to be executed.") + return + } - return if (runningTaskView == null) { - view.getTaskViewAt(0) + command.status = CommandStatus.PROCESSING + Log.d(TAG, "executing command: $command") + + val result = executeCommand(command) + Log.d(TAG, "command executed: $command with result: $result") + if (result) { + onCommandFinished(command) } else { - val nextTask = view.nextTaskView - nextTask ?: runningTaskView + Log.d(TAG, "waiting for command callback: $command") } } + /** + * Executes the task and returns true if next task can be executed. If false, then the next task + * is deferred until [.scheduleNextTask] is called + */ + private fun executeCommand(command: CommandInfo): Boolean { + if (waitForToggleCommandComplete && command.type == TOGGLE) { + Log.d(TAG, "executeCommand: $command - waiting for toggle command complete") + return true + } + + val recentsView = visibleRecentsView + Log.d(TAG, "executeCommand: $command - visibleRecentsView: $recentsView") + return if (recentsView != null) { + executeWhenRecentsIsVisible(command, recentsView) + } else { + executeWhenRecentsIsNotVisible(command) + } + } + + private fun executeWhenRecentsIsVisible( + command: CommandInfo, + recentsView: RecentsView<*, *>, + ): Boolean = + when (command.type) { + SHOW -> true // already visible + KEYBOARD_INPUT, + HIDE -> { + if (recentsView.isHandlingTouch) { + true + } else { + keyboardTaskFocusIndex = PagedView.INVALID_PAGE + val currentPage = recentsView.nextPage + val taskView = recentsView.getTaskViewAt(currentPage) + launchTask(recentsView, taskView, command) + } + } + TOGGLE -> { + val taskView = + if (recentsView.runningTaskView == null) { + recentsView.getTaskViewAt(0) + } else { + recentsView.nextTaskView ?: recentsView.runningTaskView + } + launchTask(recentsView, taskView, command) + } + HOME -> { + recentsView.startHome() + true + } + } + private fun launchTask( recents: RecentsView<*, *>, taskView: TaskView?, @@ -182,81 +206,46 @@ class OverviewCommandHelper( } } - /** - * Executes the task and returns true if next task can be executed. If false, then the next task - * is deferred until [.scheduleNextTask] is called - */ - private fun executeCommand(command: CommandInfo): Boolean { - command.status = CommandStatus.PROCESSING + private fun executeWhenRecentsIsNotVisible(command: CommandInfo): Boolean { + val recentsViewContainer = activityInterface.getCreatedContainer() as? RecentsViewContainer + val recentsView: RecentsView<*, *>? = recentsViewContainer?.getOverviewPanel() + val deviceProfile = recentsViewContainer?.getDeviceProfile() + val uiController = activityInterface.getTaskbarController() + val allowQuickSwitch = + FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get() && + uiController != null && + deviceProfile != null && + (deviceProfile.isTablet || deviceProfile.isTwoPanels) - if (waitForToggleCommandComplete && command.type == TOGGLE) { - Log.d(TAG, "executeCommand: $command - waiting for toggle command complete") - return true - } - - var recentsView = visibleRecentsView - Log.d(TAG, "executeCommand: $command - visibleRecentsView: $recentsView") - if (recentsView == null) { - val activity = activityInterface.getCreatedContainer() as? RecentsViewContainer - recentsView = activity?.getOverviewPanel() - val deviceProfile = activity?.getDeviceProfile() - val uiController = activityInterface.getTaskbarController() - val allowQuickSwitch = - FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get() && - uiController != null && - deviceProfile != null && - (deviceProfile.isTablet || deviceProfile.isTwoPanels) - - when (command.type) { - HIDE -> { - if (!allowQuickSwitch) return true - keyboardTaskFocusIndex = uiController!!.launchFocusedTask() - if (keyboardTaskFocusIndex == -1) return true - } - KEYBOARD_INPUT -> - if (allowQuickSwitch) { - uiController!!.openQuickSwitchView() - return true - } else { - keyboardTaskFocusIndex = 0 - } - HOME -> { - ActiveGestureLog.INSTANCE.addLog("OverviewCommandHelper.executeCommand(HOME)") - // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call, - // we should still call it on main thread because launcher is waiting for - // ActivityTaskManager to resume it. Also calling startActivity() on bg thread - // could potentially delay resuming launcher. See b/348668521 for more details. - touchInteractionService.startActivity(overviewComponentObserver.homeIntent) + when (command.type) { + HIDE -> { + if (!allowQuickSwitch) return true + keyboardTaskFocusIndex = uiController!!.launchFocusedTask() + if (keyboardTaskFocusIndex == -1) return true + } + KEYBOARD_INPUT -> + if (allowQuickSwitch) { + uiController!!.openQuickSwitchView() return true - } - SHOW -> - // When Recents is not currently visible, the command's type is SHOW - // when overview is triggered via the keyboard overview button or Action+Tab - // keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button - // nav is TYPE_TOGGLE. + } else { keyboardTaskFocusIndex = 0 - TOGGLE -> {} - } - } else { - return when (command.type) { - SHOW -> true // already visible - KEYBOARD_INPUT, - HIDE -> { - if (recentsView.isHandlingTouch) { - true - } else { - keyboardTaskFocusIndex = PagedView.INVALID_PAGE - val currentPage = recentsView.nextPage - val taskView = recentsView.getTaskViewAt(currentPage) - launchTask(recentsView, taskView, command) - } - } - TOGGLE -> launchTask(recentsView, getNextTask(recentsView), command) - HOME -> { - recentsView.startHome() - true } + HOME -> { + ActiveGestureLog.INSTANCE.addLog("OverviewCommandHelper.executeCommand(HOME)") + // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call, + // we should still call it on main thread because launcher is waiting for + // ActivityTaskManager to resume it. Also calling startActivity() on bg thread + // could potentially delay resuming launcher. See b/348668521 for more details. + touchInteractionService.startActivity(overviewComponentObserver.homeIntent) + return true } + SHOW -> + // When Recents is not currently visible, the command's type is SHOW + // when overview is triggered via the keyboard overview button or Action+Tab + // keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button + // nav is TYPE_TOGGLE. + keyboardTaskFocusIndex = 0 + TOGGLE -> {} } recentsView?.setKeyboardTaskFocusIndex(keyboardTaskFocusIndex) @@ -264,6 +253,7 @@ class OverviewCommandHelper( val animatorListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator) { + Log.d(TAG, "switching to Overview state - onAnimationStart: $command") super.onAnimationStart(animation) updateRecentsViewFocus(command) logShowOverviewFrom(command.type) @@ -309,9 +299,11 @@ class OverviewCommandHelper( controller: RecentsAnimationController, targets: RecentsAnimationTargets ) { + Log.d(TAG, "recents animation started: $command") updateRecentsViewFocus(command) logShowOverviewFrom(command.type) activityInterface.runOnInitBackgroundStateUI { + Log.d(TAG, "recents animation started - onInitBackgroundStateUI: $command") interactionHandler.onGestureEnded(0f, PointF()) } command.removeListener(this) @@ -320,6 +312,7 @@ class OverviewCommandHelper( override fun onRecentsAnimationCanceled( thumbnailDatas: HashMap ) { + Log.d(TAG, "recents animation canceled: $command") interactionHandler.onGestureCancelled() command.removeListener(this) @@ -366,6 +359,23 @@ class OverviewCommandHelper( onCommandFinished(command) } + /** Called when the command finishes execution. */ + private fun onCommandFinished(command: CommandInfo) { + command.status = CommandStatus.COMPLETED + if (commandQueue.first() !== command) { + Log.d( + TAG, + "next task not scheduled. First pending command type " + + "is ${commandQueue.first()} - command type is: $command" + ) + return + } + + Log.d(TAG, "command executed successfully! $command") + commandQueue.remove(command) + executeNext() + } + private fun updateRecentsViewFocus(command: CommandInfo) { val recentsView: RecentsView<*, *> = visibleRecentsView ?: return if (command.type != KEYBOARD_INPUT && command.type != HIDE && command.type != SHOW) { @@ -380,10 +390,11 @@ class OverviewCommandHelper( // here we launch overview with live tile. recentsView.viewRootImpl.touchModeChanged(false) // Ensure that recents view has focus so that it receives the followup key inputs - if (requestFocus(recentsView.getTaskViewAt(keyboardTaskFocusIndex))) return - if (requestFocus(recentsView.nextTaskView)) return - if (requestFocus(recentsView.getTaskViewAt(0))) return - requestFocus(recentsView) + // Stops requesting focused after first view gets focused. + recentsView.getTaskViewAt(keyboardTaskFocusIndex).requestFocus() || + recentsView.nextTaskView.requestFocus() || + recentsView.getTaskViewAt(0).requestFocus() || + recentsView.requestFocus() } private fun onRecentsViewFocusUpdated(command: CommandInfo) { @@ -396,11 +407,11 @@ class OverviewCommandHelper( keyboardTaskFocusIndex = PagedView.INVALID_PAGE } - private fun requestFocus(taskView: View?): Boolean { - if (taskView == null) return false - taskView.post { - taskView.requestFocus() - taskView.requestAccessibilityFocus() + private fun View?.requestFocus(): Boolean { + if (this == null) return false + post { + requestFocus() + requestAccessibilityFocus() } return true } From c6fca0e16ba677f83053cbef1389269c79f3e679 Mon Sep 17 00:00:00 2001 From: Marcelo Arteiro Date: Tue, 13 Aug 2024 13:02:50 +0000 Subject: [PATCH 18/23] Fixes wrong colors in SUW -> AllSet Screenshot: https://screenshot.googleplex.com/6Yy9bQuQqox2rPB.png Fixes: 355462084 Flag: EXEMPT bugfix Test: Manual Change-Id: I92b9839d345ab16f0229b546fb6c0e00ced02671 --- quickstep/res/values-night/styles.xml | 11 +---------- quickstep/res/values/styles.xml | 2 +- res/values/styles.xml | 4 ++++ 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/quickstep/res/values-night/styles.xml b/quickstep/res/values-night/styles.xml index 2cb633a9c4..eb8831068a 100644 --- a/quickstep/res/values-night/styles.xml +++ b/quickstep/res/values-night/styles.xml @@ -14,16 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - + - + + + + +