mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-03 01:16:49 +00:00
Revert submission 31490053-DisplayControllRefactorForCD Reason for revert: Leak is displayProvider Reverted changes: /q/submissionid:31490053-DisplayControllRefactorForCD Change-Id: Ib65150cdde765be009b5c4ce960f9fe025de9e60
438 lines
17 KiB
Kotlin
438 lines
17 KiB
Kotlin
/*
|
|
* Copyright (C) 2024 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.launcher3.taskbar
|
|
|
|
import android.animation.AnimatorTestRule
|
|
import android.content.ComponentName
|
|
import android.content.Intent
|
|
import android.platform.test.annotations.DisableFlags
|
|
import android.platform.test.annotations.EnableFlags
|
|
import android.platform.test.flag.junit.SetFlagsRule
|
|
import androidx.test.core.app.ApplicationProvider
|
|
import com.android.launcher3.Flags.FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR
|
|
import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
|
|
import com.android.launcher3.R
|
|
import com.android.launcher3.dagger.LauncherAppSingleton
|
|
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
|
|
import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatItems
|
|
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
|
|
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
|
|
import com.android.launcher3.taskbar.rules.DisplayControllerModule
|
|
import com.android.launcher3.taskbar.rules.MockedRecentsModelHelper
|
|
import com.android.launcher3.taskbar.rules.MockedRecentsModelTestRule
|
|
import com.android.launcher3.taskbar.rules.SandboxParams
|
|
import com.android.launcher3.taskbar.rules.TaskbarModeRule
|
|
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
|
|
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
|
|
import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
|
|
import com.android.launcher3.taskbar.rules.TaskbarSandboxComponent
|
|
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
|
|
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
|
|
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
|
|
import com.android.launcher3.util.AllModulesForTest
|
|
import com.android.launcher3.util.FakePrefsModule
|
|
import com.android.launcher3.util.LauncherMultivalentJUnit
|
|
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
|
|
import com.android.launcher3.util.TestUtil.getOnUiThread
|
|
import com.android.quickstep.RecentsModel
|
|
import com.android.quickstep.SystemUiProxy
|
|
import com.android.quickstep.util.DesktopTask
|
|
import com.android.systemui.shared.recents.model.Task
|
|
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
|
|
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS
|
|
import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR
|
|
import com.android.wm.shell.desktopmode.IDesktopTaskListener
|
|
import com.google.common.truth.Truth.assertThat
|
|
import dagger.BindsInstance
|
|
import dagger.Component
|
|
import org.junit.Before
|
|
import org.junit.Rule
|
|
import org.junit.Test
|
|
import org.junit.runner.RunWith
|
|
import org.mockito.kotlin.anyOrNull
|
|
import org.mockito.kotlin.doAnswer
|
|
import org.mockito.kotlin.spy
|
|
import org.mockito.kotlin.whenever
|
|
|
|
@RunWith(LauncherMultivalentJUnit::class)
|
|
@EmulatedDevices(["pixelTablet2023"])
|
|
@EnableFlags(
|
|
FLAG_TASKBAR_OVERFLOW,
|
|
FLAG_ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS,
|
|
FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
|
|
FLAG_ENABLE_BUBBLE_BAR,
|
|
)
|
|
@DisableFlags(FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR)
|
|
class TaskbarOverflowTest {
|
|
@get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
|
|
|
|
val mockRecentsModelHelper: MockedRecentsModelHelper = MockedRecentsModelHelper()
|
|
|
|
@get:Rule(order = 1)
|
|
val context =
|
|
TaskbarWindowSandboxContext.create(
|
|
SandboxParams(
|
|
{
|
|
spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) { proxy ->
|
|
doAnswer { desktopTaskListener = it.getArgument(0) }
|
|
.whenever(proxy)
|
|
.setDesktopTaskListener(anyOrNull())
|
|
}
|
|
},
|
|
DaggerTaskbarOverflowComponent.builder()
|
|
.bindRecentsModel(mockRecentsModelHelper.mockRecentsModel),
|
|
)
|
|
)
|
|
|
|
@get:Rule(order = 2) val recentsModel = MockedRecentsModelTestRule(mockRecentsModelHelper)
|
|
|
|
@get:Rule(order = 3) val taskbarModeRule = TaskbarModeRule(context)
|
|
|
|
@get:Rule(order = 4) val animatorTestRule = AnimatorTestRule(this)
|
|
|
|
@get:Rule(order = 5)
|
|
val taskbarUnitTestRule = TaskbarUnitTestRule(this, context, this::onControllersInitialized)
|
|
|
|
@InjectController lateinit var taskbarViewController: TaskbarViewController
|
|
@InjectController lateinit var recentAppsController: TaskbarRecentAppsController
|
|
@InjectController lateinit var bubbleBarViewController: BubbleBarViewController
|
|
@InjectController lateinit var bubbleStashController: BubbleStashController
|
|
|
|
private var desktopTaskListener: IDesktopTaskListener? = null
|
|
|
|
private var currentControllerInitCallback: () -> Unit = {}
|
|
set(value) {
|
|
runOnMainSync { value.invoke() }
|
|
field = value
|
|
}
|
|
|
|
private fun onControllersInitialized() {
|
|
runOnMainSync {
|
|
if (!recentAppsController.canShowRunningApps) {
|
|
recentAppsController.onDestroy()
|
|
recentAppsController.canShowRunningApps = true
|
|
recentAppsController.init(taskbarUnitTestRule.activityContext.controllers)
|
|
}
|
|
|
|
currentControllerInitCallback.invoke()
|
|
}
|
|
}
|
|
|
|
@Before
|
|
fun ensureRunningAppsShowing() {
|
|
runOnMainSync { recentsModel.resolvePendingTaskRequests() }
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(PINNED)
|
|
fun testTaskbarWithMaxNumIcons_pinned() {
|
|
addRunningAppsAndVerifyOverflowState(0)
|
|
|
|
assertThat(taskbarIconsCentered).isTrue()
|
|
assertThat(taskbarEndMargin).isAtLeast(navButtonEndSpacing)
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(TRANSIENT)
|
|
fun testTaskbarWithMaxNumIcons_transient() {
|
|
addRunningAppsAndVerifyOverflowState(0)
|
|
|
|
assertThat(taskbarIconsCentered).isTrue()
|
|
assertThat(taskbarEndMargin).isAtLeast(navButtonEndSpacing)
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(PINNED)
|
|
fun testOverflownTaskbar_pinned() {
|
|
addRunningAppsAndVerifyOverflowState(5)
|
|
|
|
assertThat(taskbarIconsCentered).isTrue()
|
|
assertThat(taskbarEndMargin).isAtLeast(navButtonEndSpacing)
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(TRANSIENT)
|
|
fun testOverflownTaskbar_transient() {
|
|
addRunningAppsAndVerifyOverflowState(5)
|
|
|
|
assertThat(taskbarIconsCentered).isTrue()
|
|
assertThat(taskbarEndMargin).isAtLeast(navButtonEndSpacing)
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(PINNED)
|
|
fun testOverflownTaskbarWithNoSpaceForRecentApps_pinned() {
|
|
val initialIconCount = currentNumberOfTaskbarIcons.coerceAtLeast(2)
|
|
|
|
// Create two "recent" desktop tasks, and then add enough hotseat items so the taskbar
|
|
// reaches max number of items with hotseat item icons, all apps and divider icons only.
|
|
// I.e. so all desktop tasks are in taskbar overflow.
|
|
createDesktopTask(2)
|
|
runOnMainSync {
|
|
val taskbarView: TaskbarView =
|
|
taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
|
|
taskbarView.updateItems(
|
|
createHotseatItems(maxNumberOfTaskbarIcons - initialIconCount),
|
|
recentAppsController.shownTasks,
|
|
)
|
|
}
|
|
|
|
// Verify that taskbar overflow view is shown (eventhough it exceeds max taskbar icons).
|
|
assertThat(currentNumberOfTaskbarIcons).isEqualTo(maxNumberOfTaskbarIcons + 1)
|
|
assertThat(taskbarOverflowIconIndex).isEqualTo(maxNumberOfTaskbarIcons)
|
|
assertThat(overflowItems).containsExactlyElementsIn(0..1)
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(PINNED)
|
|
fun testOverflownTaskbarWithNoSpaceForRecentApps_singleRecent_pinned() {
|
|
val initialIconCount = currentNumberOfTaskbarIcons.coerceAtLeast(2)
|
|
|
|
// Create a "recent" desktop task, and then add enough hotseat items so the taskbar
|
|
// reaches max number of items with hotseat item icons, all apps and divider icons only.
|
|
// I.e. so the single desktop tasks is in taskbar overflow.
|
|
createDesktopTask(1)
|
|
runOnMainSync {
|
|
val taskbarView: TaskbarView =
|
|
taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
|
|
taskbarView.updateItems(
|
|
createHotseatItems(maxNumberOfTaskbarIcons - initialIconCount),
|
|
recentAppsController.shownTasks,
|
|
)
|
|
}
|
|
|
|
// Verify that recent task is shown (eventhough it exceeds max taskbar icons), and that
|
|
// the taskbar overflow view is not added for the single recent app.
|
|
assertThat(currentNumberOfTaskbarIcons).isEqualTo(maxNumberOfTaskbarIcons + 1)
|
|
assertThat(taskbarOverflowIconIndex).isEqualTo(-1)
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(PINNED)
|
|
fun testBubbleBarReducesTaskbarMaxNumIcons_pinned() {
|
|
var initialMaxNumIconViews = maxNumberOfTaskbarIcons
|
|
assertThat(initialMaxNumIconViews).isGreaterThan(0)
|
|
|
|
currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
|
|
|
|
val maxNumIconViews = addRunningAppsAndVerifyOverflowState(2)
|
|
assertThat(maxNumIconViews).isLessThan(initialMaxNumIconViews)
|
|
|
|
assertThat(taskbarIconsCentered).isTrue()
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(TRANSIENT)
|
|
fun testBubbleBarReducesTaskbarMaxNumIcons_transient() {
|
|
var initialMaxNumIconViews = maxNumberOfTaskbarIcons
|
|
assertThat(initialMaxNumIconViews).isGreaterThan(0)
|
|
|
|
currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
|
|
|
|
val maxNumIconViews = addRunningAppsAndVerifyOverflowState(2)
|
|
assertThat(maxNumIconViews).isLessThan(initialMaxNumIconViews)
|
|
|
|
assertThat(taskbarIconsCentered).isTrue()
|
|
assertThat(taskbarEndMargin)
|
|
.isAtLeast(
|
|
navButtonEndSpacing +
|
|
bubbleBarViewController.collapsedWidthWithMaxVisibleBubbles.toInt()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(TRANSIENT)
|
|
fun testBubbleBarReducesTaskbarMaxNumIcons_transientBubbleInitiallyStashed() {
|
|
var initialMaxNumIconViews = maxNumberOfTaskbarIcons
|
|
assertThat(initialMaxNumIconViews).isGreaterThan(0)
|
|
currentControllerInitCallback = {
|
|
bubbleStashController.stashBubbleBarImmediate()
|
|
bubbleBarViewController.setHiddenForBubbles(false)
|
|
}
|
|
|
|
val maxNumIconViews = addRunningAppsAndVerifyOverflowState(2)
|
|
assertThat(maxNumIconViews).isLessThan(initialMaxNumIconViews)
|
|
|
|
assertThat(taskbarIconsCentered).isTrue()
|
|
assertThat(taskbarEndMargin)
|
|
.isAtLeast(
|
|
navButtonEndSpacing +
|
|
bubbleBarViewController.collapsedWidthWithMaxVisibleBubbles.toInt()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(TRANSIENT)
|
|
fun testStashingBubbleBarMaintainsMaxNumIcons_transient() {
|
|
currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
|
|
|
|
val initialNumIcons = currentNumberOfTaskbarIcons
|
|
val maxNumIconViews = addRunningAppsAndVerifyOverflowState(2)
|
|
|
|
runOnMainSync { bubbleStashController.stashBubbleBarImmediate() }
|
|
assertThat(maxNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
|
|
assertThat(currentNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
|
|
assertThat(taskbarOverflowIconIndex).isEqualTo(initialNumIcons.coerceAtLeast(2))
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(PINNED)
|
|
fun testHidingBubbleBarIncreasesMaxNumIcons_pinned() {
|
|
currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
|
|
|
|
val initialNumIcons = currentNumberOfTaskbarIcons
|
|
val initialMaxNumIconViews = addRunningAppsAndVerifyOverflowState(5)
|
|
|
|
currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(true) }
|
|
runOnMainSync { animatorTestRule.advanceTimeBy(150) }
|
|
|
|
val maxNumIconViews = maxNumberOfTaskbarIcons
|
|
assertThat(maxNumIconViews).isGreaterThan(initialMaxNumIconViews)
|
|
assertThat(currentNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
|
|
assertThat(taskbarOverflowIconIndex).isEqualTo(initialNumIcons.coerceAtLeast(2))
|
|
|
|
assertThat(taskbarIconsCentered).isTrue()
|
|
}
|
|
|
|
@Test
|
|
@TaskbarMode(TRANSIENT)
|
|
fun testHidingBubbleBarIncreasesMaxNumIcons_transient() {
|
|
currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(false) }
|
|
|
|
val initialNumIcons = currentNumberOfTaskbarIcons
|
|
val initialMaxNumIconViews = addRunningAppsAndVerifyOverflowState(5)
|
|
|
|
currentControllerInitCallback = { bubbleBarViewController.setHiddenForBubbles(true) }
|
|
runOnMainSync { animatorTestRule.advanceTimeBy(150) }
|
|
|
|
val maxNumIconViews = maxNumberOfTaskbarIcons
|
|
assertThat(maxNumIconViews).isGreaterThan(initialMaxNumIconViews)
|
|
assertThat(currentNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
|
|
assertThat(taskbarOverflowIconIndex).isEqualTo(initialNumIcons.coerceAtLeast(2))
|
|
|
|
assertThat(taskbarIconsCentered).isTrue()
|
|
}
|
|
|
|
private fun createDesktopTask(tasksToAdd: Int) {
|
|
val tasks =
|
|
(0..<tasksToAdd).map {
|
|
Task(Task.TaskKey(it, 0, Intent(), ComponentName("", ""), 0, 2000))
|
|
}
|
|
recentsModel.updateRecentTasks(listOf(DesktopTask(tasks)))
|
|
desktopTaskListener?.onTasksVisibilityChanged(
|
|
context.virtualDisplay.display.displayId,
|
|
tasksToAdd,
|
|
)
|
|
runOnMainSync { recentsModel.resolvePendingTaskRequests() }
|
|
}
|
|
|
|
private val navButtonEndSpacing: Int
|
|
get() {
|
|
return taskbarUnitTestRule.activityContext.resources.getDimensionPixelSize(
|
|
taskbarUnitTestRule.activityContext.deviceProfile.inv.inlineNavButtonsEndSpacing
|
|
)
|
|
}
|
|
|
|
private val taskbarOverflowIconIndex: Int
|
|
get() {
|
|
return getOnUiThread {
|
|
taskbarViewController.iconViews.indexOfFirst { it is TaskbarOverflowView }
|
|
}
|
|
}
|
|
|
|
private val maxNumberOfTaskbarIcons: Int
|
|
get() = getOnUiThread { taskbarViewController.maxNumIconViews }
|
|
|
|
private val currentNumberOfTaskbarIcons: Int
|
|
get() = getOnUiThread { taskbarViewController.iconViews.size }
|
|
|
|
private val taskbarIconsCentered: Boolean
|
|
get() {
|
|
return getOnUiThread {
|
|
val iconLayoutBounds =
|
|
taskbarViewController.transientTaskbarIconLayoutBoundsInParent
|
|
val availableWidth = taskbarUnitTestRule.activityContext.deviceProfile.widthPx
|
|
iconLayoutBounds.left - (availableWidth - iconLayoutBounds.right) < 2
|
|
}
|
|
}
|
|
|
|
private val taskbarEndMargin: Int
|
|
get() {
|
|
return getOnUiThread {
|
|
taskbarUnitTestRule.activityContext.deviceProfile.widthPx -
|
|
taskbarViewController.transientTaskbarIconLayoutBoundsInParent.right
|
|
}
|
|
}
|
|
|
|
private val overflowItems: List<Int>
|
|
get() {
|
|
return getOnUiThread {
|
|
val overflowIcon =
|
|
taskbarViewController.iconViews.firstOrNull { it is TaskbarOverflowView }
|
|
|
|
if (overflowIcon is TaskbarOverflowView) {
|
|
overflowIcon.itemIds
|
|
} else {
|
|
emptyList()
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds enough running apps for taskbar to enter overflow of `targetOverflowSize`, and verifies
|
|
* * max number of icons in the taskbar remains unchanged
|
|
* * number of icons in the taskbar is at most max number of icons
|
|
* * whether the taskbar overflow icon is shown, and its position in taskbar.
|
|
*
|
|
* Returns max number of icons.
|
|
*/
|
|
private fun addRunningAppsAndVerifyOverflowState(targetOverflowSize: Int): Int {
|
|
val maxNumIconViews = maxNumberOfTaskbarIcons
|
|
assertThat(maxNumIconViews).isGreaterThan(0)
|
|
// Assume there are at least all apps and divider icon, as they would appear once running
|
|
// apps are added, even if not present initially.
|
|
val initialIconCount = currentNumberOfTaskbarIcons.coerceAtLeast(2)
|
|
assertThat(initialIconCount).isLessThan(maxNumIconViews)
|
|
|
|
createDesktopTask(maxNumIconViews - initialIconCount + targetOverflowSize)
|
|
|
|
assertThat(maxNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
|
|
assertThat(currentNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
|
|
assertThat(taskbarOverflowIconIndex)
|
|
.isEqualTo(if (targetOverflowSize > 0) initialIconCount else -1)
|
|
if (targetOverflowSize > 0) {
|
|
assertThat(overflowItems).containsExactlyElementsIn(0..targetOverflowSize)
|
|
}
|
|
return maxNumIconViews
|
|
}
|
|
}
|
|
|
|
/** TaskbarOverflowComponent used to bind the RecentsModel. */
|
|
@LauncherAppSingleton
|
|
@Component(
|
|
modules = [AllModulesForTest::class, FakePrefsModule::class, DisplayControllerModule::class]
|
|
)
|
|
interface TaskbarOverflowComponent : TaskbarSandboxComponent {
|
|
|
|
@Component.Builder
|
|
interface Builder : TaskbarSandboxComponent.Builder {
|
|
@BindsInstance fun bindRecentsModel(model: RecentsModel): Builder
|
|
|
|
override fun build(): TaskbarOverflowComponent
|
|
}
|
|
}
|