Increase number of triggers for taskData reloading in TasksRepository

- Simplified AbsSwipeUpHandler.switchToScreenshot to remove the unused refreshView=false parameter, so we RecentsView.updateThumbnail don't need to return a TaskView
- Changed TasksRepository.setThumbnailOverride to addThumbnailOverride, that'll accumulate overrides being sent to it
- Handled RecentsView.updateThumbnail to add an override to TasksRepository
- visibleTaskIds being sent to TasksRepository now take acccount of fileering out mTmpRunningTasks
- Updated a few TODOs around thumbnail/icon reloading

Bug: 342560598
Test: TasksRepositoryTest
Flag: com.android.launcher3.enable_refactor_task_thumbnail
Change-Id: Ia892819c45f20e82fbda275fd0e39081d6362cb6
This commit is contained in:
Alex Chau
2024-07-26 21:28:18 +01:00
parent 2293967dba
commit 4c7dcc4b02
10 changed files with 119 additions and 109 deletions
@@ -2100,7 +2100,6 @@ public abstract class AbsSwipeUpHandler<T extends RecentsViewContainer,
// If there are no targets, then we don't need to capture anything
mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
} else {
boolean finishTransitionPosted = false;
// If we already have cached screenshot(s) from running tasks, skip update
boolean shouldUpdate = false;
int[] runningTaskIds = mGestureState.getRunningTaskIds(mIsSwipeForSplit);
@@ -2124,45 +2123,32 @@ public abstract class AbsSwipeUpHandler<T extends RecentsViewContainer,
}
MAIN_EXECUTOR.execute(() -> {
if (!updateThumbnail(false /* refreshView */)) {
setScreenshotCapturedState();
}
updateThumbnail();
setScreenshotCapturedState();
});
});
return;
}
finishTransitionPosted = updateThumbnail(false /* refreshView */);
updateThumbnail();
}
if (!finishTransitionPosted) {
setScreenshotCapturedState();
}
setScreenshotCapturedState();
}
}
// Returns whether finish transition was posted.
private boolean updateThumbnail(boolean refreshView) {
private void updateThumbnail() {
if (mGestureState.getEndTarget() == HOME
|| mGestureState.getEndTarget() == NEW_TASK
|| mGestureState.getEndTarget() == ALL_APPS
|| mRecentsView == null) {
// Capture the screenshot before finishing the transition to home or quickswitching to
// ensure it's taken in the correct orientation, but no need to update the thumbnail.
return false;
return;
}
boolean finishTransitionPosted = false;
TaskView updatedTaskView = mRecentsView.updateThumbnail(mTaskSnapshotCache, refreshView);
if (updatedTaskView != null && refreshView && !mCanceled) {
// Defer finishing the animation until the next launcher frame with the
// new thumbnail
finishTransitionPosted = ViewUtils.postFrameDrawn(updatedTaskView,
() -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
this::isCanceled);
}
return finishTransitionPosted;
mRecentsView.updateThumbnail(mTaskSnapshotCache);
}
private void setScreenshotCapturedState() {
@@ -46,5 +46,5 @@ interface RecentTasksRepository {
* Override [ThumbnailData] with a map of taskId to [ThumbnailData]. The override only applies
* if the tasks are already visible, and will be invalidated when tasks become invisible.
*/
fun setThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>)
fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>)
}
@@ -82,12 +82,15 @@ class TasksRepository(
override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
this.visibleTaskIds.value = visibleTaskIdList.toSet()
setThumbnailOverride(thumbnailOverride.value)
addOrUpdateThumbnailOverride(emptyMap())
}
override fun setThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
override fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
this.thumbnailOverride.value =
thumbnailOverride.filterKeys(this.visibleTaskIds.value::contains).toMap()
this.thumbnailOverride.value
.toMutableMap()
.apply { putAll(thumbnailOverride) }
.filterKeys(this.visibleTaskIds.value::contains)
}
/** Flow wrapper for [TaskThumbnailDataSource.getThumbnailInBackground] api */
@@ -58,8 +58,8 @@ class RecentsViewModel(
recentsViewData.thumbnailSplashProgress.value = taskThumbnailSplashAlpha
}
fun setThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
recentsTasksRepository.setThumbnailOverride(thumbnailOverride)
fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
recentsTasksRepository.addOrUpdateThumbnailOverride(thumbnailOverride)
}
suspend fun waitForThumbnailsToUpdate(updatedThumbnails: Map<Int, ThumbnailData>) {
@@ -1047,8 +1047,11 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
@Override
@Nullable
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
if (enableRefactorTaskThumbnail()) {
// TODO(b/342560598): Listen in TaskRepository and reload
return null;
}
if (mHandleTaskStackChanges) {
// TODO(b/342560598): Handle onTaskThumbnailChanged for new TTV.
if (!enableRefactorTaskThumbnail()) {
TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView != null) {
@@ -1067,6 +1070,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
@Override
public void onTaskIconChanged(String pkg, UserHandle user) {
// TODO(b/342560598): Listen in TaskRepository and reload.
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView tv = requireTaskViewAt(i);
Task task = tv.getFirstTask();
@@ -1082,47 +1086,38 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
@Override
public void onTaskIconChanged(int taskId) {
if (enableRefactorTaskThumbnail()) {
return;
}
TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView != null) {
taskView.refreshTaskThumbnailSplash();
}
}
/**
* Update the thumbnail(s) of the relevant TaskView.
*
* @param refreshNow Refresh immediately if it's true.
*/
@Nullable
public TaskView updateThumbnail(
HashMap<Integer, ThumbnailData> thumbnailData, boolean refreshNow) {
/** Updates the thumbnail(s) of the relevant TaskView. */
public void updateThumbnail(Map<Integer, ThumbnailData> thumbnailData) {
if (enableRefactorTaskThumbnail()) {
// TODO(b/342560598): Handle updateThumbnail for new TTV.
return null;
}
TaskView updatedTaskView = null;
for (Map.Entry<Integer, ThumbnailData> entry : thumbnailData.entrySet()) {
Integer id = entry.getKey();
ThumbnailData thumbnail = entry.getValue();
TaskView taskView = getTaskViewByTaskId(id);
if (taskView == null) {
continue;
mRecentsViewModel.addOrUpdateThumbnailOverride(thumbnailData);
} else {
for (Map.Entry<Integer, ThumbnailData> entry : thumbnailData.entrySet()) {
Integer id = entry.getKey();
ThumbnailData thumbnail = entry.getValue();
TaskView taskView = getTaskViewByTaskId(id);
if (taskView == null) {
continue;
}
// taskView could be a GroupedTaskView, so select the relevant task by ID
TaskContainer taskContainer = taskView.getTaskContainerById(id);
if (taskContainer == null) {
continue;
}
Task task = taskContainer.getTask();
TaskThumbnailViewDeprecated taskThumbnailViewDeprecated =
taskContainer.getThumbnailViewDeprecated();
taskThumbnailViewDeprecated.setThumbnail(task, thumbnail, /*refreshNow=*/false);
}
// taskView could be a GroupedTaskView, so select the relevant task by ID
TaskContainer taskAttributes = taskView.getTaskContainerById(id);
if (taskAttributes == null) {
continue;
}
Task task = taskAttributes.getTask();
TaskThumbnailViewDeprecated taskThumbnailViewDeprecated =
taskAttributes.getThumbnailViewDeprecated();
taskThumbnailViewDeprecated.setThumbnail(task, thumbnail, refreshNow);
// thumbnailData can contain 1-2 ids, but they should correspond to the same
// TaskView, so overwriting is ok
updatedTaskView = taskView;
}
return updatedTaskView;
}
@Override
@@ -2440,10 +2435,6 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
List<Task> tasksToUpdate = containers.stream()
.map(TaskContainer::getTask)
.collect(Collectors.toCollection(ArrayList::new));
if (enableRefactorTaskThumbnail()) {
visibleTaskIds.addAll(
tasksToUpdate.stream().map((task) -> task.key.id).toList());
}
if (mTmpRunningTasks != null) {
for (Task t : mTmpRunningTasks) {
// Skip loading if this is the task that we are animating into
@@ -2451,6 +2442,10 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
tasksToUpdate.removeIf(task -> task == t);
}
}
if (enableRefactorTaskThumbnail()) {
visibleTaskIds.addAll(
tasksToUpdate.stream().map((task) -> task.key.id).toList());
}
if (tasksToUpdate.isEmpty()) {
continue;
}
@@ -2507,6 +2502,11 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
mModel.preloadCacheIfNeeded();
}
if (enableRefactorTaskThumbnail()) {
// TODO(b/342560598): Listen in TaskRepository and reload.
return;
}
// Whenever the high res loading state changes, poke each of the visible tasks to see if
// they want to updated their thumbnail state
for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
@@ -61,7 +61,7 @@ class RecentsViewHelper(private val recentsViewModel: RecentsViewModel) {
// viewAttachedScope.
recentsViewModel.setRunningTaskShowScreenshot(true)
if (updatedThumbnails != null) {
recentsViewModel.setThumbnailOverride(updatedThumbnails)
recentsViewModel.addOrUpdateThumbnailOverride(updatedThumbnails)
}
viewAttachedScope.launch {
recentsViewModel.waitForRunningTaskShowScreenshotToUpdate()
@@ -1398,7 +1398,6 @@ constructor(
protected open fun refreshTaskThumbnailSplash() {
if (!enableRefactorTaskThumbnail()) {
// TODO(b/342560598) handle onTaskIconChanged
taskContainers.forEach { it.thumbnailViewDeprecated.refreshSplashView() }
}
}
@@ -65,7 +65,7 @@ class FakeTasksRepository : RecentTasksRepository {
setThumbnailOverrideInternal(thumbnailOverrideMap)
}
override fun setThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
override fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
setThumbnailOverrideInternal(thumbnailOverride)
}
@@ -193,57 +193,79 @@ class TasksRepositoryTest {
}
@Test
fun setThumbnailOverrideOverrideThumbnails() = runTest {
fun addThumbnailOverrideOverrideThumbnails() = runTest {
recentsModel.seedTasks(defaultTaskList)
val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
val thumbnailOverride = createThumbnailData()
systemUnderTest.getAllTaskData(forceRefresh = true)
systemUnderTest.setVisibleTasks(listOf(1))
systemUnderTest.setThumbnailOverride(mapOf(2 to thumbnailOverride))
systemUnderTest.setVisibleTasks(listOf(1, 2))
// .drop(1) to ignore initial null content before from thumbnail was loaded.
assertThat(systemUnderTest.getThumbnailById(1).drop(1).first()!!.thumbnail)
.isEqualTo(bitmap1)
assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail).isEqualTo(bitmap2)
}
@Test
fun setThumbnailOverrideClearedWhenTaskBecomeInvisible() = runTest {
recentsModel.seedTasks(defaultTaskList)
val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
val thumbnailOverride = createThumbnailData()
val thumbnailOverride2 = createThumbnailData()
systemUnderTest.getAllTaskData(forceRefresh = true)
systemUnderTest.setVisibleTasks(listOf(1, 2))
systemUnderTest.setThumbnailOverride(mapOf(2 to thumbnailOverride))
systemUnderTest.setVisibleTasks(listOf(1))
systemUnderTest.setVisibleTasks(listOf(1, 2))
// .drop(1) to ignore initial null content before from thumbnail was loaded.
assertThat(systemUnderTest.getThumbnailById(1).drop(1).first()!!.thumbnail)
.isEqualTo(bitmap1)
assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail).isEqualTo(bitmap2)
}
@Test
fun setThumbnailOverrideDoesNotOverrideInvisibleTasks() = runTest {
recentsModel.seedTasks(defaultTaskList)
val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
val thumbnailOverride = createThumbnailData()
systemUnderTest.getAllTaskData(forceRefresh = true)
systemUnderTest.setVisibleTasks(listOf(1, 2))
systemUnderTest.setThumbnailOverride(mapOf(2 to thumbnailOverride))
systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride2))
// .drop(1) to ignore initial null content before from thumbnail was loaded.
assertThat(systemUnderTest.getThumbnailById(1).drop(1).first()!!.thumbnail)
.isEqualTo(bitmap1)
assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail)
.isEqualTo(thumbnailOverride.thumbnail)
.isEqualTo(thumbnailOverride2.thumbnail)
}
@Test
fun addThumbnailOverrideMultipleOverrides() = runTest {
recentsModel.seedTasks(defaultTaskList)
val thumbnailOverride1 = createThumbnailData()
val thumbnailOverride2 = createThumbnailData()
val thumbnailOverride3 = createThumbnailData()
systemUnderTest.getAllTaskData(forceRefresh = true)
systemUnderTest.setVisibleTasks(listOf(1, 2))
systemUnderTest.addOrUpdateThumbnailOverride(mapOf(1 to thumbnailOverride1))
systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride2))
systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride3))
assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail)
.isEqualTo(thumbnailOverride1.thumbnail)
assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail)
.isEqualTo(thumbnailOverride3.thumbnail)
}
@Test
fun addThumbnailOverrideClearedWhenTaskBecomeInvisible() = runTest {
recentsModel.seedTasks(defaultTaskList)
val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
val thumbnailOverride1 = createThumbnailData()
val thumbnailOverride2 = createThumbnailData()
systemUnderTest.getAllTaskData(forceRefresh = true)
systemUnderTest.setVisibleTasks(listOf(1, 2))
systemUnderTest.addOrUpdateThumbnailOverride(mapOf(1 to thumbnailOverride1))
systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride2))
// Making task 2 invisible and visible again should clear the override
systemUnderTest.setVisibleTasks(listOf(1))
systemUnderTest.setVisibleTasks(listOf(1, 2))
// .drop(1) to ignore initial null content before from thumbnail was loaded.
assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail)
.isEqualTo(thumbnailOverride1.thumbnail)
assertThat(systemUnderTest.getThumbnailById(2).drop(1).first()!!.thumbnail)
.isEqualTo(bitmap2)
}
@Test
fun addThumbnailOverrideDoesNotOverrideInvisibleTasks() = runTest {
recentsModel.seedTasks(defaultTaskList)
val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
val thumbnailOverride = createThumbnailData()
systemUnderTest.getAllTaskData(forceRefresh = true)
systemUnderTest.setVisibleTasks(listOf(1))
systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride))
systemUnderTest.setVisibleTasks(listOf(1, 2))
// .drop(1) to ignore initial null content before from thumbnail was loaded.
assertThat(systemUnderTest.getThumbnailById(1).drop(1).first()!!.thumbnail)
.isEqualTo(bitmap1)
assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail).isEqualTo(bitmap2)
}
private fun createTaskWithId(taskId: Int) =
@@ -89,7 +89,7 @@ class RecentsViewModelTest {
val thumbnailUpdate = mapOf(2 to thumbnailDataOverride)
systemUnderTest.setRunningTaskShowScreenshot(true)
systemUnderTest.setThumbnailOverride(thumbnailUpdate)
systemUnderTest.addOrUpdateThumbnailOverride(thumbnailUpdate)
systemUnderTest.waitForRunningTaskShowScreenshotToUpdate()
systemUnderTest.waitForThumbnailsToUpdate(thumbnailUpdate)