Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93a4ca13 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "System Bars animation for fixed rotation transform" into rvc-dev am: 148fb0e8

Change-Id: Iaea51c0a02ef362faaf4c7e693aee10a1682cfaf
parents 7075c8a1 148fb0e8
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -498,6 +498,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
     */
    private ActivityRecord mFixedRotationLaunchingApp;

    private FixedRotationAnimationController mFixedRotationAnimationController;

    final FixedRotationTransitionListener mFixedRotationTransitionListener =
            new FixedRotationTransitionListener();

@@ -1487,6 +1489,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        return mFixedRotationLaunchingApp;
    }

    @VisibleForTesting
    @Nullable FixedRotationAnimationController getFixedRotationAnimationController() {
        return mFixedRotationAnimationController;
    }

    void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r) {
        setFixedRotationLaunchingAppUnchecked(r, ROTATION_UNDEFINED);
    }
@@ -1494,8 +1501,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
    void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
        if (mFixedRotationLaunchingApp == null && r != null) {
            mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
            if (mFixedRotationAnimationController == null) {
                mFixedRotationAnimationController = new FixedRotationAnimationController(this);
                mFixedRotationAnimationController.hide();
            }
        } else if (mFixedRotationLaunchingApp != null && r == null) {
            mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
            finishFixedRotationAnimationIfPossible();
        }
        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.
     *
+6 −0
Original line number Diff line number Diff line
@@ -560,6 +560,7 @@ public class DisplayRotation {
        }, true /* traverseTopToBottom */);
        mSeamlessRotationCount = 0;
        mRotatingSeamlessly = false;
        mDisplayContent.finishFixedRotationAnimationIfPossible();
    }

    private void prepareSeamlessRotation() {
@@ -573,6 +574,10 @@ public class DisplayRotation {
        return mRotatingSeamlessly;
    }

    boolean hasSeamlessRotatingWindow() {
        return mSeamlessRotationCount > 0;
    }

    @VisibleForTesting
    boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
        // 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");
            // Finish seamless rotation.
            mRotatingSeamlessly = false;
            mDisplayContent.finishFixedRotationAnimationIfPossible();

            updateRotationAndSendNewConfigIfChanged();
        }
+166 −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.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;
        }
    }
}
+8 −1
Original line number Diff line number Diff line
@@ -488,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
@@ -505,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 {}
+14 −1
Original line number Diff line number Diff line
@@ -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;

@@ -1060,6 +1061,11 @@ public class DisplayContentTests extends WindowTestsBase {
    @Test
    public void testApplyTopFixedRotationTransform() {
        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();
        mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);

@@ -1080,6 +1086,12 @@ public class DisplayContentTests extends WindowTestsBase {
                ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
                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 outInsets = new Rect();
        final Rect outStableInsets = new Rect();
@@ -1132,6 +1144,7 @@ public class DisplayContentTests extends WindowTestsBase {
        assertFalse(app.hasFixedRotationTransform());
        assertFalse(app2.hasFixedRotationTransform());
        assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
        assertNull(mDisplayContent.getFixedRotationAnimationController());
    }

    @Test
@@ -1310,7 +1323,7 @@ public class DisplayContentTests extends WindowTestsBase {
    }

    private static int getRotatedOrientation(DisplayContent dc) {
        return dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
        return dc.mBaseDisplayWidth > dc.mBaseDisplayHeight
                ? SCREEN_ORIENTATION_PORTRAIT
                : SCREEN_ORIENTATION_LANDSCAPE;
    }