mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-20 11:18:21 +00:00
Don't trigger getRecentTasks() calls when waiting for an old call
TaskbarRecentAppsController#reloadRecentTasksIfNeeded() triggers a call to fetch Recents tasks, that call is expensive as it goes through WM Shell and the system server. If we trigger reloadRecentTasksIfNeeded() multiple times we're queueing up multiple calls to fetch Recents tasks. With this CL we avoid queueing up multiple calls, and instead just make one new call when the old one finishes. That way we can only ever have one call getRecentTasks() call triggered through reloadRecentTasksIfNeeded() posted at any one time. Bug: 415090968 Flag: com.android.window.flags.enable_taskbar_recent_tasks_throttle_bugfix Test: TaskbarRecentAppsControllerTest Change-Id: I58b66e4564af4e64837317a9de7be398395d8568
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
package com.android.launcher3.taskbar
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.window.DesktopExperienceFlags
|
||||
import android.window.DesktopModeFlags
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.launcher3.BubbleTextView.RunningAppState
|
||||
@@ -57,6 +59,9 @@ class TaskbarRecentAppsController(
|
||||
}
|
||||
}
|
||||
|
||||
val enableRecentTasksThrottle =
|
||||
DesktopExperienceFlags.ENABLE_TASKBAR_RECENT_TASKS_THROTTLE_BUGFIX.isTrue
|
||||
|
||||
// TODO(b/343532825): Add a setting to disable Recents even when the flag is on.
|
||||
var canShowRecentApps = enableRecentsInTaskbar()
|
||||
@VisibleForTesting
|
||||
@@ -175,6 +180,11 @@ class TaskbarRecentAppsController(
|
||||
// tasks again if we have already requested it and the task list has not changed
|
||||
private var taskListChangeId = -1
|
||||
|
||||
// Whether we're currently loading recents tasks
|
||||
private var loadingRecentsTasks = false
|
||||
// Whether we need to reload recents tasks when the current loading operation is finished
|
||||
private var needsRecentsTasksReload = false
|
||||
// Whether we've loaded recents tasks at least once
|
||||
private var recentTasksLoaded = false
|
||||
|
||||
fun init(taskbarControllers: TaskbarControllers, previousShownTasks: List<GroupTask>) {
|
||||
@@ -240,24 +250,34 @@ class TaskbarRecentAppsController(
|
||||
}
|
||||
|
||||
private fun reloadRecentTasksIfNeeded() {
|
||||
if (!recentsModel.isTaskListValid(taskListChangeId)) {
|
||||
taskListChangeId =
|
||||
recentsModel.getTasks(RecentsFilterState.EMPTY_FILTER) { tasks ->
|
||||
recentTasksLoaded = true
|
||||
allRecentTasks = tasks
|
||||
val oldRunningTaskdIds = runningTaskIds
|
||||
val oldMinimizedTaskIds = minimizedTaskIds
|
||||
desktopTasks =
|
||||
allRecentTasks.filterIsInstance<DesktopTask>().flatMap { it.tasks }
|
||||
val runningTasksChanged = oldRunningTaskdIds != runningTaskIds
|
||||
val minimizedTasksChanged = oldMinimizedTaskIds != minimizedTaskIds
|
||||
if (
|
||||
onRecentsOrHotseatChanged() || runningTasksChanged || minimizedTasksChanged
|
||||
) {
|
||||
controllers.taskbarViewController.commitRunningAppsToUI()
|
||||
}
|
||||
}
|
||||
if (recentsModel.isTaskListValid(taskListChangeId)) return
|
||||
if (enableRecentTasksThrottle && loadingRecentsTasks) {
|
||||
Log.v(TAG, "reloadRecentTasksIfNeeded: tried to reload while loading recents tasks")
|
||||
needsRecentsTasksReload = true
|
||||
return
|
||||
}
|
||||
Log.v(TAG, "reloadRecentTasksIfNeeded: load recents tasks")
|
||||
// Only indicate that recents tasks are loading if the enableRecentTasksThrottle flag is on
|
||||
loadingRecentsTasks = enableRecentTasksThrottle
|
||||
taskListChangeId =
|
||||
recentsModel.getTasks(RecentsFilterState.EMPTY_FILTER) { tasks ->
|
||||
loadingRecentsTasks = false
|
||||
recentTasksLoaded = true
|
||||
allRecentTasks = tasks
|
||||
val oldRunningTaskdIds = runningTaskIds
|
||||
val oldMinimizedTaskIds = minimizedTaskIds
|
||||
desktopTasks = allRecentTasks.filterIsInstance<DesktopTask>().flatMap { it.tasks }
|
||||
val runningTasksChanged = oldRunningTaskdIds != runningTaskIds
|
||||
val minimizedTasksChanged = oldMinimizedTaskIds != minimizedTaskIds
|
||||
if (onRecentsOrHotseatChanged() || runningTasksChanged || minimizedTasksChanged) {
|
||||
controllers.taskbarViewController.commitRunningAppsToUI()
|
||||
}
|
||||
if (needsRecentsTasksReload) {
|
||||
Log.v(TAG, "reloadRecentTasksIfNeeded: reload recents tasks")
|
||||
needsRecentsTasksReload = false
|
||||
reloadRecentTasksIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -462,6 +482,8 @@ class TaskbarRecentAppsController(
|
||||
get() = tasks.map { task -> task.key.packageName }
|
||||
|
||||
private companion object {
|
||||
private val TAG = "TaskbarRecentAppsController"
|
||||
|
||||
const val MAX_RECENT_TASKS = 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ import android.content.res.Resources
|
||||
import android.graphics.Rect
|
||||
import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import android.platform.test.annotations.DisableFlags
|
||||
import android.platform.test.annotations.EnableFlags
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import android.view.Display.DEFAULT_DISPLAY
|
||||
import androidx.test.annotation.UiThreadTest
|
||||
import com.android.internal.R
|
||||
@@ -60,6 +62,7 @@ import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.argumentCaptor
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.never
|
||||
import org.mockito.kotlin.times
|
||||
@@ -72,6 +75,7 @@ import org.mockito.kotlin.whenever
|
||||
class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
|
||||
|
||||
@get:Rule val mockitoRule = MockitoJUnit.rule()
|
||||
@get:Rule val setFlagsRule = SetFlagsRule()
|
||||
@get:Rule
|
||||
val disableControllerForCertainTestsWatcher =
|
||||
object : TestWatcher() {
|
||||
@@ -118,6 +122,16 @@ class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
|
||||
recentAppsController = TaskbarRecentAppsController(mockContext, mockRecentsModel)
|
||||
recentAppsController.canShowRunningApps = canShowRunningAndRecentAppsAtInit
|
||||
recentAppsController.canShowRecentApps = canShowRunningAndRecentAppsAtInit
|
||||
|
||||
// To ensure the initial getTasks() call is not seen as "loading" for the rest of the test,
|
||||
// execute its callback.
|
||||
doAnswer {
|
||||
val callback: Consumer<ArrayList<GroupTask>> = it.getArgument(1)
|
||||
callback.accept(arrayListOf())
|
||||
taskListChangeId
|
||||
}
|
||||
.whenever(mockRecentsModel)
|
||||
.getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
recentAppsController.init(taskbarControllers, emptyList())
|
||||
taskbarControllers.onPostInit()
|
||||
|
||||
@@ -148,7 +162,7 @@ class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
|
||||
runningTasks = listOf(createTask(1, RUNNING_APP_PACKAGE_1)),
|
||||
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2),
|
||||
)
|
||||
verify(mockRecentsModel, never()).getTasks(any<Consumer<List<GroupTask>>>())
|
||||
verify(mockRecentsModel, never()).getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -166,6 +180,82 @@ class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
|
||||
verify(mockRecentsModel, times(1)).getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASKBAR_RECENT_TASKS_THROTTLE_BUGFIX)
|
||||
fun recentTasksChanged_duringGetTasksLoading_dontCallGetTasks() {
|
||||
// getTasks() should have been called once from init().
|
||||
verify(mockRecentsModel, times(1)).getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
// Override the mock answer for getTasks() so it doesn't call the callback immediately.
|
||||
doAnswer { taskListChangeId }
|
||||
.whenever(mockRecentsModel)
|
||||
.getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
recentTasksChangedListener?.onRecentTasksChanged()
|
||||
// By not invoking the callback passed to getTasks() we here emulate getTasks() loading.
|
||||
|
||||
recentTasksChangedListener?.onRecentTasksChanged()
|
||||
|
||||
// getTasks() is only called two times overall (init + once more).
|
||||
verify(mockRecentsModel, times(2)).getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASKBAR_RECENT_TASKS_THROTTLE_BUGFIX)
|
||||
fun recentTasksChanged_duringGetTasksLoading_getTasksCalledWhenLoadingDone() {
|
||||
val callbackCaptor = argumentCaptor<Consumer<List<GroupTask>>>()
|
||||
// getTasks() should have been called once from init().
|
||||
verify(mockRecentsModel, times(1)).getTasks(any(), callbackCaptor.capture())
|
||||
// Override the mock answer for getTasks() so it doesn't call the callback immediately.
|
||||
doAnswer { taskListChangeId }
|
||||
.whenever(mockRecentsModel)
|
||||
.getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
recentTasksChangedListener?.onRecentTasksChanged()
|
||||
// By not invoking the callback passed to getTasks() we here emulate getTasks() loading.
|
||||
|
||||
recentTasksChangedListener?.onRecentTasksChanged()
|
||||
callbackCaptor.lastValue.accept(emptyList())
|
||||
|
||||
// getTasks() is called again now that the first getTasks() call finished.
|
||||
verify(mockRecentsModel, times(3)).getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASKBAR_RECENT_TASKS_THROTTLE_BUGFIX)
|
||||
fun recentTasksChanged_duringGetTasksLoading_flagDisabled_callGetTasks() {
|
||||
// getTasks() should have been called once from init().
|
||||
verify(mockRecentsModel, times(1)).getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
// Override the mock answer for getTasks() so it doesn't call the callback immediately.
|
||||
doAnswer { taskListChangeId }
|
||||
.whenever(mockRecentsModel)
|
||||
.getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
recentTasksChangedListener?.onRecentTasksChanged()
|
||||
// By not invoking the callback passed to getTasks() we here emulate getTasks() loading.
|
||||
|
||||
recentTasksChangedListener?.onRecentTasksChanged()
|
||||
|
||||
// getTasks() is called once per onRecentTasksChanged() invocation (and once at init)
|
||||
verify(mockRecentsModel, times(3)).getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASKBAR_RECENT_TASKS_THROTTLE_BUGFIX)
|
||||
fun recentTasksChanged_duringGetTasksLoading_flagDisabled_getTasksNotCalledWhenLoadingDone() {
|
||||
val callbackCaptor = argumentCaptor<Consumer<List<GroupTask>>>()
|
||||
// getTasks() should have been called once from init().
|
||||
verify(mockRecentsModel, times(1)).getTasks(any(), callbackCaptor.capture())
|
||||
// Override the mock answer for getTasks() so it doesn't call the callback immediately.
|
||||
doAnswer { taskListChangeId }
|
||||
.whenever(mockRecentsModel)
|
||||
.getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
recentTasksChangedListener?.onRecentTasksChanged()
|
||||
recentTasksChangedListener?.onRecentTasksChanged()
|
||||
verify(mockRecentsModel, times(3)).getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
|
||||
callbackCaptor.lastValue.accept(emptyList())
|
||||
|
||||
// getTasks() is called once per onRecentTasksChanged() invocation (and once at init)
|
||||
verify(mockRecentsModel, times(3)).getTasks(any(), any<Consumer<List<GroupTask>>>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDesktopItemState_nullItemInfo_returnsNotRunning() {
|
||||
setInDesktopMode(true)
|
||||
|
||||
Reference in New Issue
Block a user