Merge changes I24d33b94,I9db3c5d1 into rvc-dev am: ade62d5627
Change-Id: I9bebe4cd5d30b553d92b7a8e9a9816e1a5a8a3fb
This commit is contained in:
@@ -85,4 +85,25 @@ interface AnimationAdapter {
|
||||
}
|
||||
|
||||
void dumpDebug(ProtoOutputStream proto);
|
||||
|
||||
/**
|
||||
* Gets called when the animation is about to finish and gives the client the opportunity to
|
||||
* defer finishing the animation, i.e. it keeps the leash around until the client calls
|
||||
* endDeferFinishCallback.
|
||||
* <p>
|
||||
* This has the same effect as
|
||||
* {@link com.android.server.wm.SurfaceAnimator.Animatable#shouldDeferAnimationFinish(Runnable)}
|
||||
* . The later will be evaluated first and has precedence over this method if it returns true,
|
||||
* which means that if the {@link com.android.server.wm.SurfaceAnimator.Animatable} requests to
|
||||
* defer its finish, this method won't be called so this adapter will never have access to the
|
||||
* finish callback. On the other hand, if the
|
||||
* {@link com.android.server.wm.SurfaceAnimator.Animatable}, doesn't request to defer, this
|
||||
* {@link AnimationAdapter} is responsible for ending the animation.
|
||||
*
|
||||
* @param endDeferFinishCallback The callback to call when defer finishing should be ended.
|
||||
* @return Whether the client would like to defer the animation finish.
|
||||
*/
|
||||
default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,6 +499,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
*/
|
||||
ActivityRecord mFixedRotationLaunchingApp;
|
||||
|
||||
FixedRotationAnimationController mFixedRotationAnimationController;
|
||||
|
||||
final FixedRotationTransitionListener mFixedRotationTransitionListener =
|
||||
new FixedRotationTransitionListener();
|
||||
|
||||
@@ -1528,6 +1530,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
}
|
||||
|
||||
private void startFixedRotationTransform(WindowToken token, int rotation) {
|
||||
if (mFixedRotationAnimationController == null) {
|
||||
mFixedRotationAnimationController = new FixedRotationAnimationController(
|
||||
this);
|
||||
}
|
||||
mFixedRotationAnimationController.hide(rotation);
|
||||
mTmpConfiguration.unset();
|
||||
final DisplayInfo info = computeScreenConfiguration(mTmpConfiguration, rotation);
|
||||
final WmDisplayCutout cutout = calculateDisplayCutoutForRotation(rotation);
|
||||
@@ -1549,6 +1556,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
}
|
||||
}
|
||||
|
||||
void finishFixedRotationAnimation() {
|
||||
if (mFixedRotationAnimationController != null
|
||||
&& mFixedRotationAnimationController.show()) {
|
||||
mFixedRotationAnimationController = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rotation of the display.
|
||||
*
|
||||
|
||||
@@ -560,6 +560,7 @@ public class DisplayRotation {
|
||||
}, true /* traverseTopToBottom */);
|
||||
mSeamlessRotationCount = 0;
|
||||
mRotatingSeamlessly = false;
|
||||
mDisplayContent.finishFixedRotationAnimation();
|
||||
}
|
||||
|
||||
private void prepareSeamlessRotation() {
|
||||
@@ -646,6 +647,7 @@ public class DisplayRotation {
|
||||
"Performing post-rotate rotation after seamless rotation");
|
||||
// Finish seamless rotation.
|
||||
mRotatingSeamlessly = false;
|
||||
mDisplayContent.finishFixedRotationAnimation();
|
||||
|
||||
updateRotationAndSendNewConfigIfChanged();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.res.Configuration;
|
||||
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 WindowManagerService mWmService;
|
||||
private boolean mShowRequested = true;
|
||||
private int mTargetRotation = Configuration.ORIENTATION_UNDEFINED;
|
||||
private final ArrayList<WindowState> mAnimatedWindowStates = new ArrayList<>(2);
|
||||
private final Runnable[] mDeferredFinishCallbacks;
|
||||
|
||||
public FixedRotationAnimationController(DisplayContent displayContent) {
|
||||
mWmService = displayContent.mWmService;
|
||||
addAnimatedWindow(displayContent.getDisplayPolicy().getStatusBar());
|
||||
addAnimatedWindow(displayContent.getDisplayPolicy().getNavigationBar());
|
||||
mDeferredFinishCallbacks = new Runnable[mAnimatedWindowStates.size()];
|
||||
}
|
||||
|
||||
private void addAnimatedWindow(WindowState windowState) {
|
||||
if (windowState != null) {
|
||||
mAnimatedWindowStates.add(windowState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the previously hidden {@link WindowToken} if their surfaces have already been rotated.
|
||||
*
|
||||
* @return True if the show animation has been started, in which case the caller no longer needs
|
||||
* this {@link FixedRotationAnimationController}.
|
||||
*/
|
||||
boolean show() {
|
||||
if (!mShowRequested && readyToShow()) {
|
||||
mShowRequested = true;
|
||||
for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
|
||||
WindowState windowState = mAnimatedWindowStates.get(i);
|
||||
fadeWindowToken(true, windowState.getParent(), i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void hide(int targetRotation) {
|
||||
mTargetRotation = targetRotation;
|
||||
if (mShowRequested) {
|
||||
mShowRequested = false;
|
||||
for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
|
||||
WindowState windowState = mAnimatedWindowStates.get(i);
|
||||
fadeWindowToken(false /* show */, windowState.getParent(), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
|
||||
WindowState windowState = mAnimatedWindowStates.get(i);
|
||||
mShowRequested = true;
|
||||
fadeWindowToken(true /* show */, windowState.getParent(), i);
|
||||
}
|
||||
}
|
||||
|
||||
private void fadeWindowToken(boolean show, WindowContainer<WindowToken> windowToken,
|
||||
int index) {
|
||||
Animation animation = AnimationUtils.loadAnimation(mWmService.mContext,
|
||||
show ? R.anim.fade_in : R.anim.fade_out);
|
||||
LocalAnimationAdapter.AnimationSpec windowAnimationSpec = createAnimationSpec(animation);
|
||||
|
||||
FixedRotationAnimationAdapter animationAdapter = new FixedRotationAnimationAdapter(
|
||||
windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, index);
|
||||
|
||||
// We deferred the end of the animation when hiding the token, so we need to end it now that
|
||||
// it's shown again.
|
||||
SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
|
||||
if (mDeferredFinishCallbacks[index] != null) {
|
||||
mDeferredFinishCallbacks[index].run();
|
||||
mDeferredFinishCallbacks[index] = null;
|
||||
}
|
||||
} : null;
|
||||
windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
|
||||
mShowRequested, ANIMATION_TYPE_FIXED_TRANSFORM, finishedCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all the mAnimatedWindowState's surfaces have been rotated to the
|
||||
* mTargetRotation.
|
||||
*/
|
||||
private boolean readyToShow() {
|
||||
for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) {
|
||||
WindowState windowState = mAnimatedWindowStates.get(i);
|
||||
if (windowState.getConfiguration().windowConfiguration.getRotation()
|
||||
!= mTargetRotation || windowState.mPendingSeamlessRotate != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private LocalAnimationAdapter.AnimationSpec createAnimationSpec(Animation animation) {
|
||||
return new LocalAnimationAdapter.AnimationSpec() {
|
||||
|
||||
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 int mIndex;
|
||||
|
||||
FixedRotationAnimationAdapter(AnimationSpec windowAnimationSpec,
|
||||
SurfaceAnimationRunner surfaceAnimationRunner, boolean show, int index) {
|
||||
super(windowAnimationSpec, surfaceAnimationRunner);
|
||||
mShow = show;
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
@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[mIndex] = endDeferFinishCallback;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,10 @@ class SurfaceAnimator {
|
||||
animationFinishCallback.onAnimationFinished(type, anim);
|
||||
}
|
||||
};
|
||||
if (!mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)) {
|
||||
// If both the Animatable and AnimationAdapter requests to be deferred, only the
|
||||
// first one will be called.
|
||||
if (!(mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)
|
||||
|| anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {
|
||||
resetAndInvokeFinish.run();
|
||||
}
|
||||
}
|
||||
@@ -485,6 +488,12 @@ class SurfaceAnimator {
|
||||
*/
|
||||
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}
|
||||
* @hide
|
||||
@@ -502,7 +511,8 @@ class SurfaceAnimator {
|
||||
ANIMATION_TYPE_DIMMER,
|
||||
ANIMATION_TYPE_RECENTS,
|
||||
ANIMATION_TYPE_WINDOW_ANIMATION,
|
||||
ANIMATION_TYPE_INSETS_CONTROL
|
||||
ANIMATION_TYPE_INSETS_CONTROL,
|
||||
ANIMATION_TYPE_FIXED_TRANSFORM
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface AnimationType {}
|
||||
@@ -592,6 +602,12 @@ class SurfaceAnimator {
|
||||
* Gets called when the animation is about to finish and gives the client the opportunity to
|
||||
* defer finishing the animation, i.e. it keeps the leash around until the client calls
|
||||
* {@link #cancelAnimation}.
|
||||
* <p>
|
||||
* {@link AnimationAdapter} has a similar method which is called only if this method returns
|
||||
* false. This mean that if both this {@link Animatable} and the {@link AnimationAdapter}
|
||||
* request to be deferred, this method is the sole responsible to call
|
||||
* endDeferFinishCallback. On the other hand, the animation finish might still be deferred
|
||||
* if this method return false and the one from the {@link AnimationAdapter} returns true.
|
||||
*
|
||||
* @param endDeferFinishCallback The callback to call when defer finishing should be ended.
|
||||
* @return Whether the client would like to defer the animation finish.
|
||||
|
||||
@@ -642,6 +642,9 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
final int originalRotation = getWindowConfiguration().getRotation();
|
||||
onConfigurationChanged(parent.getConfiguration());
|
||||
onCancelFixedRotationTransform(originalRotation);
|
||||
if (mDisplayContent.mFixedRotationAnimationController != null) {
|
||||
mDisplayContent.mFixedRotationAnimationController.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.times;
|
||||
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.WindowManagerService.UPDATE_FOCUS_NORMAL;
|
||||
|
||||
@@ -1059,6 +1060,8 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
@Test
|
||||
public void testApplyTopFixedRotationTransform() {
|
||||
mWm.mIsFixedRotationTransformEnabled = true;
|
||||
mDisplayContent.getDisplayPolicy().addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
|
||||
mDisplayContent.getDisplayPolicy().addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
|
||||
final Configuration config90 = new Configuration();
|
||||
mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
|
||||
|
||||
@@ -1079,6 +1082,12 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
|
||||
false /* forceUpdate */));
|
||||
|
||||
assertNotNull(mDisplayContent.mFixedRotationAnimationController);
|
||||
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 outInsets = new Rect();
|
||||
final Rect outStableInsets = new Rect();
|
||||
@@ -1131,6 +1140,9 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
assertFalse(app.hasFixedRotationTransform());
|
||||
assertFalse(app2.hasFixedRotationTransform());
|
||||
assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
|
||||
|
||||
mDisplayContent.finishFixedRotationAnimation();
|
||||
assertNull(mDisplayContent.mFixedRotationAnimationController);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -33,6 +33,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.Builder;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
@@ -52,6 +53,8 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Test class for {@link SurfaceAnimatorTest}.
|
||||
*
|
||||
@@ -267,6 +270,27 @@ public class SurfaceAnimatorTest extends WindowTestsBase {
|
||||
assertFalse(mDeferFinishAnimatable.mFinishedCallbackCalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeferFinishFromAdapter() {
|
||||
|
||||
DeferredFinishAdapter deferredFinishAdapter = new DeferredFinishAdapter();
|
||||
// Start animation
|
||||
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, deferredFinishAdapter,
|
||||
true /* hidden */,
|
||||
ANIMATION_TYPE_APP_TRANSITION);
|
||||
assertAnimating(mAnimatable);
|
||||
deferredFinishAdapter.mFinishCallback.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION,
|
||||
deferredFinishAdapter);
|
||||
|
||||
assertAnimating(mAnimatable);
|
||||
assertFalse(mAnimatable.mFinishedCallbackCalled);
|
||||
// Now end defer finishing.
|
||||
deferredFinishAdapter.mEndDeferFinishCallback.run();
|
||||
assertNotAnimating(mAnimatable);
|
||||
assertTrue(mAnimatable.mFinishedCallbackCalled);
|
||||
verify(mTransaction).remove(eq(deferredFinishAdapter.mAnimationLeash));
|
||||
}
|
||||
|
||||
private OnAnimationFinishedCallback startDeferFinishAnimatable(AnimationAdapter anim) {
|
||||
mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, anim,
|
||||
true /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
|
||||
@@ -389,4 +413,51 @@ public class SurfaceAnimatorTest extends WindowTestsBase {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DeferredFinishAdapter implements AnimationAdapter {
|
||||
|
||||
private Runnable mEndDeferFinishCallback;
|
||||
private OnAnimationFinishedCallback mFinishCallback;
|
||||
private SurfaceControl mAnimationLeash;
|
||||
|
||||
@Override
|
||||
public boolean getShowWallpaper() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAnimation(SurfaceControl animationLeash, Transaction t, int type,
|
||||
OnAnimationFinishedCallback finishCallback) {
|
||||
mFinishCallback = finishCallback;
|
||||
mAnimationLeash = animationLeash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancelled(SurfaceControl animationLeash) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDurationHint() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStatusBarTransitionsStartTime() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dumpDebug(ProtoOutputStream proto) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
|
||||
mEndDeferFinishCallback = endDeferFinishCallback;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user