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

Commit 3a3c0b22 authored by Tiger Huang's avatar Tiger Huang Committed by Automerger Merge Worker
Browse files

Merge "Play insets animation while forcibly showing system bars" into...

Merge "Play insets animation while forcibly showing system bars" into udc-qpr-dev am: 5b35c961 am: a74577eb

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23875904



Change-Id: I4561a14069f6598939aeae9a9dbea35eae2ff81e
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents b6aabe49 a74577eb
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -841,7 +841,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
        return mLastDispatchedState;
    }

    @VisibleForTesting
    public boolean onStateChanged(InsetsState state) {
        boolean stateChanged = false;
        if (!CAPTION_ON_SHELL) {
+149 −192
Original line number Diff line number Diff line
@@ -21,12 +21,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsSource.ID_IME;
import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
@@ -39,16 +34,13 @@ import android.app.StatusBarManager;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.res.Resources;
import android.os.Handler;
import android.os.IBinder;
import android.util.SparseArray;
import android.view.InsetsAnimationControlCallbacks;
import android.view.InsetsAnimationControlImpl;
import android.view.InsetsAnimationControlRunner;
import android.view.InsetsController;
import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InternalInsetsAnimationController;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.WindowInsets;
@@ -56,15 +48,15 @@ import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.DisplayThread;
import com.android.server.statusbar.StatusBarManagerInternal;

import java.io.PrintWriter;
import java.util.List;

/**
 * Policy that implements who gets control over the windows generating insets.
@@ -79,48 +71,19 @@ class InsetsPolicy {
    private final DisplayContent mDisplayContent;
    private final DisplayPolicy mPolicy;

    /** For resetting visibilities of insets sources. */
    private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() {
    /** Used to show system bars transiently. This won't affect the layout. */
    private final InsetsControlTarget mTransientControlTarget;

        @Override
        public void notifyInsetsControlChanged() {
            boolean hasLeash = false;
            final InsetsSourceControl[] controls =
                    mStateController.getControlsForDispatch(this);
            if (controls == null) {
                return;
            }
            for (InsetsSourceControl control : controls) {
                if (isTransient(control.getType())) {
                    // The visibilities of transient bars will be handled with animations.
                    continue;
                }
                final SurfaceControl leash = control.getLeash();
                if (leash != null) {
                    hasLeash = true;

                    // We use alpha to control the visibility here which aligns the logic at
                    // SurfaceAnimator.createAnimationLeash
                    final boolean visible =
                            (control.getType() & WindowInsets.Type.defaultVisible()) != 0;
                    mDisplayContent.getPendingTransaction().setAlpha(leash, visible ? 1f : 0f);
                }
            }
            if (hasLeash) {
                mDisplayContent.scheduleAnimation();
            }
        }
    };
    /** Used to show system bars permanently. This will affect the layout. */
    private final InsetsControlTarget mPermanentControlTarget;

    private WindowState mFocusedWin;
    private final BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
    private final BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
    private @InsetsType int mShowingTransientTypes;
    private @InsetsType int mForcedShowingTypes;
    private boolean mAnimatingShown;

    private final boolean mHideNavBarForKeyboard;
    private final float[] mTmpFloat9 = new float[9];

    InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
        mStateController = stateController;
@@ -128,6 +91,10 @@ class InsetsPolicy {
        mPolicy = displayContent.getDisplayPolicy();
        final Resources r = mPolicy.getContext().getResources();
        mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard);
        mTransientControlTarget = new ControlTarget(
                stateController, displayContent.mWmService.mH, "TransientControlTarget");
        mPermanentControlTarget = new ControlTarget(
                stateController, displayContent.mWmService.mH, "PermanentControlTarget");
    }

    /** Updates the target which can control system bars. */
@@ -144,13 +111,13 @@ class InsetsPolicy {
        final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow();
        mStateController.onBarControlTargetChanged(
                statusControlTarget,
                statusControlTarget == mDummyControlTarget
                statusControlTarget == mTransientControlTarget
                        ? getStatusControlTarget(focusedWin, true /* fake */)
                        : statusControlTarget == notificationShade
                                ? getStatusControlTarget(topApp, true /* fake */)
                                : null,
                navControlTarget,
                navControlTarget == mDummyControlTarget
                navControlTarget == mTransientControlTarget
                        ? getNavControlTarget(focusedWin, true /* fake */)
                        : navControlTarget == notificationShade
                                ? getNavControlTarget(topApp, true /* fake */)
@@ -200,17 +167,17 @@ class InsetsPolicy {
                    mFocusedWin,
                    (showingTransientTypes & (Type.statusBars() | Type.navigationBars())) != 0,
                    isGestureOnSystemBar);

            // The leashes can be created while updating bar control target. The surface transaction
            // of the new leashes might not be applied yet. The callback posted here ensures we can
            // get the valid leashes because the surface transaction will be applied in the next
            // animation frame which will be triggered if a new leash is created.
            mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> {
                synchronized (mDisplayContent.mWmService.mGlobalLock) {
                    startAnimation(true /* show */, null /* callback */);
        }
            });
    }

    @VisibleForTesting
    InsetsControlTarget getTransientControlTarget() {
        return  mTransientControlTarget;
    }

    @VisibleForTesting
    InsetsControlTarget getPermanentControlTarget() {
        return  mPermanentControlTarget;
    }

    void hideTransient() {
@@ -223,24 +190,9 @@ class InsetsPolicy {
                /* areVisible= */ false,
                /* wereRevealedFromSwipeOnSystemBar= */ false);

        startAnimation(false /* show */, () -> {
            synchronized (mDisplayContent.mWmService.mGlobalLock) {
                final SparseArray<InsetsSourceProvider> providers =
                        mStateController.getSourceProviders();
                for (int i = providers.size() - 1; i >= 0; i--) {
                    final InsetsSourceProvider provider = providers.valueAt(i);
                    if (!isTransient(provider.getSource().getType())) {
                        continue;
                    }
                    // We are about to clear mShowingTransientTypes, we don't want the transient bar
                    // can cause insets on the client. Restore the client visibility.
                    provider.setClientVisible(false);
                }
        mShowingTransientTypes = 0;
        updateBarControlTarget(mFocusedWin);
    }
        });
    }

    boolean isTransient(@InsetsType int type) {
        return (mShowingTransientTypes & type) != 0;
@@ -502,7 +454,7 @@ class InsetsPolicy {
    private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
            boolean fake) {
        if (!fake && isTransient(Type.statusBars())) {
            return mDummyControlTarget;
            return mTransientControlTarget;
        }
        final WindowState notificationShade = mPolicy.getNotificationShade();
        if (focusedWin == notificationShade) {
@@ -519,13 +471,13 @@ class InsetsPolicy {
        if (areTypesForciblyShowing(Type.statusBars())) {
            // Status bar is forcibly shown. We don't want the client to control the status bar, and
            // we will dispatch the real visibility of status bar to the client.
            return null;
            return mPermanentControlTarget;
        }
        if (forceShowsStatusBarTransiently() && !fake) {
            // Status bar is forcibly shown transiently, and its new visibility won't be
            // dispatched to the client so that we can keep the layout stable. We will dispatch the
            // fake control to the client, so that it can re-show the bar during this scenario.
            return mDummyControlTarget;
            return mTransientControlTarget;
        }
        if (!canBeTopFullscreenOpaqueWindow(focusedWin)
                && mPolicy.topAppHidesSystemBar(Type.statusBars())
@@ -556,7 +508,7 @@ class InsetsPolicy {
            return null;
        }
        if (!fake && isTransient(Type.navigationBars())) {
            return mDummyControlTarget;
            return mTransientControlTarget;
        }
        if (focusedWin == mPolicy.getNotificationShade()) {
            // Notification shade has control anyways, no reason to force anything.
@@ -579,13 +531,13 @@ class InsetsPolicy {
        if (areTypesForciblyShowing(Type.navigationBars())) {
            // Navigation bar is forcibly shown. We don't want the client to control the navigation
            // bar, and we will dispatch the real visibility of navigation bar to the client.
            return null;
            return mPermanentControlTarget;
        }
        if (forceShowsNavigationBarTransiently() && !fake) {
            // Navigation bar is forcibly shown transiently, and its new visibility won't be
            // dispatched to the client so that we can keep the layout stable. We will dispatch the
            // fake control to the client, so that it can re-show the bar during this scenario.
            return mDummyControlTarget;
            return mTransientControlTarget;
        }
        final WindowState notificationShade = mPolicy.getNotificationShade();
        if (!canBeTopFullscreenOpaqueWindow(focusedWin)
@@ -662,34 +614,6 @@ class InsetsPolicy {
                && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
    }

    @VisibleForTesting
    void startAnimation(boolean show, Runnable callback) {
        @InsetsType int typesReady = 0;
        final SparseArray<InsetsSourceControl> controlsReady = new SparseArray<>();
        final InsetsSourceControl[] controls =
                mStateController.getControlsForDispatch(mDummyControlTarget);
        if (controls == null) {
            if (callback != null) {
                DisplayThread.getHandler().post(callback);
            }
            return;
        }
        for (InsetsSourceControl control : controls) {
            if (isTransient(control.getType()) && control.getLeash() != null) {
                typesReady |= control.getType();
                controlsReady.put(control.getId(), new InsetsSourceControl(control));
            }
        }
        controlAnimationUnchecked(typesReady, controlsReady, show, callback);
    }

    private void controlAnimationUnchecked(int typesReady,
            SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) {
        InsetsPolicyAnimationControlListener listener =
                new InsetsPolicyAnimationControlListener(show, callback, typesReady);
        listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
    }

    private void dispatchTransientSystemBarsVisibilityChanged(
            @Nullable WindowState focusedWindow,
            boolean areVisible,
@@ -760,105 +684,138 @@ class InsetsPolicy {
        }
    }

    private class InsetsPolicyAnimationControlListener extends
            InsetsController.InternalAnimationControlListener {
        Runnable mFinishCallback;
        InsetsPolicyAnimationControlCallbacks mControlCallbacks;
    private static class ControlTarget implements InsetsControlTarget {

        InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) {
            super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
                    false /* disable */, 0 /* floatingImeBottomInsets */,
                    null /* loggingListener */, null /* jankContext */);
            mFinishCallback = finishCallback;
            mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
        private final InsetsState mState = new InsetsState();
        private final InsetsController mInsetsController;
        private final InsetsStateController mStateController;
        private final String mName;

        ControlTarget(InsetsStateController stateController, Handler handler, String name) {
            mStateController = stateController;
            mInsetsController = new InsetsController(new Host(handler, name));
            mName = name;
        }

        @Override
        protected void onAnimationFinish() {
            super.onAnimationFinish();
            if (mFinishCallback != null) {
                DisplayThread.getHandler().post(mFinishCallback);
        public void notifyInsetsControlChanged() {
            mState.set(mStateController.getRawInsetsState(), true /* copySources */);
            mInsetsController.onStateChanged(mState);
            mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this));
        }

        @Override
        public String toString() {
            return mName;
        }
    }

        private class InsetsPolicyAnimationControlCallbacks implements
                InsetsAnimationControlCallbacks {
            private InsetsAnimationControlImpl mAnimationControl = null;
            private InsetsPolicyAnimationControlListener mListener;
    private static class Host implements InsetsController.Host {

            InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
                mListener = listener;
        private final float[] mTmpFloat9 = new float[9];
        private final Handler mHandler;
        private final String mName;

        Host(Handler handler, String name) {
            mHandler = handler;
            mName = name;
        }

            private void controlAnimationUnchecked(int typesReady,
                    SparseArray<InsetsSourceControl> controls, boolean show) {
                if (typesReady == 0) {
                    // nothing to animate.
                    return;
        @Override
        public Handler getHandler() {
            return mHandler;
        }
                mAnimatingShown = show;

                final InsetsState state = mFocusedWin.getInsetsState();
        @Override
        public void notifyInsetsChanged() { }

                // We are about to playing the default animation. Passing a null frame indicates
                // the controlled types should be animated regardless of the frame.
                mAnimationControl = new InsetsAnimationControlImpl(controls,
                        null /* frame */, state, mListener, typesReady, this,
                        mListener.getDurationMs(), getInsetsInterpolator(),
                        show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show
                                ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
                                : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
                        null /* translator */, null /* statsToken */);
                SurfaceAnimationThread.getHandler().post(
                        () -> mListener.onReady(mAnimationControl, typesReady));
            }
        @Override
        public void dispatchWindowInsetsAnimationPrepare(
                @NonNull WindowInsetsAnimation animation) { }

            /** Called on SurfaceAnimationThread without global WM lock held. */
        @Override
            public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
                if (mAnimationControl.applyChangeInsets(null /* outState */)) {
                    mAnimationControl.finish(mAnimatingShown);
                }
        public Bounds dispatchWindowInsetsAnimationStart(
                @NonNull WindowInsetsAnimation animation,
                @NonNull Bounds bounds) {
            return bounds;
        }

        @Override
            public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
                // Nothing's needed here. Finish steps is handled in the listener
                // onAnimationFinished callback.
        public WindowInsets dispatchWindowInsetsAnimationProgress(
                @NonNull WindowInsets insets,
                @NonNull List<WindowInsetsAnimation> runningAnimations) {
            return insets;
        }

            /** Called on SurfaceAnimationThread without global WM lock held. */
        @Override
            public void applySurfaceParams(
                    final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
                SurfaceControl.Transaction t = new SurfaceControl.Transaction();
                for (int i = params.length - 1; i >= 0; i--) {
                    SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
                    applyParams(t, surfaceParams, mTmpFloat9);
        public void dispatchWindowInsetsAnimationEnd(
                @NonNull WindowInsetsAnimation animation) { }

        @Override
        public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... p) {
            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
            for (int i = p.length - 1; i >= 0; i--) {
                SyncRtSurfaceTransactionApplier.applyParams(t, p[i], mTmpFloat9);
            }
            t.apply();
            t.close();
        }

            // Since we don't push applySurfaceParams to a Handler-queue we don't need
            // to push release in this case.
        @Override
            public void releaseSurfaceControlFromRt(SurfaceControl sc) {
                sc.release();
        public void updateRequestedVisibleTypes(int types) { }

        @Override
        public boolean hasAnimationCallbacks() {
            return false;
        }

        @Override
            public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController>
            void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
                    WindowInsetsAnimation animation,
                    Bounds bounds) {
        public void setSystemBarsAppearance(int appearance, int mask) { }

        @Override
        public int getSystemBarsAppearance() {
            return 0;
        }

        @Override
            public void reportPerceptible(int types, boolean perceptible) {
                // No-op for now - only client windows report perceptibility for now, with policy
                // controllers assumed to always be perceptible.
        public void setSystemBarsBehavior(int behavior) { }

        @Override
        public int getSystemBarsBehavior() {
            return BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
        }

        @Override
        public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
            surfaceControl.release();
        }

        @Override
        public void addOnPreDrawRunnable(Runnable r) { }

        @Override
        public void postInsetsAnimationCallback(Runnable r) { }

        @Override
        public InputMethodManager getInputMethodManager() {
            return null;
        }

        @Nullable
        @Override
        public String getRootViewTitle() {
            return mName;
        }

        @Override
        public int dipToPx(int dips) {
            return 0;
        }

        @Nullable
        @Override
        public IBinder getWindowToken() {
            return null;
        }
    }
}
+0 −13
Original line number Diff line number Diff line
@@ -31,17 +31,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;

import android.app.StatusBarManager;
@@ -268,8 +263,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
        navBar.setHasSurface(true);
        navBarProvider.setServerVisible(true);
        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
        spyOn(policy);
        doNothing().when(policy).startAnimation(anyBoolean(), any());

        // Make both system bars invisible.
        mAppWindow.setRequestedVisibleTypes(
@@ -305,8 +298,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
        addNavigationBar().getControllableInsetProvider().setServerVisible(true);

        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
        spyOn(policy);
        doNothing().when(policy).startAnimation(anyBoolean(), any());
        policy.updateBarControlTarget(mAppWindow);
        policy.showTransient(navigationBars() | statusBars(),
                true /* isGestureOnSystemBar */);
@@ -341,8 +332,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
        mAppWindow.mAboveInsetsState.addSource(navBarSource);
        mAppWindow.mAboveInsetsState.addSource(statusBarSource);
        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
        spyOn(policy);
        doNothing().when(policy).startAnimation(anyBoolean(), any());
        policy.updateBarControlTarget(mAppWindow);
        policy.showTransient(navigationBars() | statusBars(),
                true /* isGestureOnSystemBar */);
@@ -390,8 +379,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
        final WindowState app2 = addWindow(TYPE_APPLICATION, "app");

        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
        spyOn(policy);
        doNothing().when(policy).startAnimation(anyBoolean(), any());
        policy.updateBarControlTarget(app);
        policy.showTransient(navigationBars() | statusBars(),
                true /* isGestureOnSystemBar */);
+4 −0
Original line number Diff line number Diff line
@@ -77,6 +77,10 @@ class TestDisplayContent extends DisplayContent {
        spyOn(inputMonitor);
        doNothing().when(inputMonitor).resumeDispatchingLw(any());

        final InsetsPolicy insetsPolicy = getInsetsPolicy();
        WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget());
        WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget());

        // For devices that set the sysprop ro.bootanim.set_orientation_<display_id>
        // See DisplayRotation#readDefaultDisplayRotation for context.
        // Without that, meaning of height and width in context of the tests can be swapped if
+13 −0
Original line number Diff line number Diff line
@@ -222,6 +222,10 @@ class WindowTestsBase extends SystemServiceTestsBase {
        displayPolicy.finishWindowsDrawn();
        displayPolicy.finishScreenTurningOn();

        final InsetsPolicy insetsPolicy = mDefaultDisplay.getInsetsPolicy();
        suppressInsetsAnimation(insetsPolicy.getTransientControlTarget());
        suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget());

        mTransaction = mSystemServicesTestRule.mTransaction;
        mMockSession = mock(Session.class);

@@ -278,6 +282,15 @@ class WindowTestsBase extends SystemServiceTestsBase {
        checkDeviceSpecificOverridesNotApplied();
    }

    /**
     * The test doesn't create real SurfaceControls, but mocked ones. This prevents the target from
     * controlling them, or it will cause {@link NullPointerException}.
     */
    static void suppressInsetsAnimation(InsetsControlTarget target) {
        spyOn(target);
        Mockito.doNothing().when(target).notifyInsetsControlChanged();
    }

    @After
    public void tearDown() throws Exception {
        if (mUseFakeSettingsProvider) {