• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
20 import static android.view.EventLogTags.IMF_IME_ANIM_CANCEL;
21 import static android.view.EventLogTags.IMF_IME_ANIM_FINISH;
22 import static android.view.EventLogTags.IMF_IME_ANIM_START;
23 import static android.view.InsetsAnimationControlImplProto.CURRENT_ALPHA;
24 import static android.view.InsetsAnimationControlImplProto.IS_CANCELLED;
25 import static android.view.InsetsAnimationControlImplProto.IS_FINISHED;
26 import static android.view.InsetsAnimationControlImplProto.PENDING_ALPHA;
27 import static android.view.InsetsAnimationControlImplProto.PENDING_FRACTION;
28 import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS;
29 import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH;
30 import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX;
31 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
32 import static android.view.InsetsController.AnimationType;
33 import static android.view.InsetsController.DEBUG;
34 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
35 import static android.view.InsetsController.LayoutInsetsDuringAnimation;
36 import static android.view.InsetsSource.ID_IME;
37 import static android.view.InsetsSource.SIDE_BOTTOM;
38 import static android.view.InsetsSource.SIDE_NONE;
39 import static android.view.InsetsSource.SIDE_LEFT;
40 import static android.view.InsetsSource.SIDE_RIGHT;
41 import static android.view.InsetsSource.SIDE_TOP;
42 import static android.view.WindowInsets.Type.ime;
43 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
44 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
45 import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
46 
47 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
48 
49 import android.annotation.Nullable;
50 import android.content.res.CompatibilityInfo;
51 import android.graphics.Insets;
52 import android.graphics.Matrix;
53 import android.graphics.Point;
54 import android.graphics.Rect;
55 import android.util.ArraySet;
56 import android.util.EventLog;
57 import android.util.Log;
58 import android.util.SparseArray;
59 import android.util.SparseIntArray;
60 import android.util.SparseSetArray;
61 import android.util.proto.ProtoOutputStream;
62 import android.view.InsetsSource.InternalInsetsSide;
63 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
64 import android.view.WindowInsets.Type.InsetsType;
65 import android.view.WindowInsetsAnimation.Bounds;
66 import android.view.animation.Interpolator;
67 import android.view.inputmethod.ImeTracker;
68 
69 import com.android.internal.annotations.VisibleForTesting;
70 
71 import java.util.ArrayList;
72 import java.util.Objects;
73 
74 /**
75  * Implements {@link WindowInsetsAnimationController}
76  * @hide
77  */
78 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
79 public class InsetsAnimationControlImpl implements InternalInsetsAnimationController,
80         InsetsAnimationControlRunner {
81 
82     private static final String TAG = "InsetsAnimationCtrlImpl";
83 
84     private final Rect mTmpFrame = new Rect();
85 
86     private final WindowInsetsAnimationControlListener mListener;
87     private final SparseArray<InsetsSourceControl> mControls;
88     private final SparseSetArray<InsetsSourceControl> mSideControlsMap = new SparseSetArray<>();
89 
90     /** @see WindowInsetsAnimationController#getHiddenStateInsets */
91     private final Insets mHiddenInsets;
92 
93     /** @see WindowInsetsAnimationController#getShownStateInsets */
94     private final Insets mShownInsets;
95     private final Matrix mTmpMatrix = new Matrix();
96     private final InsetsState mInitialInsetsState;
97     private final @AnimationType int mAnimationType;
98     private @LayoutInsetsDuringAnimation int mLayoutInsetsDuringAnimation;
99     private final @InsetsType int mTypes;
100     private @InsetsType int mControllingTypes;
101     private final InsetsAnimationControlCallbacks mController;
102     private final WindowInsetsAnimation mAnimation;
103     /** @see WindowInsetsAnimationController#hasZeroInsetsIme */
104     private final boolean mHasZeroInsetsIme;
105     private final CompatibilityInfo.Translator mTranslator;
106     @Nullable
107     private final ImeTracker.Token mStatsToken;
108     private Insets mCurrentInsets;
109     private Insets mPendingInsets;
110     private float mPendingFraction;
111     private boolean mFinished;
112     private boolean mCancelled;
113     private boolean mShownOnFinish;
114     private float mCurrentAlpha = 1.0f;
115     private float mPendingAlpha = 1.0f;
116     @VisibleForTesting(visibility = PACKAGE)
117     private boolean mReadyDispatched;
118     private Boolean mPerceptible;
119 
120     @VisibleForTesting
InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, @Nullable Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator, @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, CompatibilityInfo.Translator translator, @Nullable ImeTracker.Token statsToken)121     public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls,
122             @Nullable Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener,
123             @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs,
124             Interpolator interpolator, @AnimationType int animationType,
125             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
126             CompatibilityInfo.Translator translator, @Nullable ImeTracker.Token statsToken) {
127         mControls = controls;
128         mListener = listener;
129         mTypes = types;
130         mControllingTypes = types;
131         mController = controller;
132         mInitialInsetsState = new InsetsState(state, true /* copySources */);
133         if (frame != null) {
134             final SparseIntArray idSideMap = new SparseIntArray();
135             mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* idSideMap */);
136             mHiddenInsets = calculateInsets(mInitialInsetsState, frame, controls, false /* shown */,
137                     null /* idSideMap */);
138             mShownInsets = calculateInsets(mInitialInsetsState, frame, controls, true /* shown */,
139                     idSideMap);
140             mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsType(WindowInsets.Type.ime());
141             if (mHasZeroInsetsIme) {
142                 // IME has shownInsets of ZERO, and can't map to a side by default.
143                 // Map zero insets IME to bottom, making it a special case of bottom insets.
144                 idSideMap.put(ID_IME, SIDE_BOTTOM);
145             }
146             buildSideControlsMap(idSideMap, mSideControlsMap, controls);
147         } else {
148             // Passing a null frame indicates the caller wants to play the insets animation anyway,
149             // no matter the source provides insets to the frame or not.
150             mCurrentInsets = calculateInsets(mInitialInsetsState, controls, true /* shown */);
151             mHiddenInsets = calculateInsets(null, controls, false /* shown */);
152             mShownInsets = calculateInsets(null, controls, true /* shown */);
153             mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsType(WindowInsets.Type.ime());
154             buildSideControlsMap(mSideControlsMap, controls);
155         }
156         mPendingInsets = mCurrentInsets;
157 
158         mAnimation = new WindowInsetsAnimation(mTypes, interpolator,
159                 durationMs);
160         mAnimation.setAlpha(getCurrentAlpha());
161         mAnimationType = animationType;
162         mLayoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
163         mTranslator = translator;
164         mStatsToken = statsToken;
165         if (DEBUG_IME_VISIBILITY && (types & ime()) != 0) {
166             EventLog.writeEvent(IMF_IME_ANIM_START,
167                     mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
168                     mAnimationType, mCurrentAlpha, "Current:" + mCurrentInsets,
169                     "Shown:" + mShownInsets, "Hidden:" + mHiddenInsets);
170         }
171         mController.startAnimation(this, listener, types, mAnimation,
172                 new Bounds(mHiddenInsets, mShownInsets));
173     }
174 
calculatePerceptible(Insets currentInsets, float currentAlpha)175     private boolean calculatePerceptible(Insets currentInsets, float currentAlpha) {
176         return 100 * currentInsets.left >= 5 * (mShownInsets.left - mHiddenInsets.left)
177                 && 100 * currentInsets.top >= 5 * (mShownInsets.top - mHiddenInsets.top)
178                 && 100 * currentInsets.right >= 5 * (mShownInsets.right - mHiddenInsets.right)
179                 && 100 * currentInsets.bottom >= 5 * (mShownInsets.bottom - mHiddenInsets.bottom)
180                 && currentAlpha >= 0.5f;
181     }
182 
183     @Override
hasZeroInsetsIme()184     public boolean hasZeroInsetsIme() {
185         return mHasZeroInsetsIme;
186     }
187 
188     @Override
setReadyDispatched(boolean dispatched)189     public void setReadyDispatched(boolean dispatched) {
190         mReadyDispatched = dispatched;
191     }
192 
193     @Override
getHiddenStateInsets()194     public Insets getHiddenStateInsets() {
195         return mHiddenInsets;
196     }
197 
198     @Override
getShownStateInsets()199     public Insets getShownStateInsets() {
200         return mShownInsets;
201     }
202 
203     @Override
getCurrentInsets()204     public Insets getCurrentInsets() {
205         return mCurrentInsets;
206     }
207 
208     @Override
getCurrentAlpha()209     public float getCurrentAlpha() {
210         return mCurrentAlpha;
211     }
212 
213     @Override
getTypes()214     @InsetsType public int getTypes() {
215         return mTypes;
216     }
217 
218     @Override
getControllingTypes()219     public int getControllingTypes() {
220         return mControllingTypes;
221     }
222 
223     @Override
notifyControlRevoked(@nsetsType int types)224     public void notifyControlRevoked(@InsetsType int types) {
225         mControllingTypes &= ~types;
226     }
227 
228     @Override
updateSurfacePosition(SparseArray<InsetsSourceControl> controls)229     public void updateSurfacePosition(SparseArray<InsetsSourceControl> controls) {
230         for (int i = controls.size() - 1; i >= 0; i--) {
231             final InsetsSourceControl control = controls.valueAt(i);
232             final InsetsSourceControl c = mControls.get(control.getId());
233             if (c == null) {
234                 continue;
235             }
236             final Point position = control.getSurfacePosition();
237             c.setSurfacePosition(position.x, position.y);
238         }
239     }
240 
241     @Override
getAnimationType()242     public @AnimationType int getAnimationType() {
243         return mAnimationType;
244     }
245 
246     @Override
247     @Nullable
getStatsToken()248     public ImeTracker.Token getStatsToken() {
249         return mStatsToken;
250     }
251 
252     @Override
setInsetsAndAlpha(Insets insets, float alpha, float fraction)253     public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) {
254         setInsetsAndAlpha(insets, alpha, fraction, false /* allowWhenFinished */);
255     }
256 
setInsetsAndAlpha(Insets insets, float alpha, float fraction, boolean allowWhenFinished)257     private void setInsetsAndAlpha(Insets insets, float alpha, float fraction,
258             boolean allowWhenFinished) {
259         if (!allowWhenFinished && mFinished) {
260             throw new IllegalStateException(
261                     "Can't change insets on an animation that is finished.");
262         }
263         if (mCancelled) {
264             throw new IllegalStateException(
265                     "Can't change insets on an animation that is cancelled.");
266         }
267         mPendingFraction = sanitize(fraction);
268         mPendingInsets = sanitize(insets);
269         mPendingAlpha = sanitize(alpha);
270         mController.scheduleApplyChangeInsets(this);
271         boolean perceptible = calculatePerceptible(mPendingInsets, mPendingAlpha);
272         if (mPerceptible == null || perceptible != mPerceptible) {
273             mController.reportPerceptible(mTypes, perceptible);
274             mPerceptible = perceptible;
275         }
276     }
277 
278     /**
279      * @return Whether the finish callback of this animation should be invoked.
280      */
281     @VisibleForTesting
applyChangeInsets(@ullable InsetsState outState)282     public boolean applyChangeInsets(@Nullable InsetsState outState) {
283         if (mCancelled) {
284             if (DEBUG) Log.d(TAG, "applyChangeInsets canceled");
285             return false;
286         }
287         final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
288         final ArrayList<SurfaceParams> params = new ArrayList<>();
289         updateLeashesForSide(SIDE_LEFT, offset.left, params, outState, mPendingAlpha);
290         updateLeashesForSide(SIDE_TOP, offset.top, params, outState, mPendingAlpha);
291         updateLeashesForSide(SIDE_RIGHT, offset.right, params, outState, mPendingAlpha);
292         updateLeashesForSide(SIDE_BOTTOM, offset.bottom, params, outState, mPendingAlpha);
293 
294         mController.applySurfaceParams(params.toArray(new SurfaceParams[params.size()]));
295         mCurrentInsets = mPendingInsets;
296         mAnimation.setFraction(mPendingFraction);
297         mCurrentAlpha = mPendingAlpha;
298         mAnimation.setAlpha(mPendingAlpha);
299         if (mFinished) {
300             if (DEBUG) Log.d(TAG, String.format(
301                     "notifyFinished shown: %s, currentAlpha: %f, currentInsets: %s",
302                     mShownOnFinish, mCurrentAlpha, mCurrentInsets));
303             mController.notifyFinished(this, mShownOnFinish);
304             releaseLeashes();
305             if (DEBUG) Log.d(TAG, "Animation finished abruptly.");
306         }
307         return mFinished;
308     }
309 
releaseLeashes()310     private void releaseLeashes() {
311         for (int i = mControls.size() - 1; i >= 0; i--) {
312             final InsetsSourceControl c = mControls.valueAt(i);
313             if (c == null) continue;
314             c.release(mController::releaseSurfaceControlFromRt);
315         }
316     }
317 
318     @Override
finish(boolean shown)319     public void finish(boolean shown) {
320         if (mCancelled || mFinished) {
321             if (DEBUG) Log.d(TAG, "Animation already canceled or finished, not notifying.");
322             return;
323         }
324         mShownOnFinish = shown;
325         mFinished = true;
326         final Insets insets = shown ? mShownInsets : mHiddenInsets;
327         setInsetsAndAlpha(insets, mPendingAlpha, 1f /* fraction */, true /* allowWhenFinished */);
328 
329         if (DEBUG) Log.d(TAG, "notify control request finished for types: " + mTypes);
330         mListener.onFinished(this);
331         if (DEBUG_IME_VISIBILITY && (mTypes & ime()) != 0) {
332             EventLog.writeEvent(IMF_IME_ANIM_FINISH,
333                     mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
334                     mAnimationType, mCurrentAlpha, shown ? 1 : 0, Objects.toString(insets));
335         }
336     }
337 
338     @Override
339     @VisibleForTesting
getCurrentFraction()340     public float getCurrentFraction() {
341         return mAnimation.getFraction();
342     }
343 
344     @Override
cancel()345     public void cancel() {
346         if (mFinished) {
347             return;
348         }
349         mPendingInsets = mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN
350                 ? mShownInsets : mHiddenInsets;
351         mPendingAlpha = 1f;
352         applyChangeInsets(null);
353         mCancelled = true;
354         mListener.onCancelled(mReadyDispatched ? this : null);
355         if (DEBUG) Log.d(TAG, "notify Control request cancelled for types: " + mTypes);
356         if (DEBUG_IME_VISIBILITY && (mTypes & ime()) != 0) {
357             EventLog.writeEvent(IMF_IME_ANIM_CANCEL,
358                     mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
359                     mAnimationType, Objects.toString(mPendingInsets));
360         }
361         releaseLeashes();
362     }
363 
364     @Override
isFinished()365     public boolean isFinished() {
366         return mFinished;
367     }
368 
369     @Override
isCancelled()370     public boolean isCancelled() {
371         return mCancelled;
372     }
373 
374     @Override
getAnimation()375     public WindowInsetsAnimation getAnimation() {
376         return mAnimation;
377     }
378 
379     @Override
updateLayoutInsetsDuringAnimation( @ayoutInsetsDuringAnimation int layoutInsetsDuringAnimation)380     public void updateLayoutInsetsDuringAnimation(
381             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
382         mLayoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
383     }
384 
385     @Override
dumpDebug(ProtoOutputStream proto, long fieldId)386     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
387         final long token = proto.start(fieldId);
388         proto.write(IS_CANCELLED, mCancelled);
389         proto.write(IS_FINISHED, mFinished);
390         proto.write(TMP_MATRIX, Objects.toString(mTmpMatrix));
391         proto.write(PENDING_INSETS, Objects.toString(mPendingInsets));
392         proto.write(PENDING_FRACTION, mPendingFraction);
393         proto.write(SHOWN_ON_FINISH, mShownOnFinish);
394         proto.write(CURRENT_ALPHA, mCurrentAlpha);
395         proto.write(PENDING_ALPHA, mPendingAlpha);
396         proto.end(token);
397     }
398 
getControls()399     SparseArray<InsetsSourceControl> getControls() {
400         return mControls;
401     }
402 
getInsetsFromState(InsetsState state, Rect frame, @Nullable @InternalInsetsSide SparseIntArray idSideMap)403     private Insets getInsetsFromState(InsetsState state, Rect frame,
404             @Nullable @InternalInsetsSide SparseIntArray idSideMap) {
405         return state.calculateInsets(frame, null /* ignoringVisibilityState */,
406                 false /* isScreenRound */, SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode */,
407                 0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
408                 ACTIVITY_TYPE_UNDEFINED, idSideMap).getInsets(mTypes);
409     }
410 
411     /** Computes the insets relative to the given frame. */
calculateInsets(InsetsState state, Rect frame, SparseArray<InsetsSourceControl> controls, boolean shown, @Nullable @InternalInsetsSide SparseIntArray idSideMap)412     private Insets calculateInsets(InsetsState state, Rect frame,
413             SparseArray<InsetsSourceControl> controls, boolean shown,
414             @Nullable @InternalInsetsSide SparseIntArray idSideMap) {
415         for (int i = controls.size() - 1; i >= 0; i--) {
416             final InsetsSourceControl control  = controls.valueAt(i);
417             if (control == null) {
418                 // control may be null if it got revoked.
419                 continue;
420             }
421             state.setSourceVisible(control.getId(), shown);
422         }
423         return getInsetsFromState(state, frame, idSideMap);
424     }
425 
426     /** Computes the insets from the insets hints of controls. */
calculateInsets(InsetsState state, SparseArray<InsetsSourceControl> controls, boolean shownOrCurrent)427     private Insets calculateInsets(InsetsState state, SparseArray<InsetsSourceControl> controls,
428             boolean shownOrCurrent) {
429         Insets insets = Insets.NONE;
430         if (!shownOrCurrent) {
431             return insets;
432         }
433         for (int i = controls.size() - 1; i >= 0; i--) {
434             final InsetsSourceControl control  = controls.valueAt(i);
435             if (control == null) {
436                 // control may be null if it got revoked.
437                 continue;
438             }
439             if (state == null
440                     || state.isSourceOrDefaultVisible(control.getId(), control.getType())) {
441                 insets = Insets.max(insets, control.getInsetsHint());
442             }
443         }
444         return insets;
445     }
446 
sanitize(Insets insets)447     private Insets sanitize(Insets insets) {
448         if (insets == null) {
449             insets = getCurrentInsets();
450         }
451         if (hasZeroInsetsIme()) {
452             return insets;
453         }
454         return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
455     }
456 
sanitize(float alpha)457     private static float sanitize(float alpha) {
458         return alpha >= 1 ? 1 : (alpha <= 0 ? 0 : alpha);
459     }
460 
updateLeashesForSide(@nternalInsetsSide int side, int offset, ArrayList<SurfaceParams> surfaceParams, @Nullable InsetsState outState, float alpha)461     private void updateLeashesForSide(@InternalInsetsSide int side, int offset,
462             ArrayList<SurfaceParams> surfaceParams, @Nullable InsetsState outState, float alpha) {
463         final ArraySet<InsetsSourceControl> controls = mSideControlsMap.get(side);
464         if (controls == null) {
465             return;
466         }
467         // TODO: Implement behavior when inset spans over multiple types
468         for (int i = controls.size() - 1; i >= 0; i--) {
469             final InsetsSourceControl control = controls.valueAt(i);
470             final InsetsSource source = mInitialInsetsState.peekSource(control.getId());
471             final SurfaceControl leash = control.getLeash();
472 
473             mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y);
474             if (source != null) {
475                 mTmpFrame.set(source.getFrame());
476             }
477             addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame);
478 
479             // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is animated from
480             // the hidden state.
481             final boolean visible = mPendingFraction == 0
482                     ? mAnimationType != ANIMATION_TYPE_SHOW
483                     : !mFinished || mShownOnFinish;
484 
485             if (outState != null && source != null) {
486                 outState.addSource(new InsetsSource(source)
487                         .setVisible(visible)
488                         .setFrame(mTmpFrame));
489             }
490 
491             // If the system is controlling the insets source, the leash can be null.
492             if (leash != null) {
493                 SurfaceParams params = new SurfaceParams.Builder(leash)
494                         .withAlpha(alpha)
495                         .withMatrix(mTmpMatrix)
496                         .withVisibility(visible)
497                         .build();
498                 surfaceParams.add(params);
499             }
500         }
501     }
502 
addTranslationToMatrix(@nternalInsetsSide int side, int offset, Matrix m, Rect frame)503     private void addTranslationToMatrix(@InternalInsetsSide int side, int offset, Matrix m,
504             Rect frame) {
505         final float surfaceOffset = mTranslator != null
506                 ? mTranslator.translateLengthInAppWindowToScreen(offset) : offset;
507         switch (side) {
508             case SIDE_LEFT:
509                 m.postTranslate(-surfaceOffset, 0);
510                 frame.offset(-offset, 0);
511                 break;
512             case SIDE_TOP:
513                 m.postTranslate(0, -surfaceOffset);
514                 frame.offset(0, -offset);
515                 break;
516             case SIDE_RIGHT:
517                 m.postTranslate(surfaceOffset, 0);
518                 frame.offset(offset, 0);
519                 break;
520             case SIDE_BOTTOM:
521                 m.postTranslate(0, surfaceOffset);
522                 frame.offset(0, offset);
523                 break;
524         }
525     }
526 
buildSideControlsMap(SparseIntArray idSideMap, SparseSetArray<InsetsSourceControl> sideControlsMap, SparseArray<InsetsSourceControl> controls)527     private static void buildSideControlsMap(SparseIntArray idSideMap,
528             SparseSetArray<InsetsSourceControl> sideControlsMap,
529             SparseArray<InsetsSourceControl> controls) {
530         for (int i = idSideMap.size() - 1; i >= 0; i--) {
531             final int type = idSideMap.keyAt(i);
532             final int side = idSideMap.valueAt(i);
533             final InsetsSourceControl control = controls.get(type);
534             if (control == null) {
535                 // If the types that we are controlling are less than the types that the system has,
536                 // there can be some null controllers.
537                 continue;
538             }
539             sideControlsMap.add(side, control);
540         }
541     }
542 
buildSideControlsMap( SparseSetArray<InsetsSourceControl> sideControlsMap, SparseArray<InsetsSourceControl> controls)543     private static void buildSideControlsMap(
544             SparseSetArray<InsetsSourceControl> sideControlsMap,
545             SparseArray<InsetsSourceControl> controls) {
546         for (int i = controls.size() - 1; i >= 0; i--) {
547             final InsetsSourceControl control  = controls.valueAt(i);
548             if (control == null) {
549                 // control may be null if it got revoked.
550                 continue;
551             }
552             @InternalInsetsSide int side = InsetsSource.getInsetSide(control.getInsetsHint());
553             if (side == SIDE_NONE && control.getType() == WindowInsets.Type.ime()) {
554                 // IME might not provide insets when it is fullscreen or floating.
555                 side = SIDE_BOTTOM;
556             }
557             sideControlsMap.add(side, control);
558         }
559     }
560 }
561