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

Commit 6e50bae6 authored by Jorim Jaggi's avatar Jorim Jaggi Committed by Android (Google) Code Review
Browse files

Merge "Keep track of requestedVisibility when control unavailable"

parents 98bfaaf4 3182ef14
Loading
Loading
Loading
Loading
+26 −5
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.view;

import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.toPublicType;

import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
@@ -44,6 +45,12 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
     */
    private boolean mShowOnNextImeRender;

    /**
     * Tracks whether we have an outstanding request from the IME to show, but weren't able to
     * execute it because we didn't have control yet.
     */
    private boolean mImeRequestedShow;

    public ImeInsetsSourceConsumer(
            InsetsState state, Supplier<Transaction> transactionSupplier,
            InsetsController controller) {
@@ -81,13 +88,14 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
    public void onWindowFocusLost() {
        super.onWindowFocusLost();
        getImm().unregisterImeConsumer(this);
        mImeRequestedShow = false;
    }

    @Override
    public void setControl(@Nullable InsetsSourceControl control) {
        super.setControl(control);
        if (control == null) {
            hide();
    public void show(boolean fromIme) {
        super.show(fromIme);
        if (fromIme) {
            mImeRequestedShow = true;
        }
    }

@@ -99,7 +107,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
    public @ShowResult int requestShow(boolean fromIme) {
        // TODO: ResultReceiver for IME.
        // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
        if (fromIme) {

        // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
        // this code here means that we now got control, so we can start the animation immediately.
        if (fromIme || mImeRequestedShow) {
            mImeRequestedShow = false;
            return ShowResult.SHOW_IMMEDIATELY;
        }

@@ -115,6 +127,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
        getImm().notifyImeHidden();
    }

    @Override
    public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
            int[] hideTypes) {
        super.setControl(control, showTypes, hideTypes);
        if (control == null) {
            hide();
        }
    }

    private boolean isDummyOrEmptyEditor(EditorInfo info) {
        // TODO(b/123044812): Handle dummy input gracefully in IME Insets API
        return info == null || (info.fieldId <= 0 && info.inputType <= 0);
+4 −0
Original line number Diff line number Diff line
@@ -210,6 +210,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
        mListener.onCancelled();
    }

    public boolean isCancelled() {
        return mCancelled;
    }

    InsetsAnimation getAnimation() {
        return mAnimation;
    }
+50 −16
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.Property;
import android.util.SparseArray;
import android.view.InputDevice.MotionRange;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
@@ -273,7 +274,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
    private final String TAG = "InsetsControllerImpl";

    private final InsetsState mState = new InsetsState();
    private final InsetsState mTmpState = new InsetsState();
    private final InsetsState mLastDispachedState = new InsetsState();

    private final Rect mFrame = new Rect();
    private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
@@ -367,15 +368,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
        return mState;
    }

    boolean onStateChanged(InsetsState state) {
        if (mState.equals(state)) {
    public InsetsState getLastDispatchedState() {
        return mLastDispachedState;
    }

    @VisibleForTesting
    public boolean onStateChanged(InsetsState state) {
        if (mState.equals(state) && mLastDispachedState.equals(state)) {
            return false;
        }
        mState.set(state);
        mTmpState.set(state, true /* copySources */);
        mLastDispachedState.set(state, true /* copySources */);
        applyLocalVisibilityOverride();
        mViewRoot.notifyInsetsChanged();
        if (!mState.equals(mTmpState)) {
        if (!mState.equals(mLastDispachedState)) {
            sendStateToWindowManager();
        }
        return true;
@@ -419,6 +425,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
            }
        }

        int[] showTypes = new int[1];
        int[] hideTypes = new int[1];

        // Ensure to update all existing source consumers
        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
            final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
@@ -426,15 +435,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation

            // control may be null, but we still need to update the control to null if it got
            // revoked.
            consumer.setControl(control);
            consumer.setControl(control, showTypes, hideTypes);
        }

        // Ensure to create source consumers if not available yet.
        for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
            final InsetsSourceControl control = mTmpControlArray.valueAt(i);
            getSourceConsumer(control.getType()).setControl(control);
            InsetsSourceConsumer consumer = getSourceConsumer(control.getType());
            consumer.setControl(control, showTypes, hideTypes);

        }
        mTmpControlArray.clear();
        if (showTypes[0] != 0) {
            applyAnimation(showTypes[0], true /* show */, false /* fromIme */);
        }
        if (hideTypes[0] != 0) {
            applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
        }
    }

    @Override
@@ -465,7 +482,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
            @InternalInsetsType int internalType = internalTypes.valueAt(i);
            @AnimationType int animationType = getAnimationType(internalType);
            InsetsSourceConsumer consumer = getSourceConsumer(internalType);
            if (mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
            if (consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
                    || animationType == ANIMATION_TYPE_SHOW) {
                // no-op: already shown or animating in (because window visibility is
                // applied before starting animation).
@@ -488,7 +505,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
            @InternalInsetsType int internalType = internalTypes.valueAt(i);
            @AnimationType int animationType = getAnimationType(internalType);
            InsetsSourceConsumer consumer = getSourceConsumer(internalType);
            if (!mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
            if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
                    || animationType == ANIMATION_TYPE_HIDE) {
                // no-op: already hidden or animating out.
                continue;
@@ -535,7 +552,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
        final SparseArray<InsetsSourceControl> controls = new SparseArray<>();

        Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
                fromIme, internalTypes, controls);
                fromIme, internalTypes, controls, animationType);
        int typesReady = typesReadyPair.first;
        boolean imeReady = typesReadyPair.second;
        if (!imeReady) {
@@ -562,17 +579,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
     * @return Pair of (types ready to animate, IME ready to animate).
     */
    private Pair<Integer, Boolean> collectSourceControls(boolean fromIme,
            ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls) {
            ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls,
            @AnimationType int animationType) {
        int typesReady = 0;
        boolean imeReady = true;
        for (int i = internalTypes.size() - 1; i >= 0; i--) {
            InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
            boolean setVisible = !consumer.isRequestedVisible();
            if (setVisible) {
            boolean show = animationType == ANIMATION_TYPE_SHOW
                    || animationType == ANIMATION_TYPE_USER;
            boolean canRun = false;
            if (show) {
                // Show request
                switch(consumer.requestShow(fromIme)) {
                    case ShowResult.SHOW_IMMEDIATELY:
                        typesReady |= InsetsState.toPublicType(consumer.getType());
                        canRun = true;
                        break;
                    case ShowResult.IME_SHOW_DELAYED:
                        imeReady = false;
@@ -589,11 +609,22 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
                if (!fromIme) {
                    consumer.notifyHidden();
                }
                typesReady |= InsetsState.toPublicType(consumer.getType());
                canRun = true;
            }
            if (!canRun) {
                continue;
            }
            final InsetsSourceControl control = consumer.getControl();
            if (control != null) {
                controls.put(consumer.getType(), control);
                typesReady |= toPublicType(consumer.getType());
            } else if (animationType == ANIMATION_TYPE_SHOW) {

                // We don't have a control at the moment. However, we still want to update requested
                // visibility state such that in case we get control, we can apply show animation.
                consumer.show(fromIme);
            } else if (animationType == ANIMATION_TYPE_HIDE) {
                consumer.hide();
            }
        }
        return new Pair<>(typesReady, imeReady);
@@ -808,7 +839,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
    private void showDirectly(@InsetsType int types) {
        final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
        for (int i = internalTypes.size() - 1; i >= 0; i--) {
            getSourceConsumer(internalTypes.valueAt(i)).show();
            getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
        }
    }

@@ -840,6 +871,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
            @Override
            public boolean onPreDraw() {
                mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this);
                if (controller.isCancelled()) {
                    return true;
                }
                mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
                listener.onReady(controller, types);
                return true;
+42 −13
Original line number Diff line number Diff line
@@ -17,11 +17,14 @@
package android.view;

import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsState.toPublicType;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.util.MutableShort;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;

import com.android.internal.annotations.VisibleForTesting;

@@ -71,18 +74,48 @@ public class InsetsSourceConsumer {
        mRequestedVisible = InsetsState.getDefaultVisibility(type);
    }

    public void setControl(@Nullable InsetsSourceControl control) {
    /**
     * Updates the control delivered from the server.

     * @param showTypes An integer array with a single entry that determines which types a show
     *                  animation should be run after setting the control.
     * @param hideTypes An integer array with a single entry that determines which types a hide
     *                  animation should be run after setting the control.
     */
    public void setControl(@Nullable InsetsSourceControl control,
            @InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
        if (mSourceControl == control) {
            return;
        }
        mSourceControl = control;
        applyHiddenToControl();
        if (applyLocalVisibilityOverride()) {
            mController.notifyVisibilityChanged();
        }

        // We are loosing control
        if (mSourceControl == null) {
            mController.notifyControlRevoked(this);

            // Restore server visibility.
            mState.getSource(getType()).setVisible(
                    mController.getLastDispatchedState().getSource(getType()).isVisible());
            applyLocalVisibilityOverride();
            return;
        }

        // We are gaining control, and need to run an animation since previous state didn't match
        if (mRequestedVisible != mState.getSource(mType).isVisible()) {
            if (mRequestedVisible) {
                showTypes[0] |= toPublicType(getType());
            } else {
                hideTypes[0] |= toPublicType(getType());
            }
            return;
        }

        // We are gaining control, but don't need to run an animation. However make sure that the
        // leash visibility is still up to date.
        if (applyLocalVisibilityOverride()) {
            mController.notifyVisibilityChanged();
        }
        applyHiddenToControl();
    }

    @VisibleForTesting
@@ -95,7 +128,7 @@ public class InsetsSourceConsumer {
    }

    @VisibleForTesting
    public void show() {
    public void show(boolean fromIme) {
        setRequestedVisible(true);
    }

@@ -172,17 +205,13 @@ public class InsetsSourceConsumer {
     * the moment.
     */
    private void setRequestedVisible(boolean requestedVisible) {
        if (mRequestedVisible == requestedVisible) {
            return;
        }
        mRequestedVisible = requestedVisible;
        applyLocalVisibilityOverride();
        if (applyLocalVisibilityOverride()) {
            mController.notifyVisibilityChanged();
        }
    }

    private void applyHiddenToControl() {

        // TODO: Handle case properly when animation is running already (it shouldn't!)
        if (mSourceControl == null || mSourceControl.getLeash() == null) {
            return;
        }
+7 −1
Original line number Diff line number Diff line
@@ -4887,8 +4887,14 @@ public final class ViewRootImpl implements ViewParent,
                    break;
                case MSG_INSETS_CONTROL_CHANGED: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);

                    // Deliver state change before control change, such that:
                    // a) When gaining control, controller can compare with server state to evaluate
                    // whether it needs to run animation.
                    // b) When loosing control, controller can restore server state by taking last
                    // dispatched state as truth.
                    mInsetsController.onStateChanged((InsetsState) args.arg1);
                    mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
                    break;
                }
                case MSG_SHOW_INSETS: {
Loading