Merge "System Bars animation for fixed rotation transform" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
148fb0e8c2
@@ -498,6 +498,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
|||||||
*/
|
*/
|
||||||
private ActivityRecord mFixedRotationLaunchingApp;
|
private ActivityRecord mFixedRotationLaunchingApp;
|
||||||
|
|
||||||
|
private FixedRotationAnimationController mFixedRotationAnimationController;
|
||||||
|
|
||||||
final FixedRotationTransitionListener mFixedRotationTransitionListener =
|
final FixedRotationTransitionListener mFixedRotationTransitionListener =
|
||||||
new FixedRotationTransitionListener();
|
new FixedRotationTransitionListener();
|
||||||
|
|
||||||
@@ -1487,6 +1489,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
|||||||
return mFixedRotationLaunchingApp;
|
return mFixedRotationLaunchingApp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
@Nullable FixedRotationAnimationController getFixedRotationAnimationController() {
|
||||||
|
return mFixedRotationAnimationController;
|
||||||
|
}
|
||||||
|
|
||||||
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r) {
|
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r) {
|
||||||
setFixedRotationLaunchingAppUnchecked(r, ROTATION_UNDEFINED);
|
setFixedRotationLaunchingAppUnchecked(r, ROTATION_UNDEFINED);
|
||||||
}
|
}
|
||||||
@@ -1494,8 +1501,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
|||||||
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
|
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
|
||||||
if (mFixedRotationLaunchingApp == null && r != null) {
|
if (mFixedRotationLaunchingApp == null && r != null) {
|
||||||
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
|
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
|
||||||
|
if (mFixedRotationAnimationController == null) {
|
||||||
|
mFixedRotationAnimationController = new FixedRotationAnimationController(this);
|
||||||
|
mFixedRotationAnimationController.hide();
|
||||||
|
}
|
||||||
} else if (mFixedRotationLaunchingApp != null && r == null) {
|
} else if (mFixedRotationLaunchingApp != null && r == null) {
|
||||||
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
|
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
|
||||||
|
finishFixedRotationAnimationIfPossible();
|
||||||
}
|
}
|
||||||
mFixedRotationLaunchingApp = r;
|
mFixedRotationLaunchingApp = r;
|
||||||
}
|
}
|
||||||
@@ -1584,6 +1596,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Re-show the previously hidden windows if all seamless rotated windows are done. */
|
||||||
|
void finishFixedRotationAnimationIfPossible() {
|
||||||
|
final FixedRotationAnimationController controller = mFixedRotationAnimationController;
|
||||||
|
if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
|
||||||
|
controller.show();
|
||||||
|
mFixedRotationAnimationController = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update rotation of the display.
|
* Update rotation of the display.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -560,6 +560,7 @@ public class DisplayRotation {
|
|||||||
}, true /* traverseTopToBottom */);
|
}, true /* traverseTopToBottom */);
|
||||||
mSeamlessRotationCount = 0;
|
mSeamlessRotationCount = 0;
|
||||||
mRotatingSeamlessly = false;
|
mRotatingSeamlessly = false;
|
||||||
|
mDisplayContent.finishFixedRotationAnimationIfPossible();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareSeamlessRotation() {
|
private void prepareSeamlessRotation() {
|
||||||
@@ -573,6 +574,10 @@ public class DisplayRotation {
|
|||||||
return mRotatingSeamlessly;
|
return mRotatingSeamlessly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasSeamlessRotatingWindow() {
|
||||||
|
return mSeamlessRotationCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
|
boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
|
||||||
// Display doesn't need to be frozen because application has been started in correct
|
// Display doesn't need to be frozen because application has been started in correct
|
||||||
@@ -646,6 +651,7 @@ public class DisplayRotation {
|
|||||||
"Performing post-rotate rotation after seamless rotation");
|
"Performing post-rotate rotation after seamless rotation");
|
||||||
// Finish seamless rotation.
|
// Finish seamless rotation.
|
||||||
mRotatingSeamlessly = false;
|
mRotatingSeamlessly = false;
|
||||||
|
mDisplayContent.finishFixedRotationAnimationIfPossible();
|
||||||
|
|
||||||
updateRotationAndSendNewConfigIfChanged();
|
updateRotationAndSendNewConfigIfChanged();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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.AnimationSpecProto.WINDOW;
|
||||||
|
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
|
||||||
|
import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.ArrayMap;
|
||||||
|
import android.util.proto.ProtoOutputStream;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.AnimationUtils;
|
||||||
|
import android.view.animation.Transformation;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final WindowState mStatusBar;
|
||||||
|
private final WindowState mNavigationBar;
|
||||||
|
private final ArrayList<WindowToken> mAnimatedWindowToken = new ArrayList<>(2);
|
||||||
|
private final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
|
||||||
|
|
||||||
|
public FixedRotationAnimationController(DisplayContent displayContent) {
|
||||||
|
mContext = displayContent.mWmService.mContext;
|
||||||
|
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
|
||||||
|
mStatusBar = displayPolicy.getStatusBar();
|
||||||
|
// Do not animate movable navigation bar (e.g. non-gesture mode).
|
||||||
|
mNavigationBar = !displayPolicy.navigationBarCanMove()
|
||||||
|
? 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Applies hide animation on the window tokens which may be seamlessly rotated later. */
|
||||||
|
void hide() {
|
||||||
|
if (mNavigationBar != null) {
|
||||||
|
fadeWindowToken(false /* show */, mNavigationBar.mToken);
|
||||||
|
}
|
||||||
|
if (mStatusBar != null) {
|
||||||
|
fadeWindowToken(false /* show */, mStatusBar.mToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fadeWindowToken(boolean show, WindowToken windowToken) {
|
||||||
|
if (windowToken == null || windowToken.getParent() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Animation animation = AnimationUtils.loadAnimation(mContext,
|
||||||
|
show ? R.anim.fade_in : R.anim.fade_out);
|
||||||
|
final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
|
||||||
|
createAnimationSpec(animation);
|
||||||
|
|
||||||
|
final FixedRotationAnimationAdapter animationAdapter = new FixedRotationAnimationAdapter(
|
||||||
|
windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken);
|
||||||
|
|
||||||
|
// We deferred the end of the animation when hiding the token, so we need to end it now that
|
||||||
|
// it's shown again.
|
||||||
|
final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
|
||||||
|
final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken);
|
||||||
|
if (runnable != null) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
} : null;
|
||||||
|
windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
|
||||||
|
show /* hidden */, ANIMATION_TYPE_FIXED_TRANSFORM, finishedCallback);
|
||||||
|
mAnimatedWindowToken.add(windowToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalAnimationAdapter.AnimationSpec createAnimationSpec(Animation animation) {
|
||||||
|
return new LocalAnimationAdapter.AnimationSpec() {
|
||||||
|
|
||||||
|
final Transformation mTransformation = new Transformation();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getShowWallpaper() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDuration() {
|
||||||
|
return animation.getDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(SurfaceControl.Transaction t, SurfaceControl leash,
|
||||||
|
long currentPlayTime) {
|
||||||
|
mTransformation.clear();
|
||||||
|
animation.getTransformation(currentPlayTime, mTransformation);
|
||||||
|
t.setAlpha(leash, mTransformation.getAlpha());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dump(PrintWriter pw, String prefix) {
|
||||||
|
pw.print(prefix);
|
||||||
|
pw.println(animation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dumpDebugInner(ProtoOutputStream proto) {
|
||||||
|
final long token = proto.start(WINDOW);
|
||||||
|
proto.write(ANIMATION, animation.toString());
|
||||||
|
proto.end(token);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FixedRotationAnimationAdapter extends LocalAnimationAdapter {
|
||||||
|
private final boolean mShow;
|
||||||
|
private final WindowToken mToken;
|
||||||
|
|
||||||
|
FixedRotationAnimationAdapter(AnimationSpec windowAnimationSpec,
|
||||||
|
SurfaceAnimationRunner surfaceAnimationRunner, boolean show,
|
||||||
|
WindowToken token) {
|
||||||
|
super(windowAnimationSpec, surfaceAnimationRunner);
|
||||||
|
mShow = show;
|
||||||
|
mToken = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
|
||||||
|
// We defer the end of the hide animation to ensure the tokens stay hidden until
|
||||||
|
// we show them again.
|
||||||
|
if (!mShow) {
|
||||||
|
mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -488,6 +488,12 @@ class SurfaceAnimator {
|
|||||||
*/
|
*/
|
||||||
static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
|
static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation when a fixed rotation transform is applied to a window token.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bitmask to include all animation types. This is NOT an {@link AnimationType}
|
* Bitmask to include all animation types. This is NOT an {@link AnimationType}
|
||||||
* @hide
|
* @hide
|
||||||
@@ -505,7 +511,8 @@ class SurfaceAnimator {
|
|||||||
ANIMATION_TYPE_DIMMER,
|
ANIMATION_TYPE_DIMMER,
|
||||||
ANIMATION_TYPE_RECENTS,
|
ANIMATION_TYPE_RECENTS,
|
||||||
ANIMATION_TYPE_WINDOW_ANIMATION,
|
ANIMATION_TYPE_WINDOW_ANIMATION,
|
||||||
ANIMATION_TYPE_INSETS_CONTROL
|
ANIMATION_TYPE_INSETS_CONTROL,
|
||||||
|
ANIMATION_TYPE_FIXED_TRANSFORM
|
||||||
})
|
})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@interface AnimationType {}
|
@interface AnimationType {}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
|
|||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||||
|
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
|
||||||
import static com.android.server.wm.WindowContainer.POSITION_TOP;
|
import static com.android.server.wm.WindowContainer.POSITION_TOP;
|
||||||
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
|
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
|
||||||
|
|
||||||
@@ -1060,6 +1061,11 @@ public class DisplayContentTests extends WindowTestsBase {
|
|||||||
@Test
|
@Test
|
||||||
public void testApplyTopFixedRotationTransform() {
|
public void testApplyTopFixedRotationTransform() {
|
||||||
mWm.mIsFixedRotationTransformEnabled = true;
|
mWm.mIsFixedRotationTransformEnabled = true;
|
||||||
|
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
|
||||||
|
// Only non-movable (gesture) navigation bar will be animated by fixed rotation animation.
|
||||||
|
doReturn(false).when(displayPolicy).navigationBarCanMove();
|
||||||
|
displayPolicy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
|
||||||
|
displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
|
||||||
final Configuration config90 = new Configuration();
|
final Configuration config90 = new Configuration();
|
||||||
mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
|
mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
|
||||||
|
|
||||||
@@ -1080,6 +1086,12 @@ public class DisplayContentTests extends WindowTestsBase {
|
|||||||
ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
|
ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
|
||||||
false /* forceUpdate */));
|
false /* forceUpdate */));
|
||||||
|
|
||||||
|
assertNotNull(mDisplayContent.getFixedRotationAnimationController());
|
||||||
|
assertTrue(mStatusBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
|
||||||
|
ANIMATION_TYPE_FIXED_TRANSFORM));
|
||||||
|
assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
|
||||||
|
ANIMATION_TYPE_FIXED_TRANSFORM));
|
||||||
|
|
||||||
final Rect outFrame = new Rect();
|
final Rect outFrame = new Rect();
|
||||||
final Rect outInsets = new Rect();
|
final Rect outInsets = new Rect();
|
||||||
final Rect outStableInsets = new Rect();
|
final Rect outStableInsets = new Rect();
|
||||||
@@ -1132,6 +1144,7 @@ public class DisplayContentTests extends WindowTestsBase {
|
|||||||
assertFalse(app.hasFixedRotationTransform());
|
assertFalse(app.hasFixedRotationTransform());
|
||||||
assertFalse(app2.hasFixedRotationTransform());
|
assertFalse(app2.hasFixedRotationTransform());
|
||||||
assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
|
assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
|
||||||
|
assertNull(mDisplayContent.getFixedRotationAnimationController());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1310,7 +1323,7 @@ public class DisplayContentTests extends WindowTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int getRotatedOrientation(DisplayContent dc) {
|
private static int getRotatedOrientation(DisplayContent dc) {
|
||||||
return dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
|
return dc.mBaseDisplayWidth > dc.mBaseDisplayHeight
|
||||||
? SCREEN_ORIENTATION_PORTRAIT
|
? SCREEN_ORIENTATION_PORTRAIT
|
||||||
: SCREEN_ORIENTATION_LANDSCAPE;
|
: SCREEN_ORIENTATION_LANDSCAPE;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user