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

Commit 92d2dd36 authored by Tarandeep Singh's avatar Tarandeep Singh
Browse files

IME transitions without pre-rendering.

Support new IME inset api transitions without using pre-rendering.
This would be the default behavior when ViewRootImpl#sNewInsetsMode > 0
and pre-rendering is not enabled.

Bug: 111084606
Bug: 118599175
Test: Manually verify by just enabling Insets API and keeping
    pre-rendering off.
     1. Build and flash
     2. adb shell setprop persist.wm.new_insets 1
     3. adb reboot
     4. Make sure tapping on edit text brings keyboard up with new
     transition and back closes IME with various apps.
     5. Make sure IME behavior is unchanged for apps with
        ADJUST_RESIZE like whatsapp.
Test: atest CtsInputMethodTestCases CtsInputMethodServiceHostTestCases

Change-Id: If33e9dd45e549e49757237fa66051351b858875d
parent f43ba0c1
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.inputmethodservice;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;

import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -62,6 +63,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
@@ -595,12 +597,12 @@ public class InputMethodService extends AbstractInputMethodService {
            if (DEBUG) Log.v(TAG, "hideSoftInput()");
            final boolean wasVisible = mIsPreRendered
                    ? mDecorViewVisible && mWindowVisible : isInputViewShown();
            applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */);
            if (mIsPreRendered) {
                if (DEBUG) {
                    Log.v(TAG, "Making IME window invisible");
                }
                setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
                applyVisibilityInInsetsConsumer(false /* setVisible */);
                onPreRenderedWindowVisibilityChanged(false /* setVisible */);
            } else {
                mShowInputFlags = 0;
@@ -632,11 +634,11 @@ public class InputMethodService extends AbstractInputMethodService {
                    if (DEBUG) {
                        Log.v(TAG, "Making IME window visible");
                    }
                    applyVisibilityInInsetsConsumer(true /* setVisible */);
                    onPreRenderedWindowVisibilityChanged(true /* setVisible */);
                } else {
                    showWindow(true);
                }
                applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */);
            }
            // If user uses hard keyboard, IME button should always be shown.
            setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
@@ -1974,16 +1976,20 @@ public class InputMethodService extends AbstractInputMethodService {

    /**
     * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when
     * pre-rendering is enabled.
     * {@link ViewRootImpl.sNewInsetsMode} is enabled.
     * @param setVisible {@code true} to make it visible, false to hide it.
     */
    private void applyVisibilityInInsetsConsumer(boolean setVisible) {
        if (!mIsPreRendered) {
    private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
        if (!isVisibilityAppliedUsingInsetsConsumer()) {
            return;
        }
        mPrivOps.applyImeVisibility(setVisible);
    }

    private boolean isVisibilityAppliedUsingInsetsConsumer() {
        return ViewRootImpl.sNewInsetsMode > NEW_INSETS_MODE_NONE;
    }

    private void finishViews(boolean finishingInput) {
        if (mInputViewStarted) {
            if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
@@ -2007,7 +2013,11 @@ public class InputMethodService extends AbstractInputMethodService {
        mWindowVisible = false;
        finishViews(false /* finishingInput */);
        if (mDecorViewVisible) {
            // When insets API is enabled, it is responsible for client and server side
            // visibility of IME window.
            if (!isVisibilityAppliedUsingInsetsConsumer()) {
                mWindow.hide();
            }
            mDecorViewVisible = false;
            onWindowHidden();
            mDecorViewWasVisible = false;
+2 −8
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
     * editor {@link #mFocusedEditor} if {@link #isServedEditorRendered} is {@code true}.
     */
    private boolean mShowOnNextImeRender;
    private boolean mHasWindowFocus;

    public ImeInsetsSourceConsumer(
            InsetsState state, Supplier<Transaction> transactionSupplier,
@@ -68,23 +67,18 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
    }

    public void applyImeVisibility(boolean setVisible) {
        if (!mHasWindowFocus) {
            // App window doesn't have focus, any visibility changes would be no-op.
            return;
        }

        mController.applyImeVisibility(setVisible);
    }

    @Override
    public void onWindowFocusGained() {
        mHasWindowFocus = true;
        super.onWindowFocusGained();
        getImm().registerImeConsumer(this);
    }

    @Override
    public void onWindowFocusLost() {
        mHasWindowFocus = false;
        super.onWindowFocusLost();
        getImm().unregisterImeConsumer(this);
    }

+17 −7
Original line number Diff line number Diff line
@@ -296,7 +296,8 @@ public class InsetsController implements WindowInsetsController {
        final ArraySet<Integer> internalTypes = mState.toInternalType(types);
        final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();

        Pair<Integer, Boolean> typesReadyPair = collectConsumers(fromIme, internalTypes, consumers);
        Pair<Integer, Boolean> typesReadyPair = collectConsumers(
                fromIme, internalTypes, consumers, listener);
        int typesReady = typesReadyPair.first;
        boolean isReady = typesReadyPair.second;
        if (!isReady) {
@@ -324,13 +325,16 @@ public class InsetsController implements WindowInsetsController {
     * @return Pair of (types ready to animate, is ready to animate).
     */
    private Pair<Integer, Boolean> collectConsumers(boolean fromIme,
            ArraySet<Integer> internalTypes, SparseArray<InsetsSourceConsumer> consumers) {
            ArraySet<Integer> internalTypes, SparseArray<InsetsSourceConsumer> consumers,
            WindowInsetsAnimationControlListener listener) {
        int typesReady = 0;
        boolean isReady = true;
        for (int i = internalTypes.size() - 1; i >= 0; i--) {
            InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
            if (consumer.getControl() != null) {
                if (!consumer.isVisible()) {
            // Double check for IME that IME target window has focus.
            if (consumer.getType() != TYPE_IME || consumer.hasWindowFocus()) {
                boolean setVisible = !consumer.isVisible();
                if (setVisible) {
                    // Show request
                    switch(consumer.requestShow(fromIme)) {
                        case ShowResult.SHOW_IMMEDIATELY:
@@ -357,8 +361,11 @@ public class InsetsController implements WindowInsetsController {
                }
                consumers.put(consumer.getType(), consumer);
            } else {
                // TODO: Let calling app know it's not possible, or wait
                // TODO: Remove it from types
                // window doesnt have focus, no-op.
                isReady = false;
                // TODO: Let the calling app know that window has lost focus and
                //       show()/hide()/controlWindowInsetsAnimation requests will be ignored.
                typesReady &= ~InsetsState.toPublicType(consumer.getType());
            }
        }
        return new Pair<>(typesReady, isReady);
@@ -533,8 +540,11 @@ public class InsetsController implements WindowInsetsController {

            @Override
            public void onCancelled() {
                // Animator can be null when it is cancelled before onReady() completes.
                if (mAnimator != null) {
                    mAnimator.cancel();
                }
            }

            private void onAnimationFinish() {
                mAnimationDirection = DIRECTION_NONE;
+11 −3
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ public class InsetsSourceConsumer {
    private final @InternalInsetType int mType;
    private final InsetsState mState;
    private @Nullable InsetsSourceControl mSourceControl;
    private boolean mHasWindowFocus;

    public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
            Supplier<Transaction> transactionSupplier, InsetsController controller) {
@@ -104,12 +105,20 @@ public class InsetsSourceConsumer {
    /**
     * Called when current window gains focus
     */
    public void onWindowFocusGained() {}
    public void onWindowFocusGained() {
        mHasWindowFocus = true;
    }

    /**
     * Called when current window loses focus.
     */
    public void onWindowFocusLost() {}
    public void onWindowFocusLost() {
        mHasWindowFocus = false;
    }

    boolean hasWindowFocus() {
        return mHasWindowFocus;
    }

    boolean applyLocalVisibilityOverride() {

@@ -153,7 +162,6 @@ public class InsetsSourceConsumer {
            return;
        }
        mVisible = visible;
        applyHiddenToControl();
        applyLocalVisibilityOverride();
        mController.notifyVisibilityChanged();
    }
+4 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.view;

import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SIZE;
@@ -161,13 +162,15 @@ public class InsetsState implements Parcelable {
                continue;
            }

            boolean skipNonImeInImeMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_IME
                    && source.getType() != TYPE_IME;
            boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
                    && (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR);
            boolean skipIme = source.getType() == TYPE_IME
                    && (legacySoftInputMode & LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == 0;
            boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
                    && (toPublicType(type) & Type.compatSystemInsets()) != 0;
            if (skipSystemBars || skipIme || skipLegacyTypes) {
            if (skipSystemBars || skipIme || skipLegacyTypes || skipNonImeInImeMode) {
                typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible();
                continue;
            }
Loading