1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM; 20 21 import android.view.animation.AlphaAnimation; 22 import android.view.animation.Animation; 23 import android.view.animation.AnimationUtils; 24 25 import com.android.internal.R; 26 27 import java.util.ArrayList; 28 29 /** 30 * Controller to fade out and in windows when the display is changing rotation. It can be used for 31 * both fixed rotation and normal rotation to hide some non-activity windows. The caller should show 32 * the windows until they are drawn with the new rotation. 33 */ 34 public class FadeRotationAnimationController extends FadeAnimationController { 35 36 private final ArrayList<WindowToken> mTargetWindowTokens = new ArrayList<>(); 37 private final WindowManagerService mService; 38 /** If non-null, it usually indicates that there will be a screen rotation animation. */ 39 private final Runnable mFrozenTimeoutRunnable; 40 private final WindowToken mNavBarToken; 41 42 /** A runnable which gets called when the {@link #show()} is called. */ 43 private Runnable mOnShowRunnable; 44 FadeRotationAnimationController(DisplayContent displayContent)45 public FadeRotationAnimationController(DisplayContent displayContent) { 46 super(displayContent); 47 mService = displayContent.mWmService; 48 mFrozenTimeoutRunnable = mService.mDisplayFrozen ? () -> { 49 synchronized (mService.mGlobalLock) { 50 displayContent.finishFadeRotationAnimationIfPossible(); 51 mService.mWindowPlacerLocked.performSurfacePlacement(); 52 } 53 } : null; 54 final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); 55 final WindowState navigationBar = displayPolicy.getNavigationBar(); 56 if (navigationBar != null) { 57 mNavBarToken = navigationBar.mToken; 58 final RecentsAnimationController controller = mService.getRecentsAnimationController(); 59 final boolean navBarControlledByRecents = 60 controller != null && controller.isNavigationBarAttachedToApp(); 61 // Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation 62 // bar is currently controlled by recents animation. 63 if (!displayPolicy.navigationBarCanMove() && !navBarControlledByRecents) { 64 mTargetWindowTokens.add(mNavBarToken); 65 } 66 } else { 67 mNavBarToken = null; 68 } 69 // Collect the target windows to fade out. The display won't wait for them to unfreeze. 70 final WindowState notificationShade = displayPolicy.getNotificationShade(); 71 displayContent.forAllWindows(w -> { 72 if (w.mActivityRecord == null && w.mHasSurface && !w.mForceSeamlesslyRotate 73 && !w.mIsWallpaper && !w.mIsImWindow && w != navigationBar 74 && w != notificationShade) { 75 mTargetWindowTokens.add(w.mToken); 76 } 77 }, true /* traverseTopToBottom */); 78 } 79 80 /** Applies show animation on the previously hidden window tokens. */ show()81 void show() { 82 for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { 83 final WindowToken windowToken = mTargetWindowTokens.get(i); 84 fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM); 85 } 86 mTargetWindowTokens.clear(); 87 if (mFrozenTimeoutRunnable != null) { 88 mService.mH.removeCallbacks(mFrozenTimeoutRunnable); 89 } 90 if (mOnShowRunnable != null) { 91 mOnShowRunnable.run(); 92 mOnShowRunnable = null; 93 } 94 } 95 96 /** 97 * Returns {@code true} if all target windows are shown. It only takes effects if this 98 * controller is created for normal rotation. 99 */ show(WindowToken token)100 boolean show(WindowToken token) { 101 if (mFrozenTimeoutRunnable != null && mTargetWindowTokens.remove(token)) { 102 fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM); 103 if (mTargetWindowTokens.isEmpty()) { 104 mService.mH.removeCallbacks(mFrozenTimeoutRunnable); 105 return true; 106 } 107 } 108 return false; 109 } 110 111 /** Applies hide animation on the window tokens which may be seamlessly rotated later. */ hide()112 void hide() { 113 for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { 114 final WindowToken windowToken = mTargetWindowTokens.get(i); 115 fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM); 116 } 117 if (mFrozenTimeoutRunnable != null) { 118 mService.mH.postDelayed(mFrozenTimeoutRunnable, 119 WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION); 120 } 121 } 122 123 /** Returns {@code true} if the window is handled by this controller. */ isHandledToken(WindowToken token)124 boolean isHandledToken(WindowToken token) { 125 return token == mNavBarToken || isTargetToken(token); 126 } 127 128 /** Returns {@code true} if the controller will run fade animations on the window. */ isTargetToken(WindowToken token)129 boolean isTargetToken(WindowToken token) { 130 return mTargetWindowTokens.contains(token); 131 } 132 setOnShowRunnable(Runnable onShowRunnable)133 void setOnShowRunnable(Runnable onShowRunnable) { 134 mOnShowRunnable = onShowRunnable; 135 } 136 137 @Override getFadeInAnimation()138 public Animation getFadeInAnimation() { 139 if (mFrozenTimeoutRunnable != null) { 140 // Use a shorter animation so it is easier to align with screen rotation animation. 141 return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter); 142 } 143 return super.getFadeInAnimation(); 144 } 145 146 @Override getFadeOutAnimation()147 public Animation getFadeOutAnimation() { 148 if (mFrozenTimeoutRunnable != null) { 149 // Hide the window immediately because screen should have been covered by screenshot. 150 return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */); 151 } 152 return super.getFadeOutAnimation(); 153 } 154 } 155