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

Commit 77190b77 authored by Tiger Huang's avatar Tiger Huang Committed by Android (Google) Code Review
Browse files

Merge "Disable user animations on insets whose visible frame is empty (refined)" into rvc-dev

parents c3260a4c 618dbe02
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -471,6 +471,10 @@ public class InputMethodService extends AbstractInputMethodService {

    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
        onComputeInsets(mTmpInsets);
        if (!mViewsCreated) {
            // The IME views are not ready, keep visible insets untouched.
            mTmpInsets.visibleTopInsets = 0;
        }
        if (isExtractViewShown()) {
            // In true fullscreen mode, we just say the window isn't covering
            // any content so we don't impact whatever is behind.
+64 −9
Original line number Diff line number Diff line
@@ -514,6 +514,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
    /** Set of inset types for which an animation was started since last resetting this field */
    private @InsetsType int mLastStartedAnimTypes;

    /** Set of inset types which cannot be controlled by the user animation */
    private @InsetsType int mDisabledUserAnimationInsetsTypes;

    private Runnable mInvokeControllableInsetsChangedListeners =
            this::invokeControllableInsetsChangedListeners;

    public InsetsController(Host host) {
        this(host, (controller, type) -> {
            if (type == ITYPE_IME) {
@@ -628,22 +634,57 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation

    private void updateState(InsetsState newState) {
        mState.setDisplayFrame(newState.getDisplayFrame());
        for (int i = 0; i < InsetsState.SIZE; i++) {
            InsetsSource source = newState.peekSource(i);
            if (source == null) continue;;
            getSourceConsumer(source.getType()).updateSource(source);
        @InsetsType int disabledUserAnimationTypes = 0;
        @InsetsType int[] cancelledUserAnimationTypes = {0};
        for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
            InsetsSource source = newState.peekSource(type);
            if (source == null) continue;
            @AnimationType int animationType = getAnimationType(type);
            if (!source.isUserControllable()) {
                @InsetsType int insetsType = toPublicType(type);
                // The user animation is not allowed when visible frame is empty.
                disabledUserAnimationTypes |= insetsType;
                if (animationType == ANIMATION_TYPE_USER) {
                    // Existing user animation needs to be cancelled.
                    animationType = ANIMATION_TYPE_NONE;
                    cancelledUserAnimationTypes[0] |= insetsType;
                }
            }
            getSourceConsumer(type).updateSource(source, animationType);
        }
        for (int i = 0; i < InsetsState.SIZE; i++) {
            InsetsSource source = mState.peekSource(i);
        for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
            InsetsSource source = mState.peekSource(type);
            if (source == null) continue;
            if (newState.peekSource(source.getType()) == null) {
                mState.removeSource(source.getType());
            if (newState.peekSource(type) == null) {
                mState.removeSource(type);
            }
        }
        if (mCaptionInsetsHeight != 0) {
            mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top,
                    mFrame.right, mFrame.top + mCaptionInsetsHeight));
        }

        updateDisabledUserAnimationTypes(disabledUserAnimationTypes);

        if (cancelledUserAnimationTypes[0] != 0) {
            mHandler.post(() -> show(cancelledUserAnimationTypes[0]));
        }
    }

    private void updateDisabledUserAnimationTypes(@InsetsType int disabledUserAnimationTypes) {
        @InsetsType int diff = mDisabledUserAnimationInsetsTypes ^ disabledUserAnimationTypes;
        if (diff != 0) {
            for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
                InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
                if (consumer.getControl() != null
                        && (toPublicType(consumer.getType()) & diff) != 0) {
                    mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
                    mHandler.post(mInvokeControllableInsetsChangedListeners);
                    break;
                }
            }
            mDisabledUserAnimationInsetsTypes = disabledUserAnimationTypes;
        }
    }

    private boolean captionInsetsUnchanged() {
@@ -847,6 +888,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
                    + " while an existing " + Type.toString(mTypesBeingCancelled)
                    + " is being cancelled.");
        }
        if (animationType == ANIMATION_TYPE_USER) {
            final @InsetsType int disabledTypes = types & mDisabledUserAnimationInsetsTypes;
            if (DEBUG) Log.d(TAG, "user animation disabled types: " + disabledTypes);
            types &= ~mDisabledUserAnimationInsetsTypes;

            if (fromIme && (disabledTypes & ime()) != 0
                    && !mState.getSource(ITYPE_IME).isVisible()) {
                // We've requested IMM to show IME, but the IME is not controllable. We need to
                // cancel the request.
                getSourceConsumer(ITYPE_IME).hide(true, animationType);
            }
        }
        if (types == 0) {
            // nothing to animate.
            listener.onCancelled(null);
@@ -1320,7 +1373,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
        @InsetsType int result = 0;
        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
            InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
            if (consumer.getControl() != null) {
            InsetsSource source = mState.peekSource(consumer.mType);
            if (consumer.getControl() != null && source != null && source.isUserControllable()) {
                result |= toPublicType(consumer.mType);
            }
        }
@@ -1331,6 +1385,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
     * @return The types that are now animating due to a listener invoking control/show/hide
     */
    private @InsetsType int invokeControllableInsetsChangedListeners() {
        mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
        mLastStartedAnimTypes = 0;
        @InsetsType int types = calculateControllableTypes();
        int size = mControllableInsetsChangedListeners.size();
+5 −0
Original line number Diff line number Diff line
@@ -92,6 +92,11 @@ public class InsetsSource implements Parcelable {
        return mVisible;
    }

    boolean isUserControllable() {
        // If mVisibleFrame is null, it will be the same area as mFrame.
        return mVisibleFrame == null || !mVisibleFrame.isEmpty();
    }

    /**
     * Calculates the insets this source will cause to a client window.
     *
+4 −4
Original line number Diff line number Diff line
@@ -18,8 +18,8 @@ package android.view;

import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsController.DEBUG;
import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsState.toPublicType;

import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -284,9 +284,9 @@ public class InsetsSourceConsumer {
    }

    @VisibleForTesting(visibility = PACKAGE)
    public void updateSource(InsetsSource newSource) {
    public void updateSource(InsetsSource newSource, @AnimationType int animationType) {
        InsetsSource source = mState.peekSource(mType);
        if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE
        if (source == null || animationType == ANIMATION_TYPE_NONE
                || source.getFrame().equals(newSource.getFrame())) {
            mPendingFrame = null;
            mPendingVisibleFrame = null;
@@ -295,7 +295,7 @@ public class InsetsSourceConsumer {
        }

        // Frame is changing while animating. Keep note of the new frame but keep existing frame
        // until animaition is finished.
        // until animation is finished.
        newSource = new InsetsSource(newSource);
        mPendingFrame = new Rect(newSource.getFrame());
        mPendingVisibleFrame = newSource.getVisibleFrame() != null
+4 −14
Original line number Diff line number Diff line
@@ -26,13 +26,11 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import android.app.Instrumentation;
import android.content.Context;
@@ -135,37 +133,29 @@ public class InsetsSourceConsumerTest {
        InsetsSourceConsumer consumer = new InsetsSourceConsumer(
                ITYPE_IME, state, null, controller);

        when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE);

        InsetsSource source = new InsetsSource(ITYPE_IME);
        source.setFrame(0, 1, 2, 3);
        consumer.updateSource(new InsetsSource(source));

        when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_USER);
        consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_NONE);

        // While we're animating, updates are delayed
        source.setFrame(4, 5, 6, 7);
        consumer.updateSource(new InsetsSource(source));
        consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
        assertEquals(new Rect(0, 1, 2, 3), state.peekSource(ITYPE_IME).getFrame());

        // Finish the animation, now the pending frame should be applied
        when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE);
        assertTrue(consumer.notifyAnimationFinished());
        assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());

        when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_USER);

        // Animating again, updates are delayed
        source.setFrame(8, 9, 10, 11);
        consumer.updateSource(new InsetsSource(source));
        consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
        assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());

        // Updating with the current frame triggers a different code path, verify this clears
        // the pending 8, 9, 10, 11 frame:
        source.setFrame(4, 5, 6, 7);
        consumer.updateSource(new InsetsSource(source));
        consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);

        when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE);
        assertFalse(consumer.notifyAnimationFinished());
        assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
    }
Loading