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
InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener, @InsetType int types, Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier, InsetsController controller)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
getHiddenStateInsets()107     public Insets getHiddenStateInsets() {
108         return mHiddenInsets;
109     }
110 
111     @Override
getShownStateInsets()112     public Insets getShownStateInsets() {
113         return mShownInsets;
114     }
115 
116     @Override
getCurrentInsets()117     public Insets getCurrentInsets() {
118         return mCurrentInsets;
119     }
120 
121     @Override
122     @InsetType
getTypes()123     public int getTypes() {
124         return mTypes;
125     }
126 
127     @Override
changeInsets(Insets insets)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      */
applyChangeInsets(InsetsState state)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
finish(int shownTypes)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
onCancelled()192     public void onCancelled() {
193         if (mFinished) {
194             return;
195         }
196         mCancelled = true;
197         mListener.onCancelled();
198     }
199 
getAnimation()200     InsetsAnimation getAnimation() {
201         return mAnimation;
202     }
203 
calculateInsets(InsetsState state, Rect frame, SparseArray<InsetsSourceConsumer> consumers, boolean shown, @Nullable @InsetSide SparseIntArray typeSideMap)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 
getInsetsFromState(InsetsState state, Rect frame, @Nullable @InsetSide SparseIntArray typeSideMap)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 
sanitize(Insets insets)222     private Insets sanitize(Insets insets) {
223         return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
224     }
225 
updateLeashesForSide(@nsetSide int side, int offset, int inset, ArrayList<SurfaceParams> surfaceParams, InsetsState state)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 
addTranslationToMatrix(@nsetSide int side, int inset, Matrix m, Rect frame)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 
buildTypeSourcesMap(SparseIntArray typeSideMap, SparseSetArray<InsetsSourceConsumer> sideSourcesMap, SparseArray<InsetsSourceConsumer> consumers)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