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

Commit 7fb715c4 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Introduce Window.setContentOnApplyWindowInsetsListener

When root-level content containers fit insets, they used to just
apply and consume the entire system insets. However, with the new
Inset APIs, and with deprecating ADJUST_RESIZE IME flag, we want
to give apps an easy way to customize this behavior.

For that, we introduce Window.setOnContentApplyWindowInsetsListener
that returns what kind of margins/padding should be applied and
what should be dispatched to the content views. This is essentially
a replacement for SYSTEM_UI_FLAG_LAYOUT_* as well as
SOFT_INPUT_ADJUST_RESIZE: It allows apps to choose which insets
should be handled on the window level vs view level.

For that, we mark the window decor views as
FRAMEWORK_OPTIONAL_FIT_SYSTEM_WINDOWS, in order to distinguish the
case when support library calls makeOptionalFitSystemWindows(). This
is because of two reasons:
- We don't want the listener to be invoked twice.
- We can not do the compat ping-pong between onApplyWindowInsets
and fitSystemWindows. This is because during the ping-pong, the
result of the OnContentApplyWindowInsetsListener would be lost.
However, we still need to do the compat ping-pong for
ActionBarOverlayLayout in the support library (until that gets
migrated to use onApplyWindowInsets), so we have this separate
dispatching path that only gets used for framework optional
fitting views.

Test: WindowTest
Bug: 118118435
Change-Id: I4b514addd9e094163062d651972f85615b4a35db
parent 871fe0a9
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -53858,6 +53858,7 @@ package android.view {
    method public final void removeOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener);
    method public boolean requestFeature(int);
    method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
    method public void resetOnContentApplyWindowInsetsListener();
    method public abstract void restoreHierarchyState(android.os.Bundle);
    method public abstract android.os.Bundle saveHierarchyState();
    method public void setAllowEnterTransitionOverlap(boolean);
@@ -53896,6 +53897,7 @@ package android.view {
    method public abstract void setNavigationBarColor(@ColorInt int);
    method public void setNavigationBarContrastEnforced(boolean);
    method public void setNavigationBarDividerColor(@ColorInt int);
    method public void setOnContentApplyWindowInsetsListener(@Nullable android.view.Window.OnContentApplyWindowInsetsListener);
    method public void setPreferMinimalPostProcessing(boolean);
    method public void setReenterTransition(android.transition.Transition);
    method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
@@ -53990,6 +53992,10 @@ package android.view {
    method @Nullable public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
  }
  public static interface Window.OnContentApplyWindowInsetsListener {
    method @NonNull public android.util.Pair<android.graphics.Insets,android.view.WindowInsets> onContentApplyWindowInsets(@NonNull android.view.WindowInsets);
  }
  public static interface Window.OnFrameMetricsAvailableListener {
    method public void onFrameMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
  }
+1 −1
Original line number Diff line number Diff line
@@ -1194,7 +1194,7 @@ public class InputMethodService extends AbstractInputMethodService {
                Context.LAYOUT_INFLATER_SERVICE);
        mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
                WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
        mWindow.getWindow().setFitWindowInsetsTypes(WindowInsets.Type.systemBars());
        mWindow.getWindow().getAttributes().setFitWindowInsetsTypes(WindowInsets.Type.systemBars());
        mWindow.getWindow().addPrivateFlags(PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND);
        mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener(
                (v, insets) -> v.onApplyWindowInsets(
+1 −1
Original line number Diff line number Diff line
@@ -1137,7 +1137,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
        mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
                mCallbacks, this, mDispatcherState,
                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true);
        mWindow.getWindow().setFitWindowInsetsTypes(0 /* types */);
        mWindow.getWindow().getAttributes().setFitWindowInsetsTypes(0 /* types */);
        mWindow.getWindow().addFlags(
                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED |
                        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
+68 −20
Original line number Diff line number Diff line
@@ -23,6 +23,10 @@ import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SI
import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowInsetsAnimationCallback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static java.lang.Math.max;
@@ -97,6 +101,7 @@ import android.util.FloatProperty;
import android.util.LayoutDirection;
import android.util.Log;
import android.util.LongSparseLongArray;
import android.util.Pair;
import android.util.Pools.SynchronizedPool;
import android.util.Property;
import android.util.SparseArray;
@@ -110,6 +115,7 @@ import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Window.OnContentApplyWindowInsetsListener;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
import android.view.WindowInsetsAnimationCallback.DispatchMode;
@@ -140,6 +146,7 @@ import android.widget.FrameLayout;
import android.widget.ScrollBarDrawable;
import com.android.internal.R;
import com.android.internal.policy.DecorView;
import com.android.internal.view.TooltipPopup;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.widget.ScrollBarUtils;
@@ -1510,6 +1517,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * Set for framework elements that use FITS_SYSTEM_WINDOWS, to indicate
     * that they are optional and should be skipped if the window has
     * requested system UI flags that ignore those insets for layout.
     * <p>
     * This is only used for support library as of Android R. The framework now uses
     * {@link #PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS} such that it can skip the legacy
     * insets path that loses insets information.
     */
    static final int OPTIONAL_FITS_SYSTEM_WINDOWS = 0x00000800;
@@ -2258,7 +2269,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * be extended in the future to hold our own class with more than just
     * a Rect. :)
     */
    static final ThreadLocal<Rect> sThreadLocal = new ThreadLocal<Rect>();
    static final ThreadLocal<Rect> sThreadLocal = ThreadLocal.withInitial(Rect::new);
    /**
     * Map used to store views' tags.
@@ -3420,6 +3431,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     *                          1       PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
     *                         1        PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE
     *                         11       PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK
     *                        1         PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS
     * |-------|-------|-------|-------|
     */
@@ -3457,6 +3469,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
            | PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
    /**
     * @see #OPTIONAL_FITS_SYSTEM_WINDOWS
     */
    static final int PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS = 0x000000100;
    /* End of masks for mPrivateFlags4 */
    /** @hide */
@@ -10983,23 +11000,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private boolean fitSystemWindowsInt(Rect insets) {
        if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
            mUserPaddingStart = UNDEFINED_PADDING;
            mUserPaddingEnd = UNDEFINED_PADDING;
            Rect localInsets = sThreadLocal.get();
            if (localInsets == null) {
                localInsets = new Rect();
                sThreadLocal.set(localInsets);
            }
            boolean res = computeFitSystemWindows(insets, localInsets);
            mUserPaddingLeftInitial = localInsets.left;
            mUserPaddingRightInitial = localInsets.right;
            internalSetPadding(localInsets.left, localInsets.top,
                    localInsets.right, localInsets.bottom);
            applyInsets(localInsets);
            return res;
        }
        return false;
    }
    private void applyInsets(Rect insets) {
        mUserPaddingStart = UNDEFINED_PADDING;
        mUserPaddingEnd = UNDEFINED_PADDING;
        mUserPaddingLeftInitial = insets.left;
        mUserPaddingRightInitial = insets.right;
        internalSetPadding(insets.left, insets.top, insets.right, insets.bottom);
    }
    /**
     * Called when the view should apply {@link WindowInsets} according to its internal policy.
     *
@@ -11026,6 +11042,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * @return The supplied insets with any applied insets consumed
     */
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        if ((mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
                && (mViewFlags & FITS_SYSTEM_WINDOWS) != 0) {
            return onApplyFrameworkOptionalFitSystemWindows(insets);
        }
        if ((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS) == 0) {
            // We weren't called from within a direct call to fitSystemWindows,
            // call into it as a fallback in case we're in a class that overrides it
@@ -11042,6 +11062,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return insets;
    }
    private WindowInsets onApplyFrameworkOptionalFitSystemWindows(WindowInsets insets) {
        Rect localInsets = sThreadLocal.get();
        WindowInsets result = computeSystemWindowInsets(insets, localInsets);
        applyInsets(localInsets);
        return result;
    }
    /**
     * Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying
     * window insets to this view. The listener's
@@ -11332,17 +11359,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * @return Insets that should be passed along to views under this one
     */
    public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) {
        if ((mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) == 0
                || mAttachInfo == null
                || ((mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0)) {
            outLocalInsets.set(in.getSystemWindowInsetsAsRect());
            return in.consumeSystemWindowInsets().inset(outLocalInsets);
        } else {
        boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
                || (mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0;
        if (isOptionalFitSystemWindows && mAttachInfo != null) {
            OnContentApplyWindowInsetsListener listener =
                    mAttachInfo.mContentOnApplyWindowInsetsListener;
            if (listener == null) {
                // The application wants to take care of fitting system window for
                // the content.
                outLocalInsets.setEmpty();
                return in;
            }
            Pair<Insets, WindowInsets> result = listener.onContentApplyWindowInsets(in);
            outLocalInsets.set(result.first.toRect());
            return result.second;
        } else {
            outLocalInsets.set(in.getSystemWindowInsetsAsRect());
            return in.consumeSystemWindowInsets().inset(outLocalInsets);
        }
    }
    /**
@@ -11412,7 +11446,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    /**
     * For use by PhoneWindow to make its own system window fitting optional.
     * @see #OPTIONAL_FITS_SYSTEM_WINDOWS
     * @hide
     */
    @UnsupportedAppUsage
@@ -11420,6 +11454,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        setFlags(OPTIONAL_FITS_SYSTEM_WINDOWS, OPTIONAL_FITS_SYSTEM_WINDOWS);
    }
    /**
     * @see #PFLAG4_OPTIONAL_FITS_SYSTEM_WINDOWS
     * @hide
     */
    public void makeFrameworkOptionalFitsSystemWindows() {
        mPrivateFlags4 |= PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS;
    }
    /**
     * Returns the visibility status for this view.
     *
@@ -28378,6 +28420,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * window.
     */
    final static class AttachInfo {
        interface Callbacks {
            void playSoundEffect(int effectId);
            boolean performHapticFeedback(int effectId, boolean always);
@@ -28816,6 +28859,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         */
        ContentCaptureManager mContentCaptureManager;
        /**
         * Listener used to fit content on window level.
         */
        OnContentApplyWindowInsetsListener mContentOnApplyWindowInsetsListener;
        /**
         * Creates a new set of attachment information with the specified
         * events handler and thread.
+14 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import android.util.Pools;
import android.util.Pools.SynchronizedPool;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.Window.OnContentApplyWindowInsetsListener;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
import android.view.WindowInsetsAnimationCallback.DispatchMode;
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
@@ -1527,6 +1528,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        }
    }

    /**
     * @hide
     */
    @Override
    public void makeFrameworkOptionalFitsSystemWindows() {
        super.makeFrameworkOptionalFitsSystemWindows();
        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            children[i].makeFrameworkOptionalFitsSystemWindows();
        }
    }

    @Override
    public void dispatchDisplayHint(int hint) {
        super.dispatchDisplayHint(hint);
Loading