blob: bf16e3dedd499827aa9cf09be59af70cc0e938ee [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
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080019import static android.view.InsetsState.TYPE_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;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080038import android.view.InsetsState.InternalInsetType;
Jorim Jaggib6030952018-10-23 18:31:52 +020039import android.view.SurfaceControl.Transaction;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080040import android.view.WindowInsets.Type;
Jorim Jaggib6030952018-10-23 18:31:52 +020041import android.view.WindowInsets.Type.InsetType;
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(
70 0,
71 (int) (startValue.top + fraction * (endValue.top - startValue.top)),
72 0,
73 (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
223 public void show(@InsetType int types) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800224 show(types, false /* fromIme */);
225 }
226
227 private void show(@InsetType int types, boolean fromIme) {
228 // 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) {
235 // Only one animator (with multiple InsetType) can run at a time.
236 // 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
253 public void hide(@InsetType int types) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800254 int typesReady = 0;
Jorim Jaggib6030952018-10-23 18:31:52 +0200255 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
256 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800257 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
258 if (mAnimationDirection == DIRECTION_SHOW) {
259 cancelExistingAnimation();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800260 } else if (!consumer.isVisible()
261 && (mAnimationDirection == DIRECTION_NONE
262 || mAnimationDirection == DIRECTION_HIDE)) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800263 // no-op: already hidden or animating out.
264 continue;
265 }
266 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggib6030952018-10-23 18:31:52 +0200267 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800268 applyAnimation(typesReady, false /* show */, false /* fromIme */);
Jorim Jaggib6030952018-10-23 18:31:52 +0200269 }
270
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100271 @Override
272 public void controlWindowInsetsAnimation(@InsetType int types,
273 WindowInsetsAnimationControlListener listener) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800274 controlWindowInsetsAnimation(types, listener, false /* fromIme */);
275 }
276
277 private void controlWindowInsetsAnimation(@InsetType int types,
278 WindowInsetsAnimationControlListener listener, boolean fromIme) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800279 // If the frame of our window doesn't span the entire display, the control API makes very
280 // little sense, as we don't deal with negative insets. So just cancel immediately.
281 if (!mState.getDisplayFrame().equals(mFrame)) {
282 listener.onCancelled();
283 return;
284 }
285 controlAnimationUnchecked(types, listener, mFrame, fromIme);
286 }
287
288 private void controlAnimationUnchecked(@InsetType int types,
289 WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800290 if (types == 0) {
291 // nothing to animate.
292 return;
293 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100294 cancelExistingControllers(types);
295
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100296 final ArraySet<Integer> internalTypes = mState.toInternalType(types);
297 final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800298
299 Pair<Integer, Boolean> typesReadyPair = collectConsumers(fromIme, internalTypes, consumers);
300 int typesReady = typesReadyPair.first;
301 boolean isReady = typesReadyPair.second;
302 if (!isReady) {
303 // IME isn't ready, all requested types would be shown once IME is ready.
304 mPendingTypesToShow = typesReady;
305 // TODO: listener for pending types.
306 return;
307 }
308
309 // pending types from previous request.
310 typesReady = collectPendingConsumers(typesReady, consumers);
311
312 if (typesReady == 0) {
313 listener.onCancelled();
314 return;
315 }
316
317 final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
Tarandeep Singha6f35612019-01-11 19:50:46 -0800318 frame, mState, listener, typesReady,
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800319 () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
320 mAnimationControls.add(controller);
321 }
322
323 /**
324 * @return Pair of (types ready to animate, is ready to animate).
325 */
326 private Pair<Integer, Boolean> collectConsumers(boolean fromIme,
327 ArraySet<Integer> internalTypes, SparseArray<InsetsSourceConsumer> consumers) {
328 int typesReady = 0;
329 boolean isReady = true;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100330 for (int i = internalTypes.size() - 1; i >= 0; i--) {
331 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
332 if (consumer.getControl() != null) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800333 if (!consumer.isVisible()) {
334 // Show request
335 switch(consumer.requestShow(fromIme)) {
336 case ShowResult.SHOW_IMMEDIATELY:
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100337 typesReady |= InsetsState.toPublicType(consumer.getType());
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800338 break;
339 case ShowResult.SHOW_DELAYED:
340 isReady = false;
341 break;
342 case ShowResult.SHOW_FAILED:
343 // IME cannot be shown (since it didn't have focus), proceed
344 // with animation of other types.
345 if (mPendingTypesToShow != 0) {
346 // remove IME from pending because view no longer has focus.
347 mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
348 }
349 break;
350 }
351 } else {
352 // Hide request
353 // TODO: Move notifyHidden() to beginning of the hide animation
354 // (when visibility actually changes using hideDirectly()).
355 consumer.notifyHidden();
356 typesReady |= InsetsState.toPublicType(consumer.getType());
357 }
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100358 consumers.put(consumer.getType(), consumer);
359 } else {
360 // TODO: Let calling app know it's not possible, or wait
361 // TODO: Remove it from types
362 }
363 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800364 return new Pair<>(typesReady, isReady);
365 }
366
367 private int collectPendingConsumers(@InsetType int typesReady,
368 SparseArray<InsetsSourceConsumer> consumers) {
369 if (mPendingTypesToShow != 0) {
370 typesReady |= mPendingTypesToShow;
371 final ArraySet<Integer> internalTypes = mState.toInternalType(mPendingTypesToShow);
372 for (int i = internalTypes.size() - 1; i >= 0; i--) {
373 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
374 consumers.put(consumer.getType(), consumer);
375 }
376 mPendingTypesToShow = 0;
377 }
378 return typesReady;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100379 }
380
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100381 private void cancelExistingControllers(@InsetType int types) {
382 for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
383 InsetsAnimationControlImpl control = mAnimationControls.get(i);
384 if ((control.getTypes() & types) != 0) {
385 cancelAnimation(control);
386 }
387 }
388 }
389
390 @VisibleForTesting
391 public void notifyFinished(InsetsAnimationControlImpl controller, int shownTypes) {
392 mAnimationControls.remove(controller);
393 hideDirectly(controller.getTypes() & ~shownTypes);
394 showDirectly(controller.getTypes() & shownTypes);
395 }
396
397 void notifyControlRevoked(InsetsSourceConsumer consumer) {
398 for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
399 InsetsAnimationControlImpl control = mAnimationControls.get(i);
400 if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
401 cancelAnimation(control);
402 }
403 }
404 }
405
406 private void cancelAnimation(InsetsAnimationControlImpl control) {
407 control.onCancelled();
408 mAnimationControls.remove(control);
409 }
410
Jorim Jaggic8d60382018-10-31 17:06:06 +0100411 private void applyLocalVisibilityOverride() {
412 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
413 final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
414 controller.applyLocalVisibilityOverride();
415 }
416 }
417
Jorim Jaggib6030952018-10-23 18:31:52 +0200418 @VisibleForTesting
419 public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetType int type) {
420 InsetsSourceConsumer controller = mSourceConsumers.get(type);
421 if (controller != null) {
422 return controller;
423 }
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800424 controller = createConsumerOfType(type);
Jorim Jaggib6030952018-10-23 18:31:52 +0200425 mSourceConsumers.put(type, controller);
426 return controller;
427 }
428
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100429 @VisibleForTesting
430 public void notifyVisibilityChanged() {
Jorim Jaggic8d60382018-10-31 17:06:06 +0100431 mViewRoot.notifyInsetsChanged();
Jorim Jaggie35c0592018-11-06 16:21:08 +0100432 sendStateToWindowManager();
433 }
434
435 /**
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800436 * Called when current window gains focus.
437 */
438 public void onWindowFocusGained() {
439 getSourceConsumer(TYPE_IME).onWindowFocusGained();
440 }
441
442 /**
443 * Called when current window loses focus.
444 */
445 public void onWindowFocusLost() {
446 getSourceConsumer(TYPE_IME).onWindowFocusLost();
447 }
448
449 ViewRootImpl getViewRoot() {
450 return mViewRoot;
451 }
452
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800453 /**
454 * Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden.
455 * @hide
456 */
457 @VisibleForTesting
458 public void applyImeVisibility(boolean setVisible) {
459 if (setVisible) {
460 show(Type.IME, true /* fromIme */);
461 } else {
462 hide(Type.IME);
463 }
464 }
465
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800466 private InsetsSourceConsumer createConsumerOfType(int type) {
467 if (type == TYPE_IME) {
468 return new ImeInsetsSourceConsumer(mState, Transaction::new, this);
469 } else {
470 return new InsetsSourceConsumer(type, mState, Transaction::new, this);
471 }
472 }
473
474 /**
Jorim Jaggie35c0592018-11-06 16:21:08 +0100475 * Sends the local visibility state back to window manager.
476 */
477 private void sendStateToWindowManager() {
478 InsetsState tmpState = new InsetsState();
479 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
480 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
481 if (consumer.getControl() != null) {
482 tmpState.addSource(mState.getSource(consumer.getType()));
483 }
484 }
485
486 // TODO: Put this on a dispatcher thread.
487 try {
488 mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState);
489 } catch (RemoteException e) {
490 Log.e(TAG, "Failed to call insetsModified", e);
491 }
Jorim Jaggic8d60382018-10-31 17:06:06 +0100492 }
493
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800494 private void applyAnimation(@InsetType final int types, boolean show, boolean fromIme) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800495 if (types == 0) {
496 // nothing to animate.
497 return;
498 }
Tarandeep Singha6f35612019-01-11 19:50:46 -0800499
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800500 WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100501
502 private WindowInsetsAnimationController mController;
503 private ObjectAnimator mAnimator;
504
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800505 @Override
506 public void onReady(WindowInsetsAnimationController controller, int types) {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100507 mController = controller;
Jorim Jaggi67684882019-01-22 17:36:34 +0100508 if (show) {
509 showDirectly(types);
510 } else {
511 hideDirectly(types);
512 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800513 mAnimator = ObjectAnimator.ofObject(
514 controller,
515 new InsetsProperty(),
516 sEvaluator,
517 show ? controller.getHiddenStateInsets() : controller.getShownStateInsets(),
518 show ? controller.getShownStateInsets() : controller.getHiddenStateInsets()
519 );
Jorim Jaggi79742592019-01-18 17:36:10 +0100520 mAnimator.setDuration(show
521 ? ANIMATION_DURATION_SHOW_MS
522 : ANIMATION_DURATION_HIDE_MS);
523 mAnimator.setInterpolator(INTERPOLATOR);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800524 mAnimator.addListener(new AnimatorListenerAdapter() {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800525
526 @Override
527 public void onAnimationEnd(Animator animation) {
528 onAnimationFinish();
529 }
530 });
531 mAnimator.start();
532 }
533
534 @Override
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100535 public void onCancelled() {
536 mAnimator.cancel();
537 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800538
539 private void onAnimationFinish() {
540 mAnimationDirection = DIRECTION_NONE;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100541 mController.finish(show ? types : 0);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800542 }
543 };
Tarandeep Singha6f35612019-01-11 19:50:46 -0800544
545 // Show/hide animations always need to be relative to the display frame, in order that shown
546 // and hidden state insets are correct.
547 controlAnimationUnchecked(types, listener, mState.getDisplayFrame(), fromIme);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800548 }
549
Jorim Jaggi67684882019-01-22 17:36:34 +0100550 private void hideDirectly(@InsetType int types) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800551 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
552 for (int i = internalTypes.size() - 1; i >= 0; i--) {
553 getSourceConsumer(internalTypes.valueAt(i)).hide();
554 }
555 }
556
Jorim Jaggi67684882019-01-22 17:36:34 +0100557 private void showDirectly(@InsetType 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)).show();
561 }
562 }
563
564 /**
565 * Cancel on-going animation to show/hide {@link InsetType}.
566 */
567 @VisibleForTesting
568 public void cancelExistingAnimation() {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100569 cancelExistingControllers(all());
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800570 }
571
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200572 void dump(String prefix, PrintWriter pw) {
573 pw.println(prefix); pw.println("InsetsController:");
574 mState.dump(prefix + " ", pw);
575 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800576
Jorim Jaggifae3e272019-01-14 14:05:05 +0100577 @VisibleForTesting
578 public void dispatchAnimationStarted(WindowInsetsAnimationListener.InsetsAnimation animation) {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800579 mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation);
580 }
581
Jorim Jaggifae3e272019-01-14 14:05:05 +0100582 @VisibleForTesting
583 public void dispatchAnimationFinished(WindowInsetsAnimationListener.InsetsAnimation animation) {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800584 mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
585 }
586
Jorim Jaggifae3e272019-01-14 14:05:05 +0100587 @VisibleForTesting
588 public void scheduleApplyChangeInsets() {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800589 if (!mAnimCallbackScheduled) {
590 mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
591 mAnimCallback, null /* token*/);
592 mAnimCallbackScheduled = true;
593 }
594 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200595}