Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package android.view;
     18 
     19 import static android.view.InsetsState.INSET_SIDE_BOTTOM;
     20 import static android.view.InsetsState.INSET_SIDE_LEFT;
     21 import static android.view.InsetsState.INSET_SIDE_RIGHT;
     22 import static android.view.InsetsState.INSET_SIDE_TOP;
     23 import static android.view.InsetsState.toPublicType;
     24 
     25 import android.annotation.Nullable;
     26 import android.graphics.Insets;
     27 import android.graphics.Matrix;
     28 import android.graphics.Rect;
     29 import android.os.UidProto.Sync;
     30 import android.util.ArraySet;
     31 import android.util.SparseArray;
     32 import android.util.SparseIntArray;
     33 import android.util.SparseSetArray;
     34 import android.view.InsetsState.InsetSide;
     35 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
     36 import android.view.WindowInsets.Type.InsetType;
     37 import android.view.WindowInsetsAnimationListener.InsetsAnimation;
     38 import android.view.WindowManager.LayoutParams;
     39 
     40 import com.android.internal.annotations.VisibleForTesting;
     41 
     42 import java.util.ArrayList;
     43 import java.util.function.Function;
     44 import java.util.function.Supplier;
     45 
     46 /**
     47  * Implements {@link WindowInsetsAnimationController}
     48  * @hide
     49  */
     50 @VisibleForTesting
     51 public class InsetsAnimationControlImpl implements WindowInsetsAnimationController  {
     52 
     53     private final Rect mTmpFrame = new Rect();
     54 
     55     private final WindowInsetsAnimationControlListener mListener;
     56     private final SparseArray<InsetsSourceConsumer> mConsumers;
     57     private final SparseIntArray mTypeSideMap = new SparseIntArray();
     58     private final SparseSetArray<InsetsSourceConsumer> mSideSourceMap = new SparseSetArray<>();
     59 
     60     /** @see WindowInsetsAnimationController#getHiddenStateInsets */
     61     private final Insets mHiddenInsets;
     62 
     63     /** @see WindowInsetsAnimationController#getShownStateInsets */
     64     private final Insets mShownInsets;
     65     private final Matrix mTmpMatrix = new Matrix();
     66     private final InsetsState mInitialInsetsState;
     67     private final @InsetType int mTypes;
     68     private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
     69     private final InsetsController mController;
     70     private final WindowInsetsAnimationListener.InsetsAnimation mAnimation;
     71     private final Rect mFrame;
     72     private Insets mCurrentInsets;
     73     private Insets mPendingInsets;
     74     private boolean mFinished;
     75     private boolean mCancelled;
     76     private int mFinishedShownTypes;
     77 
     78     @VisibleForTesting
     79     public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
     80             InsetsState state, WindowInsetsAnimationControlListener listener,
     81             @InsetType int types,
     82             Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier,
     83             InsetsController controller) {
     84         mConsumers = consumers;
     85         mListener = listener;
     86         mTypes = types;
     87         mTransactionApplierSupplier = transactionApplierSupplier;
     88         mController = controller;
     89         mInitialInsetsState = new InsetsState(state, true /* copySources */);
     90         mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
     91         mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
     92                 null /* typeSideMap */);
     93         mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */,
     94                 mTypeSideMap);
     95         mFrame = new Rect(frame);
     96         buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers);
     97 
     98         // TODO: Check for controllability first and wait for IME if needed.
     99         listener.onReady(this, types);
    100 
    101         mAnimation = new WindowInsetsAnimationListener.InsetsAnimation(mTypes, mHiddenInsets,
    102                 mShownInsets);
    103         mController.dispatchAnimationStarted(mAnimation);
    104     }
    105 
    106     @Override
    107     public Insets getHiddenStateInsets() {
    108         return mHiddenInsets;
    109     }
    110 
    111     @Override
    112     public Insets getShownStateInsets() {
    113         return mShownInsets;
    114     }
    115 
    116     @Override
    117     public Insets getCurrentInsets() {
    118         return mCurrentInsets;
    119     }
    120 
    121     @Override
    122     @InsetType
    123     public int getTypes() {
    124         return mTypes;
    125     }
    126 
    127     @Override
    128     public void changeInsets(Insets insets) {
    129         if (mFinished) {
    130             throw new IllegalStateException(
    131                     "Can't change insets on an animation that is finished.");
    132         }
    133         if (mCancelled) {
    134             throw new IllegalStateException(
    135                     "Can't change insets on an animation that is cancelled.");
    136         }
    137         mPendingInsets = sanitize(insets);
    138         mController.scheduleApplyChangeInsets();
    139     }
    140 
    141     @VisibleForTesting
    142     /**
    143      * @return Whether the finish callback of this animation should be invoked.
    144      */
    145     public boolean applyChangeInsets(InsetsState state) {
    146         if (mCancelled) {
    147             return false;
    148         }
    149         final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
    150         ArrayList<SurfaceParams> params = new ArrayList<>();
    151         if (offset.left != 0) {
    152             updateLeashesForSide(INSET_SIDE_LEFT, offset.left, mPendingInsets.left, params, state);
    153         }
    154         if (offset.top != 0) {
    155             updateLeashesForSide(INSET_SIDE_TOP, offset.top, mPendingInsets.top, params, state);
    156         }
    157         if (offset.right != 0) {
    158             updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, mPendingInsets.right, params,
    159                     state);
    160         }
    161         if (offset.bottom != 0) {
    162             updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params,
    163                     state);
    164         }
    165         SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
    166         applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
    167         mCurrentInsets = mPendingInsets;
    168         if (mFinished) {
    169             mController.notifyFinished(this, mFinishedShownTypes);
    170         }
    171         return mFinished;
    172     }
    173 
    174     @Override
    175     public void finish(int shownTypes) {
    176         if (mCancelled) {
    177             return;
    178         }
    179         InsetsState state = new InsetsState(mController.getState());
    180         for (int i = mConsumers.size() - 1; i >= 0; i--) {
    181             InsetsSourceConsumer consumer = mConsumers.valueAt(i);
    182             boolean visible = (shownTypes & toPublicType(consumer.getType())) != 0;
    183             state.getSource(consumer.getType()).setVisible(visible);
    184         }
    185         Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */);
    186         changeInsets(insets);
    187         mFinished = true;
    188         mFinishedShownTypes = shownTypes;
    189     }
    190 
    191     @VisibleForTesting
    192     public void onCancelled() {
    193         if (mFinished) {
    194             return;
    195         }
    196         mCancelled = true;
    197         mListener.onCancelled();
    198     }
    199 
    200     InsetsAnimation getAnimation() {
    201         return mAnimation;
    202     }
    203 
    204     private Insets calculateInsets(InsetsState state, Rect frame,
    205             SparseArray<InsetsSourceConsumer> consumers, boolean shown,
    206             @Nullable @InsetSide SparseIntArray typeSideMap) {
    207         for (int i = consumers.size() - 1; i >= 0; i--) {
    208             state.getSource(consumers.valueAt(i).getType()).setVisible(shown);
    209         }
    210         return getInsetsFromState(state, frame, typeSideMap);
    211     }
    212 
    213     private Insets getInsetsFromState(InsetsState state, Rect frame,
    214             @Nullable @InsetSide SparseIntArray typeSideMap) {
    215         return state.calculateInsets(frame, false /* isScreenRound */,
    216                 false /* alwaysConsumerNavBar */, null /* displayCutout */,
    217                 null /* legacyContentInsets */, null /* legacyStableInsets */,
    218                 LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/, typeSideMap)
    219                .getInsets(mTypes);
    220     }
    221 
    222     private Insets sanitize(Insets insets) {
    223         return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
    224     }
    225 
    226     private void updateLeashesForSide(@InsetSide int side, int offset, int inset,
    227             ArrayList<SurfaceParams> surfaceParams, InsetsState state) {
    228         ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
    229         // TODO: Implement behavior when inset spans over multiple types
    230         for (int i = items.size() - 1; i >= 0; i--) {
    231             final InsetsSourceConsumer consumer = items.valueAt(i);
    232             final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
    233             final InsetsSourceControl control = consumer.getControl();
    234             final SurfaceControl leash = consumer.getControl().getLeash();
    235 
    236             mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y);
    237             mTmpFrame.set(source.getFrame());
    238             addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame);
    239 
    240             state.getSource(source.getType()).setFrame(mTmpFrame);
    241             surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f, inset != 0));
    242         }
    243     }
    244 
    245     private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m, Rect frame) {
    246         switch (side) {
    247             case INSET_SIDE_LEFT:
    248                 m.postTranslate(-inset, 0);
    249                 frame.offset(-inset, 0);
    250                 break;
    251             case INSET_SIDE_TOP:
    252                 m.postTranslate(0, -inset);
    253                 frame.offset(0, -inset);
    254                 break;
    255             case INSET_SIDE_RIGHT:
    256                 m.postTranslate(inset, 0);
    257                 frame.offset(inset, 0);
    258                 break;
    259             case INSET_SIDE_BOTTOM:
    260                 m.postTranslate(0, inset);
    261                 frame.offset(0, inset);
    262                 break;
    263         }
    264     }
    265 
    266     private static void buildTypeSourcesMap(SparseIntArray typeSideMap,
    267             SparseSetArray<InsetsSourceConsumer> sideSourcesMap,
    268             SparseArray<InsetsSourceConsumer> consumers) {
    269         for (int i = typeSideMap.size() - 1; i >= 0; i--) {
    270             int type = typeSideMap.keyAt(i);
    271             int side = typeSideMap.valueAt(i);
    272             sideSourcesMap.add(side, consumers.get(type));
    273         }
    274     }
    275 }
    276