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

Commit c22eec9d authored by Adrian Roos's avatar Adrian Roos
Browse files

IME animation: hide IME-related navbar icons until perceptible

During transitions and while the IME is controlled by the app,
the IME may be "visible" as far as the IME framework is concerned,
but not actually perceptible by the user due to offsets or alpha
applied to the leash.

This may lead to out-of-place navbar symbols for the IME, especially
in gesture nav.

To avoid this, the ImeInsetsSourceConsumer now notifies the IMF of
whether it has made the IME unperceptible to the user.

For now, we ignore the cases where the IME is controlled by something
other than the client window - in that case, we just revert to the
previous behavior of it being always considered perceptible.

Fixes: 158079255
Test: manual, launch email compose activity, observe that back button only indicates once IME appears
Test: atest InsetsAnimationControlImplTest
Change-Id: I4dc9d6513d0559156f7da39244f3fc5ebc952ed4
parent 70ad3311
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.view.InsetsState.ITYPE_IME;


import android.annotation.Nullable;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcel;
import android.text.TextUtils;
import android.text.TextUtils;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceControl.Transaction;
@@ -153,6 +154,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
        return mIsRequestedVisibleAwaitingControl || isRequestedVisible();
        return mIsRequestedVisibleAwaitingControl || isRequestedVisible();
    }
    }


    @Override
    public void onPerceptible(boolean perceptible) {
        super.onPerceptible(perceptible);
        final IBinder window = mController.getHost().getWindowToken();
        if (window != null) {
            getImm().reportPerceptible(window, perceptible);
        }
    }

    private boolean isDummyOrEmptyEditor(EditorInfo info) {
    private boolean isDummyOrEmptyEditor(EditorInfo info) {
        // TODO(b/123044812): Handle dummy input gracefully in IME Insets API
        // TODO(b/123044812): Handle dummy input gracefully in IME Insets API
        return info == null || (info.fieldId <= 0 && info.inputType <= 0);
        return info == null || (info.fieldId <= 0 && info.inputType <= 0);
+12 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package android.view;
package android.view;


import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimation.Bounds;


/**
/**
@@ -64,4 +65,15 @@ public interface InsetsAnimationControlCallbacks {
     * previous calls to applySurfaceParams.
     * previous calls to applySurfaceParams.
     */
     */
    void releaseSurfaceControlFromRt(SurfaceControl sc);
    void releaseSurfaceControlFromRt(SurfaceControl sc);

    /**
     * Reports that the perceptibility of the given types has changed to the given value.
     *
     * A type is perceptible if it is not (almost) entirely off-screen and not (almost) entirely
     * transparent.
     *
     * @param types the (public) types whose perceptibility has changed
     * @param perceptible true, if the types are now perceptible, false if they are not perceptible
     */
    void reportPerceptible(@InsetsType int types, boolean perceptible);
}
}
+14 −0
Original line number Original line Diff line number Diff line
@@ -87,6 +87,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
    private float mPendingAlpha = 1.0f;
    private float mPendingAlpha = 1.0f;
    @VisibleForTesting(visibility = PACKAGE)
    @VisibleForTesting(visibility = PACKAGE)
    public boolean mReadyDispatched;
    public boolean mReadyDispatched;
    private Boolean mPerceptible;


    @VisibleForTesting
    @VisibleForTesting
    public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame,
    public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame,
@@ -121,6 +122,14 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
                new Bounds(mHiddenInsets, mShownInsets));
                new Bounds(mHiddenInsets, mShownInsets));
    }
    }


    private boolean calculatePerceptible(Insets currentInsets, float currentAlpha) {
        return 100 * currentInsets.left >= 5 * (mShownInsets.left - mHiddenInsets.left)
                && 100 * currentInsets.top >= 5 * (mShownInsets.top - mHiddenInsets.top)
                && 100 * currentInsets.right >= 5 * (mShownInsets.right - mHiddenInsets.right)
                && 100 * currentInsets.bottom >= 5 * (mShownInsets.bottom - mHiddenInsets.bottom)
                && currentAlpha >= 0.5f;
    }

    @Override
    @Override
    public boolean hasZeroInsetsIme() {
    public boolean hasZeroInsetsIme() {
        return mHasZeroInsetsIme;
        return mHasZeroInsetsIme;
@@ -175,6 +184,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
        mPendingInsets = sanitize(insets);
        mPendingInsets = sanitize(insets);
        mPendingAlpha = sanitize(alpha);
        mPendingAlpha = sanitize(alpha);
        mController.scheduleApplyChangeInsets(this);
        mController.scheduleApplyChangeInsets(this);
        boolean perceptible = calculatePerceptible(mPendingInsets, mPendingAlpha);
        if (mPerceptible == null || perceptible != mPerceptible) {
            mController.reportPerceptible(mTypes, perceptible);
            mPerceptible = perceptible;
        }
    }
    }


    @VisibleForTesting
    @VisibleForTesting
+5 −0
Original line number Original line Diff line number Diff line
@@ -90,6 +90,11 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
            // Since we don't push the SurfaceParams to the RT we can release directly
            // Since we don't push the SurfaceParams to the RT we can release directly
            sc.release();
            sc.release();
        }
        }

        @Override
        public void reportPerceptible(int types, boolean perceptible) {
            mMainThreadHandler.post(() -> mOuterCallbacks.reportPerceptible(types, perceptible));
        }
    };
    };


    @UiThread
    @UiThread
+19 −0
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.CancellationSignal;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Handler;
import android.os.IBinder;
import android.os.Trace;
import android.os.Trace;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.Log;
import android.util.Log;
@@ -165,6 +166,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation


        /** @see ViewRootImpl#dipToPx */
        /** @see ViewRootImpl#dipToPx */
        int dipToPx(int dips);
        int dipToPx(int dips);

        /**
         * @return token associated with the host, if it has one.
         */
        @Nullable
        IBinder getWindowToken();
    }
    }


    private static final String TAG = "InsetsController";
    private static final String TAG = "InsetsController";
@@ -1353,6 +1360,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
        mHost.releaseSurfaceControlFromRt(sc);
        mHost.releaseSurfaceControlFromRt(sc);
    }
    }


    @Override
    public void reportPerceptible(int types, boolean perceptible) {
        final ArraySet<Integer> internalTypes = toInternalType(types);
        final int size = mSourceConsumers.size();
        for (int i = 0; i < size; i++) {
            final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
            if (internalTypes.contains(consumer.getType())) {
                consumer.onPerceptible(perceptible);
            }
        }
    }

    Host getHost() {
    Host getHost() {
        return mHost;
        return mHost;
    }
    }
Loading