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 45 /** Whether to use constant zero alpha animation. */ 46 private boolean mHideImmediately; 47 FadeRotationAnimationController(DisplayContent displayContent)48 public FadeRotationAnimationController(DisplayContent displayContent) { 49 super(displayContent); 50 mService = displayContent.mWmService; 51 mFrozenTimeoutRunnable = mService.mDisplayFrozen ? () -> { 52 synchronized (mService.mGlobalLock) { 53 displayContent.finishFadeRotationAnimationIfPossible(); 54 mService.mWindowPlacerLocked.performSurfacePlacement(); 55 } 56 } : null; 57 if (mFrozenTimeoutRunnable != null) { 58 // Hide the windows immediately because screen should have been covered by screenshot. 59 mHideImmediately = true; 60 } 61 final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); 62 final WindowState navigationBar = displayPolicy.getNavigationBar(); 63 if (navigationBar != null) { 64 mNavBarToken = navigationBar.mToken; 65 final RecentsAnimationController controller = mService.getRecentsAnimationController(); 66 final boolean navBarControlledByRecents = 67 controller != null && controller.isNavigationBarAttachedToApp(); 68 // Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation 69 // bar is currently controlled by recents animation. 70 if (!displayPolicy.navigationBarCanMove() && !navBarControlledByRecents) { 71 mTargetWindowTokens.add(mNavBarToken); 72 } 73 } else { 74 mNavBarToken = null; 75 } 76 // Collect the target windows to fade out. The display won't wait for them to unfreeze. 77 final WindowState notificationShade = displayPolicy.getNotificationShade(); 78 displayContent.forAllWindows(w -> { 79 if (w.mActivityRecord == null && w.mHasSurface && !w.mForceSeamlesslyRotate 80 && !w.mIsWallpaper && !w.mIsImWindow && w != navigationBar 81 && w != notificationShade) { 82 mTargetWindowTokens.add(w.mToken); 83 } 84 }, true /* traverseTopToBottom */); 85 } 86 87 /** Applies show animation on the previously hidden window tokens. */ show()88 void show() { 89 for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { 90 final WindowToken windowToken = mTargetWindowTokens.get(i); 91 fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM); 92 } 93 mTargetWindowTokens.clear(); 94 if (mFrozenTimeoutRunnable != null) { 95 mService.mH.removeCallbacks(mFrozenTimeoutRunnable); 96 } 97 if (mOnShowRunnable != null) { 98 mOnShowRunnable.run(); 99 mOnShowRunnable = null; 100 } 101 } 102 103 /** 104 * Returns {@code true} if all target windows are shown. It only takes effects if this 105 * controller is created for normal rotation. 106 */ show(WindowToken token)107 boolean show(WindowToken token) { 108 if (mFrozenTimeoutRunnable != null && mTargetWindowTokens.remove(token)) { 109 fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM); 110 if (mTargetWindowTokens.isEmpty()) { 111 mService.mH.removeCallbacks(mFrozenTimeoutRunnable); 112 return true; 113 } 114 } 115 return false; 116 } 117 118 /** Applies hide animation on the window tokens which may be seamlessly rotated later. */ hide()119 void hide() { 120 for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { 121 final WindowToken windowToken = mTargetWindowTokens.get(i); 122 fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM); 123 } 124 if (mFrozenTimeoutRunnable != null) { 125 mService.mH.postDelayed(mFrozenTimeoutRunnable, 126 WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION); 127 } 128 } 129 130 /** Hides the window immediately until it is drawn in new rotation. */ hideImmediately(WindowToken windowToken)131 void hideImmediately(WindowToken windowToken) { 132 final boolean original = mHideImmediately; 133 mHideImmediately = true; 134 mTargetWindowTokens.add(windowToken); 135 fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM); 136 mHideImmediately = original; 137 } 138 139 /** Returns {@code true} if the window is handled by this controller. */ isHandledToken(WindowToken token)140 boolean isHandledToken(WindowToken token) { 141 return token == mNavBarToken || isTargetToken(token); 142 } 143 144 /** Returns {@code true} if the controller will run fade animations on the window. */ isTargetToken(WindowToken token)145 boolean isTargetToken(WindowToken token) { 146 return mTargetWindowTokens.contains(token); 147 } 148 setOnShowRunnable(Runnable onShowRunnable)149 void setOnShowRunnable(Runnable onShowRunnable) { 150 mOnShowRunnable = onShowRunnable; 151 } 152 153 @Override getFadeInAnimation()154 public Animation getFadeInAnimation() { 155 if (mFrozenTimeoutRunnable != null) { 156 // Use a shorter animation so it is easier to align with screen rotation animation. 157 return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter); 158 } 159 return super.getFadeInAnimation(); 160 } 161 162 @Override getFadeOutAnimation()163 public Animation getFadeOutAnimation() { 164 if (mHideImmediately) { 165 return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */); 166 } 167 return super.getFadeOutAnimation(); 168 } 169 } 170