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

Commit 5bb571dc authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

A brave new world for window insets (5/n)

Implement controlWindowInsetsAnimation

Based on the leashes we have on the client, and the insets the
client has requested, we are able to move the surfaces around
such that the resulting insets will match what the client
requested.

Bug: 118118435
Change-Id: I0616e53455a6544aaf374c1b0eb10e258aced21d
parent 7fa78c54
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -14134,8 +14134,11 @@ package android.graphics {
  public final class Insets {
    method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets);
    method public static android.graphics.Insets max(android.graphics.Insets, android.graphics.Insets);
    method public static android.graphics.Insets min(android.graphics.Insets, android.graphics.Insets);
    method public static android.graphics.Insets of(int, int, int, int);
    method public static android.graphics.Insets of(android.graphics.Rect);
    method public static android.graphics.Insets subtract(android.graphics.Insets, android.graphics.Insets);
    field public static final android.graphics.Insets NONE;
    field public final int bottom;
    field public final int left;
+7 −0
Original line number Diff line number Diff line
@@ -54,6 +54,13 @@ public class SparseSetArray<T> {
        return set.contains(value);
    }

    /**
     * @return the set of items at index n
     */
    public ArraySet<T> get(int n) {
        return mData.get(n);
    }

    /**
     * Remove a value from index n.
     * @return TRUE when the value existed at the given index and removed, FALSE otherwise.
+197 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 static android.view.InsetsState.INSET_SIDE_BOTTOM;
import static android.view.InsetsState.INSET_SIDE_LEFT;
import static android.view.InsetsState.INSET_SIDE_RIGHT;
import static android.view.InsetsState.INSET_SIDE_TOP;

import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.UidProto.Sync;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
import android.view.InsetsState.InsetSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetType;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Implements {@link WindowInsetsAnimationController}
 * @hide
 */
@VisibleForTesting
public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {

    private final WindowInsetsAnimationControlListener mListener;
    private final SparseArray<InsetsSourceConsumer> mConsumers;
    private final SparseIntArray mTypeSideMap = new SparseIntArray();
    private final SparseSetArray<InsetsSourceConsumer> mSideSourceMap = new SparseSetArray<>();

    /** @see WindowInsetsAnimationController#getHiddenStateInsets */
    private final Insets mHiddenInsets;

    /** @see WindowInsetsAnimationController#getShownStateInsets */
    private final Insets mShownInsets;
    private final Matrix mTmpMatrix = new Matrix();
    private final InsetsState mInitialInsetsState;
    private final @InsetType int mTypes;
    private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;

    private Insets mCurrentInsets;

    @VisibleForTesting
    public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
            InsetsState state, WindowInsetsAnimationControlListener listener,
            @InsetType int types,
            Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) {
        mConsumers = consumers;
        mListener = listener;
        mTypes = types;
        mTransactionApplierSupplier = transactionApplierSupplier;
        mInitialInsetsState = new InsetsState(state);
        mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
        mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
                null /* typeSideMap */);
        mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */,
                mTypeSideMap);
        buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers);

        // TODO: Check for controllability first and wait for IME if needed.
        listener.onReady(this, types);
    }

    @Override
    public Insets getHiddenStateInsets() {
        return mHiddenInsets;
    }

    @Override
    public Insets getShownStateInsets() {
        return mShownInsets;
    }

    @Override
    public Insets getCurrentInsets() {
        return mCurrentInsets;
    }

    @Override
    @InsetType
    public int getTypes() {
        return mTypes;
    }

    @Override
    public void changeInsets(Insets insets) {
        insets = sanitize(insets);
        final Insets offset = Insets.subtract(mShownInsets, insets);
        ArrayList<SurfaceParams> params = new ArrayList<>();
        if (offset.left != 0) {
            updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params);
        }
        if (offset.top != 0) {
            updateLeashesForSide(INSET_SIDE_TOP, offset.top, params);
        }
        if (offset.right != 0) {
            updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params);
        }
        if (offset.bottom != 0) {
            updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params);
        }
        SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
        applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
        mCurrentInsets = insets;
    }

    @Override
    public void finish(int shownTypes) {
        // TODO
    }

    private Insets calculateInsets(InsetsState state, Rect frame,
            SparseArray<InsetsSourceConsumer> consumers, boolean shown,
            @Nullable @InsetSide SparseIntArray typeSideMap) {
        for (int i = consumers.size() - 1; i >= 0; i--) {
            state.getSource(consumers.valueAt(i).getType()).setVisible(shown);
        }
        return getInsetsFromState(state, frame, typeSideMap);
    }

    private Insets getInsetsFromState(InsetsState state, Rect frame,
            @Nullable @InsetSide SparseIntArray typeSideMap) {
        return state.calculateInsets(frame, false /* isScreenRound */,
                false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
                .getSystemWindowInsets();
    }

    private Insets sanitize(Insets insets) {
        return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
    }

    private void updateLeashesForSide(@InsetSide int side, int inset,
            ArrayList<SurfaceParams> surfaceParams) {
        ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
        // TODO: Implement behavior when inset spans over multiple types
        for (int i = items.size() - 1; i >= 0; i--) {
            final InsetsSourceConsumer consumer = items.valueAt(i);
            final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
            final SurfaceControl leash = consumer.getControl().getLeash();
            mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top);
            addTranslationToMatrix(side, inset, mTmpMatrix);
            surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f));
        }
    }

    private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) {
        switch (side) {
            case INSET_SIDE_LEFT:
                m.postTranslate(-inset, 0);
                break;
            case INSET_SIDE_TOP:
                m.postTranslate(0, -inset);
                break;
            case INSET_SIDE_RIGHT:
                m.postTranslate(inset, 0);
                break;
            case INSET_SIDE_BOTTOM:
                m.postTranslate(0, inset);
                break;
        }
    }

    private static void buildTypeSourcesMap(SparseIntArray typeSideMap,
            SparseSetArray<InsetsSourceConsumer> sideSourcesMap,
            SparseArray<InsetsSourceConsumer> consumers) {
        for (int i = typeSideMap.size() - 1; i >= 0; i--) {
            int type = typeSideMap.keyAt(i);
            int side = typeSideMap.valueAt(i);
            sideSourcesMap.add(side, consumers.get(type));
        }
    }
}
+30 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.view.InsetsState.InternalInsetType;
import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;
import java.util.ArrayList;

/**
 * Implements {@link WindowInsetsController} on the client.
@@ -41,6 +42,7 @@ public class InsetsController implements WindowInsetsController {
    private final ViewRootImpl mViewRoot;

    private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
    private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();

    public InsetsController(ViewRootImpl viewRoot) {
        mViewRoot = viewRoot;
@@ -67,9 +69,11 @@ public class InsetsController implements WindowInsetsController {
    /**
     * @see InsetsState#calculateInsets
     */
    WindowInsets calculateInsets(boolean isScreenRound,
    @VisibleForTesting
    public WindowInsets calculateInsets(boolean isScreenRound,
            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
                null /* typeSideMap */);
    }

    /**
@@ -116,6 +120,28 @@ public class InsetsController implements WindowInsetsController {
        }
    }

    @Override
    public void controlWindowInsetsAnimation(@InsetType int types,
            WindowInsetsAnimationControlListener listener) {

        // TODO: Check whether we already have a controller.
        final ArraySet<Integer> internalTypes = mState.toInternalType(types);
        final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
        for (int i = internalTypes.size() - 1; i >= 0; i--) {
            InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
            if (consumer.getControl() != null) {
                consumers.put(consumer.getType(), consumer);
            } else {
                // TODO: Let calling app know it's not possible, or wait
                // TODO: Remove it from types
            }
        }
        final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
                mFrame, mState, listener, types,
                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView));
        mAnimationControls.add(controller);
    }

    private void applyLocalVisibilityOverride() {
        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
            final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
@@ -134,7 +160,8 @@ public class InsetsController implements WindowInsetsController {
        return controller;
    }

    void notifyVisibilityChanged() {
    @VisibleForTesting
    public void notifyVisibilityChanged() {
        mViewRoot.notifyInsetsChanged();
    }

+2 −1
Original line number Diff line number Diff line
@@ -70,7 +70,8 @@ public class InsetsSource implements Parcelable {
     *
     * @param relativeFrame The frame to calculate the insets relative to.
     * @param ignoreVisibility If true, always reports back insets even if source isn't visible.
     * @return The resulting insets.
     * @return The resulting insets. The contract is that only one side will be occupied by a
     *         source.
     */
    public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
        if (!ignoreVisibility && !mVisible) {
Loading