blob: eab3f108d17a9aafb8fd823995d0b58f03a52257 [file] [log] [blame]
/*
* 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;
/** A runnable which gets called when the {@link #show()} is called. */
private Runnable mOnShowRunnable;
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;
}
// Collect the target windows to fade out. The display won't wait for them to unfreeze.
final WindowState notificationShade = displayPolicy.getNotificationShade();
displayContent.forAllWindows(w -> {
if (w.mActivityRecord == null && w.mHasSurface && !w.mForceSeamlesslyRotate
&& !w.mIsWallpaper && !w.mIsImWindow && w != navigationBar
&& w != notificationShade) {
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);
}
if (mOnShowRunnable != null) {
mOnShowRunnable.run();
mOnShowRunnable = null;
}
}
/**
* 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 isHandledToken(WindowToken token) {
return token == mNavBarToken || isTargetToken(token);
}
/** Returns {@code true} if the controller will run fade animations on the window. */
boolean isTargetToken(WindowToken token) {
return mTargetWindowTokens.contains(token);
}
void setOnShowRunnable(Runnable onShowRunnable) {
mOnShowRunnable = onShowRunnable;
}
@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();
}
}