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

Commit 57157ac5 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Fix WindowInsetsController lifecycle

Provide a recording insets controller before the window gets
created, and replay the commands once a view gets attached. This
allows the client to use the controller in Activity.onCreate.

Test: WindowInsetsControllerTests
Bug: 118118435
Change-Id: I1a825ecc4367c02b27f2d08cd5442325315d4f89
parent 920105c3
Loading
Loading
Loading
Loading
+181 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view;

import android.os.CancellationSignal;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;

/**
 * An insets controller that keeps track of pending requests. This is such that an app can freely
 * use {@link WindowInsetsController} before the view root is attached during activity startup.
 * @hide
 */
public class PendingInsetsController implements WindowInsetsController {

    private static final int KEEP_BEHAVIOR = -1;
    private final ArrayList<PendingRequest> mRequests = new ArrayList<>();
    private @Appearance int mAppearance;
    private @Appearance int mAppearanceMask;
    private @Behavior int mBehavior = KEEP_BEHAVIOR;
    private final InsetsState mDummyState = new InsetsState();
    private InsetsController mReplayedInsetsController;

    @Override
    public void show(int types) {
        if (mReplayedInsetsController != null) {
            mReplayedInsetsController.show(types);
        } else {
            mRequests.add(new ShowRequest(types));
        }
    }

    @Override
    public void hide(int types) {
        if (mReplayedInsetsController != null) {
            mReplayedInsetsController.hide(types);
        } else {
            mRequests.add(new HideRequest(types));
        }
    }

    @Override
    public CancellationSignal controlWindowInsetsAnimation(int types, long durationMillis,
            Interpolator interpolator,
            WindowInsetsAnimationControlListener listener) {
        if (mReplayedInsetsController != null) {
            return mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
                    interpolator, listener);
        } else {
            listener.onCancelled();
            CancellationSignal cancellationSignal = new CancellationSignal();
            cancellationSignal.cancel();
            return cancellationSignal;
        }
    }

    @Override
    public void setSystemBarsAppearance(int appearance, int mask) {
        if (mReplayedInsetsController != null) {
            mReplayedInsetsController.setSystemBarsAppearance(appearance, mask);
        } else {
            mAppearance = (mAppearance & ~mask) | (appearance & mask);
            mAppearanceMask |= mask;
        }
    }

    @Override
    public int getSystemBarsAppearance() {
        if (mReplayedInsetsController != null) {
            return mReplayedInsetsController.getSystemBarsAppearance();
        }
        return mAppearance;
    }

    @Override
    public void setSystemBarsBehavior(int behavior) {
        if (mReplayedInsetsController != null) {
            mReplayedInsetsController.setSystemBarsBehavior(behavior);
        } else {
            mBehavior = behavior;
        }
    }

    @Override
    public int getSystemBarsBehavior() {
        if (mReplayedInsetsController != null) {
            return mReplayedInsetsController.getSystemBarsBehavior();
        }
        return mBehavior;
    }

    @Override
    public InsetsState getState() {
        return mDummyState;
    }

    /**
     * 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.
     */
    @VisibleForTesting
    public void replayAndAttach(InsetsController controller) {
        if (mBehavior != KEEP_BEHAVIOR) {
            controller.setSystemBarsBehavior(mBehavior);
        }
        if (mAppearanceMask != 0) {
            controller.setSystemBarsAppearance(mAppearance, mAppearanceMask);
        }
        int size = mRequests.size();
        for (int i = 0; i < size; i++) {
            mRequests.get(i).replay(controller);
        }

        // Reset all state so it doesn't get applied twice just in case
        mRequests.clear();
        mBehavior = KEEP_BEHAVIOR;
        mAppearance = 0;
        mAppearanceMask = 0;

        // After replaying, we forward everything directly to the replayed instance.
        mReplayedInsetsController = controller;
    }

    /**
     * Detaches the controller to no longer forward calls to the real instance.
     */
    @VisibleForTesting
    public void detach() {
        mReplayedInsetsController = null;
    }

    private interface PendingRequest {
        void replay(InsetsController controller);
    }

    private static class ShowRequest implements PendingRequest {

        private final @InsetsType int mTypes;

        public ShowRequest(int types) {
            mTypes = types;
        }

        @Override
        public void replay(InsetsController controller) {
            controller.show(mTypes);
        }
    }

    private static class HideRequest implements PendingRequest {

        private final @InsetsType int mTypes;

        public HideRequest(int types) {
            mTypes = types;
        }

        @Override
        public void replay(InsetsController controller) {
            controller.hide(mTypes);
        }
    }
}
+6 −2
Original line number Diff line number Diff line
@@ -11415,14 +11415,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    /**
     * Retrieves the single {@link WindowInsetsController} of the window this view is attached to.
     *
     * @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
     *         a window.
     * @return The {@link WindowInsetsController} or {@code null} if the view is neither attached to
     *         a window nor a view tree with a decor.
     * @see Window#getInsetsController()
     */
    public @Nullable WindowInsetsController getWindowInsetsController() {
        if (mAttachInfo != null) {
            return mAttachInfo.mViewRootImpl.getInsetsController();
        }
        ViewParent parent = getParent();
        if (parent instanceof View) {
            return ((View) parent).getWindowInsetsController();
        }
        return null;
    }
+8 −0
Original line number Diff line number Diff line
@@ -1117,6 +1117,14 @@ public final class ViewRootImpl implements ViewParent,
                mFirstInputStage = nativePreImeStage;
                mFirstPostImeInputStage = earlyPostImeStage;
                mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;

                if (mView instanceof RootViewSurfaceTaker) {
                    PendingInsetsController pendingInsetsController =
                            ((RootViewSurfaceTaker) mView).providePendingInsetsController();
                    if (pendingInsetsController != null) {
                        pendingInsetsController.replayAndAttach(mInsetsController);
                    }
                }
            }
        }
    }
+20 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.InputQueue;
import android.view.InsetsState;
import android.view.InsetsController;
import android.view.InsetsState.InternalInsetsType;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
@@ -85,6 +86,7 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.PendingInsetsController;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
@@ -286,6 +288,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
     */
    private boolean mUseNewInsetsApi;

    private PendingInsetsController mPendingInsetsController = new PendingInsetsController();

    DecorView(Context context, int featureId, PhoneWindow window,
            WindowManager.LayoutParams params) {
        super(context);
@@ -1780,6 +1784,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
            getViewRootImpl().removeWindowCallbacks(this);
            mWindowResizeCallbacksAdded = false;
        }

        mPendingInsetsController.detach();
    }

    @Override
@@ -1819,6 +1825,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        updateColorViewTranslations();
    }

    @Override
    public PendingInsetsController providePendingInsetsController() {
        return mPendingInsetsController;
    }

    private ActionMode createActionMode(
            int type, ActionMode.Callback2 callback, View originatingView) {
        switch (type) {
@@ -2539,6 +2550,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        return AccessibilityNodeInfo.ROOT_ITEM_ID;
    }

    @Override
    public WindowInsetsController getWindowInsetsController() {
        if (isAttachedToWindow()) {
            return super.getWindowInsetsController();
        } else {
            return mPendingInsetsController;
        }
    }

    @Override
    public String toString() {
        return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
+4 −0
Original line number Diff line number Diff line
@@ -15,8 +15,11 @@
 */
package com.android.internal.view;

import android.annotation.Nullable;
import android.view.InputQueue;
import android.view.PendingInsetsController;
import android.view.SurfaceHolder;
import android.view.WindowInsetsController;

/** hahahah */
public interface RootViewSurfaceTaker {
@@ -26,4 +29,5 @@ public interface RootViewSurfaceTaker {
    void setSurfaceKeepScreenOn(boolean keepOn);
    InputQueue.Callback willYouTakeTheInputQueue();
    void onRootViewScrollYChanged(int scrollY);
    @Nullable PendingInsetsController providePendingInsetsController();
}
Loading