From 045814a2c609f801938e54bb607a01b4a8406c25 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Thu, 3 Oct 2024 23:16:29 +0000 Subject: [PATCH 1/4] 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 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ee98cd4bdf739c2f93f7cf0a20764acb2a837b0c) Merged-In: I37ed281a993b1d2fa3634754378314511f3295f0 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 a6d651c951..10b6081393 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -238,6 +238,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"); @@ -450,7 +452,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 @@ -1548,9 +1551,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 2a0aa4ccb1..d2dd639511 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; @@ -54,6 +62,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; @@ -207,7 +216,7 @@ public abstract class AbsSwipeUpHandlerTestCase< runOnMainSync(() -> { absSwipeUpHandler.startNewTask(unused -> {}); - verify(mRecentsAnimationController).finish(anyBoolean(), any()); + verifyRecentsAnimationFinishedAndCallCallback(); }); } @@ -217,10 +226,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 c4f9510874ae0c75db03e5e7dab5b3df5a4acfe6 Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Mon, 7 Oct 2024 15:16:43 -0700 Subject: [PATCH 2/4] Fix Taskbar to Hotseat Animation for Non-Predective back apps - Refactored Taskbar animation duration to under one method and added logic to sync animation duration according to enableScalingRevealHomeAnimation. - Change the interpolator for TaskbarLauncherStateController based on enableScalingRevealHomeAnimation. Test: Presubmit, Manual Bug: 369378541 Flag: EXEMPT Bug Fix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:0a65c5bd72d755856148faeb070a5d2ad2c79cb0) Merged-In: I942829b475f91e9102801b758eb7ee0d27eee912 Change-Id: I942829b475f91e9102801b758eb7ee0d27eee912 --- .../taskbar/LauncherTaskbarUIController.java | 19 +++++++++++++------ .../TaskbarLauncherStateController.java | 7 ++++++- .../uioverrides/QuickstepLauncher.java | 4 ++++ .../util/ScalingWorkspaceRevealAnim.kt | 3 ++- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index 477f90cedf..5374280c71 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -17,6 +17,8 @@ package com.android.launcher3.taskbar; import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY; +import static com.android.launcher3.QuickstepTransitionManager.TASKBAR_TO_APP_DURATION; +import static com.android.launcher3.QuickstepTransitionManager.getTaskbarToHomeDuration; import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR_TRANSITION_DURATION; import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE; import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES; @@ -32,7 +34,6 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Flags; import com.android.launcher3.LauncherState; -import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.logging.InstanceId; @@ -204,11 +205,17 @@ public class LauncherTaskbarUIController extends TaskbarUIController { isVisible, fromInitOrDestroy, /* startAnimation= */ true, - DisplayController.isTransientTaskbar(mLauncher) - ? TRANSIENT_TASKBAR_TRANSITION_DURATION - : (!isVisible - ? QuickstepTransitionManager.TASKBAR_TO_APP_DURATION - : QuickstepTransitionManager.getTaskbarToHomeDuration())); + getTaskbarAnimationDuration(isVisible)); + } + + private int getTaskbarAnimationDuration(boolean isVisible) { + if (isVisible && !mLauncher.getPredictiveBackToHomeInProgress()) { + return getTaskbarToHomeDuration(); + } else { + return DisplayController.isTransientTaskbar(mLauncher) + ? TRANSIENT_TASKBAR_TRANSITION_DURATION + : TASKBAR_TO_APP_DURATION; + } } @Nullable diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 39ddb407fa..2be7162e84 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -16,6 +16,7 @@ package com.android.launcher3.taskbar; import static com.android.app.animation.Interpolators.EMPHASIZED; +import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation; import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_ALIGNMENT; import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_STASH; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; @@ -39,6 +40,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.os.SystemClock; import android.util.Log; +import android.view.animation.Interpolator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -58,6 +60,7 @@ import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationController; +import com.android.quickstep.util.ScalingWorkspaceRevealAnim; import com.android.quickstep.util.SystemUiFlagUtils; import com.android.quickstep.views.RecentsView; import com.android.systemui.animation.ViewRootSync; @@ -666,7 +669,9 @@ public class TaskbarLauncherStateController { animatorSet.play(iconAlignAnim); } - animatorSet.setInterpolator(EMPHASIZED); + Interpolator interpolator = enableScalingRevealHomeAnimation() + ? ScalingWorkspaceRevealAnim.SCALE_INTERPOLATOR : EMPHASIZED; + animatorSet.setInterpolator(interpolator); if (start) { animatorSet.start(); diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index e80e838e90..6371646422 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -1300,6 +1300,10 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, mTISBindHelper.setPredictiveBackToHomeInProgress(isInProgress); } + public boolean getPredictiveBackToHomeInProgress() { + return mIsPredictiveBackToHomeInProgress; + } + @Override public boolean areDesktopTasksVisible() { DesktopVisibilityController desktopVisibilityController = getDesktopVisibilityController(); diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt index f547a7fba1..df08ac8882 100644 --- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt +++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt @@ -60,7 +60,8 @@ class ScalingWorkspaceRevealAnim( * Custom interpolator for both the home and wallpaper scaling. Necessary because EMPHASIZED * is too aggressive, but EMPHASIZED_DECELERATE is too soft. */ - private val SCALE_INTERPOLATOR = + @JvmField + val SCALE_INTERPOLATOR = PathInterpolator( Path().apply { moveTo(0f, 0f) From 219371c4ef517511ae095082c30ba9f43ae13c15 Mon Sep 17 00:00:00 2001 From: Charlie Anderson Date: Tue, 8 Oct 2024 14:05:03 -0400 Subject: [PATCH 3/4] Prevent archived apps content description from being overridden unexpectedly Bug: 372035896 Test: manually tested archiving/unarchiving with Talkback Flag: EXEMPT bugfix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:91a2aad67f2f5a9b8773d49daaf8c634884867e3) Merged-In: Ief7f7d176c6ed23a8d7798cf31506e0a9c8921ed Change-Id: Ief7f7d176c6ed23a8d7798cf31506e0a9c8921ed --- src/com/android/launcher3/BubbleTextView.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 8121e53542..5224ee39d1 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -1130,6 +1130,9 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, if (itemInfo.isDisabled()) { setContentDescription(getContext().getString(R.string.disabled_app_label, itemInfo.contentDescription)); + } else if (itemInfo instanceof WorkspaceItemInfo wai && wai.isArchived()) { + setContentDescription( + getContext().getString(R.string.app_archived_title, itemInfo.title)); } else if (hasDot()) { int count = mDotInfo.getNotificationCount(); setContentDescription( @@ -1142,8 +1145,16 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } private void setDownloadStateContentDescription(ItemInfoWithIcon info, int progressLevel) { - if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_ARCHIVED) != 0 && progressLevel == 0) { - setContentDescription(getContext().getString(R.string.app_archived_title, info.title)); + if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_ARCHIVED) != 0 + && progressLevel == 0) { + if (mIcon instanceof PreloadIconDrawable) { + // Tell user that download is pending and not to tap to download again. + setContentDescription(getContext().getString( + R.string.app_waiting_download_title, info.title)); + } else { + setContentDescription(getContext().getString( + R.string.app_archived_title, info.title)); + } } else if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) { String percentageString = NumberFormat.getPercentInstance() From 72417bb06c88c56941e3f8832cf31a8b6dd939cc Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Thu, 24 Oct 2024 15:29:34 -0700 Subject: [PATCH 4/4] Fix Taskbar Touchable Region when overview is in split select mode The problem: when Overview is in split slect mode second split app pick from taskbar take you back home. Why, because upon touch the taskbar touch region get set to empty and then we later don't meet any conditions to set default touch region to something. The solution: change the condition of checking overview in onTaskbarOrBubblebarWindowHeightOrInsetsChanged so that we check that taskbar is in any overview s tate and we don't rely on the stash controller flag alone. we follow similar logic in updateInsetsTouchability, but we gate that only behind transient taskba r hence this was not an issue for transient taskbar. Test: Manual Bug: 373559050 Flag: EXEMPT bugfix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:9aa005b657bebe60867b2c2961c2f5deac8cf359) Merged-In: Ia507354f42959ce9d320114a313bfc533186b524 Change-Id: Ia507354f42959ce9d320114a313bfc533186b524 --- .../com/android/launcher3/taskbar/TaskbarInsetsController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt index 685c109eec..858f682c43 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt @@ -149,7 +149,7 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas } if ( taskbarStashController.isInApp || - taskbarStashController.isInOverview || + controllers.uiController.isInOverviewUi || DisplayController.showLockedTaskbarOnHome(context) ) { // only add the taskbar touch region if not on home