blob: 40e6f4b2fce8b65ee2ae680d30f3090b8524bdcd [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;
Yohei Yukawaf35971d2020-06-16 21:31:35 +000021import static android.view.InsetsController.DEBUG;
Tiger Huang618dbe022020-06-19 00:12:55 +080022import static android.view.InsetsState.getDefaultVisibility;
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 /**
Adrian Roosc22eec92020-06-12 18:48:10 +0200264 * Reports that this source's perceptibility has changed
265 *
266 * @param perceptible true if the source is perceptible, false otherwise.
267 * @see InsetsAnimationControlCallbacks#reportPerceptible
268 */
269 public void onPerceptible(boolean perceptible) {
270 }
271
272 /**
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800273 * Notify listeners that window is now hidden.
274 */
275 void notifyHidden() {
276 // no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
277 }
278
Jorim Jaggia51168a2019-12-27 15:17:44 +0100279 /**
Tarandeep Singh94c9a832020-02-03 14:55:30 -0800280 * Remove surface on which this consumer type is drawn.
281 */
282 public void removeSurface() {
283 // no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
284 }
285
Adrian Roos021f4a72020-06-05 17:06:07 +0200286 @VisibleForTesting(visibility = PACKAGE)
Tiger Huang618dbe022020-06-19 00:12:55 +0800287 public void updateSource(InsetsSource newSource, @AnimationType int animationType) {
Jorim Jaggi33a21832020-04-06 14:15:46 +0200288 InsetsSource source = mState.peekSource(mType);
Tiger Huang618dbe022020-06-19 00:12:55 +0800289 if (source == null || animationType == ANIMATION_TYPE_NONE
Jorim Jaggi33a21832020-04-06 14:15:46 +0200290 || source.getFrame().equals(newSource.getFrame())) {
Adrian Roos021f4a72020-06-05 17:06:07 +0200291 mPendingFrame = null;
292 mPendingVisibleFrame = null;
Jorim Jaggi33a21832020-04-06 14:15:46 +0200293 mState.addSource(newSource);
294 return;
295 }
296
297 // Frame is changing while animating. Keep note of the new frame but keep existing frame
Tiger Huang618dbe022020-06-19 00:12:55 +0800298 // until animation is finished.
Jorim Jaggi33a21832020-04-06 14:15:46 +0200299 newSource = new InsetsSource(newSource);
300 mPendingFrame = new Rect(newSource.getFrame());
301 mPendingVisibleFrame = newSource.getVisibleFrame() != null
302 ? new Rect(newSource.getVisibleFrame())
303 : null;
304 newSource.setFrame(source.getFrame());
305 newSource.setVisibleFrame(source.getVisibleFrame());
306 mState.addSource(newSource);
Taran Singh85661e32020-05-07 14:45:34 -0700307 if (DEBUG) Log.d(TAG, "updateSource: " + newSource);
Jorim Jaggi33a21832020-04-06 14:15:46 +0200308 }
309
Adrian Roos021f4a72020-06-05 17:06:07 +0200310 @VisibleForTesting(visibility = PACKAGE)
311 public boolean notifyAnimationFinished() {
Jorim Jaggi33a21832020-04-06 14:15:46 +0200312 if (mPendingFrame != null) {
313 InsetsSource source = mState.getSource(mType);
314 source.setFrame(mPendingFrame);
315 source.setVisibleFrame(mPendingVisibleFrame);
316 mPendingFrame = null;
317 mPendingVisibleFrame = null;
318 return true;
319 }
320 return false;
321 }
322
Tarandeep Singh94c9a832020-02-03 14:55:30 -0800323 /**
Jorim Jaggia51168a2019-12-27 15:17:44 +0100324 * Sets requested visibility from the client, regardless of whether we are able to control it at
325 * the moment.
326 */
Taran Singh9641cfc2020-04-09 16:59:10 -0700327 protected void setRequestedVisible(boolean requestedVisible) {
Tiger Huange480e5f2020-04-16 23:26:49 +0800328 if (mRequestedVisible != requestedVisible) {
329 mRequestedVisible = requestedVisible;
330 mIsAnimationPending = false;
Taran Singh85661e32020-05-07 14:45:34 -0700331 if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
Tiger Huange480e5f2020-04-16 23:26:49 +0800332 }
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100333 if (applyLocalVisibilityOverride()) {
334 mController.notifyVisibilityChanged();
335 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200336 }
337
338 private void applyHiddenToControl() {
Jorim Jaggi026ed752020-01-29 00:30:24 +0100339 if (mSourceControl == null || mSourceControl.getLeash() == null) {
Jorim Jaggib6030952018-10-23 18:31:52 +0200340 return;
341 }
342
Jorim Jaggib6030952018-10-23 18:31:52 +0200343 final Transaction t = mTransactionSupplier.get();
Taran Singh85661e32020-05-07 14:45:34 -0700344 if (DEBUG) Log.d(TAG, "applyHiddenToControl: " + mRequestedVisible);
Jorim Jaggia51168a2019-12-27 15:17:44 +0100345 if (mRequestedVisible) {
Jorim Jaggic8d60382018-10-31 17:06:06 +0100346 t.show(mSourceControl.getLeash());
Jorim Jaggie35c0592018-11-06 16:21:08 +0100347 } else {
348 t.hide(mSourceControl.getLeash());
Jorim Jaggib6030952018-10-23 18:31:52 +0200349 }
350 t.apply();
Adrian Roosc22eec92020-06-12 18:48:10 +0200351 onPerceptible(mRequestedVisible);
Jorim Jaggib6030952018-10-23 18:31:52 +0200352 }
353}