blob: 3c93bb7a59b242cb35e796a54613f56b34bf767d [file] [log] [blame]
Jorim Jaggif96c90a2018-09-26 16:55:15 +02001/*
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
17package android.view;
18
Tiger Huang332793b2019-10-29 23:21:27 +080019import static android.view.InsetsState.ITYPE_IME;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +010020import static android.view.InsetsState.toPublicType;
21import static android.view.WindowInsets.Type.all;
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080022
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080023import android.animation.Animator;
24import android.animation.AnimatorListenerAdapter;
25import android.animation.ObjectAnimator;
26import android.animation.TypeEvaluator;
27import android.annotation.IntDef;
Jorim Jaggib6030952018-10-23 18:31:52 +020028import android.annotation.NonNull;
Jorim Jaggi02a741f2018-12-12 17:40:19 -080029import android.graphics.Insets;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020030import android.graphics.Rect;
Jorim Jaggie35c0592018-11-06 16:21:08 +010031import android.os.RemoteException;
Jorim Jaggib6030952018-10-23 18:31:52 +020032import android.util.ArraySet;
Jorim Jaggie35c0592018-11-06 16:21:08 +010033import android.util.Log;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080034import android.util.Pair;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080035import android.util.Property;
Jorim Jaggib6030952018-10-23 18:31:52 +020036import android.util.SparseArray;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080037import android.view.InsetsSourceConsumer.ShowResult;
Tiger Huang332793b2019-10-29 23:21:27 +080038import android.view.InsetsState.InternalInsetsType;
Jorim Jaggib6030952018-10-23 18:31:52 +020039import android.view.SurfaceControl.Transaction;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080040import android.view.WindowInsets.Type;
Tiger Huang332793b2019-10-29 23:21:27 +080041import android.view.WindowInsets.Type.InsetsType;
Jorim Jaggi79742592019-01-18 17:36:10 +010042import android.view.animation.Interpolator;
43import android.view.animation.PathInterpolator;
Jorim Jaggib6030952018-10-23 18:31:52 +020044
45import com.android.internal.annotations.VisibleForTesting;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020046
47import java.io.PrintWriter;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +010048import java.util.ArrayList;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020049
50/**
51 * Implements {@link WindowInsetsController} on the client.
Jorim Jaggib6030952018-10-23 18:31:52 +020052 * @hide
Jorim Jaggif96c90a2018-09-26 16:55:15 +020053 */
Jorim Jaggib6030952018-10-23 18:31:52 +020054public class InsetsController implements WindowInsetsController {
Jorim Jaggif96c90a2018-09-26 16:55:15 +020055
Jorim Jaggi79742592019-01-18 17:36:10 +010056 private static final int ANIMATION_DURATION_SHOW_MS = 275;
57 private static final int ANIMATION_DURATION_HIDE_MS = 340;
58 private static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080059 private static final int DIRECTION_NONE = 0;
60 private static final int DIRECTION_SHOW = 1;
61 private static final int DIRECTION_HIDE = 2;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080062
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080063 @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE})
64 private @interface AnimationDirection{}
65
66 /**
67 * Translation animation evaluator.
68 */
69 private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of(
Jorim Jaggi956ca412019-01-07 14:49:14 +010070 (int) (startValue.left + fraction * (endValue.left - startValue.left)),
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080071 (int) (startValue.top + fraction * (endValue.top - startValue.top)),
Jorim Jaggi956ca412019-01-07 14:49:14 +010072 (int) (startValue.right + fraction * (endValue.right - startValue.right)),
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080073 (int) (startValue.bottom + fraction * (endValue.bottom - startValue.bottom)));
74
75 /**
76 * Linear animation property
77 */
78 private static class InsetsProperty extends Property<WindowInsetsAnimationController, Insets> {
79 InsetsProperty() {
80 super(Insets.class, "Insets");
81 }
82
83 @Override
84 public Insets get(WindowInsetsAnimationController object) {
85 return object.getCurrentInsets();
86 }
87 @Override
88 public void set(WindowInsetsAnimationController object, Insets value) {
89 object.changeInsets(value);
90 }
91 }
92
Jorim Jaggie35c0592018-11-06 16:21:08 +010093 private final String TAG = "InsetsControllerImpl";
94
Jorim Jaggif96c90a2018-09-26 16:55:15 +020095 private final InsetsState mState = new InsetsState();
Jorim Jaggie35c0592018-11-06 16:21:08 +010096 private final InsetsState mTmpState = new InsetsState();
97
Jorim Jaggif96c90a2018-09-26 16:55:15 +020098 private final Rect mFrame = new Rect();
Jorim Jaggib6030952018-10-23 18:31:52 +020099 private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
Jorim Jaggic8d60382018-10-31 17:06:06 +0100100 private final ViewRootImpl mViewRoot;
Jorim Jaggib6030952018-10-23 18:31:52 +0200101
102 private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100103 private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100104 private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800105 private WindowInsets mLastInsets;
106
107 private boolean mAnimCallbackScheduled;
108
109 private final Runnable mAnimCallback;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200110
Jorim Jaggi73f3e8a2019-01-14 13:06:23 +0100111 private final Rect mLastLegacyContentInsets = new Rect();
112 private final Rect mLastLegacyStableInsets = new Rect();
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800113 private @AnimationDirection int mAnimationDirection;
Jorim Jaggi73f3e8a2019-01-14 13:06:23 +0100114
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800115 private int mPendingTypesToShow;
116
Jorim Jaggi648e5882019-01-24 13:24:02 +0100117 private int mLastLegacySoftInputMode;
118
Jorim Jaggic8d60382018-10-31 17:06:06 +0100119 public InsetsController(ViewRootImpl viewRoot) {
120 mViewRoot = viewRoot;
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800121 mAnimCallback = () -> {
122 mAnimCallbackScheduled = false;
123 if (mAnimationControls.isEmpty()) {
124 return;
125 }
126
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100127 mTmpFinishedControls.clear();
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800128 InsetsState state = new InsetsState(mState, true /* copySources */);
129 for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100130 InsetsAnimationControlImpl control = mAnimationControls.get(i);
131 if (mAnimationControls.get(i).applyChangeInsets(state)) {
132 mTmpFinishedControls.add(control);
133 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800134 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100135
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800136 WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
Brad Stenninge0573692019-03-11 13:52:46 -0700137 mLastInsets.shouldAlwaysConsumeSystemBars(), mLastInsets.getDisplayCutout(),
Jorim Jaggi648e5882019-01-24 13:24:02 +0100138 mLastLegacyContentInsets, mLastLegacyStableInsets, mLastLegacySoftInputMode,
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800139 null /* typeSideMap */);
140 mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100141
142 for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) {
143 dispatchAnimationFinished(mTmpFinishedControls.get(i).getAnimation());
144 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800145 };
Jorim Jaggic8d60382018-10-31 17:06:06 +0100146 }
147
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100148 @VisibleForTesting
149 public void onFrameChanged(Rect frame) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800150 if (mFrame.equals(frame)) {
151 return;
152 }
153 mViewRoot.notifyInsetsChanged();
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200154 mFrame.set(frame);
155 }
156
157 public InsetsState getState() {
158 return mState;
159 }
160
Jorim Jaggic8d60382018-10-31 17:06:06 +0100161 boolean onStateChanged(InsetsState state) {
162 if (mState.equals(state)) {
163 return false;
164 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200165 mState.set(state);
Jorim Jaggie35c0592018-11-06 16:21:08 +0100166 mTmpState.set(state, true /* copySources */);
Jorim Jaggic8d60382018-10-31 17:06:06 +0100167 applyLocalVisibilityOverride();
168 mViewRoot.notifyInsetsChanged();
Jorim Jaggie35c0592018-11-06 16:21:08 +0100169 if (!mState.equals(mTmpState)) {
170 sendStateToWindowManager();
171 }
Jorim Jaggic8d60382018-10-31 17:06:06 +0100172 return true;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200173 }
174
175 /**
176 * @see InsetsState#calculateInsets
177 */
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100178 @VisibleForTesting
179 public WindowInsets calculateInsets(boolean isScreenRound,
Brad Stenninge0573692019-03-11 13:52:46 -0700180 boolean alwaysConsumeSystemBars, DisplayCutout cutout, Rect legacyContentInsets,
Jorim Jaggi648e5882019-01-24 13:24:02 +0100181 Rect legacyStableInsets, int legacySoftInputMode) {
Jorim Jaggi73f3e8a2019-01-14 13:06:23 +0100182 mLastLegacyContentInsets.set(legacyContentInsets);
183 mLastLegacyStableInsets.set(legacyStableInsets);
Jorim Jaggi648e5882019-01-24 13:24:02 +0100184 mLastLegacySoftInputMode = legacySoftInputMode;
Brad Stenninge0573692019-03-11 13:52:46 -0700185 mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeSystemBars, cutout,
Jorim Jaggi648e5882019-01-24 13:24:02 +0100186 legacyContentInsets, legacyStableInsets, legacySoftInputMode,
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100187 null /* typeSideMap */);
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800188 return mLastInsets;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200189 }
190
Jorim Jaggib6030952018-10-23 18:31:52 +0200191 /**
192 * Called when the server has dispatched us a new set of inset controls.
193 */
194 public void onControlsChanged(InsetsSourceControl[] activeControls) {
195 if (activeControls != null) {
196 for (InsetsSourceControl activeControl : activeControls) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800197 if (activeControl != null) {
198 // TODO(b/122982984): Figure out why it can be null.
199 mTmpControlArray.put(activeControl.getType(), activeControl);
200 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200201 }
202 }
203
204 // Ensure to update all existing source consumers
205 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
206 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
207 final InsetsSourceControl control = mTmpControlArray.get(consumer.getType());
208
209 // control may be null, but we still need to update the control to null if it got
210 // revoked.
211 consumer.setControl(control);
212 }
213
214 // Ensure to create source consumers if not available yet.
215 for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
216 final InsetsSourceControl control = mTmpControlArray.valueAt(i);
217 getSourceConsumer(control.getType()).setControl(control);
218 }
219 mTmpControlArray.clear();
220 }
221
222 @Override
Tiger Huang332793b2019-10-29 23:21:27 +0800223 public void show(@InsetsType int types) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800224 show(types, false /* fromIme */);
225 }
226
Tiger Huang332793b2019-10-29 23:21:27 +0800227 void show(@InsetsType int types, boolean fromIme) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800228 // TODO: Support a ResultReceiver for IME.
229 // TODO(b/123718661): Make show() work for multi-session IME.
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800230 int typesReady = 0;
Jorim Jaggib6030952018-10-23 18:31:52 +0200231 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
232 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800233 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
234 if (mAnimationDirection == DIRECTION_HIDE) {
Tiger Huang332793b2019-10-29 23:21:27 +0800235 // Only one animator (with multiple InsetsType) can run at a time.
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800236 // previous one should be cancelled for simplicity.
237 cancelExistingAnimation();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800238 } else if (consumer.isVisible()
239 && (mAnimationDirection == DIRECTION_NONE
240 || mAnimationDirection == DIRECTION_HIDE)) {
241 // no-op: already shown or animating in (because window visibility is
242 // applied before starting animation).
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800243 // TODO: When we have more than one types: handle specific case when
244 // show animation is going on, but the current type is not becoming visible.
245 continue;
246 }
247 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggib6030952018-10-23 18:31:52 +0200248 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800249 applyAnimation(typesReady, true /* show */, fromIme);
Jorim Jaggib6030952018-10-23 18:31:52 +0200250 }
251
252 @Override
Tiger Huang332793b2019-10-29 23:21:27 +0800253 public void hide(@InsetsType int types) {
Taran Singhd7fc5862019-10-10 14:45:17 +0200254 hide(types, false /* fromIme */);
255 }
256
Tiger Huang332793b2019-10-29 23:21:27 +0800257 void hide(@InsetsType int types, boolean fromIme) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800258 int typesReady = 0;
Jorim Jaggib6030952018-10-23 18:31:52 +0200259 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
260 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800261 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
262 if (mAnimationDirection == DIRECTION_SHOW) {
263 cancelExistingAnimation();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800264 } else if (!consumer.isVisible()
265 && (mAnimationDirection == DIRECTION_NONE
266 || mAnimationDirection == DIRECTION_HIDE)) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800267 // no-op: already hidden or animating out.
268 continue;
269 }
270 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggib6030952018-10-23 18:31:52 +0200271 }
Taran Singhd7fc5862019-10-10 14:45:17 +0200272 applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
Jorim Jaggib6030952018-10-23 18:31:52 +0200273 }
274
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100275 @Override
Tiger Huang332793b2019-10-29 23:21:27 +0800276 public void controlWindowInsetsAnimation(@InsetsType int types,
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100277 WindowInsetsAnimationControlListener listener) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800278 controlWindowInsetsAnimation(types, listener, false /* fromIme */);
279 }
280
Tiger Huang332793b2019-10-29 23:21:27 +0800281 private void controlWindowInsetsAnimation(@InsetsType int types,
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800282 WindowInsetsAnimationControlListener listener, boolean fromIme) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800283 // If the frame of our window doesn't span the entire display, the control API makes very
284 // little sense, as we don't deal with negative insets. So just cancel immediately.
285 if (!mState.getDisplayFrame().equals(mFrame)) {
286 listener.onCancelled();
287 return;
288 }
289 controlAnimationUnchecked(types, listener, mFrame, fromIme);
290 }
291
Tiger Huang332793b2019-10-29 23:21:27 +0800292 private void controlAnimationUnchecked(@InsetsType int types,
Tarandeep Singha6f35612019-01-11 19:50:46 -0800293 WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800294 if (types == 0) {
295 // nothing to animate.
296 return;
297 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100298 cancelExistingControllers(types);
299
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100300 final ArraySet<Integer> internalTypes = mState.toInternalType(types);
301 final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800302
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700303 Pair<Integer, Boolean> typesReadyPair = collectConsumers(
304 fromIme, internalTypes, consumers, listener);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800305 int typesReady = typesReadyPair.first;
306 boolean isReady = typesReadyPair.second;
307 if (!isReady) {
308 // IME isn't ready, all requested types would be shown once IME is ready.
309 mPendingTypesToShow = typesReady;
310 // TODO: listener for pending types.
311 return;
312 }
313
314 // pending types from previous request.
315 typesReady = collectPendingConsumers(typesReady, consumers);
316
317 if (typesReady == 0) {
318 listener.onCancelled();
319 return;
320 }
321
322 final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
Tarandeep Singha6f35612019-01-11 19:50:46 -0800323 frame, mState, listener, typesReady,
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800324 () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
325 mAnimationControls.add(controller);
326 }
327
328 /**
329 * @return Pair of (types ready to animate, is ready to animate).
330 */
331 private Pair<Integer, Boolean> collectConsumers(boolean fromIme,
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700332 ArraySet<Integer> internalTypes, SparseArray<InsetsSourceConsumer> consumers,
333 WindowInsetsAnimationControlListener listener) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800334 int typesReady = 0;
335 boolean isReady = true;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100336 for (int i = internalTypes.size() - 1; i >= 0; i--) {
337 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
Taran Singhd7fc5862019-10-10 14:45:17 +0200338 boolean setVisible = !consumer.isVisible();
339 if (setVisible) {
340 // Show request
341 switch(consumer.requestShow(fromIme)) {
342 case ShowResult.SHOW_IMMEDIATELY:
343 typesReady |= InsetsState.toPublicType(consumer.getType());
344 break;
345 case ShowResult.SHOW_DELAYED:
346 isReady = false;
347 break;
348 case ShowResult.SHOW_FAILED:
349 // IME cannot be shown (since it didn't have focus), proceed
350 // with animation of other types.
351 if (mPendingTypesToShow != 0) {
352 // remove IME from pending because view no longer has focus.
Tiger Huang332793b2019-10-29 23:21:27 +0800353 mPendingTypesToShow &= ~InsetsState.toPublicType(ITYPE_IME);
Taran Singhd7fc5862019-10-10 14:45:17 +0200354 }
355 break;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800356 }
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100357 } else {
Taran Singhd7fc5862019-10-10 14:45:17 +0200358 // Hide request
359 // TODO: Move notifyHidden() to beginning of the hide animation
360 // (when visibility actually changes using hideDirectly()).
361 if (!fromIme) {
362 consumer.notifyHidden();
363 }
364 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100365 }
Taran Singhd7fc5862019-10-10 14:45:17 +0200366 consumers.put(consumer.getType(), consumer);
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100367 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800368 return new Pair<>(typesReady, isReady);
369 }
370
Tiger Huang332793b2019-10-29 23:21:27 +0800371 private int collectPendingConsumers(@InsetsType int typesReady,
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800372 SparseArray<InsetsSourceConsumer> consumers) {
373 if (mPendingTypesToShow != 0) {
374 typesReady |= mPendingTypesToShow;
375 final ArraySet<Integer> internalTypes = mState.toInternalType(mPendingTypesToShow);
376 for (int i = internalTypes.size() - 1; i >= 0; i--) {
377 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
378 consumers.put(consumer.getType(), consumer);
379 }
380 mPendingTypesToShow = 0;
381 }
382 return typesReady;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100383 }
384
Tiger Huang332793b2019-10-29 23:21:27 +0800385 private void cancelExistingControllers(@InsetsType int types) {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100386 for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
387 InsetsAnimationControlImpl control = mAnimationControls.get(i);
388 if ((control.getTypes() & types) != 0) {
389 cancelAnimation(control);
390 }
391 }
392 }
393
394 @VisibleForTesting
395 public void notifyFinished(InsetsAnimationControlImpl controller, int shownTypes) {
396 mAnimationControls.remove(controller);
397 hideDirectly(controller.getTypes() & ~shownTypes);
398 showDirectly(controller.getTypes() & shownTypes);
399 }
400
401 void notifyControlRevoked(InsetsSourceConsumer consumer) {
402 for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
403 InsetsAnimationControlImpl control = mAnimationControls.get(i);
404 if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
405 cancelAnimation(control);
406 }
407 }
408 }
409
410 private void cancelAnimation(InsetsAnimationControlImpl control) {
411 control.onCancelled();
412 mAnimationControls.remove(control);
413 }
414
Jorim Jaggic8d60382018-10-31 17:06:06 +0100415 private void applyLocalVisibilityOverride() {
416 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
417 final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
418 controller.applyLocalVisibilityOverride();
419 }
420 }
421
Jorim Jaggib6030952018-10-23 18:31:52 +0200422 @VisibleForTesting
Tiger Huang332793b2019-10-29 23:21:27 +0800423 public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetsType int type) {
Jorim Jaggib6030952018-10-23 18:31:52 +0200424 InsetsSourceConsumer controller = mSourceConsumers.get(type);
425 if (controller != null) {
426 return controller;
427 }
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800428 controller = createConsumerOfType(type);
Jorim Jaggib6030952018-10-23 18:31:52 +0200429 mSourceConsumers.put(type, controller);
430 return controller;
431 }
432
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100433 @VisibleForTesting
434 public void notifyVisibilityChanged() {
Jorim Jaggic8d60382018-10-31 17:06:06 +0100435 mViewRoot.notifyInsetsChanged();
Jorim Jaggie35c0592018-11-06 16:21:08 +0100436 sendStateToWindowManager();
437 }
438
439 /**
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800440 * Called when current window gains focus.
441 */
442 public void onWindowFocusGained() {
Tiger Huang332793b2019-10-29 23:21:27 +0800443 getSourceConsumer(ITYPE_IME).onWindowFocusGained();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800444 }
445
446 /**
447 * Called when current window loses focus.
448 */
449 public void onWindowFocusLost() {
Tiger Huang332793b2019-10-29 23:21:27 +0800450 getSourceConsumer(ITYPE_IME).onWindowFocusLost();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800451 }
452
453 ViewRootImpl getViewRoot() {
454 return mViewRoot;
455 }
456
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800457 /**
458 * Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden.
459 * @hide
460 */
461 @VisibleForTesting
462 public void applyImeVisibility(boolean setVisible) {
463 if (setVisible) {
464 show(Type.IME, true /* fromIme */);
465 } else {
466 hide(Type.IME);
467 }
468 }
469
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800470 private InsetsSourceConsumer createConsumerOfType(int type) {
Tiger Huang332793b2019-10-29 23:21:27 +0800471 if (type == ITYPE_IME) {
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800472 return new ImeInsetsSourceConsumer(mState, Transaction::new, this);
473 } else {
474 return new InsetsSourceConsumer(type, mState, Transaction::new, this);
475 }
476 }
477
478 /**
Jorim Jaggie35c0592018-11-06 16:21:08 +0100479 * Sends the local visibility state back to window manager.
480 */
481 private void sendStateToWindowManager() {
482 InsetsState tmpState = new InsetsState();
483 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
484 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
485 if (consumer.getControl() != null) {
486 tmpState.addSource(mState.getSource(consumer.getType()));
487 }
488 }
489
490 // TODO: Put this on a dispatcher thread.
491 try {
492 mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState);
493 } catch (RemoteException e) {
494 Log.e(TAG, "Failed to call insetsModified", e);
495 }
Jorim Jaggic8d60382018-10-31 17:06:06 +0100496 }
497
Tiger Huang332793b2019-10-29 23:21:27 +0800498 private void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800499 if (types == 0) {
500 // nothing to animate.
501 return;
502 }
Tarandeep Singha6f35612019-01-11 19:50:46 -0800503
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800504 WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100505
506 private WindowInsetsAnimationController mController;
507 private ObjectAnimator mAnimator;
508
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800509 @Override
510 public void onReady(WindowInsetsAnimationController controller, int types) {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100511 mController = controller;
Jorim Jaggi67684882019-01-22 17:36:34 +0100512 if (show) {
513 showDirectly(types);
514 } else {
515 hideDirectly(types);
516 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800517 mAnimator = ObjectAnimator.ofObject(
518 controller,
519 new InsetsProperty(),
520 sEvaluator,
521 show ? controller.getHiddenStateInsets() : controller.getShownStateInsets(),
522 show ? controller.getShownStateInsets() : controller.getHiddenStateInsets()
523 );
Jorim Jaggi79742592019-01-18 17:36:10 +0100524 mAnimator.setDuration(show
525 ? ANIMATION_DURATION_SHOW_MS
526 : ANIMATION_DURATION_HIDE_MS);
527 mAnimator.setInterpolator(INTERPOLATOR);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800528 mAnimator.addListener(new AnimatorListenerAdapter() {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800529
530 @Override
531 public void onAnimationEnd(Animator animation) {
532 onAnimationFinish();
533 }
534 });
535 mAnimator.start();
536 }
537
538 @Override
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100539 public void onCancelled() {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700540 // Animator can be null when it is cancelled before onReady() completes.
541 if (mAnimator != null) {
542 mAnimator.cancel();
543 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100544 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800545
546 private void onAnimationFinish() {
547 mAnimationDirection = DIRECTION_NONE;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100548 mController.finish(show ? types : 0);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800549 }
550 };
Tarandeep Singha6f35612019-01-11 19:50:46 -0800551
552 // Show/hide animations always need to be relative to the display frame, in order that shown
553 // and hidden state insets are correct.
554 controlAnimationUnchecked(types, listener, mState.getDisplayFrame(), fromIme);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800555 }
556
Tiger Huang332793b2019-10-29 23:21:27 +0800557 private void hideDirectly(@InsetsType int types) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800558 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
559 for (int i = internalTypes.size() - 1; i >= 0; i--) {
560 getSourceConsumer(internalTypes.valueAt(i)).hide();
561 }
562 }
563
Tiger Huang332793b2019-10-29 23:21:27 +0800564 private void showDirectly(@InsetsType int types) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800565 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
566 for (int i = internalTypes.size() - 1; i >= 0; i--) {
567 getSourceConsumer(internalTypes.valueAt(i)).show();
568 }
569 }
570
571 /**
Tiger Huang332793b2019-10-29 23:21:27 +0800572 * Cancel on-going animation to show/hide {@link InsetsType}.
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800573 */
574 @VisibleForTesting
575 public void cancelExistingAnimation() {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100576 cancelExistingControllers(all());
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800577 }
578
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200579 void dump(String prefix, PrintWriter pw) {
580 pw.println(prefix); pw.println("InsetsController:");
581 mState.dump(prefix + " ", pw);
582 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800583
Jorim Jaggifae3e272019-01-14 14:05:05 +0100584 @VisibleForTesting
585 public void dispatchAnimationStarted(WindowInsetsAnimationListener.InsetsAnimation animation) {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800586 mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation);
587 }
588
Jorim Jaggifae3e272019-01-14 14:05:05 +0100589 @VisibleForTesting
590 public void dispatchAnimationFinished(WindowInsetsAnimationListener.InsetsAnimation animation) {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800591 mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
592 }
593
Jorim Jaggifae3e272019-01-14 14:05:05 +0100594 @VisibleForTesting
595 public void scheduleApplyChangeInsets() {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800596 if (!mAnimCallbackScheduled) {
597 mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
598 mAnimCallback, null /* token*/);
599 mAnimCallbackScheduled = true;
600 }
601 }
Jorim Jaggib7848b72018-12-28 14:38:21 +0100602
603 @Override
604 public void setSystemBarsAppearance(@Appearance int appearance) {
605 if (mViewRoot.mWindowAttributes.insetsFlags.appearance != appearance) {
606 mViewRoot.mWindowAttributes.insetsFlags.appearance = appearance;
607 mViewRoot.mWindowAttributesChanged = true;
608 mViewRoot.scheduleTraversals();
609 }
610 }
611
612 @Override
613 public void setSystemBarsBehavior(@Behavior int behavior) {
614 if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) {
615 mViewRoot.mWindowAttributes.insetsFlags.behavior = behavior;
616 mViewRoot.mWindowAttributesChanged = true;
617 mViewRoot.scheduleTraversals();
618 }
619 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200620}