From d8c3328d4d9c5b3ffa95b0e9d45494bee07b8423 Mon Sep 17 00:00:00 2001 From: Liran Binyamin Date: Wed, 2 Oct 2024 12:21:41 -0400 Subject: [PATCH 1/8] Pass bubble flyout from wm shell to launcher Flag: com.android.wm.shell.enable_bubble_bar Bug: 277815200 Test: atest BubbleBarViewAnimatorTest Change-Id: I8666765937a491903c6fc042d2d629374be4c70d --- .../bubbles/testing/FakeBubbleViewFactory.kt | 14 +++++- .../taskbar/bubbles/BubbleViewTest.kt | 14 +++++- .../animation/BubbleBarViewAnimatorTest.kt | 48 ++++++++++++------- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/quickstep/testing/com/android/launcher3/taskbar/bubbles/testing/FakeBubbleViewFactory.kt b/quickstep/testing/com/android/launcher3/taskbar/bubbles/testing/FakeBubbleViewFactory.kt index 37a07c3647..473d8ef559 100644 --- a/quickstep/testing/com/android/launcher3/taskbar/bubbles/testing/FakeBubbleViewFactory.kt +++ b/quickstep/testing/com/android/launcher3/taskbar/bubbles/testing/FakeBubbleViewFactory.kt @@ -54,7 +54,19 @@ object FakeBubbleViewFactory { val flags = if (suppressNotification) Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION else 0 val bubbleInfo = - BubbleInfo(key, flags, null, null, 0, context.packageName, null, null, false, true) + BubbleInfo( + key, + flags, + null, + null, + 0, + context.packageName, + null, + null, + false, + true, + null, + ) val bubbleView = inflater.inflate(R.layout.bubblebar_item_view, parent, false) as BubbleView val dotPath = PathParser.createPathFromPathData( diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt index 94f9cf5398..2caff01057 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt @@ -65,7 +65,19 @@ class BubbleViewTest { overflowView.setOverflow(BubbleBarOverflow(overflowView), bitmap) val bubbleInfo = - BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false, true) + BubbleInfo( + "key", + 0, + null, + null, + 0, + context.packageName, + null, + null, + false, + true, + null, + ) bubbleView = inflater.inflate(R.layout.bubblebar_item_view, null, false) as BubbleView bubble = BubbleBarBubble(bubbleInfo, bubbleView, bitmap, bitmap, Color.WHITE, Path(), "") diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt index 84e872da14..39131659be 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt @@ -86,7 +86,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpandedNoOp, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -135,7 +135,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpandedNoOp, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -183,7 +183,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpandedNoOp, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -228,7 +228,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpandedNoOp, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -274,7 +274,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpandedNoOp, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -311,7 +311,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpanded, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -355,7 +355,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpanded, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -405,7 +405,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpanded, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -454,7 +454,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpandedNoOp, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -504,7 +504,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpanded, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -538,7 +538,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpandedNoOp, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -577,7 +577,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpanded, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -625,7 +625,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpanded, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -666,7 +666,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpandedNoOp, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -713,7 +713,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpanded, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -760,7 +760,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpanded, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -818,7 +818,7 @@ class BubbleBarViewAnimatorTest { bubbleBarView, bubbleStashController, onExpanded, - animatorScheduler + animatorScheduler, ) InstrumentationRegistry.getInstrumentation().runOnMainSync { @@ -870,7 +870,19 @@ class BubbleBarViewAnimatorTest { bubbleBarView.addView(overflowView) val bubbleInfo = - BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false, true) + BubbleInfo( + "key", + 0, + null, + null, + 0, + context.packageName, + null, + null, + false, + true, + null, + ) bubbleView = inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView bubble = From 936fec94bd77394be9cc7cec1102ed65e00bf6e4 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Wed, 2 Oct 2024 11:42:48 -0700 Subject: [PATCH 2/8] Allow injecting bubble controllers in taskbar test Update TaskbarUnitTestRule @InjectController annotation to support injecting bubble controllers. Bubble controllers are optional and only initialized if the feature flag is enabled. Add a test to check that bubble controllers are only initialized when the flag is on. Bug: 371017282 Test: TaskbarUnitTestRuleTest Flag: TEST_ONLY Change-Id: Ibea2dbe0db854d530ef93ac53204a361572fa5b2 --- .../taskbar/rules/TaskbarUnitTestRule.kt | 38 ++++++++++++--- .../taskbar/rules/TaskbarUnitTestRuleTest.kt | 46 +++++++++++++++++++ 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt index cb5e464dde..b0d706f4c3 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt @@ -28,9 +28,11 @@ import androidx.test.rule.ServiceTestRule import com.android.launcher3.LauncherAppState import com.android.launcher3.statehandlers.DesktopVisibilityController import com.android.launcher3.taskbar.TaskbarActivityContext +import com.android.launcher3.taskbar.TaskbarControllers import com.android.launcher3.taskbar.TaskbarManager import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks import com.android.launcher3.taskbar.TaskbarViewController +import com.android.launcher3.taskbar.bubbles.BubbleControllers import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric @@ -38,6 +40,9 @@ import com.android.launcher3.util.TestUtil import com.android.quickstep.AllAppsActionManager import com.android.quickstep.TouchInteractionService import com.android.quickstep.TouchInteractionService.TISBinder +import java.lang.reflect.Field +import java.lang.reflect.ParameterizedType +import java.util.Optional import org.junit.Assume.assumeTrue import org.junit.rules.RuleChain import org.junit.rules.TestRule @@ -180,19 +185,38 @@ class TaskbarUnitTestRule( fun recreateTaskbar() = instrumentation.runOnMainSync { taskbarManager.recreateTaskbar() } private fun injectControllers() { - val controllers = activityContext.controllers - val controllerFieldsByType = controllers.javaClass.fields.associateBy { it.type } + val bubbleControllerTypes = + BubbleControllers::class.java.fields.map { f -> + if (f.type == Optional::class.java) { + (f.genericType as ParameterizedType).actualTypeArguments[0] as Class<*> + } else { + f.type + } + } testInstance.javaClass.fields .filter { it.isAnnotationPresent(InjectController::class.java) } .forEach { - it.set( - testInstance, - controllerFieldsByType[it.type]?.get(controllers) - ?: throw NoSuchElementException("Failed to find controller for ${it.type}"), - ) + val controllers: Any = + if (it.type in bubbleControllerTypes) { + activityContext.controllers.bubbleControllers.orElseThrow { + NoSuchElementException("Bubble controllers are not initialized") + } + } else { + activityContext.controllers + } + injectController(it, testInstance, controllers) } } + private fun injectController(field: Field, testInstance: Any, controllers: Any) { + val controllerFieldsByType = controllers.javaClass.fields.associateBy { it.type } + field.set( + testInstance, + controllerFieldsByType[field.type]?.get(controllers) + ?: throw NoSuchElementException("Failed to find controller for ${field.type}"), + ) + } + /** * Annotates test controller fields to inject the corresponding controllers from the current * [TaskbarControllers] instance. diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRuleTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRuleTest.kt index 5d4fdc5418..52ca78d720 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRuleTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRuleTest.kt @@ -16,18 +16,24 @@ package com.android.launcher3.taskbar.rules +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.android.launcher3.taskbar.TaskbarActivityContext import com.android.launcher3.taskbar.TaskbarKeyguardController import com.android.launcher3.taskbar.TaskbarManager import com.android.launcher3.taskbar.TaskbarStashController +import com.android.launcher3.taskbar.bubbles.BubbleBarController import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.NavBarKidsMode import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.UserSetupMode import com.android.launcher3.util.LauncherMultivalentJUnit import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices +import com.android.wm.shell.Flags import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertThrows +import org.junit.Rule import org.junit.Test import org.junit.runner.Description import org.junit.runner.RunWith @@ -39,6 +45,8 @@ class TaskbarUnitTestRuleTest { private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext) + @get:Rule(order = 0) val setFlagsRule = SetFlagsRule() + @Test fun testSetup_taskbarInitialized() { onSetup { assertThat(activityContext).isInstanceOf(TaskbarActivityContext::class.java) } @@ -127,6 +135,44 @@ class TaskbarUnitTestRuleTest { } } + @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_BAR) + @Test + fun testInjectBubbleController_bubbleFlagOn_isInjected() { + val testClass = + object { + @InjectController lateinit var controller: BubbleBarController + val isInjected: Boolean + get() = ::controller.isInitialized + } + + TaskbarUnitTestRule(testClass, context).apply(EMPTY_STATEMENT, DESCRIPTION).evaluate() + + onSetup(TaskbarUnitTestRule(testClass, context)) { + assertThat(testClass.isInjected).isTrue() + } + } + + @DisableFlags(Flags.FLAG_ENABLE_BUBBLE_BAR) + @Test + fun testInjectBubbleController_bubbleFlagOff_exceptionThrown() { + val testClass = + object { + @InjectController lateinit var controller: BubbleBarController + } + + // We cannot use #assertThrows because we also catch an assumption violated exception when + // running #evaluate on devices that do not support Taskbar. + val result = + try { + TaskbarUnitTestRule(testClass, context) + .apply(EMPTY_STATEMENT, DESCRIPTION) + .evaluate() + } catch (e: NoSuchElementException) { + e + } + assertThat(result).isInstanceOf(NoSuchElementException::class.java) + } + @Test fun testUserSetupMode_default_isComplete() { onSetup { assertThat(activityContext.isUserSetupComplete).isTrue() } From 35b87d33b2a723fd21bfb170f77c095d8640fae5 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Wed, 2 Oct 2024 16:48:43 -0700 Subject: [PATCH 3/8] Inject bubble controllers directly Replace injecting bubble controllers class with each controller separately.c Bug: 371017282 Test: TaskbarStashControllerTest Flag: TEST_ONLY Change-Id: Iccb870b0976b51fcd5a987cf208531da413921d1 --- .../taskbar/TaskbarStashControllerTest.kt | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt index e7364464af..d7ce4ed39e 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt @@ -35,7 +35,8 @@ import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATI import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_ALPHA_DURATION import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_DURATION import com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_STASH -import com.android.launcher3.taskbar.bubbles.BubbleControllers +import com.android.launcher3.taskbar.bubbles.BubbleBarViewController +import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController import com.android.launcher3.taskbar.rules.TaskbarModeRule import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.THREE_BUTTONS @@ -52,7 +53,6 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR import com.google.common.truth.Truth.assertThat -import java.util.Optional import org.junit.After import org.junit.Before import org.junit.Rule @@ -60,6 +60,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(LauncherMultivalentJUnit::class) +@EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @EmulatedDevices(["pixelTablet2023"]) class TaskbarStashControllerTest { private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext) @@ -74,7 +75,8 @@ class TaskbarStashControllerTest { @InjectController lateinit var stashedHandleViewController: StashedHandleViewController @InjectController lateinit var dragLayerController: TaskbarDragLayerController @InjectController lateinit var autohideSuspendController: TaskbarAutohideSuspendController - @InjectController lateinit var bubbleControllers: Optional + @InjectController lateinit var bubbleBarViewController: BubbleBarViewController + @InjectController lateinit var bubbleStashController: BubbleStashController private val activityContext by taskbarUnitTestRule::activityContext @@ -420,60 +422,55 @@ class TaskbarStashControllerTest { } @Test - @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @TaskbarMode(TRANSIENT) fun testUpdateAndAnimateTransientTaskbar_unstashTaskbarWithBubbles_bubbleBarUnstashes() { getInstrumentation().runOnMainSync { - bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false) - bubbleControllers.get().bubbleStashController.stashBubbleBarImmediate() + bubbleBarViewController.setHiddenForBubbles(false) + bubbleStashController.stashBubbleBarImmediate() stashController.updateAndAnimateTransientTaskbar(false, true) } - assertThat(bubbleControllers.get().bubbleStashController.isStashed).isFalse() + assertThat(bubbleStashController.isStashed).isFalse() } @Test - @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @TaskbarMode(TRANSIENT) fun testUpdateAndAnimateTransientTaskbar_unstashTaskbarWithoutBubbles_bubbleBarStashed() { getInstrumentation().runOnMainSync { - bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false) - bubbleControllers.get().bubbleStashController.stashBubbleBarImmediate() + bubbleBarViewController.setHiddenForBubbles(false) + bubbleStashController.stashBubbleBarImmediate() stashController.updateAndAnimateTransientTaskbar(false, false) } - assertThat(bubbleControllers.get().bubbleStashController.isStashed).isTrue() + assertThat(bubbleStashController.isStashed).isTrue() } @Test - @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @TaskbarMode(TRANSIENT) fun testUpdateAndAnimateTransientTaskbar_stashTaskbarWithBubbles_bubbleBarStashes() { getInstrumentation().runOnMainSync { - bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false) - bubbleControllers.get().bubbleStashController.showBubbleBarImmediate() + bubbleBarViewController.setHiddenForBubbles(false) + bubbleStashController.showBubbleBarImmediate() stashController.updateAndAnimateTransientTaskbar(true, true) } - assertThat(bubbleControllers.get().bubbleStashController.isStashed).isTrue() + assertThat(bubbleStashController.isStashed).isTrue() } @Test - @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @TaskbarMode(TRANSIENT) fun testUpdateAndAnimateTransientTaskbar_stashTaskbarWithoutBubbles_bubbleBarUnstashed() { getInstrumentation().runOnMainSync { - bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false) - bubbleControllers.get().bubbleStashController.showBubbleBarImmediate() + bubbleBarViewController.setHiddenForBubbles(false) + bubbleStashController.showBubbleBarImmediate() stashController.updateAndAnimateTransientTaskbar(true, false) } - assertThat(bubbleControllers.get().bubbleStashController.isStashed).isFalse() + assertThat(bubbleStashController.isStashed).isFalse() } @Test - @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @TaskbarMode(TRANSIENT) fun testUpdateAndAnimateTransientTaskbar_bubbleBarExpandedBeforeTimeout_expandedAfterwards() { getInstrumentation().runOnMainSync { - bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false) - bubbleControllers.get().bubbleBarViewController.isExpanded = true + bubbleBarViewController.setHiddenForBubbles(false) + bubbleBarViewController.isExpanded = true stashController.updateAndAnimateTransientTaskbar(false) animatorTestRule.advanceTimeBy(stashController.stashDuration) } @@ -483,7 +480,7 @@ class TaskbarStashControllerTest { stashController.timeoutAlarm.finishAlarm() animatorTestRule.advanceTimeBy(stashController.stashDuration) } - assertThat(bubbleControllers.get().bubbleBarViewController.isExpanded).isTrue() + assertThat(bubbleBarViewController.isExpanded).isTrue() } @Test From b5fdc80bb2d5b2dcfa4e6200f24ddef12efe7893 Mon Sep 17 00:00:00 2001 From: Catherine Liang Date: Tue, 24 Sep 2024 17:50:52 +0000 Subject: [PATCH 4/8] Handle messages to update Launcher preview color Receive color update messages with a list of color resource ids and a corresponding list of color overrides, and adjust the preview accordingly. Flag: com.android.systemui.shared.new_customization_picker_ui Test: manually verified that the selected colors are previewed Bug: 350718581 Change-Id: Ibcde0575416df00b76627ac1004f21f5f9af1196 --- .../graphics/GridCustomizationsProvider.java | 6 ++ .../graphics/LauncherPreviewRenderer.java | 38 +++++++++-- .../graphics/PreviewSurfaceRenderer.java | 68 ++++++++++++++++--- .../launcher3/widget/LocalColorExtractor.java | 5 ++ 4 files changed, 103 insertions(+), 14 deletions(-) diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java index 27ec838b98..259e54307d 100644 --- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java +++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java @@ -95,6 +95,7 @@ public class GridCustomizationsProvider extends ContentProvider { private static final int MESSAGE_ID_UPDATE_PREVIEW = 1337; private static final int MESSAGE_ID_UPDATE_GRID = 7414; + private static final int MESSAGE_ID_UPDATE_COLOR = 856; // Set of all active previews used to track duplicate memory allocations private final Set mActivePreviews = @@ -289,6 +290,11 @@ public class GridCustomizationsProvider extends ContentProvider { renderer.updateGrid(gridName); } break; + case MESSAGE_ID_UPDATE_COLOR: + if (Flags.newCustomizationPickerUi()) { + renderer.previewColor(message.getData()); + } + break; default: // Unknown command, destroy lifecycle Log.d(TAG, "Unknown preview command: " + message.what + ", destroying preview"); diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index 40c0cc65c9..f0e4fc444c 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -98,6 +98,7 @@ import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.LauncherWidgetHolder; import com.android.launcher3.widget.LocalColorExtractor; import com.android.launcher3.widget.util.WidgetSizes; +import com.android.systemui.shared.Flags; import java.util.ArrayList; import java.util.Collections; @@ -150,6 +151,14 @@ public class LauncherPreviewRenderer extends ContextWrapper InvariantDeviceProfile idp, WallpaperColors wallpaperColorsOverride, @Nullable final SparseArray launcherWidgetSpanInfo) { + this(context, idp, null, wallpaperColorsOverride, launcherWidgetSpanInfo); + } + + public LauncherPreviewRenderer(Context context, + InvariantDeviceProfile idp, + SparseIntArray previewColorOverride, + WallpaperColors wallpaperColorsOverride, + @Nullable final SparseArray launcherWidgetSpanInfo) { super(context); mUiHandler = new Handler(Looper.getMainLooper()); @@ -206,12 +215,29 @@ public class LauncherPreviewRenderer extends ContextWrapper mWorkspaceScreens.put(Workspace.SECOND_SCREEN_ID, rightPanel); } - WallpaperColors wallpaperColors = wallpaperColorsOverride != null - ? wallpaperColorsOverride - : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM); - mWallpaperColorResources = wallpaperColors != null - ? LocalColorExtractor.newInstance(context).generateColorsOverride(wallpaperColors) - : null; + if (Flags.newCustomizationPickerUi()) { + if (previewColorOverride != null) { + mWallpaperColorResources = previewColorOverride; + } else if (wallpaperColorsOverride != null) { + mWallpaperColorResources = LocalColorExtractor.newInstance( + context).generateColorsOverride(wallpaperColorsOverride); + } else { + WallpaperColors wallpaperColors = WallpaperManager.getInstance( + context).getWallpaperColors(FLAG_SYSTEM); + mWallpaperColorResources = wallpaperColors != null + ? LocalColorExtractor.newInstance(context).generateColorsOverride( + wallpaperColors) + : null; + } + } else { + WallpaperColors wallpaperColors = wallpaperColorsOverride != null + ? wallpaperColorsOverride + : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM); + mWallpaperColorResources = wallpaperColors != null + ? LocalColorExtractor.newInstance(context).generateColorsOverride( + wallpaperColors) + : null; + } mAppWidgetHost = new LauncherPreviewAppWidgetHost(context); } diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java index 1b23d75527..e3c2d366c6 100644 --- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java +++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java @@ -32,6 +32,7 @@ import android.os.IBinder; import android.util.Log; import android.util.Size; import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.ContextThemeWrapper; import android.view.Display; import android.view.SurfaceControlViewHost; @@ -81,6 +82,8 @@ public class PreviewSurfaceRenderer { private static final String KEY_VIEW_HEIGHT = "height"; private static final String KEY_DISPLAY_ID = "display_id"; private static final String KEY_COLORS = "wallpaper_colors"; + private static final String KEY_COLOR_RESOURCE_IDS = "color_resource_ids"; + private static final String KEY_COLOR_VALUES = "color_values"; private Context mContext; private final IBinder mHostToken; @@ -91,6 +94,7 @@ public class PreviewSurfaceRenderer { private final int mDisplayId; private final Display mDisplay; private final WallpaperColors mWallpaperColors; + private SparseIntArray mPreviewColorOverride; private final RunnableList mLifeCycleTracker; private final SurfaceControlViewHost mSurfaceControlViewHost; @@ -110,6 +114,9 @@ public class PreviewSurfaceRenderer { mGridName = InvariantDeviceProfile.getCurrentGridName(context); } mWallpaperColors = bundle.getParcelable(KEY_COLORS); + if (Flags.newCustomizationPickerUi()) { + updateColorOverrides(bundle); + } mHideQsb = bundle.getBoolean(GridCustomizationsProvider.KEY_HIDE_BOTTOM_ROW); mHostToken = bundle.getBinder(KEY_HOST_TOKEN); @@ -217,20 +224,60 @@ public class PreviewSurfaceRenderer { } } + /** + * Updates the colors of the preview. + * + * @param bundle Bundle with an int array of color ids and an int array of overriding colors. + */ + public void previewColor(Bundle bundle) { + updateColorOverrides(bundle); + loadAsync(); + } + + private void updateColorOverrides(Bundle bundle) { + int[] ids = bundle.getIntArray(KEY_COLOR_RESOURCE_IDS); + int[] colors = bundle.getIntArray(KEY_COLOR_VALUES); + if (ids != null && colors != null) { + mPreviewColorOverride = new SparseIntArray(); + for (int i = 0; i < ids.length; i++) { + mPreviewColorOverride.put(ids[i], colors[i]); + } + } else { + mPreviewColorOverride = null; + } + } + /*** * Generates a new context overriding the theme color and the display size without affecting the * main application context */ private Context getPreviewContext() { Context context = mContext.createDisplayContext(mDisplay); - if (mWallpaperColors == null) { + if (Flags.newCustomizationPickerUi()) { + if (mPreviewColorOverride != null) { + LocalColorExtractor.newInstance(context) + .applyColorsOverride(context, mPreviewColorOverride); + } else if (mWallpaperColors != null) { + LocalColorExtractor.newInstance(context) + .applyColorsOverride(context, mWallpaperColors); + } + if (mWallpaperColors != null) { + return new ContextThemeWrapper(context, + Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints())); + } else { + return new ContextThemeWrapper(context, + Themes.getActivityThemeRes(context)); + } + } else { + if (mWallpaperColors == null) { + return new ContextThemeWrapper(context, + Themes.getActivityThemeRes(context)); + } + LocalColorExtractor.newInstance(context) + .applyColorsOverride(context, mWallpaperColors); return new ContextThemeWrapper(context, - Themes.getActivityThemeRes(context)); + Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints())); } - LocalColorExtractor.newInstance(context) - .applyColorsOverride(context, mWallpaperColors); - return new ContextThemeWrapper(context, - Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints())); } @WorkerThread @@ -300,8 +347,13 @@ public class PreviewSurfaceRenderer { if (mDestroyed) { return; } - mRenderer = new LauncherPreviewRenderer(inflationContext, idp, - mWallpaperColors, launcherWidgetSpanInfo); + if (Flags.newCustomizationPickerUi()) { + mRenderer = new LauncherPreviewRenderer(inflationContext, idp, mPreviewColorOverride, + mWallpaperColors, launcherWidgetSpanInfo); + } else { + mRenderer = new LauncherPreviewRenderer(inflationContext, idp, + mWallpaperColors, launcherWidgetSpanInfo); + } mRenderer.hideBottomRow(mHideQsb); View view = mRenderer.getRenderedView(dataModel, widgetProviderInfoMap); // This aspect scales the view to fit in the surface and centers it diff --git a/src/com/android/launcher3/widget/LocalColorExtractor.java b/src/com/android/launcher3/widget/LocalColorExtractor.java index 7b500c72c4..d26eb38d4b 100644 --- a/src/com/android/launcher3/widget/LocalColorExtractor.java +++ b/src/com/android/launcher3/widget/LocalColorExtractor.java @@ -48,4 +48,9 @@ public class LocalColorExtractor implements ResourceBasedOverride { public SparseIntArray generateColorsOverride(WallpaperColors colors) { return null; } + + /** + * Updates the base context to contain the colors override + */ + public void applyColorsOverride(Context base, SparseIntArray override) { } } From 9a949ff4ca6a0b7017f3f74a7f648aaed90d5c62 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Fri, 4 Oct 2024 09:48:46 -0700 Subject: [PATCH 5/8] [Dagger] Make WellbeingModel provided by DaggerSingletonObject Test: presubmit Bug: 361850561 Flag: NONE - dagger Change-Id: Ie23411457185ab31380f3b0efb660e7dd9d73f29 --- .../launcher3/model/WellbeingModel.java | 19 +++++++++++++++---- .../dagger/QuickstepBaseAppComponent.java | 3 +++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java index fb17f15925..94a18142e5 100644 --- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java +++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java @@ -44,23 +44,31 @@ import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import com.android.launcher3.R; +import com.android.launcher3.dagger.ApplicationContext; +import com.android.launcher3.dagger.LauncherAppSingleton; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.RemoteActionShortcut; import com.android.launcher3.popup.SystemShortcut; +import com.android.launcher3.util.DaggerSingletonObject; +import com.android.launcher3.util.DaggerSingletonTracker; +import com.android.launcher3.util.ExecutorUtil; import com.android.launcher3.util.Executors; -import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.views.ActivityContext; +import com.android.quickstep.dagger.QuickstepBaseAppComponent; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import javax.inject.Inject; + /** * Data model for digital wellbeing status of apps. */ +@LauncherAppSingleton public final class WellbeingModel implements SafeCloseable { private static final String TAG = "WellbeingModel"; private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000}; @@ -75,8 +83,8 @@ public final class WellbeingModel implements SafeCloseable { private static final String EXTRA_PACKAGES = "packages"; private static final String EXTRA_SUCCESS = "success"; - public static final MainThreadInitializedObject INSTANCE = - new MainThreadInitializedObject<>(WellbeingModel::new); + public static final DaggerSingletonObject INSTANCE = + new DaggerSingletonObject<>(QuickstepBaseAppComponent::getWellbeingModel); private final Context mContext; private final String mWellbeingProviderPkg; @@ -93,7 +101,9 @@ public final class WellbeingModel implements SafeCloseable { private boolean mIsInTest; - private WellbeingModel(final Context context) { + @Inject + WellbeingModel(@ApplicationContext final Context context, + DaggerSingletonTracker tracker) { mContext = context; mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg); mWorkerHandler = new Handler(TextUtils.isEmpty(mWellbeingProviderPkg) @@ -112,6 +122,7 @@ public final class WellbeingModel implements SafeCloseable { } }; mWorkerHandler.post(this::initializeInBackground); + ExecutorUtil.executeSyncOnMainOrFail(() -> tracker.addCloseable(this)); } @WorkerThread diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java index f2d571554d..eef1f96c21 100644 --- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java +++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java @@ -18,6 +18,7 @@ package com.android.quickstep.dagger; import com.android.launcher3.dagger.LauncherAppComponent; import com.android.launcher3.dagger.LauncherBaseAppComponent; +import com.android.launcher3.model.WellbeingModel; import com.android.quickstep.logging.SettingsChangeLogger; /** @@ -30,4 +31,6 @@ import com.android.quickstep.logging.SettingsChangeLogger; */ public interface QuickstepBaseAppComponent extends LauncherBaseAppComponent { SettingsChangeLogger getSettingsChangeLogger(); + + WellbeingModel getWellbeingModel(); } From 75675e123d7c41ecfb80aeda8d3b32bc8c2287b4 Mon Sep 17 00:00:00 2001 From: Liran Binyamin Date: Wed, 2 Oct 2024 17:07:11 -0400 Subject: [PATCH 6/8] Rename bubble bar flyout fields Long term we'd like to use bubbles for any app, so this change uses more general names instead of the chat-specific naming Flag: com.android.wm.shell.enable_bubble_bar Bug: 277815200 Test: built and tested on a device Change-Id: Idec2aa7d9a8df5df0df49231e8871298084f8aa1 --- quickstep/res/layout/bubblebar_flyout.xml | 10 ++-- .../bubbles/flyout/BubbleBarFlyoutMessage.kt | 7 +-- .../bubbles/flyout/BubbleBarFlyoutView.kt | 48 ++++++++--------- .../BubbleBarFlyoutViewScreenshotTest.kt | 54 +++++++------------ .../flyout/BubbleBarFlyoutControllerTest.kt | 5 +- 5 files changed, 50 insertions(+), 74 deletions(-) diff --git a/quickstep/res/layout/bubblebar_flyout.xml b/quickstep/res/layout/bubblebar_flyout.xml index fc1e91457e..e3338bf7b9 100644 --- a/quickstep/res/layout/bubblebar_flyout.xml +++ b/quickstep/res/layout/bubblebar_flyout.xml @@ -19,7 +19,7 @@ xmlns:tools="http://schemas.android.com/tools"> diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt index 7298297d5c..14b456c73f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt @@ -18,9 +18,4 @@ package com.android.launcher3.taskbar.bubbles.flyout import android.graphics.drawable.Drawable -data class BubbleBarFlyoutMessage( - val senderAvatar: Drawable?, - val senderName: CharSequence, - val message: CharSequence, - val isGroupChat: Boolean, -) +data class BubbleBarFlyoutMessage(val icon: Drawable?, val title: String, val message: String) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt index 8d84ddf8ab..2022a427db 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt @@ -44,11 +44,11 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly const val MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA = 0.75f } - private val sender: TextView by - lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_name) } + private val title: TextView by + lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_title) } - private val avatar: ImageView by - lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_avatar) } + private val icon: ImageView by + lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_icon) } private val message: TextView by lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_text) } @@ -171,8 +171,8 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly /** Sets the data for the flyout and starts playing the expand animation. */ fun showFromCollapsed(flyoutMessage: BubbleBarFlyoutMessage, expandAnimation: () -> Unit) { - avatar.alpha = 0f - sender.alpha = 0f + icon.alpha = 0f + title.alpha = 0f message.alpha = 0f setData(flyoutMessage) val txToCollapsedPosition = @@ -202,18 +202,18 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly private fun setData(flyoutMessage: BubbleBarFlyoutMessage) { // the avatar is only displayed in group chat messages - if (flyoutMessage.senderAvatar != null && flyoutMessage.isGroupChat) { - avatar.visibility = VISIBLE - avatar.setImageDrawable(flyoutMessage.senderAvatar) + if (flyoutMessage.icon != null) { + icon.visibility = VISIBLE + icon.setImageDrawable(flyoutMessage.icon) } else { - avatar.visibility = GONE + icon.visibility = GONE } val minTextViewWidth: Int val maxTextViewWidth: Int - if (avatar.visibility == VISIBLE) { - minTextViewWidth = minFlyoutWidth - avatar.width - flyoutPadding * 2 - maxTextViewWidth = maxFlyoutWidth - avatar.width - flyoutPadding * 2 + if (icon.visibility == VISIBLE) { + minTextViewWidth = minFlyoutWidth - icon.width - flyoutPadding * 2 + maxTextViewWidth = maxFlyoutWidth - icon.width - flyoutPadding * 2 } else { // when there's no avatar, the width of the text view is constant, so we're setting the // min and max to the same value @@ -221,13 +221,13 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly maxTextViewWidth = minTextViewWidth } - if (flyoutMessage.senderName.isEmpty()) { - sender.visibility = GONE + if (flyoutMessage.title.isEmpty()) { + title.visibility = GONE } else { - sender.minWidth = minTextViewWidth - sender.maxWidth = maxTextViewWidth - sender.text = flyoutMessage.senderName - sender.visibility = VISIBLE + title.minWidth = minTextViewWidth + title.maxWidth = maxTextViewWidth + title.text = flyoutMessage.title + title.visibility = VISIBLE } message.minWidth = minTextViewWidth @@ -240,17 +240,17 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly expansionProgress = fraction updateTranslationForAnimation(message) - updateTranslationForAnimation(sender) - updateTranslationForAnimation(avatar) + updateTranslationForAnimation(title) + updateTranslationForAnimation(icon) // start fading in the content only after we're past the threshold val alpha = ((expansionProgress - MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA) / (1f - MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA)) .coerceIn(0f, 1f) - sender.alpha = alpha + title.alpha = alpha message.alpha = alpha - avatar.alpha = alpha + icon.alpha = alpha translationZ = collapsedElevation + (flyoutElevation - collapsedElevation) * expansionProgress @@ -368,7 +368,7 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly ) ) backgroundColor = ta.getColor(0, defaultBackgroundColor) - sender.setTextColor(ta.getColor(1, defaultTextColor)) + title.setTextColor(ta.getColor(1, defaultTextColor)) message.setTextColor(ta.getColor(2, defaultTextColor)) ta.recycle() backgroundPaint.color = backgroundColor diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt index 6aba6a3c78..11c7fe9823 100644 --- a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt +++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt @@ -63,12 +63,7 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { val flyout = BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false)) flyout.showFromCollapsed( - BubbleBarFlyoutMessage( - senderAvatar = null, - senderName = "sender", - message = "message", - isGroupChat = false, - ) + BubbleBarFlyoutMessage(icon = null, title = "sender", message = "message") ) {} flyout.updateExpansionProgress(1f) flyout @@ -82,12 +77,7 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { val flyout = BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true)) flyout.showFromCollapsed( - BubbleBarFlyoutMessage( - senderAvatar = null, - senderName = "sender", - message = "message", - isGroupChat = false, - ) + BubbleBarFlyoutMessage(icon = null, title = "sender", message = "message") ) {} flyout.updateExpansionProgress(1f) flyout @@ -102,10 +92,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true)) flyout.showFromCollapsed( BubbleBarFlyoutMessage( - senderAvatar = null, - senderName = "sender", + icon = null, + title = "sender", message = "really, really, really, really, really long message. like really.", - isGroupChat = false, ) ) {} flyout.updateExpansionProgress(1f) @@ -121,10 +110,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false)) flyout.showFromCollapsed( BubbleBarFlyoutMessage( - senderAvatar = ColorDrawable(Color.RED), - senderName = "sender", + icon = ColorDrawable(Color.RED), + title = "sender", message = "message", - isGroupChat = true, ) ) {} flyout.updateExpansionProgress(1f) @@ -140,10 +128,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true)) flyout.showFromCollapsed( BubbleBarFlyoutMessage( - senderAvatar = ColorDrawable(Color.RED), - senderName = "sender", + icon = ColorDrawable(Color.RED), + title = "sender", message = "message", - isGroupChat = true, ) ) {} flyout.updateExpansionProgress(1f) @@ -159,10 +146,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true)) flyout.showFromCollapsed( BubbleBarFlyoutMessage( - senderAvatar = ColorDrawable(Color.RED), - senderName = "sender", + icon = ColorDrawable(Color.RED), + title = "sender", message = "really, really, really, really, really long message. like really.", - isGroupChat = true, ) ) {} flyout.updateExpansionProgress(1f) @@ -178,10 +164,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true)) flyout.showFromCollapsed( BubbleBarFlyoutMessage( - senderAvatar = ColorDrawable(Color.RED), - senderName = "sender", + icon = ColorDrawable(Color.RED), + title = "sender", message = "collapsed on left", - isGroupChat = true, ) ) {} flyout.updateExpansionProgress(0f) @@ -197,10 +182,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false)) flyout.showFromCollapsed( BubbleBarFlyoutMessage( - senderAvatar = ColorDrawable(Color.RED), - senderName = "sender", + icon = ColorDrawable(Color.RED), + title = "sender", message = "collapsed on right", - isGroupChat = true, ) ) {} flyout.updateExpansionProgress(0f) @@ -222,10 +206,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { ) flyout.showFromCollapsed( BubbleBarFlyoutMessage( - senderAvatar = ColorDrawable(Color.RED), - senderName = "sender", + icon = ColorDrawable(Color.RED), + title = "sender", message = "expanded 90% on left", - isGroupChat = true, ) ) {} flyout.updateExpansionProgress(0.9f) @@ -247,10 +230,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) { ) flyout.showFromCollapsed( BubbleBarFlyoutMessage( - senderAvatar = ColorDrawable(Color.RED), - senderName = "sender", + icon = ColorDrawable(Color.RED), + title = "sender", message = "expanded 80% on right", - isGroupChat = true, ) ) {} flyout.updateExpansionProgress(0.8f) diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt index d857ae5579..fdafce082b 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt @@ -39,8 +39,7 @@ class BubbleBarFlyoutControllerTest { private lateinit var flyoutController: BubbleBarFlyoutController private lateinit var flyoutContainer: FrameLayout private val context = ApplicationProvider.getApplicationContext() - private val flyoutMessage = - BubbleBarFlyoutMessage(senderAvatar = null, "sender name", "message", isGroupChat = false) + private val flyoutMessage = BubbleBarFlyoutMessage(icon = null, "sender name", "message") private var onLeft = true @Before @@ -87,7 +86,7 @@ class BubbleBarFlyoutControllerTest { flyoutController.setUpFlyout(flyoutMessage) assertThat(flyoutContainer.childCount).isEqualTo(1) val flyout = flyoutContainer.getChildAt(0) - val sender = flyout.findViewById(R.id.bubble_flyout_name) + val sender = flyout.findViewById(R.id.bubble_flyout_title) assertThat(sender.text).isEqualTo("sender name") val message = flyout.findViewById(R.id.bubble_flyout_text) assertThat(message.text).isEqualTo("message") From ee98cd4bdf739c2f93f7cf0a20764acb2a837b0c Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Thu, 3 Oct 2024 23:16:29 +0000 Subject: [PATCH 7/8] Don't invalidate swipe handler until parallel anim finishes - When invalidateHandler() is called, it calls endRunningWindowAnim() which includes mParallelRunningAnim. This causes a jump if mParallelRunningAnim was not already finished, so we now wait to invalidate the handler after mParallelRunningAnim ends. Flag: EXEMPT bugfix Test: AbsSwipeUpHandlerTestCase (added two tests for this) Fixes: 370208192 Change-Id: I37ed281a993b1d2fa3634754378314511f3295f0 --- .../android/quickstep/AbsSwipeUpHandler.java | 8 ++- .../quickstep/AbsSwipeUpHandlerTestCase.java | 60 ++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 864328f883..211d20969b 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -237,6 +237,8 @@ public abstract class AbsSwipeUpHandler< getNextStateFlag("STATE_SCALED_CONTROLLER_HOME"); private static final int STATE_SCALED_CONTROLLER_RECENTS = getNextStateFlag("STATE_SCALED_CONTROLLER_RECENTS"); + private static final int STATE_PARALLEL_ANIM_FINISHED = + getNextStateFlag("STATE_PARALLEL_ANIM_FINISHED"); protected static final int STATE_HANDLER_INVALIDATED = getNextStateFlag("STATE_HANDLER_INVALIDATED"); @@ -453,7 +455,8 @@ public abstract class AbsSwipeUpHandler< mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED | STATE_SCALED_CONTROLLER_HOME, this::finishCurrentTransitionToHome); - mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED, + mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED + | STATE_PARALLEL_ANIM_FINISHED, this::reset); mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED @@ -1540,9 +1543,12 @@ public abstract class AbsSwipeUpHandler< @Override public void onAnimationEnd(Animator animation) { mParallelRunningAnim = null; + mStateCallback.setStateOnUiThread(STATE_PARALLEL_ANIM_FINISHED); } }); mParallelRunningAnim.start(); + } else { + mStateCallback.setStateOnUiThread(STATE_PARALLEL_ANIM_FINISHED); } } diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java index 87a7cdae14..52c1f8945a 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java @@ -16,15 +16,23 @@ package com.android.quickstep; +import static com.android.quickstep.AbsSwipeUpHandler.STATE_HANDLER_INVALIDATED; + +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.animation.ValueAnimator; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; @@ -57,6 +65,7 @@ import com.android.systemui.shared.system.InputConsumerController; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -221,7 +230,7 @@ public abstract class AbsSwipeUpHandlerTestCase< runOnMainSync(() -> { absSwipeUpHandler.startNewTask(unused -> {}); - verify(mRecentsAnimationController).finish(anyBoolean(), any()); + verifyRecentsAnimationFinishedAndCallCallback(); }); } @@ -231,10 +240,57 @@ public abstract class AbsSwipeUpHandlerTestCase< runOnMainSync(() -> { verify(mRecentsAnimationController).detachNavigationBarFromApp(true); - verify(mRecentsAnimationController).finish(anyBoolean(), any(), anyBoolean()); + verifyRecentsAnimationFinishedAndCallCallback(); }); } + @Test + public void testHomeGesture_invalidatesHandlerAfterParallelAnim() { + ValueAnimator parallelAnim = new ValueAnimator(); + parallelAnim.setRepeatCount(ValueAnimator.INFINITE); + when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any())) + .thenReturn(parallelAnim); + SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME); + runOnMainSync(() -> { + parallelAnim.start(); + verifyRecentsAnimationFinishedAndCallCallback(); + assertFalse(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)); + parallelAnim.end(); + assertTrue(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)); + }); + } + + @Test + public void testHomeGesture_invalidatesHandlerIfNoParallelAnim() { + when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any())) + .thenReturn(null); + SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME); + runOnMainSync(() -> { + verifyRecentsAnimationFinishedAndCallCallback(); + assertTrue(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)); + }); + } + + /** + * Verifies that RecentsAnimationController#finish() is called, and captures and runs any + * callback that was passed to it. This ensures that STATE_CURRENT_TASK_FINISHED is correctly + * set for example. + */ + private void verifyRecentsAnimationFinishedAndCallCallback() { + ArgumentCaptor finishCallback = ArgumentCaptor.forClass(Runnable.class); + // Check if the 2 parameter method is called. + verify(mRecentsAnimationController, atLeast(0)).finish( + anyBoolean(), finishCallback.capture()); + if (finishCallback.getAllValues().isEmpty()) { + // Check if the 3 parameter method is called. + verify(mRecentsAnimationController).finish( + anyBoolean(), finishCallback.capture(), anyBoolean()); + } + if (finishCallback.getValue() != null) { + finishCallback.getValue().run(); + } + } + private SWIPE_HANDLER createSwipeUpHandlerForGesture(GestureState.GestureEndTarget endTarget) { boolean isQuickSwitch = endTarget == GestureState.GestureEndTarget.NEW_TASK; From f3f182a7efd8284caa4ef77da873bc3b985f27f1 Mon Sep 17 00:00:00 2001 From: Schneider Victor-Tulias Date: Mon, 30 Sep 2024 12:03:35 -0400 Subject: [PATCH 8/8] Add Recents Window logs to ProtoLog Flag: com.android.launcher3.enable_recents_window_proto_log Bug: 292269949 Test: checked protologs in winscope Change-Id: Iaafe58a655e9ad7293b579c74b7e7ecaa2df1ea5 --- aconfig/launcher.aconfig | 7 ++ .../fallback/window/RecentsWindowManager.kt | 66 ++++++++----------- .../util/QuickstepProtoLogGroup.java | 5 +- .../util/RecentsWindowProtoLogProxy.java | 60 +++++++++++++++++ 4 files changed, 99 insertions(+), 39 deletions(-) create mode 100644 quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig index 9fb5b7b061..76b119f579 100644 --- a/aconfig/launcher.aconfig +++ b/aconfig/launcher.aconfig @@ -434,6 +434,13 @@ flag { bug: "293182501" } +flag { + name: "enable_recents_window_proto_log" + namespace: "launcher" + description: "Enables tracking recents window logs in ProtoLog" + bug: "292269949" +} + flag { name: "coordinate_workspace_scale" diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt index 8ce61f5b5c..b5830fdc13 100644 --- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt +++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt @@ -18,11 +18,9 @@ package com.android.quickstep.fallback.window import android.animation.AnimatorSet import android.app.ActivityOptions -import android.content.ComponentName import android.content.Context import android.content.LocusId import android.os.Bundle -import android.util.Log import android.view.KeyEvent import android.view.LayoutInflater import android.view.MotionEvent @@ -30,7 +28,6 @@ import android.view.RemoteAnimationAdapter import android.view.RemoteAnimationTarget import android.view.SurfaceControl import android.view.View -import android.view.Window import android.view.WindowManager import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY import android.window.RemoteTransition @@ -63,6 +60,7 @@ import com.android.quickstep.fallback.RecentsState.HOME import com.android.quickstep.fallback.RecentsState.MODAL_TASK import com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT import com.android.quickstep.util.RecentsAtomicAnimationFactory +import com.android.quickstep.util.RecentsWindowProtoLogProxy import com.android.quickstep.util.SplitSelectStateController import com.android.quickstep.util.TISBindHelper import com.android.quickstep.views.OverviewActionsView @@ -71,9 +69,12 @@ import com.android.quickstep.views.RecentsViewContainer import java.util.function.Predicate /** - * Class that will manage RecentsView lifecycle within a window and interface correctly - * where needed. This allows us to run RecentsView in a window where needed. - * todo: b/365776320, b/365777482 + * Class that will manage RecentsView lifecycle within a window and interface correctly where + * needed. This allows us to run RecentsView in a window where needed. todo: b/365776320, + * b/365777482 + * + * To add new protologs, see [RecentsWindowProtoLogProxy]. To enable logging to logcat, see + * [QuickstepProtoLogGroup.Constants.DEBUG_RECENTS_WINDOW] */ class RecentsWindowManager(context: Context) : RecentsWindowContext(context), RecentsViewContainer, StatefulContainer { @@ -81,12 +82,12 @@ class RecentsWindowManager(context: Context) : companion object { private const val HOME_APPEAR_DURATION: Long = 250 private const val TAG = "RecentsWindowManager" - private const val DEBUG = false } protected var recentsView: FallbackRecentsView? = null private val windowContext: Context = createWindowContext(TYPE_APPLICATION_OVERLAY, null) - private val windowManager: WindowManager = windowContext.getSystemService(WindowManager::class.java)!! + private val windowManager: WindowManager = + windowContext.getSystemService(WindowManager::class.java)!! private var layoutInflater: LayoutInflater = LayoutInflater.from(this).cloneInContext(this) private var stateManager: StateManager = StateManager(this, RecentsState.BG_LAUNCHER) @@ -138,11 +139,11 @@ class RecentsWindowManager(context: Context) : private val mAnimationToHomeFactory = RemoteAnimationFactory { - _: Int, - appTargets: Array?, - wallpaperTargets: Array?, - nonAppTargets: Array?, - result: LauncherAnimationRunner.AnimationResult? -> + _: Int, + appTargets: Array?, + wallpaperTargets: Array?, + nonAppTargets: Array?, + result: LauncherAnimationRunner.AnimationResult? -> val controller = getStateManager().createAnimationToNewWorkspace(BG_LAUNCHER, HOME_APPEAR_DURATION) controller.dispatchOnStart() @@ -171,6 +172,7 @@ class RecentsWindowManager(context: Context) : } fun cleanup() { + RecentsWindowProtoLogProxy.logCleanup(isShown) if (isShown) { windowManager.removeViewImmediate(windowView) isShown = false @@ -178,6 +180,7 @@ class RecentsWindowManager(context: Context) : } fun startRecentsWindow() { + RecentsWindowProtoLogProxy.logStartRecentsWindow(isShown, windowView == null) if (isShown) return if (windowView == null) { windowView = layoutInflater.inflate(R.layout.fallback_recents_activity, null) @@ -245,37 +248,24 @@ class RecentsWindowManager(context: Context) : override fun onStateSetStart(state: RecentsState?) { super.onStateSetStart(state) - logState(state, "state started:") + RecentsWindowProtoLogProxy.logOnStateSetStart(getStateName(state)) } override fun onStateSetEnd(state: RecentsState?) { super.onStateSetEnd(state) - logState(state, "state ended:") + RecentsWindowProtoLogProxy.logOnStateSetEnd(getStateName(state)) } - private fun logState(state: RecentsState?, prefix: String) { - if (!DEBUG) { - return - } - if (state != null) { - when (state) { - DEFAULT -> Log.d(TAG, prefix + "default") - MODAL_TASK -> { - Log.d(TAG, prefix + "MODAL_TASK") - } - BACKGROUND_APP -> { - Log.d(TAG, prefix + "BACKGROUND_APP") - } - HOME -> { - Log.d(TAG, prefix + "HOME") - } - BG_LAUNCHER -> { - Log.d(TAG, prefix + "BG_LAUNCHER") - } - OVERVIEW_SPLIT_SELECT -> { - Log.d(TAG, prefix + "OVERVIEW_SPLIT_SELECT") - } - } + private fun getStateName(state: RecentsState?): String { + return when (state) { + null -> "NULL" + DEFAULT -> "default" + MODAL_TASK -> "MODAL_TASK" + BACKGROUND_APP -> "BACKGROUND_APP" + HOME -> "HOME" + BG_LAUNCHER -> "BG_LAUNCHER" + OVERVIEW_SPLIT_SELECT -> "OVERVIEW_SPLIT_SELECT" + else -> "ordinal=" + state.ordinal } } diff --git a/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java b/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java index d0863f8f6b..7b81b9a2e0 100644 --- a/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java +++ b/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java @@ -26,7 +26,8 @@ import java.util.UUID; /** Enums used to interface with the ProtoLog API. */ public enum QuickstepProtoLogGroup implements IProtoLogGroup { - ACTIVE_GESTURE_LOG(true, true, false, "ActiveGestureLog"); + ACTIVE_GESTURE_LOG(true, true, false, "ActiveGestureLog"), + RECENTS_WINDOW(true, true, Constants.DEBUG_RECENTS_WINDOW, "RecentsWindow"); private final boolean mEnabled; private volatile boolean mLogToProto; @@ -95,6 +96,8 @@ public enum QuickstepProtoLogGroup implements IProtoLogGroup { private static final class Constants { + private static final boolean DEBUG_RECENTS_WINDOW = false; + private static final int LOG_START_ID = (int) (UUID.nameUUIDFromBytes(QuickstepProtoLogGroup.class.getName().getBytes()) .getMostSignificantBits() % Integer.MAX_VALUE); diff --git a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java new file mode 100644 index 0000000000..f54ad67697 --- /dev/null +++ b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.util; + +import static com.android.launcher3.Flags.enableRecentsWindowProtoLog; +import static com.android.quickstep.util.QuickstepProtoLogGroup.RECENTS_WINDOW; + +import androidx.annotation.NonNull; + +import com.android.internal.protolog.ProtoLog; +import com.android.internal.protolog.common.IProtoLogGroup; + +/** + * Proxy class used for Recents Window ProtoLog support. + *

+ * This file will have all of its static strings in the + * {@link ProtoLog#d(IProtoLogGroup, String, Object...)} calls replaced by dynamic code/strings. + *

+ * When a new Recents Window log needs to be added to the codebase, add it here under a new unique + * method. Or, if an existing entry needs to be modified, simply update it here. + */ +public class RecentsWindowProtoLogProxy { + + public static void logOnStateSetStart(@NonNull String stateName) { + if (!enableRecentsWindowProtoLog()) return; + ProtoLog.d(RECENTS_WINDOW, "onStateSetStart: %s", stateName); + } + + public static void logOnStateSetEnd(@NonNull String stateName) { + if (!enableRecentsWindowProtoLog()) return; + ProtoLog.d(RECENTS_WINDOW, "onStateSetEnd: %s", stateName); + } + + public static void logStartRecentsWindow(boolean isShown, boolean windowViewIsNull) { + if (!enableRecentsWindowProtoLog()) return; + ProtoLog.d(RECENTS_WINDOW, + "Starting recents window: isShow= %b, windowViewIsNull=%b", + isShown, + windowViewIsNull); + } + + public static void logCleanup(boolean isShown) { + if (!enableRecentsWindowProtoLog()) return; + ProtoLog.d(RECENTS_WINDOW, "Cleaning up recents window: isShow= %b", isShown); + } +}