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

Commit 2382d897 authored by Shawn Lin's avatar Shawn Lin Committed by Android (Google) Code Review
Browse files

Merge "Attach navigation bar to app during quick switching"

parents 83254fb5 0e0d7bbd
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
@@ -380,6 +380,14 @@ public class DisplayPolicy {

    private RefreshRatePolicy mRefreshRatePolicy;

    /**
     * If true, attach the navigation bar to the current transition app.
     * The value is read from config_attachNavBarToAppDuringTransition and could be overlaid by RRO
     * when the navigation bar mode is changed.
     */
    private boolean mShouldAttachNavBarToAppDuringTransition;
    private NavBarFadeAnimationController mNavBarFadeAnimationController;

    // -------- PolicyHandler --------
    private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
    private static final int MSG_DISPOSE_INPUT_CONSUMER = 3;
@@ -1086,6 +1094,7 @@ public class DisplayPolicy {
                break;
            case TYPE_NAVIGATION_BAR:
                mNavigationBar = win;
                updateNavBarFadeController();
                mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
                        (displayFrames, windowState, inOutFrame) -> {

@@ -1231,6 +1240,7 @@ public class DisplayPolicy {
            mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null);
        } else if (mNavigationBar == win || mNavigationBarAlt == win) {
            mNavigationBar = null;
            updateNavBarFadeController();
            mNavigationBarAlt = null;
            mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null);
        } else if (mNotificationShade == win) {
@@ -2195,6 +2205,13 @@ public class DisplayPolicy {
                        - getNavigationBarFrameHeight(portraitRotation, uiMode);

        updateConfigurationAndScreenSizeDependentBehaviors();

        final boolean shouldAttach =
                res.getBoolean(R.bool.config_attachNavBarToAppDuringTransition);
        if (mShouldAttachNavBarToAppDuringTransition != shouldAttach) {
            mShouldAttachNavBarToAppDuringTransition = shouldAttach;
            updateNavBarFadeController();
        }
    }

    void updateConfigurationAndScreenSizeDependentBehaviors() {
@@ -3177,4 +3194,26 @@ public class DisplayPolicy {

        return Rect.intersects(targetWindow.getFrame(), navBarWindow.getFrame());
    }

    /**
     * @return Whether we should attach navigation bar to the app during transition.
     */
    boolean shouldAttachNavBarToAppDuringTransition() {
        return mShouldAttachNavBarToAppDuringTransition && mNavigationBar != null;
    }

    @Nullable NavBarFadeAnimationController getNavBarFadeAnimationController() {
        return mNavBarFadeAnimationController;
    }

    private void updateNavBarFadeController() {
        if (shouldAttachNavBarToAppDuringTransition()) {
            if (mNavBarFadeAnimationController == null) {
                mNavBarFadeAnimationController =
                        new NavBarFadeAnimationController(mDisplayContent);
            }
        } else {
            mNavBarFadeAnimationController = null;
        }
    }
}
+156 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.WindowAnimationSpecProto.ANIMATION;

import android.annotation.NonNull;
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;

/**
 * An animation controller to fade-in/out for a window token.
 */
public class FadeAnimationController {
    protected final Context mContext;
    private final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();

    public FadeAnimationController(DisplayContent displayContent) {
        mContext = displayContent.mWmService.mContext;
    }

    /**
     * @return a fade-in Animation.
     */
    public Animation getFadeInAnimation() {
        return AnimationUtils.loadAnimation(mContext, R.anim.fade_in);
    }

    /**
     * @return a fade-out Animation.
     */
    public Animation getFadeOutAnimation() {
        return AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
    }

    /**
     * Run the fade in/out animation for a window token.
     *
     * @param show true for fade-in, otherwise for fade-out.
     * @param windowToken the window token to run the animation.
     * @param animationType the animation type defined in SurfaceAnimator.
     */
    public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
        if (windowToken == null || windowToken.getParent() == null) {
            return;
        }

        final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
        if (animation == null) {
            return;
        }

        final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
                createAnimationSpec(animation);

        final FadeAnimationAdapter animationAdapter = new FadeAnimationAdapter(
                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 */, animationType, finishedCallback);
    }

    private LocalAnimationAdapter.AnimationSpec createAnimationSpec(@NonNull 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 FadeAnimationAdapter extends LocalAnimationAdapter {
        private final boolean mShow;
        private final WindowToken mToken;

        FadeAnimationAdapter(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;
        }
    }
}
+10 −105
Original line number Diff line number Diff line
@@ -16,21 +16,8 @@

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;

/**
@@ -40,16 +27,14 @@ import java.util.ArrayList;
 * 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 {
public class FixedRotationAnimationController extends FadeAnimationController {

    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;
        super(displayContent);
        final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
        mStatusBar = displayPolicy.getStatusBar();
        // Do not animate movable navigation bar (e.g. non-gesture mode).
@@ -62,105 +47,25 @@ public class FixedRotationAnimationController {
    void show() {
        for (int i = mAnimatedWindowToken.size() - 1; i >= 0; i--) {
            final WindowToken windowToken = mAnimatedWindowToken.get(i);
            fadeWindowToken(true /* show */, windowToken);
            fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
        }
    }

    /** Applies hide animation on the window tokens which may be seamlessly rotated later. */
    void hide() {
        if (mNavigationBar != null) {
            fadeWindowToken(false /* show */, mNavigationBar.mToken);
            fadeWindowToken(false /* show */, mNavigationBar.mToken,
                    ANIMATION_TYPE_FIXED_TRANSFORM);
        }
        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();
            fadeWindowToken(false /* show */, mStatusBar.mToken,
                    ANIMATION_TYPE_FIXED_TRANSFORM);
        }

            @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;
        }
    public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
        super.fadeWindowToken(show, windowToken, animationType);
        mAnimatedWindowToken.add(windowToken);
    }
}
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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_APP_TRANSITION;

import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

/**
 * Controller to fade in and out  navigation bar during app transition when
 * config_attachNavBarToAppDuringTransition is true.
 */
public class NavBarFadeAnimationController extends FadeAnimationController{
    private static final int FADE_IN_DURATION = 266;
    private static final int FADE_OUT_DURATION = 133;
    private static final Interpolator FADE_IN_INTERPOLATOR =
            new PathInterpolator(0f, 0f, 0f, 1f);
    private static final Interpolator FADE_OUT_INTERPOLATOR =
            new PathInterpolator(0.2f, 0f, 1f, 1f);

    private final WindowState mNavigationBar;
    private Animation mFadeInAnimation;
    private Animation mFadeOutAnimation;

    public NavBarFadeAnimationController(DisplayContent displayContent) {
        super(displayContent);
        mNavigationBar = displayContent.getDisplayPolicy().getNavigationBar();
        mFadeInAnimation = new AlphaAnimation(0f, 1f);
        mFadeInAnimation.setDuration(FADE_IN_DURATION);
        mFadeInAnimation.setInterpolator(FADE_IN_INTERPOLATOR);

        mFadeOutAnimation = new AlphaAnimation(1f, 0f);
        mFadeOutAnimation.setDuration(FADE_OUT_DURATION);
        mFadeOutAnimation.setInterpolator(FADE_OUT_INTERPOLATOR);
    }

    @Override
    public Animation getFadeInAnimation() {
        return mFadeInAnimation;
    }

    @Override
    public Animation getFadeOutAnimation() {
        return mFadeOutAnimation;
    }

    /**
     * Run the fade-in/out animation for the navigation bar.
     *
     * @param show true for fade-in, otherwise for fade-out.
     */
    public void fadeWindowToken(boolean show) {
        fadeWindowToken(show, mNavigationBar.mToken, ANIMATION_TYPE_APP_TRANSITION);
    }
}
+71 −1
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.window.TaskSnapshot;
import android.app.WindowConfiguration;
import android.graphics.Point;
import android.graphics.Rect;
@@ -54,6 +53,7 @@ import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
import android.window.TaskSnapshot;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.SoftInputShowHideReason;
@@ -147,6 +147,9 @@ public class RecentsAnimationController implements DeathRecipient {
    // Whether to take a screenshot when handling a deferred cancel
    private boolean mCancelDeferredWithScreenshot;

    @VisibleForTesting
    boolean mShouldAttachNavBarToAppDuringTransition;

    /**
     * Animates the screenshot of task that used to be controlled by RecentsAnimation.
     * @see {@link #setCancelOnNextTransitionStart}
@@ -390,6 +393,8 @@ public class RecentsAnimationController implements DeathRecipient {
        mDisplayId = displayId;
        mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
        mDisplayContent = service.mRoot.getDisplayContent(displayId);
        mShouldAttachNavBarToAppDuringTransition =
                mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition();
    }

    /**
@@ -439,6 +444,10 @@ public class RecentsAnimationController implements DeathRecipient {
            return;
        }

        if (mShouldAttachNavBarToAppDuringTransition) {
            attachNavBarToApp();
        }

        // Adjust the wallpaper visibility for the showing target activity
        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                "setHomeApp(%s)", targetActivity.getName());
@@ -572,6 +581,63 @@ public class RecentsAnimationController implements DeathRecipient {
        }
    }

    @VisibleForTesting
    WindowToken getNavigationBarWindowToken() {
        WindowState navBar = mDisplayContent.getDisplayPolicy().getNavigationBar();
        if (navBar != null) {
            return navBar.mToken;
        }
        return null;
    }

    private void attachNavBarToApp() {
        ActivityRecord topActivity = null;
        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
            final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
            final Task task = adapter.mTask;
            if (!task.isHomeOrRecentsRootTask()) {
                topActivity = task.getTopVisibleActivity();
                break;
            }
        }
        final WindowToken navToken = getNavigationBarWindowToken();
        if (topActivity == null || navToken == null) {
            return;
        }

        final SurfaceControl.Transaction t = navToken.getPendingTransaction();
        final SurfaceControl navSurfaceControl = navToken.getSurfaceControl();
        t.reparent(navSurfaceControl, topActivity.getSurfaceControl());
        t.show(navSurfaceControl);

        final WindowContainer imeContainer = mDisplayContent.getImeContainer();
        if (imeContainer.isVisible()) {
            t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1);
        } else {
            // Place the nav bar on top of anything else in the top activity.
            t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
        }
    }

    private void restoreNavBarFromApp(boolean animate) {
        // Reparent the SurfaceControl of nav bar token back.
        final WindowToken navToken = getNavigationBarWindowToken();
        final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
        if (navToken != null) {
            final WindowContainer parent = navToken.getParent();
            t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
        }

        if (animate) {
            // Run fade-in animation to show navigation bar back to bottom of the display.
            final NavBarFadeAnimationController controller =
                    mDisplayContent.getDisplayPolicy().getNavBarFadeAnimationController();
            if (controller != null) {
                controller.fadeWindowToken(true);
            }
        }
    }

    void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) {
        if (mRunner != null) {
            // No need to send task appeared when the task target already exists, or when the
@@ -790,6 +856,10 @@ public class RecentsAnimationController implements DeathRecipient {
            removeWallpaperAnimation(wallpaperAdapter);
        }

        if (mShouldAttachNavBarToAppDuringTransition) {
            restoreNavBarFromApp(reorderMode == REORDER_MOVE_TO_TOP);
        }

        // Clear any pending failsafe runnables
        mService.mH.removeCallbacks(mFailsafeRunnable);
        mDisplayContent.mAppTransition.unregisterListener(mAppTransitionListener);
Loading