From 05cd2b61044ab66f8c2f23b9e91c09f901039148 Mon Sep 17 00:00:00 2001 From: Issei Suzuki Date: Fri, 20 May 2022 16:28:36 +0000 Subject: [PATCH] Exclude launcher from app transition animation while recents is running. Changed approach to handle conflict between app transition and recents. In order to fix flicker which happens when recents and app transition start in a short time, we delayed app transition while recents was running. However this approach brought side effects such as b/232984498. When recents starts, we just want to wait that the launcher activitiy finishes rendering and commit its visibility without animation. Added a flag DisplayContent#mExcludeLauncherFromAnimation flag, so that we can now explicity declare whether we want to apply animation on the launcher or not. Bug: 223499269 Bug: 231711212 Bug: 232984498 Test: atest com.android.server.wm.AppTransitionTests Test: atest AppTransitionTest + manual tests Test 1 1. Launch Gmail app 2. Click icon on the bottom tab (e.g. Chat) 3. Swipe up from the bottom (immediately after step 2) 4. Verify closing animation only plays once Test 2 1. Launch "Google TV" 2. Play a trailer 3. Full screen and PIP mode switch twice 4. Verify PIP window is shown Test 3 1. Change phone to portlait mode 2. Launch Photo app 3. Swipe up from the bottom 4. Verify no rotation animation on the launcher Test 4 1. Install 3P launcher and set it default. 2. Launch Gmail app 3. Swipe up to go back home 4. Launch Chrome app 5. Swipe up and hold to go to overview. 6. Scroll to Gmail app and click. 7. Verify Gmail app is launched without delay. Change-Id: I0ccb99479684d17453ce57e8797024c0cd233ac3 --- data/etc/services.core.protolog.json | 18 ++--- .../server/wm/AppTransitionController.java | 74 ++++++++++++------- .../android/server/wm/AppTransitionTests.java | 37 +++++++--- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 51ed8567063ea..f7c2b732a77b2 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1879,12 +1879,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/TaskFragment.java" }, - "-240296576": { - "message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, "-237664290": { "message": "Pause the recording session on display %s", "level": "VERBOSE", @@ -1987,6 +1981,12 @@ "group": "WM_DEBUG_CONTENT_RECORDING", "at": "com\/android\/server\/wm\/ContentRecorder.java" }, + "-134793542": { + "message": "handleAppTransitionReady: displayId=%d appTransition={%s} excludeLauncherFromAnimation=%b openingApps=[%s] closingApps=[%s] transit=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS", + "at": "com\/android\/server\/wm\/AppTransitionController.java" + }, "-134091882": { "message": "Screenshotting Activity %s", "level": "VERBOSE", @@ -2491,12 +2491,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "323235828": { - "message": "Delaying app transition for recents animation to finish", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, "327461496": { "message": "Complete pause: %s", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index fa506ca68e571..140ac333e3af1 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -76,6 +76,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Trace; import android.util.ArrayMap; @@ -145,7 +146,8 @@ public class AppTransitionController { * Returns the currently visible window that is associated with the wallpaper in case we are * transitioning from an activity with a wallpaper to one without. */ - private @Nullable WindowState getOldWallpaper() { + @Nullable + private WindowState getOldWallpaper() { final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); final @TransitionType int firstTransit = mDisplayContent.mAppTransition.getFirstAppTransition(); @@ -165,6 +167,16 @@ public class AppTransitionController { ? null : wallpaperTarget; } + @NonNull + private static ArraySet getAppsForAnimation( + @NonNull ArraySet apps, boolean excludeLauncherFromAnimation) { + final ArraySet appsForAnimation = new ArraySet<>(apps); + if (excludeLauncherFromAnimation) { + appsForAnimation.removeIf(ConfigurationContainer::isActivityTypeHome); + } + return appsForAnimation; + } + /** * Handle application transition for given display. */ @@ -214,18 +226,29 @@ public class AppTransitionController { mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded( mDisplayContent.mOpeningApps); - final @TransitionOldType int transit = getTransitCompatType( - mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps, - mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers, + // Remove launcher from app transition animation while recents is running. Recents animation + // is managed outside of app transition framework, so we just need to commit visibility. + final boolean excludeLauncherFromAnimation = + mDisplayContent.mOpeningApps.stream().anyMatch( + (app) -> app.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) + || mDisplayContent.mClosingApps.stream().anyMatch( + (app) -> app.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)); + final ArraySet openingAppsForAnimation = getAppsForAnimation( + mDisplayContent.mOpeningApps, excludeLauncherFromAnimation); + final ArraySet closingAppsForAnimation = getAppsForAnimation( + mDisplayContent.mClosingApps, excludeLauncherFromAnimation); + + @TransitionOldType final int transit = getTransitCompatType( + mDisplayContent.mAppTransition, openingAppsForAnimation, closingAppsForAnimation, + mDisplayContent.mChangingContainers, mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(), mDisplayContent.mSkipAppTransitionAnimation); mDisplayContent.mSkipAppTransitionAnimation = false; ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "handleAppTransitionReady: displayId=%d appTransition={%s}" - + " openingApps=[%s] closingApps=[%s] transit=%s", - mDisplayContent.mDisplayId, - appTransition.toString(), + + " excludeLauncherFromAnimation=%b openingApps=[%s] closingApps=[%s] transit=%s", + mDisplayContent.mDisplayId, appTransition.toString(), excludeLauncherFromAnimation, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, AppTransition.appTransitionOldToString(transit)); @@ -233,13 +256,15 @@ public class AppTransitionController { // what will control the animation theme. If all closing windows are obscured, then there is // no need to do an animation. This is the case, for example, when this transition is being // done behind a dream window. - final ArraySet activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps, - mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers); - final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes); + final ArraySet activityTypes = collectActivityTypes(openingAppsForAnimation, + closingAppsForAnimation, mDisplayContent.mChangingContainers); + final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes, + openingAppsForAnimation, closingAppsForAnimation, + mDisplayContent.mChangingContainers); final ActivityRecord topOpeningApp = - getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */); + getTopApp(openingAppsForAnimation, false /* ignoreHidden */); final ActivityRecord topClosingApp = - getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */); + getTopApp(closingAppsForAnimation, false /* ignoreHidden */); final ActivityRecord topChangingApp = getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */); final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity); @@ -251,14 +276,14 @@ public class AppTransitionController { overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes); } - final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mClosingApps) - || containsVoiceInteraction(mDisplayContent.mOpeningApps); + final boolean voiceInteraction = containsVoiceInteraction(closingAppsForAnimation) + || containsVoiceInteraction(openingAppsForAnimation); final int layoutRedo; mService.mSurfaceAnimationRunner.deferStartingAnimations(); try { - applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, - animLp, voiceInteraction); + applyAnimations(openingAppsForAnimation, closingAppsForAnimation, transit, animLp, + voiceInteraction); handleClosingApps(); handleOpeningApps(); handleChangingApps(transit); @@ -310,7 +335,7 @@ public class AppTransitionController { * case we are transitioning from an activity with a wallpaper to one * without. Otherwise null. */ - static @TransitionOldType int getTransitCompatType(AppTransition appTransition, + @TransitionOldType static int getTransitCompatType(AppTransition appTransition, ArraySet openingApps, ArraySet closingApps, ArraySet changingContainers, @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) { @@ -341,8 +366,8 @@ public class AppTransitionController { if (skipAppTransitionAnimation) { return WindowManager.TRANSIT_OLD_UNSET; } - final @TransitionFlags int flags = appTransition.getTransitFlags(); - final @TransitionType int firstTransit = appTransition.getFirstAppTransition(); + @TransitionFlags final int flags = appTransition.getTransitFlags(); + @TransitionType final int firstTransit = appTransition.getFirstAppTransition(); // Special transitions // TODO(new-app-transitions): Revisit if those can be rewritten by using flags. @@ -720,11 +745,9 @@ public class AppTransitionController { */ @Nullable private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit, - ArraySet activityTypes) { + ArraySet activityTypes, ArraySet openingApps, + ArraySet closingApps, ArraySet changingApps) { ActivityRecord result; - final ArraySet closingApps = mDisplayContent.mClosingApps; - final ArraySet openingApps = mDisplayContent.mOpeningApps; - final ArraySet changingApps = mDisplayContent.mChangingContainers; // Remote animations always win, but fullscreen tokens override non-fullscreen tokens. result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, @@ -1152,11 +1175,6 @@ public class AppTransitionController { if (activity == null) { continue; } - if (activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) { - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "Delaying app transition for recents animation to finish"); - return false; - } ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Check opening app=%s: allDrawn=%b startingDisplayed=%b " + "startingMoved=%b isRelaunching()=%b startingWindow=%s", diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 35921585d56fe..436cf36587d8e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CHANGE; @@ -25,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; @@ -409,38 +412,50 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - public void testDelayWhileRecents() { + public void testExcludeLauncher() { final DisplayContent dc = createNewDisplay(Display.STATE_ON); doReturn(false).when(dc).onDescendantOrientationChanged(any()); final Task task = createTask(dc); - // Simulate activity1 launches activity2. + // Simulate activity1 launches activity2 final ActivityRecord activity1 = createActivityRecord(task); activity1.setVisible(true); activity1.mVisibleRequested = false; activity1.allDrawn = true; + dc.mClosingApps.add(activity1); final ActivityRecord activity2 = createActivityRecord(task); activity2.setVisible(false); activity2.mVisibleRequested = true; activity2.allDrawn = true; - - dc.mClosingApps.add(activity1); dc.mOpeningApps.add(activity2); dc.prepareAppTransition(TRANSIT_OPEN); - assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_OPEN)); + + // Simulate start recents + final ActivityRecord homeActivity = createActivityRecord(dc, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_HOME); + homeActivity.setVisible(false); + homeActivity.mVisibleRequested = true; + homeActivity.allDrawn = true; + dc.mOpeningApps.add(homeActivity); + dc.prepareAppTransition(TRANSIT_NONE); + doReturn(true).when(task) + .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS)); // Wait until everything in animation handler get executed to prevent the exiting window // from being removed during WindowSurfacePlacer Traversal. waitUntilHandlersIdle(); - // Start recents - doReturn(true).when(task) - .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS)); - dc.mAppTransitionController.handleAppTransitionReady(); - verify(activity1, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean()); - verify(activity2, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean()); + verify(activity1).commitVisibility(eq(false), anyBoolean(), anyBoolean()); + verify(activity1).applyAnimation(any(), eq(TRANSIT_OLD_ACTIVITY_OPEN), eq(false), + anyBoolean(), any()); + verify(activity2).commitVisibility(eq(true), anyBoolean(), anyBoolean()); + verify(activity2).applyAnimation(any(), eq(TRANSIT_OLD_ACTIVITY_OPEN), eq(true), + anyBoolean(), any()); + verify(homeActivity).commitVisibility(eq(true), anyBoolean(), anyBoolean()); + verify(homeActivity, never()).applyAnimation(any(), anyInt(), anyBoolean(), anyBoolean(), + any()); } @Test