blob: 565846638acc09e46caca63ee2633080fbb40d8e [file] [log] [blame]
Jorim Jaggib6030952018-10-23 18:31:52 +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
Jorim Jaggi33a21832020-04-06 14:15:46 +020019import static android.view.InsetsController.ANIMATION_TYPE_NONE;
Tarandeep Singhb9538cd2020-02-20 17:51:18 -080020import static android.view.InsetsController.AnimationType;
Tiger Huang026dec42020-06-11 17:50:04 +080021import static android.view.InsetsState.getDefaultVisibility;
Yohei Yukawaf35971d2020-06-16 21:31:35 +000022import static android.view.InsetsController.DEBUG;
Jorim Jaggi3182ef12020-01-30 00:16:18 +010023import static android.view.InsetsState.toPublicType;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +010024
Adrian Roos021f4a72020-06-05 17:06:07 +020025import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
26
Tarandeep Singh46d59f02019-01-29 18:09:15 -080027import android.annotation.IntDef;
Jorim Jaggib6030952018-10-23 18:31:52 +020028import android.annotation.Nullable;
Jorim Jaggi33a21832020-04-06 14:15:46 +020029import android.graphics.Rect;
Taran Singh85661e32020-05-07 14:45:34 -070030import android.util.Log;
Tiger Huang332793b2019-10-29 23:21:27 +080031import android.view.InsetsState.InternalInsetsType;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080032import android.view.SurfaceControl.Transaction;
Jorim Jaggi3182ef12020-01-30 00:16:18 +010033import android.view.WindowInsets.Type.InsetsType;
Jorim Jaggib6030952018-10-23 18:31:52 +020034
35import com.android.internal.annotations.VisibleForTesting;
36
Tarandeep Singh46d59f02019-01-29 18:09:15 -080037import java.lang.annotation.Retention;
38import java.lang.annotation.RetentionPolicy;
Jorim Jaggib6030952018-10-23 18:31:52 +020039import java.util.function.Supplier;
40
41/**
42 * Controls the visibility and animations of a single window insets source.
43 * @hide
44 */
45public class InsetsSourceConsumer {
46
Tarandeep Singh46d59f02019-01-29 18:09:15 -080047 @Retention(RetentionPolicy.SOURCE)
Jorim Jaggid7f10ed2020-01-08 21:41:55 +010048 @IntDef(value = {ShowResult.SHOW_IMMEDIATELY, ShowResult.IME_SHOW_DELAYED, ShowResult.IME_SHOW_FAILED})
Tarandeep Singh46d59f02019-01-29 18:09:15 -080049 @interface ShowResult {
50 /**
51 * Window type is ready to be shown, will be shown immidiately.
52 */
53 int SHOW_IMMEDIATELY = 0;
54 /**
55 * Result will be delayed. Window needs to be prepared or request is not from controller.
56 * Request will be delegated to controller and may or may not be shown.
57 */
Jorim Jaggid7f10ed2020-01-08 21:41:55 +010058 int IME_SHOW_DELAYED = 1;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080059 /**
60 * Window will not be shown because one of the conditions couldn't be met.
61 * (e.g. in IME's case, when no editor is focused.)
62 */
Jorim Jaggid7f10ed2020-01-08 21:41:55 +010063 int IME_SHOW_FAILED = 2;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080064 }
65
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080066 protected final InsetsController mController;
Jorim Jaggia51168a2019-12-27 15:17:44 +010067 protected boolean mRequestedVisible;
Tarandeep Singhb9538cd2020-02-20 17:51:18 -080068 protected final InsetsState mState;
69 protected final @InternalInsetsType int mType;
70
Taran Singh85661e32020-05-07 14:45:34 -070071 private static final String TAG = "InsetsSourceConsumer";
Jorim Jaggib6030952018-10-23 18:31:52 +020072 private final Supplier<Transaction> mTransactionSupplier;
Jorim Jaggic8d60382018-10-31 17:06:06 +010073 private @Nullable InsetsSourceControl mSourceControl;
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070074 private boolean mHasWindowFocus;
Jorim Jaggi33a21832020-04-06 14:15:46 +020075 private Rect mPendingFrame;
76 private Rect mPendingVisibleFrame;
Jorim Jaggib6030952018-10-23 18:31:52 +020077
Tiger Huange480e5f2020-04-16 23:26:49 +080078 /**
79 * Indicates if we have the pending animation. When we have the control, we need to play the
80 * animation if the requested visibility is different from the current state. But if we haven't
81 * had a leash yet, we will set this flag, and play the animation once we get the leash.
82 */
83 private boolean mIsAnimationPending;
84
Tiger Huang332793b2019-10-29 23:21:27 +080085 public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state,
Jorim Jaggic8d60382018-10-31 17:06:06 +010086 Supplier<Transaction> transactionSupplier, InsetsController controller) {
Jorim Jaggib6030952018-10-23 18:31:52 +020087 mType = type;
88 mState = state;
89 mTransactionSupplier = transactionSupplier;
Jorim Jaggic8d60382018-10-31 17:06:06 +010090 mController = controller;
Tiger Huang2ab590a2020-05-19 21:55:13 +080091 mRequestedVisible = getDefaultVisibility(type);
Jorim Jaggib6030952018-10-23 18:31:52 +020092 }
93
Jorim Jaggi3182ef12020-01-30 00:16:18 +010094 /**
95 * Updates the control delivered from the server.
96
97 * @param showTypes An integer array with a single entry that determines which types a show
98 * animation should be run after setting the control.
99 * @param hideTypes An integer array with a single entry that determines which types a hide
100 * animation should be run after setting the control.
101 */
102 public void setControl(@Nullable InsetsSourceControl control,
103 @InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
Jorim Jaggic8d60382018-10-31 17:06:06 +0100104 if (mSourceControl == control) {
Jorim Jaggib6030952018-10-23 18:31:52 +0200105 return;
106 }
Jorim Jaggiee540702020-04-02 21:40:52 +0200107 SurfaceControl oldLeash = mSourceControl != null ? mSourceControl.getLeash() : null;
108
Rob Carred48db32020-03-05 13:31:01 -0800109 final InsetsSourceControl lastControl = mSourceControl;
Jorim Jaggic8d60382018-10-31 17:06:06 +0100110 mSourceControl = control;
Taran Singh85661e32020-05-07 14:45:34 -0700111 if (control != null) {
112 if (DEBUG) Log.d(TAG, String.format("setControl -> %s on %s",
113 InsetsState.typeToString(control.getType()),
114 mController.getHost().getRootViewTitle()));
115 }
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100116 // We are loosing control
117 if (mSourceControl == null) {
118 mController.notifyControlRevoked(this);
119
120 // Restore server visibility.
121 mState.getSource(getType()).setVisible(
122 mController.getLastDispatchedState().getSource(getType()).isVisible());
123 applyLocalVisibilityOverride();
Rob Carred48db32020-03-05 13:31:01 -0800124 } else {
125 // We are gaining control, and need to run an animation since previous state
126 // didn't match
Tiger Huange480e5f2020-04-16 23:26:49 +0800127 final boolean requestedVisible = isRequestedVisibleAwaitingControl();
128 final boolean needAnimation = requestedVisible != mState.getSource(mType).isVisible();
129 if (control.getLeash() != null && (needAnimation || mIsAnimationPending)) {
Taran Singh85661e32020-05-07 14:45:34 -0700130 if (DEBUG) Log.d(TAG, String.format("Gaining control in %s, requestedVisible: %b",
131 mController.getHost().getRootViewTitle(), requestedVisible));
Tiger Huange480e5f2020-04-16 23:26:49 +0800132 if (requestedVisible) {
Rob Carred48db32020-03-05 13:31:01 -0800133 showTypes[0] |= toPublicType(getType());
134 } else {
135 hideTypes[0] |= toPublicType(getType());
136 }
Tiger Huange480e5f2020-04-16 23:26:49 +0800137 mIsAnimationPending = false;
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100138 } else {
Tiger Huange480e5f2020-04-16 23:26:49 +0800139 if (needAnimation) {
140 // We need animation but we haven't had a leash yet. Set this flag that when we
141 // get the leash we can play the deferred animation.
142 mIsAnimationPending = true;
143 }
Jorim Jaggi49b9f6c2020-03-24 22:28:38 +0100144 // We are gaining control, but don't need to run an animation.
145 // However make sure that the leash visibility is still up to date.
146 if (applyLocalVisibilityOverride()) {
147 mController.notifyVisibilityChanged();
Jorim Jaggiee540702020-04-02 21:40:52 +0200148 }
149
150 // If we have a new leash, make sure visibility is up-to-date, even though we
151 // didn't want to run an animation above.
152 SurfaceControl newLeash = mSourceControl.getLeash();
153 if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) {
Jorim Jaggi49b9f6c2020-03-24 22:28:38 +0100154 applyHiddenToControl();
155 }
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100156 }
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100157 }
Rob Carred48db32020-03-05 13:31:01 -0800158 if (lastControl != null) {
Robert Carr4c101c32020-03-31 14:43:25 -0700159 lastControl.release(SurfaceControl::release);
Jorim Jaggie35c0592018-11-06 16:21:08 +0100160 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200161 }
162
163 @VisibleForTesting
164 public InsetsSourceControl getControl() {
Jorim Jaggic8d60382018-10-31 17:06:06 +0100165 return mSourceControl;
Jorim Jaggib6030952018-10-23 18:31:52 +0200166 }
167
Taran Singh9641cfc2020-04-09 16:59:10 -0700168 /**
169 * Determines if the consumer will be shown after control is available.
170 * Note: for system bars this method is same as {@link #isRequestedVisible()}.
171 *
172 * @return {@code true} if consumer has a pending show.
173 */
174 protected boolean isRequestedVisibleAwaitingControl() {
175 return isRequestedVisible();
176 }
177
Jorim Jaggib6030952018-10-23 18:31:52 +0200178 int getType() {
179 return mType;
180 }
181
182 @VisibleForTesting
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100183 public void show(boolean fromIme) {
Taran Singh85661e32020-05-07 14:45:34 -0700184 if (DEBUG) Log.d(TAG, String.format("Call show() for type: %s fromIme: %b ",
185 InsetsState.typeToString(mType), fromIme));
Jorim Jaggia51168a2019-12-27 15:17:44 +0100186 setRequestedVisible(true);
Jorim Jaggib6030952018-10-23 18:31:52 +0200187 }
188
189 @VisibleForTesting
190 public void hide() {
Taran Singh85661e32020-05-07 14:45:34 -0700191 if (DEBUG) Log.d(TAG, String.format("Call hide for %s on %s",
192 InsetsState.typeToString(mType), mController.getHost().getRootViewTitle()));
Jorim Jaggia51168a2019-12-27 15:17:44 +0100193 setRequestedVisible(false);
Jorim Jaggib6030952018-10-23 18:31:52 +0200194 }
195
Tarandeep Singhb9538cd2020-02-20 17:51:18 -0800196 void hide(boolean animationFinished, @AnimationType int animationType) {
Tarandeep Singh94c9a832020-02-03 14:55:30 -0800197 hide();
198 }
199
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800200 /**
201 * Called when current window gains focus
202 */
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700203 public void onWindowFocusGained() {
204 mHasWindowFocus = true;
205 }
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800206
207 /**
208 * Called when current window loses focus.
209 */
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700210 public void onWindowFocusLost() {
211 mHasWindowFocus = false;
212 }
213
214 boolean hasWindowFocus() {
215 return mHasWindowFocus;
216 }
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800217
Jorim Jaggie35c0592018-11-06 16:21:08 +0100218 boolean applyLocalVisibilityOverride() {
Tiger Huang2ab590a2020-05-19 21:55:13 +0800219 final InsetsSource source = mState.peekSource(mType);
220 final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType);
221 final boolean hasControl = mSourceControl != null;
222
223 // We still need to let the legacy app know the visibility change even if we don't have the
Tiger Huang026dec42020-06-11 17:50:04 +0800224 // control. If we don't have the source, we don't change the requested visibility for making
225 // the callback behavior compatible.
Tiger Huang2ab590a2020-05-19 21:55:13 +0800226 mController.updateCompatSysUiVisibility(
Tiger Huang026dec42020-06-11 17:50:04 +0800227 mType, (hasControl || source == null) ? mRequestedVisible : isVisible, hasControl);
Jorim Jaggic8d60382018-10-31 17:06:06 +0100228
229 // If we don't have control, we are not able to change the visibility.
Tiger Huang2ab590a2020-05-19 21:55:13 +0800230 if (!hasControl) {
Taran Singh85661e32020-05-07 14:45:34 -0700231 if (DEBUG) Log.d(TAG, "applyLocalVisibilityOverride: No control in "
232 + mController.getHost().getRootViewTitle()
233 + " requestedVisible " + mRequestedVisible);
Jorim Jaggie35c0592018-11-06 16:21:08 +0100234 return false;
Jorim Jaggic8d60382018-10-31 17:06:06 +0100235 }
Tiger Huang2ab590a2020-05-19 21:55:13 +0800236 if (isVisible == mRequestedVisible) {
Jorim Jaggie35c0592018-11-06 16:21:08 +0100237 return false;
238 }
Taran Singh85661e32020-05-07 14:45:34 -0700239 if (DEBUG) Log.d(TAG, String.format("applyLocalVisibilityOverride: %s requestedVisible: %b",
240 mController.getHost().getRootViewTitle(), mRequestedVisible));
Jorim Jaggia51168a2019-12-27 15:17:44 +0100241 mState.getSource(mType).setVisible(mRequestedVisible);
Jorim Jaggie35c0592018-11-06 16:21:08 +0100242 return true;
Jorim Jaggic8d60382018-10-31 17:06:06 +0100243 }
244
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800245 @VisibleForTesting
Jorim Jaggia51168a2019-12-27 15:17:44 +0100246 public boolean isRequestedVisible() {
247 return mRequestedVisible;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800248 }
249
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800250 /**
251 * Request to show current window type.
252 *
253 * @param fromController {@code true} if request is coming from controller.
254 * (e.g. in IME case, controller is
255 * {@link android.inputmethodservice.InputMethodService}).
256 * @return @see {@link ShowResult}.
257 */
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100258 @VisibleForTesting
259 public @ShowResult int requestShow(boolean fromController) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800260 return ShowResult.SHOW_IMMEDIATELY;
261 }
262
263 /**
264 * Notify listeners that window is now hidden.
265 */
266 void notifyHidden() {
267 // no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
268 }
269
Jorim Jaggia51168a2019-12-27 15:17:44 +0100270 /**
Tarandeep Singh94c9a832020-02-03 14:55:30 -0800271 * Remove surface on which this consumer type is drawn.
272 */
273 public void removeSurface() {
274 // no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
275 }
276
Adrian Roos021f4a72020-06-05 17:06:07 +0200277 @VisibleForTesting(visibility = PACKAGE)
Yohei Yukawaf35971d2020-06-16 21:31:35 +0000278 public void updateSource(InsetsSource newSource) {
Jorim Jaggi33a21832020-04-06 14:15:46 +0200279 InsetsSource source = mState.peekSource(mType);
Yohei Yukawaf35971d2020-06-16 21:31:35 +0000280 if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE
Jorim Jaggi33a21832020-04-06 14:15:46 +0200281 || source.getFrame().equals(newSource.getFrame())) {
Adrian Roos021f4a72020-06-05 17:06:07 +0200282 mPendingFrame = null;
283 mPendingVisibleFrame = null;
Jorim Jaggi33a21832020-04-06 14:15:46 +0200284 mState.addSource(newSource);
285 return;
286 }
287
288 // Frame is changing while animating. Keep note of the new frame but keep existing frame
Yohei Yukawaf35971d2020-06-16 21:31:35 +0000289 // until animaition is finished.
Jorim Jaggi33a21832020-04-06 14:15:46 +0200290 newSource = new InsetsSource(newSource);
291 mPendingFrame = new Rect(newSource.getFrame());
292 mPendingVisibleFrame = newSource.getVisibleFrame() != null
293 ? new Rect(newSource.getVisibleFrame())
294 : null;
295 newSource.setFrame(source.getFrame());
296 newSource.setVisibleFrame(source.getVisibleFrame());
297 mState.addSource(newSource);
Taran Singh85661e32020-05-07 14:45:34 -0700298 if (DEBUG) Log.d(TAG, "updateSource: " + newSource);
Jorim Jaggi33a21832020-04-06 14:15:46 +0200299 }
300
Adrian Roos021f4a72020-06-05 17:06:07 +0200301 @VisibleForTesting(visibility = PACKAGE)
302 public boolean notifyAnimationFinished() {
Jorim Jaggi33a21832020-04-06 14:15:46 +0200303 if (mPendingFrame != null) {
304 InsetsSource source = mState.getSource(mType);
305 source.setFrame(mPendingFrame);
306 source.setVisibleFrame(mPendingVisibleFrame);
307 mPendingFrame = null;
308 mPendingVisibleFrame = null;
309 return true;
310 }
311 return false;
312 }
313
Tarandeep Singh94c9a832020-02-03 14:55:30 -0800314 /**
Jorim Jaggia51168a2019-12-27 15:17:44 +0100315 * Sets requested visibility from the client, regardless of whether we are able to control it at
316 * the moment.
317 */
Taran Singh9641cfc2020-04-09 16:59:10 -0700318 protected void setRequestedVisible(boolean requestedVisible) {
Tiger Huange480e5f2020-04-16 23:26:49 +0800319 if (mRequestedVisible != requestedVisible) {
320 mRequestedVisible = requestedVisible;
321 mIsAnimationPending = false;
Taran Singh85661e32020-05-07 14:45:34 -0700322 if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
Tiger Huange480e5f2020-04-16 23:26:49 +0800323 }
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100324 if (applyLocalVisibilityOverride()) {
325 mController.notifyVisibilityChanged();
326 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200327 }
328
329 private void applyHiddenToControl() {
Jorim Jaggi026ed752020-01-29 00:30:24 +0100330 if (mSourceControl == null || mSourceControl.getLeash() == null) {
Jorim Jaggib6030952018-10-23 18:31:52 +0200331 return;
332 }
333
Jorim Jaggib6030952018-10-23 18:31:52 +0200334 final Transaction t = mTransactionSupplier.get();
Taran Singh85661e32020-05-07 14:45:34 -0700335 if (DEBUG) Log.d(TAG, "applyHiddenToControl: " + mRequestedVisible);
Jorim Jaggia51168a2019-12-27 15:17:44 +0100336 if (mRequestedVisible) {
Jorim Jaggic8d60382018-10-31 17:06:06 +0100337 t.show(mSourceControl.getLeash());
Jorim Jaggie35c0592018-11-06 16:21:08 +0100338 } else {
339 t.hide(mSourceControl.getLeash());
Jorim Jaggib6030952018-10-23 18:31:52 +0200340 }
341 t.apply();
342 }
343}