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

Commit ed35b177 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Add OnControllableInsetsChangedListener

It's useful for apps to know which inset types they can currently
control, as otherwise they have to poll by calling
controlInsetsAnimation repeatedly.

This can be used when apps want to apply a custom animation
immediately during startup as soon as possible.

Fixes: 150780468
Test: InsetsControllerTest
Test: CTS will be added soon
Test: WindowInsetsActivity
Change-Id: Ic0388c11d759843d3ac9edd8ef23904c9ce05c46
parent ed3c321c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -55587,10 +55587,12 @@ package android.view {
  }
  public interface WindowInsetsController {
    method public void addOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener);
    method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
    method public int getSystemBarsAppearance();
    method public int getSystemBarsBehavior();
    method public void hide(int);
    method public void removeOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener);
    method public void setSystemBarsAppearance(int, int);
    method public void setSystemBarsBehavior(int);
    method public void show(int);
@@ -55601,6 +55603,10 @@ package android.view {
    field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
  }
  public static interface WindowInsetsController.OnControllableInsetsChangedListener {
    method public void onControllableInsetsChanged(@NonNull android.view.WindowInsetsController, int);
  }
  public interface WindowManager extends android.view.ViewManager {
    method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
    method @Deprecated public android.view.Display getDefaultDisplay();
+8 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;

import java.lang.annotation.Retention;
@@ -94,6 +95,13 @@ public class SoftInputWindow extends Dialog {
                lp.token = token;
                getWindow().setAttributes(lp);
                updateWindowState(SoftInputWindowState.TOKEN_SET);

                // As soon as we have a token, make sure the window is added (but not shown) by
                // setting visibility to INVISIBLE and calling show() on Dialog. Note that
                // WindowInsetsController.OnControllableInsetsChangedListener relies on the window
                // being added to function.
                getWindow().getDecorView().setVisibility(View.INVISIBLE);
                show();
                return;
            case SoftInputWindowState.TOKEN_SET:
            case SoftInputWindowState.SHOWN_AT_LEAST_ONCE:
+65 −3
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -59,6 +60,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;

/**
@@ -306,6 +308,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
    private SyncRtSurfaceTransactionApplier mApplier;

    private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
    private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
            = new ArrayList<>();

    /** Set of inset types for which an animation was started since last resetting this field */
    private @InsetsType int mLastStartedAnimTypes;

    public InsetsController(ViewRootImpl viewRoot) {
        this(viewRoot, (controller, type) -> {
@@ -459,6 +466,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation

        }
        mTmpControlArray.clear();

        // Do not override any animations that the app started in the OnControllableInsetsChanged
        // listeners.
        int animatingTypes = invokeControllableInsetsChangedListeners();
        showTypes[0] &= ~animatingTypes;
        hideTypes[0] &= ~animatingTypes;

        if (showTypes[0] != 0) {
            applyAnimation(showTypes[0], true /* show */, false /* fromIme */);
        }
@@ -542,9 +556,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
    private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types,
            WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
            @Nullable Interpolator interpolator, @AnimationType int animationType) {
        // If the frame of our window doesn't span the entire display, the control API makes very
        // little sense, as we don't deal with negative insets. So just cancel immediately.
        if (!mState.getDisplayFrame().equals(mFrame)) {
        if (!checkDisplayFramesForControlling()) {
            listener.onCancelled();
            CancellationSignal cancellationSignal = new CancellationSignal();
            cancellationSignal.cancel();
@@ -554,6 +566,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
                false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
    }

    private boolean checkDisplayFramesForControlling() {

        // If the frame of our window doesn't span the entire display, the control API makes very
        // little sense, as we don't deal with negative insets. So just cancel immediately.
        return mState.getDisplayFrame().equals(mFrame);
    }

    private CancellationSignal controlAnimationUnchecked(@InsetsType int types,
            WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
            long durationMs, Interpolator interpolator, boolean fade,
@@ -567,6 +586,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
            return cancellationSignal;
        }
        cancelExistingControllers(types);
        mLastStartedAnimTypes |= types;

        final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
        final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
@@ -994,6 +1014,48 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
        return mViewRoot.mWindowAttributes.insetsFlags.behavior;
    }

    private @InsetsType int calculateControllableTypes() {
        if (!checkDisplayFramesForControlling()) {
            return 0;
        }
        @InsetsType int result = 0;
        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
            InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
            if (consumer.getControl() != null) {
                result |= toPublicType(consumer.mType);
            }
        }
        return result;
    }

    /**
     * @return The types that are now animating due to a listener invoking control/show/hide
     */
    private @InsetsType int invokeControllableInsetsChangedListeners() {
        mLastStartedAnimTypes = 0;
        @InsetsType int types = calculateControllableTypes();
        int size = mControllableInsetsChangedListeners.size();
        for (int i = 0; i < size; i++) {
            mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types);
        }
        return mLastStartedAnimTypes;
    }

    @Override
    public void addOnControllableInsetsChangedListener(
            OnControllableInsetsChangedListener listener) {
        Objects.requireNonNull(listener);
        mControllableInsetsChangedListeners.add(listener);
        listener.onControllableInsetsChanged(this, calculateControllableTypes());
    }

    @Override
    public void removeOnControllableInsetsChangedListener(
            OnControllableInsetsChangedListener listener) {
        Objects.requireNonNull(listener);
        mControllableInsetsChangedListeners.remove(listener);
    }

    /**
     * At the time we receive new leashes (e.g. InsetsSourceConsumer is processing
     * setControl) we need to release the old leash. But we may have already scheduled
+29 −0
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ public class PendingInsetsController implements WindowInsetsController {
    private @Behavior int mBehavior = KEEP_BEHAVIOR;
    private final InsetsState mDummyState = new InsetsState();
    private InsetsController mReplayedInsetsController;
    private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
            = new ArrayList<>();

    @Override
    public void show(int types) {
@@ -112,6 +114,27 @@ public class PendingInsetsController implements WindowInsetsController {
        return mDummyState;
    }

    @Override
    public void addOnControllableInsetsChangedListener(
            OnControllableInsetsChangedListener listener) {
        if (mReplayedInsetsController != null) {
            mReplayedInsetsController.addOnControllableInsetsChangedListener(listener);
        } else {
            mControllableInsetsChangedListeners.add(listener);
            listener.onControllableInsetsChanged(this, 0);
        }
    }

    @Override
    public void removeOnControllableInsetsChangedListener(
            OnControllableInsetsChangedListener listener) {
        if (mReplayedInsetsController != null) {
            mReplayedInsetsController.removeOnControllableInsetsChangedListener(listener);
        } else {
            mControllableInsetsChangedListeners.remove(listener);
        }
    }

    /**
     * Replays the commands on {@code controller} and attaches it to this instance such that any
     * calls will be forwarded to the real instance in the future.
@@ -128,9 +151,15 @@ public class PendingInsetsController implements WindowInsetsController {
        for (int i = 0; i < size; i++) {
            mRequests.get(i).replay(controller);
        }
        size = mControllableInsetsChangedListeners.size();
        for (int i = 0; i < size; i++) {
            controller.addOnControllableInsetsChangedListener(
                    mControllableInsetsChangedListeners.get(i));
        }

        // Reset all state so it doesn't get applied twice just in case
        mRequests.clear();
        mControllableInsetsChangedListeners.clear();
        mBehavior = KEEP_BEHAVIOR;
        mAppearance = 0;
        mAppearanceMask = 0;
+53 −0
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Insets;
import android.inputmethodservice.InputMethodService;
import android.os.CancellationSignal;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;

@@ -212,4 +214,55 @@ public interface WindowInsetsController {
     * @hide
     */
    InsetsState getState();

    /**
     * Adds a {@link OnControllableInsetsChangedListener} to the window insets controller.
     *
     * @param listener The listener to add.
     *
     * @see OnControllableInsetsChangedListener
     * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
     */
    void addOnControllableInsetsChangedListener(
            @NonNull OnControllableInsetsChangedListener listener);

    /**
     * Removes a {@link OnControllableInsetsChangedListener} from the window insets controller.
     *
     * @param listener The listener to remove.
     *
     * @see OnControllableInsetsChangedListener
     * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
     */
    void removeOnControllableInsetsChangedListener(
            @NonNull OnControllableInsetsChangedListener listener);

    /**
     * Listener to be notified when the set of controllable {@link InsetsType} controlled by a
     * {@link WindowInsetsController} changes.
     * <p>
     * Once a {@link InsetsType} becomes controllable, the app will be able to control the window
     * that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}.
     * <p>
     * Note: When listening to controllability of the {@link Type#ime},
     * {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService}
     * decides to cancel the show request. This could happen when there is a hardware keyboard
     * attached.
     *
     * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
     * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
     */
    interface OnControllableInsetsChangedListener {

        /**
         * Called when the set of controllable {@link InsetsType} changes.
         *
         * @param controller The controller for which the set of controllable {@link InsetsType}s
         *                   are changing.
         * @param typeMask Bitwise type-mask of the {@link InsetsType}s the controller is currently
         *                 able to control.
         */
        void onControllableInsetsChanged(@NonNull WindowInsetsController controller,
                @InsetsType int typeMask);
    }
}
Loading