Loading services/core/java/com/android/server/wm/AnimationAdapter.java +21 −0 Original line number Diff line number Diff line Loading @@ -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; } } services/core/java/com/android/server/wm/DisplayContent.java +14 −0 Original line number Diff line number Diff line Loading @@ -499,6 +499,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ ActivityRecord mFixedRotationLaunchingApp; FixedRotationAnimationController mFixedRotationAnimationController; final FixedRotationTransitionListener mFixedRotationTransitionListener = new FixedRotationTransitionListener(); Loading Loading @@ -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); Loading @@ -1549,6 +1556,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } void finishFixedRotationAnimation() { if (mFixedRotationAnimationController != null && mFixedRotationAnimationController.show()) { mFixedRotationAnimationController = null; } } /** * Update rotation of the display. * Loading services/core/java/com/android/server/wm/DisplayRotation.java +2 −0 Original line number Diff line number Diff line Loading @@ -560,6 +560,7 @@ public class DisplayRotation { }, true /* traverseTopToBottom */); mSeamlessRotationCount = 0; mRotatingSeamlessly = false; mDisplayContent.finishFixedRotationAnimation(); } private void prepareSeamlessRotation() { Loading Loading @@ -646,6 +647,7 @@ public class DisplayRotation { "Performing post-rotate rotation after seamless rotation"); // Finish seamless rotation. mRotatingSeamlessly = false; mDisplayContent.finishFixedRotationAnimation(); updateRotationAndSendNewConfigIfChanged(); } Loading services/core/java/com/android/server/wm/FixedRotationAnimationController.java 0 → 100644 +197 −0 Original line number Diff line number Diff line /* * 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; } } } services/core/java/com/android/server/wm/SurfaceAnimator.java +18 −2 Original line number Diff line number Diff line Loading @@ -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(); } } Loading Loading @@ -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 Loading @@ -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 {} Loading Loading @@ -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. Loading Loading
services/core/java/com/android/server/wm/AnimationAdapter.java +21 −0 Original line number Diff line number Diff line Loading @@ -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; } }
services/core/java/com/android/server/wm/DisplayContent.java +14 −0 Original line number Diff line number Diff line Loading @@ -499,6 +499,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ ActivityRecord mFixedRotationLaunchingApp; FixedRotationAnimationController mFixedRotationAnimationController; final FixedRotationTransitionListener mFixedRotationTransitionListener = new FixedRotationTransitionListener(); Loading Loading @@ -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); Loading @@ -1549,6 +1556,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } void finishFixedRotationAnimation() { if (mFixedRotationAnimationController != null && mFixedRotationAnimationController.show()) { mFixedRotationAnimationController = null; } } /** * Update rotation of the display. * Loading
services/core/java/com/android/server/wm/DisplayRotation.java +2 −0 Original line number Diff line number Diff line Loading @@ -560,6 +560,7 @@ public class DisplayRotation { }, true /* traverseTopToBottom */); mSeamlessRotationCount = 0; mRotatingSeamlessly = false; mDisplayContent.finishFixedRotationAnimation(); } private void prepareSeamlessRotation() { Loading Loading @@ -646,6 +647,7 @@ public class DisplayRotation { "Performing post-rotate rotation after seamless rotation"); // Finish seamless rotation. mRotatingSeamlessly = false; mDisplayContent.finishFixedRotationAnimation(); updateRotationAndSendNewConfigIfChanged(); } Loading
services/core/java/com/android/server/wm/FixedRotationAnimationController.java 0 → 100644 +197 −0 Original line number Diff line number Diff line /* * 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; } } }
services/core/java/com/android/server/wm/SurfaceAnimator.java +18 −2 Original line number Diff line number Diff line Loading @@ -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(); } } Loading Loading @@ -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 Loading @@ -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 {} Loading Loading @@ -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. Loading