From 3c24c421f6615010b709dc660e6f22f74345e3ac Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Thu, 21 Nov 2024 17:10:55 -0500 Subject: [PATCH] Animate out/in the correct task view when recents change. Flag: com.android.launcher3.taskbar_recents_layout_transition Bug: 343521765 Test: go/testedequals Change-Id: I6c7708ed9f03eff8469b5f2e75ce00e545b03f54 --- .../launcher3/taskbar/TaskbarView.java | 18 +++- .../taskbar/TaskbarViewController.java | 1 + .../launcher3/taskbar/TaskbarViewTestUtil.kt | 2 +- .../TaskbarViewWithLayoutTransitionTest.kt | 94 +++++++++++++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index 2945374624..a27658ae92 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -34,6 +34,7 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.util.ArraySet; import android.util.AttributeSet; import android.view.DisplayCutout; import android.view.InputDevice; @@ -74,8 +75,10 @@ import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.function.Predicate; /** @@ -134,6 +137,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar private final int mNumStaticViews; + private Set mPrevRecentTasks = Collections.emptySet(); + public TaskbarView(@NonNull Context context) { this(context, null); } @@ -636,6 +641,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } // Add Recent/Running icons. + final Set recentTasksSet = new ArraySet<>(recentTasks); for (GroupTask task : recentTasks) { if (mTaskbarOverflowView != null && overflownTasks != null && overflownTasks.size() < itemsToAddToOverflow) { @@ -664,12 +670,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } View recentIcon = null; - while (isNextViewInSection(GroupTask.class)) { + // If a task is new, we should not reuse a view so that it animates in when it is added. + final boolean canReuseView = !taskbarRecentsLayoutTransition() + || mPrevRecentTasks.contains(task); + while (canReuseView && isNextViewInSection(GroupTask.class)) { recentIcon = getChildAt(mNextViewIndex); // see if the view can be reused if ((recentIcon.getSourceLayoutResId() != expectedLayoutResId) - || (isCollection && (recentIcon.getTag() != task))) { + || (isCollection && (recentIcon.getTag() != task)) + // Remove view corresponding to removed task so that it animates out. + || (taskbarRecentsLayoutTransition() + && !recentTasksSet.contains(recentIcon.getTag()))) { removeAndRecycle(recentIcon); recentIcon = null; } else { @@ -699,6 +711,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar while (isNextViewInSection(GroupTask.class)) { removeAndRecycle(getChildAt(mNextViewIndex)); } + + mPrevRecentTasks = recentTasksSet; } private boolean isNextViewInSection(Class tagClass) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index fcdeee1b51..89f4f5959a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -1096,6 +1096,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar public void commitRunningAppsToUI() { mModelCallbacks.commitRunningAppsToUI(); if (taskbarRecentsLayoutTransition() && mTaskbarView.getLayoutTransition() == null) { + // Set up after the first commit so that the initial recents do not animate (janky). mTaskbarView.setLayoutTransition(createLayoutTransitionForRunningApps()); } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt index a6bdbb06e0..f2dcf775f5 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt @@ -99,7 +99,7 @@ class TaskbarViewSubject(failureMetadata: FailureMetadata, private val view: Tas /** Verifies that recents from [startIndex] have IDs that match [expectedIds] in order. */ fun hasRecentsOrder(startIndex: Int, expectedIds: List) { val actualIds = - view.iconViews.slice(startIndex.. + get() = taskbarView.iconViews + @Before fun obtainView() { taskbarView = taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view) @@ -131,4 +136,93 @@ class TaskbarViewWithLayoutTransitionTest { } assertThat(taskbarView).hasIconTypes(RECENT, DIVIDER, HOTSEAT, ALL_APPS) } + + @Test + fun testUpdateItems_addRecentsItem_viewAddedOnRight() { + runOnMainSync { + taskbarView.updateItems(emptyArray(), createRecents(1)) + val prevIconViews = iconViews + + val newRecents = createRecents(2) + taskbarView.updateItems(emptyArray(), newRecents) + + assertThat(taskbarView).hasRecentsOrder(startIndex = 2, expectedIds = listOf(0, 1)) + assertThat(iconViews[2]).isSameInstanceAs(prevIconViews[2]) + assertThat(iconViews.last() in prevIconViews).isFalse() + } + } + + @Test + @ForceRtl + fun testUpdateItems_rtl_addRecentsItem_viewAddedOnLeft() { + runOnMainSync { + taskbarView.updateItems(emptyArray(), createRecents(1)) + val prevIconViews = iconViews + + val newRecents = createRecents(2) + taskbarView.updateItems(emptyArray(), newRecents) + + assertThat(taskbarView).hasRecentsOrder(startIndex = 0, expectedIds = listOf(1, 0)) + assertThat(iconViews[1]).isSameInstanceAs(prevIconViews.first()) + assertThat(iconViews.first() in prevIconViews).isFalse() + } + } + + @Test + fun testUpdateItems_removeFirstRecentsItem_correspondingViewRemoved() { + runOnMainSync { + val recents = createRecents(2) + taskbarView.updateItems(emptyArray(), recents) + + val expectedViewToRemove = iconViews[2] + assertThat(expectedViewToRemove.tag).isEqualTo(recents.first()) + + taskbarView.updateItems(emptyArray(), listOf(recents.last())) + assertThat(expectedViewToRemove in iconViews).isFalse() + } + } + + @Test + fun testUpdateItems_removeLastRecentsItem_correspondingViewRemoved() { + runOnMainSync { + val recents = createRecents(2) + taskbarView.updateItems(emptyArray(), recents) + + val expectedViewToRemove = iconViews[3] + assertThat(expectedViewToRemove.tag).isEqualTo(recents.last()) + + taskbarView.updateItems(emptyArray(), listOf(recents.first())) + assertThat(expectedViewToRemove in iconViews).isFalse() + } + } + + @Test + @ForceRtl + fun testUpdateItems_rtl_removeFirstRecentsItem_correspondingViewRemoved() { + runOnMainSync { + val recents = createRecents(2) + taskbarView.updateItems(emptyArray(), recents) + + val expectedViewToRemove = iconViews[1] + assertThat(expectedViewToRemove.tag).isEqualTo(recents.first()) + + taskbarView.updateItems(emptyArray(), listOf(recents.last())) + assertThat(expectedViewToRemove in iconViews).isFalse() + } + } + + @Test + @ForceRtl + fun testUpdateItems_rtl_removeLastRecentsItem_correspondingViewRemoved() { + runOnMainSync { + val recents = createRecents(2) + taskbarView.updateItems(emptyArray(), recents) + + val expectedViewToRemove = iconViews[0] + assertThat(expectedViewToRemove.tag).isEqualTo(recents.last()) + + taskbarView.updateItems(emptyArray(), listOf(recents.first())) + assertThat(expectedViewToRemove in iconViews).isFalse() + } + } }