Merge "Only wait for significant windows to unfreeze display" into sc-dev am: b3ba0074bf

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13432356

Change-Id: I50a22dec9665dfbbef2e8d668111949e9266ccc4
This commit is contained in:
Riddle Hsu
2021-03-23 05:09:57 +00:00
committed by Automerger Merge Worker
11 changed files with 241 additions and 108 deletions

View File

@@ -516,7 +516,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** The delay to avoid toggling the animation quickly. */
private static final long FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS = 250;
private FixedRotationAnimationController mFixedRotationAnimationController;
private FadeRotationAnimationController mFadeRotationAnimationController;
final FixedRotationTransitionListener mFixedRotationTransitionListener =
new FixedRotationTransitionListener();
@@ -1590,8 +1590,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@VisibleForTesting
@Nullable FixedRotationAnimationController getFixedRotationAnimationController() {
return mFixedRotationAnimationController;
@Nullable FadeRotationAnimationController getFadeRotationAnimationController() {
return mFadeRotationAnimationController;
}
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r) {
@@ -1601,13 +1601,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
if (mFixedRotationLaunchingApp == null && r != null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
startFixedRotationAnimation(
startFadeRotationAnimation(
// Delay the hide animation to avoid blinking by clicking navigation bar that
// may toggle fixed rotation in a short time.
r == mFixedRotationTransitionListener.mAnimatingRecents /* shouldDebounce */);
} else if (mFixedRotationLaunchingApp != null && r == null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
finishFixedRotationAnimationIfPossible();
finishFadeRotationAnimationIfPossible();
}
mFixedRotationLaunchingApp = r;
}
@@ -1714,12 +1714,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*
* @return {@code true} if the animation is executed right now.
*/
private boolean startFixedRotationAnimation(boolean shouldDebounce) {
private boolean startFadeRotationAnimation(boolean shouldDebounce) {
if (shouldDebounce) {
mWmService.mH.postDelayed(() -> {
synchronized (mWmService.mGlobalLock) {
if (mFixedRotationLaunchingApp != null
&& startFixedRotationAnimation(false /* shouldDebounce */)) {
&& startFadeRotationAnimation(false /* shouldDebounce */)) {
// Apply the transaction so the animation leash can take effect immediately.
getPendingTransaction().apply();
}
@@ -1727,23 +1727,41 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}, FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS);
return false;
}
if (mFixedRotationAnimationController == null) {
mFixedRotationAnimationController = new FixedRotationAnimationController(this);
mFixedRotationAnimationController.hide();
if (mFadeRotationAnimationController == null) {
mFadeRotationAnimationController = new FadeRotationAnimationController(this);
mFadeRotationAnimationController.hide();
return true;
}
return false;
}
/** Re-show the previously hidden windows if all seamless rotated windows are done. */
void finishFixedRotationAnimationIfPossible() {
final FixedRotationAnimationController controller = mFixedRotationAnimationController;
void finishFadeRotationAnimationIfPossible() {
final FadeRotationAnimationController controller = mFadeRotationAnimationController;
if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
controller.show();
mFixedRotationAnimationController = null;
mFadeRotationAnimationController = null;
}
}
/** Shows the given window which may be hidden for screen frozen. */
void finishFadeRotationAnimation(WindowState w) {
final FadeRotationAnimationController controller = mFadeRotationAnimationController;
if (controller != null && controller.show(w.mToken)) {
mFadeRotationAnimationController = null;
}
}
/** Returns {@code true} if the display should wait for the given window to stop freezing. */
boolean waitForUnfreeze(WindowState w) {
if (w.mForceSeamlesslyRotate) {
// The window should look no different before and after rotation.
return false;
}
final FadeRotationAnimationController controller = mFadeRotationAnimationController;
return controller == null || !controller.isTargetToken(w.mToken);
}
void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) {
if (mFixedRotationLaunchingApp != null) {
// The insets state of fixed rotation app is a rotated copy. Make sure the visibilities
@@ -2964,6 +2982,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mScreenRotationAnimation.kill();
}
mScreenRotationAnimation = screenRotationAnimation;
// Hide the windows which are not significant in rotation animation. So that the windows
// don't need to block the unfreeze time.
if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()
&& mFadeRotationAnimationController == null) {
startFadeRotationAnimation(false /* shouldDebounce */);
}
}
public ScreenRotationAnimation getRotationAnimation() {

View File

@@ -626,7 +626,7 @@ public class DisplayRotation {
}, true /* traverseTopToBottom */);
mSeamlessRotationCount = 0;
mRotatingSeamlessly = false;
mDisplayContent.finishFixedRotationAnimationIfPossible();
mDisplayContent.finishFadeRotationAnimationIfPossible();
}
private void prepareSeamlessRotation() {
@@ -717,7 +717,7 @@ public class DisplayRotation {
"Performing post-rotate rotation after seamless rotation");
// Finish seamless rotation.
mRotatingSeamlessly = false;
mDisplayContent.finishFixedRotationAnimationIfPossible();
mDisplayContent.finishFadeRotationAnimationIfPossible();
updateRotationAndSendNewConfigIfChanged();
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import com.android.internal.R;
import java.util.ArrayList;
/**
* Controller to fade out and in windows when the display is changing rotation. It can be used for
* both fixed rotation and normal rotation to hide some non-activity windows. The caller should show
* the windows until they are drawn with the new rotation.
*/
public class FadeRotationAnimationController extends FadeAnimationController {
private final ArrayList<WindowToken> mTargetWindowTokens = new ArrayList<>();
private final WindowManagerService mService;
/** If non-null, it usually indicates that there will be a screen rotation animation. */
private final Runnable mFrozenTimeoutRunnable;
private final WindowToken mNavBarToken;
public FadeRotationAnimationController(DisplayContent displayContent) {
super(displayContent);
mService = displayContent.mWmService;
mFrozenTimeoutRunnable = mService.mDisplayFrozen ? () -> {
synchronized (mService.mGlobalLock) {
displayContent.finishFadeRotationAnimationIfPossible();
mService.mWindowPlacerLocked.performSurfacePlacement();
}
} : null;
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
final WindowState navigationBar = displayPolicy.getNavigationBar();
if (navigationBar != null) {
mNavBarToken = navigationBar.mToken;
final RecentsAnimationController controller = mService.getRecentsAnimationController();
final boolean navBarControlledByRecents =
controller != null && controller.isNavigationBarAttachedToApp();
// Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation
// bar is currently controlled by recents animation.
if (!displayPolicy.navigationBarCanMove() && !navBarControlledByRecents) {
mTargetWindowTokens.add(mNavBarToken);
}
} else {
mNavBarToken = null;
}
displayContent.forAllWindows(w -> {
if (w.mActivityRecord == null && w.mHasSurface && !w.mForceSeamlesslyRotate
&& !w.mIsWallpaper && !w.mIsImWindow && w != navigationBar) {
mTargetWindowTokens.add(w.mToken);
}
}, true /* traverseTopToBottom */);
}
/** Applies show animation on the previously hidden window tokens. */
void show() {
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final WindowToken windowToken = mTargetWindowTokens.get(i);
fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
}
mTargetWindowTokens.clear();
if (mFrozenTimeoutRunnable != null) {
mService.mH.removeCallbacks(mFrozenTimeoutRunnable);
}
}
/**
* Returns {@code true} if all target windows are shown. It only takes effects if this
* controller is created for normal rotation.
*/
boolean show(WindowToken token) {
if (mFrozenTimeoutRunnable != null && mTargetWindowTokens.remove(token)) {
fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM);
if (mTargetWindowTokens.isEmpty()) {
mService.mH.removeCallbacks(mFrozenTimeoutRunnable);
return true;
}
}
return false;
}
/** Applies hide animation on the window tokens which may be seamlessly rotated later. */
void hide() {
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final WindowToken windowToken = mTargetWindowTokens.get(i);
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
}
if (mFrozenTimeoutRunnable != null) {
mService.mH.postDelayed(mFrozenTimeoutRunnable,
WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION);
}
}
/** Returns {@code true} if the window is handled by this controller. */
boolean isTargetToken(WindowToken token) {
return token == mNavBarToken || mTargetWindowTokens.contains(token);
}
@Override
public Animation getFadeInAnimation() {
if (mFrozenTimeoutRunnable != null) {
// Use a shorter animation so it is easier to align with screen rotation animation.
return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
}
return super.getFadeInAnimation();
}
@Override
public Animation getFadeOutAnimation() {
if (mFrozenTimeoutRunnable != null) {
// Hide the window immediately because screen should have been covered by screenshot.
return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */);
}
return super.getFadeOutAnimation();
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
import java.util.ArrayList;
/**
* Controller to fade out and in system ui when applying a fixed rotation transform to a window
* token.
*
* The system bars will be fade out when the fixed rotation transform starts and will be fade in
* once all surfaces have been rotated.
*/
public class FixedRotationAnimationController extends FadeAnimationController {
private final WindowState mStatusBar;
private final WindowState mNavigationBar;
private final ArrayList<WindowToken> mAnimatedWindowToken = new ArrayList<>(2);
public FixedRotationAnimationController(DisplayContent displayContent) {
super(displayContent);
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
mStatusBar = displayPolicy.getStatusBar();
final RecentsAnimationController controller =
displayContent.mWmService.getRecentsAnimationController();
final boolean navBarControlledByRecents =
controller != null && controller.isNavigationBarAttachedToApp();
// Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation bar
// is currently controlled by recents animation.
mNavigationBar = !displayPolicy.navigationBarCanMove()
&& !navBarControlledByRecents ? displayPolicy.getNavigationBar() : null;
}
/** Applies show animation on the previously hidden window tokens. */
void show() {
for (int i = mAnimatedWindowToken.size() - 1; i >= 0; i--) {
final WindowToken windowToken = mAnimatedWindowToken.get(i);
fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
}
}
/** Applies hide animation on the window tokens which may be seamlessly rotated later. */
void hide() {
if (mNavigationBar != null) {
fadeWindowToken(false /* show */, mNavigationBar.mToken,
ANIMATION_TYPE_FIXED_TRANSFORM);
}
if (mStatusBar != null) {
fadeWindowToken(false /* show */, mStatusBar.mToken,
ANIMATION_TYPE_FIXED_TRANSFORM);
}
}
@Override
public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
super.fadeWindowToken(show, windowToken, animationType);
mAnimatedWindowToken.add(windowToken);
}
}

View File

@@ -77,7 +77,7 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
final boolean shouldAttachNavBarToApp =
displayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
&& service.getRecentsAnimationController() == null
&& displayContent.getFixedRotationAnimationController() == null;
&& displayContent.getFadeRotationAnimationController() == null;
if (shouldAttachNavBarToApp) {
startNavigationBarWindowAnimation(
displayContent, durationHint, statusBarTransitionDelay, targets,

View File

@@ -608,8 +608,8 @@ public class RecentsAnimationController implements DeathRecipient {
private void attachNavigationBarToApp() {
if (!mShouldAttachNavBarToAppDuringTransition
// Skip the case where the nav bar is controlled by fixed rotation.
|| mDisplayContent.getFixedRotationAnimationController() != null) {
// Skip the case where the nav bar is controlled by fade rotation.
|| mDisplayContent.getFadeRotationAnimationController() != null) {
return;
}
ActivityRecord topActivity = null;

View File

@@ -1514,11 +1514,20 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void setOrientationChanging(boolean changing) {
mOrientationChanging = changing;
mOrientationChangeTimedOut = false;
if (mOrientationChanging == changing) {
return;
}
mOrientationChanging = changing;
if (changing) {
mLastFreezeDuration = 0;
mWmService.mRoot.mOrientationChangeComplete = false;
if (mWmService.mRoot.mOrientationChangeComplete
&& mDisplayContent.waitForUnfreeze(this)) {
mWmService.mRoot.mOrientationChangeComplete = false;
}
} else {
// The orientation change is completed. If it was hidden by the animation, reshow it.
mDisplayContent.finishFadeRotationAnimation(this);
}
}

View File

@@ -656,8 +656,10 @@ class WindowStateAnimator {
if (w.getOrientationChanging()) {
if (!w.isDrawn()) {
w.mWmService.mRoot.mOrientationChangeComplete = false;
mAnimator.mLastWindowFreezeSource = w;
if (w.mDisplayContent.waitForUnfreeze(w)) {
w.mWmService.mRoot.mOrientationChangeComplete = false;
mAnimator.mLastWindowFreezeSource = w;
}
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Orientation continue waiting for draw in %s", w);
} else {

View File

@@ -46,6 +46,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -1267,6 +1268,42 @@ public class DisplayContentTests extends WindowTestsBase {
is(Configuration.ORIENTATION_PORTRAIT));
}
@Test
public void testHybridRotationAnimation() {
final DisplayContent displayContent = mDefaultDisplay;
final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
final WindowState app = createWindow(null, TYPE_BASE_APPLICATION, "app");
final WindowState[] windows = { statusBar, navBar, app };
makeWindowVisible(windows);
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.addWindowLw(statusBar, statusBar.mAttrs);
displayPolicy.addWindowLw(navBar, navBar.mAttrs);
final ScreenRotationAnimation rotationAnim = new ScreenRotationAnimation(displayContent,
displayContent.getRotation());
spyOn(rotationAnim);
// Assume that the display rotation is changed so it is frozen in preparation for animation.
doReturn(true).when(rotationAnim).hasScreenshot();
mWm.mDisplayFrozen = true;
displayContent.setRotationAnimation(rotationAnim);
// The fade rotation animation also starts to hide some non-app windows.
assertNotNull(displayContent.getFadeRotationAnimationController());
assertTrue(statusBar.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
for (WindowState w : windows) {
w.setOrientationChanging(true);
}
// The display only waits for the app window to unfreeze.
assertFalse(displayContent.waitForUnfreeze(statusBar));
assertFalse(displayContent.waitForUnfreeze(navBar));
assertTrue(displayContent.waitForUnfreeze(app));
// If all windows animated by fade rotation animation have done the orientation change,
// the animation controller should be cleared.
statusBar.setOrientationChanging(false);
navBar.setOrientationChanging(false);
assertNull(displayContent.getFadeRotationAnimationController());
}
@UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR })
@Test
public void testApplyTopFixedRotationTransform() {
@@ -1275,6 +1312,7 @@ public class DisplayContentTests extends WindowTestsBase {
doReturn(false).when(displayPolicy).navigationBarCanMove();
displayPolicy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
makeWindowVisible(mStatusBarWindow, mNavBarWindow);
final Configuration config90 = new Configuration();
mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
@@ -1297,7 +1335,7 @@ public class DisplayContentTests extends WindowTestsBase {
ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
false /* forceUpdate */));
assertNotNull(mDisplayContent.getFixedRotationAnimationController());
assertNotNull(mDisplayContent.getFadeRotationAnimationController());
assertTrue(mStatusBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
ANIMATION_TYPE_FIXED_TRANSFORM));
assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
@@ -1381,7 +1419,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(app.hasFixedRotationTransform());
assertFalse(app2.hasFixedRotationTransform());
assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
assertNull(mDisplayContent.getFixedRotationAnimationController());
assertNull(mDisplayContent.getFadeRotationAnimationController());
}
@Test

View File

@@ -556,11 +556,11 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
}
@Test
public void testNotAttachNavigationBar_controlledByFixedRotationAnimation() {
public void testNotAttachNavigationBar_controlledByFadeRotationAnimation() {
setupForShouldAttachNavBarDuringTransition();
FixedRotationAnimationController mockController =
mock(FixedRotationAnimationController.class);
doReturn(mockController).when(mDefaultDisplay).getFixedRotationAnimationController();
FadeRotationAnimationController mockController =
mock(FadeRotationAnimationController.class);
doReturn(mockController).when(mDefaultDisplay).getFadeRotationAnimationController();
final ActivityRecord homeActivity = createHomeActivity();
initializeRecentsAnimationController(mController, homeActivity);
assertFalse(mController.isNavigationBarAttachedToApp());

View File

@@ -578,10 +578,10 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
@Test
public void testNonAppTarget_notSendNavBar_controlledByFixedRotation() throws Exception {
final FixedRotationAnimationController mockController =
mock(FixedRotationAnimationController.class);
doReturn(mockController).when(mDisplayContent).getFixedRotationAnimationController();
public void testNonAppTarget_notSendNavBar_controlledByFadeRotation() throws Exception {
final FadeRotationAnimationController mockController =
mock(FadeRotationAnimationController.class);
doReturn(mockController).when(mDisplayContent).getFadeRotationAnimationController();
final int transit = TRANSIT_OLD_TASK_OPEN;
setupForNonAppTargetNavBar(transit, true);