blob: 258600019e711c58864d5958cd63ea435aca2ca5 [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;
20
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080021import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.animation.ObjectAnimator;
24import android.animation.TypeEvaluator;
25import android.annotation.IntDef;
Jorim Jaggib6030952018-10-23 18:31:52 +020026import android.annotation.NonNull;
Jorim Jaggi02a741f2018-12-12 17:40:19 -080027import android.graphics.Insets;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020028import android.graphics.Rect;
Jorim Jaggie35c0592018-11-06 16:21:08 +010029import android.os.RemoteException;
Jorim Jaggib6030952018-10-23 18:31:52 +020030import android.util.ArraySet;
Jorim Jaggie35c0592018-11-06 16:21:08 +010031import android.util.Log;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080032import android.util.Pair;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080033import android.util.Property;
Jorim Jaggib6030952018-10-23 18:31:52 +020034import android.util.SparseArray;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080035import android.view.InsetsSourceConsumer.ShowResult;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080036import android.view.InsetsState.InternalInsetType;
Jorim Jaggib6030952018-10-23 18:31:52 +020037import android.view.SurfaceControl.Transaction;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080038import android.view.WindowInsets.Type;
Jorim Jaggib6030952018-10-23 18:31:52 +020039import android.view.WindowInsets.Type.InsetType;
Jorim Jaggi79742592019-01-18 17:36:10 +010040import android.view.animation.Interpolator;
41import android.view.animation.PathInterpolator;
Jorim Jaggib6030952018-10-23 18:31:52 +020042
43import com.android.internal.annotations.VisibleForTesting;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020044
45import java.io.PrintWriter;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +010046import java.util.ArrayList;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020047
48/**
49 * Implements {@link WindowInsetsController} on the client.
Jorim Jaggib6030952018-10-23 18:31:52 +020050 * @hide
Jorim Jaggif96c90a2018-09-26 16:55:15 +020051 */
Jorim Jaggib6030952018-10-23 18:31:52 +020052public class InsetsController implements WindowInsetsController {
Jorim Jaggif96c90a2018-09-26 16:55:15 +020053
Jorim Jaggi79742592019-01-18 17:36:10 +010054 private static final int ANIMATION_DURATION_SHOW_MS = 275;
55 private static final int ANIMATION_DURATION_HIDE_MS = 340;
56 private static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080057 private static final int DIRECTION_NONE = 0;
58 private static final int DIRECTION_SHOW = 1;
59 private static final int DIRECTION_HIDE = 2;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080060
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080061 @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE})
62 private @interface AnimationDirection{}
63
64 /**
65 * Translation animation evaluator.
66 */
67 private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of(
68 0,
69 (int) (startValue.top + fraction * (endValue.top - startValue.top)),
70 0,
71 (int) (startValue.bottom + fraction * (endValue.bottom - startValue.bottom)));
72
73 /**
74 * Linear animation property
75 */
76 private static class InsetsProperty extends Property<WindowInsetsAnimationController, Insets> {
77 InsetsProperty() {
78 super(Insets.class, "Insets");
79 }
80
81 @Override
82 public Insets get(WindowInsetsAnimationController object) {
83 return object.getCurrentInsets();
84 }
85 @Override
86 public void set(WindowInsetsAnimationController object, Insets value) {
87 object.changeInsets(value);
88 }
89 }
90
Jorim Jaggie35c0592018-11-06 16:21:08 +010091 private final String TAG = "InsetsControllerImpl";
92
Jorim Jaggif96c90a2018-09-26 16:55:15 +020093 private final InsetsState mState = new InsetsState();
Jorim Jaggie35c0592018-11-06 16:21:08 +010094 private final InsetsState mTmpState = new InsetsState();
95
Jorim Jaggif96c90a2018-09-26 16:55:15 +020096 private final Rect mFrame = new Rect();
Jorim Jaggib6030952018-10-23 18:31:52 +020097 private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
Jorim Jaggic8d60382018-10-31 17:06:06 +010098 private final ViewRootImpl mViewRoot;
Jorim Jaggib6030952018-10-23 18:31:52 +020099
100 private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100101 private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800102 private WindowInsets mLastInsets;
103
104 private boolean mAnimCallbackScheduled;
105
106 private final Runnable mAnimCallback;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200107
Jorim Jaggi73f3e8a2019-01-14 13:06:23 +0100108 private final Rect mLastLegacyContentInsets = new Rect();
109 private final Rect mLastLegacyStableInsets = new Rect();
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800110 private ObjectAnimator mAnimator;
111 private @AnimationDirection int mAnimationDirection;
Jorim Jaggi73f3e8a2019-01-14 13:06:23 +0100112
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800113 private int mPendingTypesToShow;
114
Jorim Jaggi648e5882019-01-24 13:24:02 +0100115 private int mLastLegacySoftInputMode;
116
Jorim Jaggic8d60382018-10-31 17:06:06 +0100117 public InsetsController(ViewRootImpl viewRoot) {
118 mViewRoot = viewRoot;
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800119 mAnimCallback = () -> {
120 mAnimCallbackScheduled = false;
121 if (mAnimationControls.isEmpty()) {
122 return;
123 }
124
125 InsetsState state = new InsetsState(mState, true /* copySources */);
126 for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
127 mAnimationControls.get(i).applyChangeInsets(state);
128 }
129 WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
130 mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
Jorim Jaggi648e5882019-01-24 13:24:02 +0100131 mLastLegacyContentInsets, mLastLegacyStableInsets, mLastLegacySoftInputMode,
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800132 null /* typeSideMap */);
133 mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
134 };
Jorim Jaggic8d60382018-10-31 17:06:06 +0100135 }
136
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200137 void onFrameChanged(Rect frame) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800138 if (mFrame.equals(frame)) {
139 return;
140 }
141 mViewRoot.notifyInsetsChanged();
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200142 mFrame.set(frame);
143 }
144
145 public InsetsState getState() {
146 return mState;
147 }
148
Jorim Jaggic8d60382018-10-31 17:06:06 +0100149 boolean onStateChanged(InsetsState state) {
150 if (mState.equals(state)) {
151 return false;
152 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200153 mState.set(state);
Jorim Jaggie35c0592018-11-06 16:21:08 +0100154 mTmpState.set(state, true /* copySources */);
Jorim Jaggic8d60382018-10-31 17:06:06 +0100155 applyLocalVisibilityOverride();
156 mViewRoot.notifyInsetsChanged();
Jorim Jaggie35c0592018-11-06 16:21:08 +0100157 if (!mState.equals(mTmpState)) {
158 sendStateToWindowManager();
159 }
Jorim Jaggic8d60382018-10-31 17:06:06 +0100160 return true;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200161 }
162
163 /**
164 * @see InsetsState#calculateInsets
165 */
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100166 @VisibleForTesting
167 public WindowInsets calculateInsets(boolean isScreenRound,
Jorim Jaggi73f3e8a2019-01-14 13:06:23 +0100168 boolean alwaysConsumeNavBar, DisplayCutout cutout, Rect legacyContentInsets,
Jorim Jaggi648e5882019-01-24 13:24:02 +0100169 Rect legacyStableInsets, int legacySoftInputMode) {
Jorim Jaggi73f3e8a2019-01-14 13:06:23 +0100170 mLastLegacyContentInsets.set(legacyContentInsets);
171 mLastLegacyStableInsets.set(legacyStableInsets);
Jorim Jaggi648e5882019-01-24 13:24:02 +0100172 mLastLegacySoftInputMode = legacySoftInputMode;
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800173 mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
Jorim Jaggi648e5882019-01-24 13:24:02 +0100174 legacyContentInsets, legacyStableInsets, legacySoftInputMode,
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100175 null /* typeSideMap */);
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800176 return mLastInsets;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200177 }
178
Jorim Jaggib6030952018-10-23 18:31:52 +0200179 /**
180 * Called when the server has dispatched us a new set of inset controls.
181 */
182 public void onControlsChanged(InsetsSourceControl[] activeControls) {
183 if (activeControls != null) {
184 for (InsetsSourceControl activeControl : activeControls) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800185 if (activeControl != null) {
186 // TODO(b/122982984): Figure out why it can be null.
187 mTmpControlArray.put(activeControl.getType(), activeControl);
188 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200189 }
190 }
191
192 // Ensure to update all existing source consumers
193 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
194 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
195 final InsetsSourceControl control = mTmpControlArray.get(consumer.getType());
196
197 // control may be null, but we still need to update the control to null if it got
198 // revoked.
199 consumer.setControl(control);
200 }
201
202 // Ensure to create source consumers if not available yet.
203 for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
204 final InsetsSourceControl control = mTmpControlArray.valueAt(i);
205 getSourceConsumer(control.getType()).setControl(control);
206 }
207 mTmpControlArray.clear();
208 }
209
210 @Override
211 public void show(@InsetType int types) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800212 show(types, false /* fromIme */);
213 }
214
215 private void show(@InsetType int types, boolean fromIme) {
216 // TODO: Support a ResultReceiver for IME.
217 // TODO(b/123718661): Make show() work for multi-session IME.
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800218 int typesReady = 0;
Jorim Jaggib6030952018-10-23 18:31:52 +0200219 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
220 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800221 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
222 if (mAnimationDirection == DIRECTION_HIDE) {
223 // Only one animator (with multiple InsetType) can run at a time.
224 // previous one should be cancelled for simplicity.
225 cancelExistingAnimation();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800226 } else if (consumer.isVisible()
227 && (mAnimationDirection == DIRECTION_NONE
228 || mAnimationDirection == DIRECTION_HIDE)) {
229 // no-op: already shown or animating in (because window visibility is
230 // applied before starting animation).
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800231 // TODO: When we have more than one types: handle specific case when
232 // show animation is going on, but the current type is not becoming visible.
233 continue;
234 }
235 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggib6030952018-10-23 18:31:52 +0200236 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800237 applyAnimation(typesReady, true /* show */, fromIme);
Jorim Jaggib6030952018-10-23 18:31:52 +0200238 }
239
240 @Override
241 public void hide(@InsetType int types) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800242 int typesReady = 0;
Jorim Jaggib6030952018-10-23 18:31:52 +0200243 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
244 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800245 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
246 if (mAnimationDirection == DIRECTION_SHOW) {
247 cancelExistingAnimation();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800248 } else if (!consumer.isVisible()
249 && (mAnimationDirection == DIRECTION_NONE
250 || mAnimationDirection == DIRECTION_HIDE)) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800251 // no-op: already hidden or animating out.
252 continue;
253 }
254 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggib6030952018-10-23 18:31:52 +0200255 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800256 applyAnimation(typesReady, false /* show */, false /* fromIme */);
Jorim Jaggib6030952018-10-23 18:31:52 +0200257 }
258
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100259 @Override
260 public void controlWindowInsetsAnimation(@InsetType int types,
261 WindowInsetsAnimationControlListener listener) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800262 controlWindowInsetsAnimation(types, listener, false /* fromIme */);
263 }
264
265 private void controlWindowInsetsAnimation(@InsetType int types,
266 WindowInsetsAnimationControlListener listener, boolean fromIme) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800267 // If the frame of our window doesn't span the entire display, the control API makes very
268 // little sense, as we don't deal with negative insets. So just cancel immediately.
269 if (!mState.getDisplayFrame().equals(mFrame)) {
270 listener.onCancelled();
271 return;
272 }
273 controlAnimationUnchecked(types, listener, mFrame, fromIme);
274 }
275
276 private void controlAnimationUnchecked(@InsetType int types,
277 WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800278 if (types == 0) {
279 // nothing to animate.
280 return;
281 }
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100282 // TODO: Check whether we already have a controller.
283 final ArraySet<Integer> internalTypes = mState.toInternalType(types);
284 final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800285
286 Pair<Integer, Boolean> typesReadyPair = collectConsumers(fromIme, internalTypes, consumers);
287 int typesReady = typesReadyPair.first;
288 boolean isReady = typesReadyPair.second;
289 if (!isReady) {
290 // IME isn't ready, all requested types would be shown once IME is ready.
291 mPendingTypesToShow = typesReady;
292 // TODO: listener for pending types.
293 return;
294 }
295
296 // pending types from previous request.
297 typesReady = collectPendingConsumers(typesReady, consumers);
298
299 if (typesReady == 0) {
300 listener.onCancelled();
301 return;
302 }
303
304 final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
Tarandeep Singha6f35612019-01-11 19:50:46 -0800305 frame, mState, listener, typesReady,
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800306 () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
307 mAnimationControls.add(controller);
308 }
309
310 /**
311 * @return Pair of (types ready to animate, is ready to animate).
312 */
313 private Pair<Integer, Boolean> collectConsumers(boolean fromIme,
314 ArraySet<Integer> internalTypes, SparseArray<InsetsSourceConsumer> consumers) {
315 int typesReady = 0;
316 boolean isReady = true;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100317 for (int i = internalTypes.size() - 1; i >= 0; i--) {
318 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
319 if (consumer.getControl() != null) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800320 if (!consumer.isVisible()) {
321 // Show request
322 switch(consumer.requestShow(fromIme)) {
323 case ShowResult.SHOW_IMMEDIATELY:
324 typesReady |= InsetsState.toPublicType(TYPE_IME);
325 break;
326 case ShowResult.SHOW_DELAYED:
327 isReady = false;
328 break;
329 case ShowResult.SHOW_FAILED:
330 // IME cannot be shown (since it didn't have focus), proceed
331 // with animation of other types.
332 if (mPendingTypesToShow != 0) {
333 // remove IME from pending because view no longer has focus.
334 mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
335 }
336 break;
337 }
338 } else {
339 // Hide request
340 // TODO: Move notifyHidden() to beginning of the hide animation
341 // (when visibility actually changes using hideDirectly()).
342 consumer.notifyHidden();
343 typesReady |= InsetsState.toPublicType(consumer.getType());
344 }
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100345 consumers.put(consumer.getType(), consumer);
346 } else {
347 // TODO: Let calling app know it's not possible, or wait
348 // TODO: Remove it from types
349 }
350 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800351 return new Pair<>(typesReady, isReady);
352 }
353
354 private int collectPendingConsumers(@InsetType int typesReady,
355 SparseArray<InsetsSourceConsumer> consumers) {
356 if (mPendingTypesToShow != 0) {
357 typesReady |= mPendingTypesToShow;
358 final ArraySet<Integer> internalTypes = mState.toInternalType(mPendingTypesToShow);
359 for (int i = internalTypes.size() - 1; i >= 0; i--) {
360 InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
361 consumers.put(consumer.getType(), consumer);
362 }
363 mPendingTypesToShow = 0;
364 }
365 return typesReady;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100366 }
367
Jorim Jaggic8d60382018-10-31 17:06:06 +0100368 private void applyLocalVisibilityOverride() {
369 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
370 final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
371 controller.applyLocalVisibilityOverride();
372 }
373 }
374
Jorim Jaggib6030952018-10-23 18:31:52 +0200375 @VisibleForTesting
376 public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetType int type) {
377 InsetsSourceConsumer controller = mSourceConsumers.get(type);
378 if (controller != null) {
379 return controller;
380 }
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800381 controller = createConsumerOfType(type);
Jorim Jaggib6030952018-10-23 18:31:52 +0200382 mSourceConsumers.put(type, controller);
383 return controller;
384 }
385
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100386 @VisibleForTesting
387 public void notifyVisibilityChanged() {
Jorim Jaggic8d60382018-10-31 17:06:06 +0100388 mViewRoot.notifyInsetsChanged();
Jorim Jaggie35c0592018-11-06 16:21:08 +0100389 sendStateToWindowManager();
390 }
391
392 /**
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800393 * Called when current window gains focus.
394 */
395 public void onWindowFocusGained() {
396 getSourceConsumer(TYPE_IME).onWindowFocusGained();
397 }
398
399 /**
400 * Called when current window loses focus.
401 */
402 public void onWindowFocusLost() {
403 getSourceConsumer(TYPE_IME).onWindowFocusLost();
404 }
405
406 ViewRootImpl getViewRoot() {
407 return mViewRoot;
408 }
409
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800410 /**
411 * Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden.
412 * @hide
413 */
414 @VisibleForTesting
415 public void applyImeVisibility(boolean setVisible) {
416 if (setVisible) {
417 show(Type.IME, true /* fromIme */);
418 } else {
419 hide(Type.IME);
420 }
421 }
422
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800423 private InsetsSourceConsumer createConsumerOfType(int type) {
424 if (type == TYPE_IME) {
425 return new ImeInsetsSourceConsumer(mState, Transaction::new, this);
426 } else {
427 return new InsetsSourceConsumer(type, mState, Transaction::new, this);
428 }
429 }
430
431 /**
Jorim Jaggie35c0592018-11-06 16:21:08 +0100432 * Sends the local visibility state back to window manager.
433 */
434 private void sendStateToWindowManager() {
435 InsetsState tmpState = new InsetsState();
436 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
437 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
438 if (consumer.getControl() != null) {
439 tmpState.addSource(mState.getSource(consumer.getType()));
440 }
441 }
442
443 // TODO: Put this on a dispatcher thread.
444 try {
445 mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState);
446 } catch (RemoteException e) {
447 Log.e(TAG, "Failed to call insetsModified", e);
448 }
Jorim Jaggic8d60382018-10-31 17:06:06 +0100449 }
450
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800451 private void applyAnimation(@InsetType final int types, boolean show, boolean fromIme) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800452 if (types == 0) {
453 // nothing to animate.
454 return;
455 }
Tarandeep Singha6f35612019-01-11 19:50:46 -0800456
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800457 WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
458 @Override
459 public void onReady(WindowInsetsAnimationController controller, int types) {
Jorim Jaggi67684882019-01-22 17:36:34 +0100460 if (show) {
461 showDirectly(types);
462 } else {
463 hideDirectly(types);
464 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800465 mAnimator = ObjectAnimator.ofObject(
466 controller,
467 new InsetsProperty(),
468 sEvaluator,
469 show ? controller.getHiddenStateInsets() : controller.getShownStateInsets(),
470 show ? controller.getShownStateInsets() : controller.getHiddenStateInsets()
471 );
Jorim Jaggi79742592019-01-18 17:36:10 +0100472 mAnimator.setDuration(show
473 ? ANIMATION_DURATION_SHOW_MS
474 : ANIMATION_DURATION_HIDE_MS);
475 mAnimator.setInterpolator(INTERPOLATOR);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800476 mAnimator.addListener(new AnimatorListenerAdapter() {
477 @Override
478 public void onAnimationCancel(Animator animation) {
479 onAnimationFinish();
480 }
481
482 @Override
483 public void onAnimationEnd(Animator animation) {
484 onAnimationFinish();
485 }
486 });
487 mAnimator.start();
488 }
489
490 @Override
491 public void onCancelled() {}
492
493 private void onAnimationFinish() {
494 mAnimationDirection = DIRECTION_NONE;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800495 }
496 };
497 // TODO: Instead of clearing this here, properly wire up
498 // InsetsAnimationControlImpl.finish() to remove this from mAnimationControls.
499 mAnimationControls.clear();
Tarandeep Singha6f35612019-01-11 19:50:46 -0800500
501 // Show/hide animations always need to be relative to the display frame, in order that shown
502 // and hidden state insets are correct.
503 controlAnimationUnchecked(types, listener, mState.getDisplayFrame(), fromIme);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800504 }
505
Jorim Jaggi67684882019-01-22 17:36:34 +0100506 private void hideDirectly(@InsetType int types) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800507 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
508 for (int i = internalTypes.size() - 1; i >= 0; i--) {
509 getSourceConsumer(internalTypes.valueAt(i)).hide();
510 }
511 }
512
Jorim Jaggi67684882019-01-22 17:36:34 +0100513 private void showDirectly(@InsetType int types) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800514 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
515 for (int i = internalTypes.size() - 1; i >= 0; i--) {
516 getSourceConsumer(internalTypes.valueAt(i)).show();
517 }
518 }
519
520 /**
521 * Cancel on-going animation to show/hide {@link InsetType}.
522 */
523 @VisibleForTesting
524 public void cancelExistingAnimation() {
525 mAnimationDirection = DIRECTION_NONE;
526 if (mAnimator != null) {
527 mAnimator.cancel();
528 }
529 }
530
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200531 void dump(String prefix, PrintWriter pw) {
532 pw.println(prefix); pw.println("InsetsController:");
533 mState.dump(prefix + " ", pw);
534 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800535
Jorim Jaggifae3e272019-01-14 14:05:05 +0100536 @VisibleForTesting
537 public void dispatchAnimationStarted(WindowInsetsAnimationListener.InsetsAnimation animation) {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800538 mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation);
539 }
540
Jorim Jaggifae3e272019-01-14 14:05:05 +0100541 @VisibleForTesting
542 public void dispatchAnimationFinished(WindowInsetsAnimationListener.InsetsAnimation animation) {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800543 mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
544 }
545
Jorim Jaggifae3e272019-01-14 14:05:05 +0100546 @VisibleForTesting
547 public void scheduleApplyChangeInsets() {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800548 if (!mAnimCallbackScheduled) {
549 mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
550 mAnimCallback, null /* token*/);
551 mAnimCallbackScheduled = true;
552 }
553 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200554}