blob: 9bf2e01a6bd14fe24b1ac22ba4a86a3bf248fb27 [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
Jorim Jaggi7f761872020-01-10 18:24:27 +010019import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
Jorim Jaggi90990792019-01-21 23:00:20 +010020import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070021import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
Adrian Roos11dfd272019-03-25 19:21:26 +010022import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010023import static android.view.ViewRootImpl.sNewInsetsMode;
Adrian Roos11dfd272019-03-25 19:21:26 +010024import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
Jorim Jaggi90990792019-01-21 23:00:20 +010025import static android.view.WindowInsets.Type.SIZE;
Adrian Roos11dfd272019-03-25 19:21:26 +010026import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
Tiger Huange16645a2020-02-25 22:24:39 +080027import static android.view.WindowInsets.Type.displayCutout;
Jorim Jaggi761a5ab2020-01-06 23:16:30 +010028import static android.view.WindowInsets.Type.ime;
Jorim Jaggibcf99ff2018-12-03 18:04:26 +010029import static android.view.WindowInsets.Type.indexOf;
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010030import static android.view.WindowInsets.Type.isVisibleInsetsType;
Jorim Jaggi761a5ab2020-01-06 23:16:30 +010031import static android.view.WindowInsets.Type.systemBars;
32import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
33import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
Jorim Jaggibcf99ff2018-12-03 18:04:26 +010034
Jorim Jaggif96c90a2018-09-26 16:55:15 +020035import android.annotation.IntDef;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +010036import android.annotation.Nullable;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020037import android.graphics.Insets;
38import android.graphics.Rect;
39import android.os.Parcel;
40import android.os.Parcelable;
41import android.util.ArrayMap;
Jorim Jaggib6030952018-10-23 18:31:52 +020042import android.util.ArraySet;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +010043import android.util.SparseIntArray;
Jorim Jaggib6030952018-10-23 18:31:52 +020044import android.view.WindowInsets.Type;
Tiger Huang332793b2019-10-29 23:21:27 +080045import android.view.WindowInsets.Type.InsetsType;
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010046import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020047
Yunfan Chenfae0aea2020-02-22 20:57:57 +090048import com.android.internal.annotations.VisibleForTesting;
49
Jorim Jaggif96c90a2018-09-26 16:55:15 +020050import java.io.PrintWriter;
51import java.lang.annotation.Retention;
52import java.lang.annotation.RetentionPolicy;
Jorim Jaggibfa95a72020-06-18 22:51:49 +020053import java.util.Arrays;
Tarandeep Singha6f35612019-01-11 19:50:46 -080054import java.util.Objects;
Taran Singh85661e32020-05-07 14:45:34 -070055import java.util.StringJoiner;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020056
57/**
58 * Holder for state of system windows that cause window insets for all other windows in the system.
59 * @hide
60 */
61public class InsetsState implements Parcelable {
62
63 /**
64 * Internal representation of inset source types. This is different from the public API in
65 * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
66 * at the same time.
67 */
68 @Retention(RetentionPolicy.SOURCE)
Tiger Huang332793b2019-10-29 23:21:27 +080069 @IntDef(prefix = "ITYPE", value = {
70 ITYPE_STATUS_BAR,
71 ITYPE_NAVIGATION_BAR,
72 ITYPE_CAPTION_BAR,
73 ITYPE_TOP_GESTURES,
74 ITYPE_BOTTOM_GESTURES,
75 ITYPE_LEFT_GESTURES,
76 ITYPE_RIGHT_GESTURES,
77 ITYPE_TOP_TAPPABLE_ELEMENT,
78 ITYPE_BOTTOM_TAPPABLE_ELEMENT,
Tiger Huange16645a2020-02-25 22:24:39 +080079 ITYPE_LEFT_DISPLAY_CUTOUT,
80 ITYPE_TOP_DISPLAY_CUTOUT,
81 ITYPE_RIGHT_DISPLAY_CUTOUT,
82 ITYPE_BOTTOM_DISPLAY_CUTOUT,
Heemin Seogd79e4f42020-05-06 15:32:50 -070083 ITYPE_IME,
84 ITYPE_CLIMATE_BAR,
85 ITYPE_EXTRA_NAVIGATION_BAR
Jorim Jaggif96c90a2018-09-26 16:55:15 +020086 })
Tiger Huang332793b2019-10-29 23:21:27 +080087 public @interface InternalInsetsType {}
Jorim Jaggif96c90a2018-09-26 16:55:15 +020088
Charles Chenb8070fb2020-02-24 15:42:59 +080089 /**
90 * Special value to be used to by methods returning an {@link InternalInsetsType} to indicate
91 * that the objects/parameters aren't associated with an {@link InternalInsetsType}
92 */
93 public static final int ITYPE_INVALID = -1;
94
Jorim Jaggif96c90a2018-09-26 16:55:15 +020095 static final int FIRST_TYPE = 0;
96
Tiger Huang332793b2019-10-29 23:21:27 +080097 public static final int ITYPE_STATUS_BAR = FIRST_TYPE;
98 public static final int ITYPE_NAVIGATION_BAR = 1;
99 public static final int ITYPE_CAPTION_BAR = 2;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200100
Tiger Huang332793b2019-10-29 23:21:27 +0800101 public static final int ITYPE_TOP_GESTURES = 3;
102 public static final int ITYPE_BOTTOM_GESTURES = 4;
103 public static final int ITYPE_LEFT_GESTURES = 5;
104 public static final int ITYPE_RIGHT_GESTURES = 6;
105 public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 7;
106 public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 8;
Adrian Roos11dfd272019-03-25 19:21:26 +0100107
Tiger Huange16645a2020-02-25 22:24:39 +0800108 public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 9;
109 public static final int ITYPE_TOP_DISPLAY_CUTOUT = 10;
110 public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 11;
111 public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 12;
112
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200113 /** Input method window. */
Tiger Huange16645a2020-02-25 22:24:39 +0800114 public static final int ITYPE_IME = 13;
Adrian Roos11dfd272019-03-25 19:21:26 +0100115
Heemin Seogd79e4f42020-05-06 15:32:50 -0700116 /** Additional system decorations inset type. */
117 public static final int ITYPE_CLIMATE_BAR = 14;
118 public static final int ITYPE_EXTRA_NAVIGATION_BAR = 15;
119
120 static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200121 public static final int SIZE = LAST_TYPE + 1;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200122
123 // Derived types
124
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200125 /** A shelf is the same as the navigation bar. */
Tiger Huang332793b2019-10-29 23:21:27 +0800126 public static final int ITYPE_SHELF = ITYPE_NAVIGATION_BAR;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200127
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100128 @Retention(RetentionPolicy.SOURCE)
Tiger Huang332793b2019-10-29 23:21:27 +0800129 @IntDef(prefix = "IINSETS_SIDE", value = {
130 ISIDE_LEFT,
131 ISIDE_TOP,
132 ISIDE_RIGHT,
133 ISIDE_BOTTOM,
134 ISIDE_FLOATING,
135 ISIDE_UNKNOWN
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100136 })
Tiger Huang332793b2019-10-29 23:21:27 +0800137 public @interface InternalInsetsSide {}
138 static final int ISIDE_LEFT = 0;
139 static final int ISIDE_TOP = 1;
140 static final int ISIDE_RIGHT = 2;
141 static final int ISIDE_BOTTOM = 3;
142 static final int ISIDE_FLOATING = 4;
143 static final int ISIDE_UNKNOWN = 5;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100144
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200145 private InsetsSource[] mSources = new InsetsSource[SIZE];
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200146
Tarandeep Singha6f35612019-01-11 19:50:46 -0800147 /**
148 * The frame of the display these sources are relative to.
149 */
150 private final Rect mDisplayFrame = new Rect();
151
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200152 public InsetsState() {
153 }
154
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100155 public InsetsState(InsetsState copy) {
156 set(copy);
157 }
158
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800159 public InsetsState(InsetsState copy, boolean copySources) {
160 set(copy, copySources);
161 }
162
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200163 /**
164 * Calculates {@link WindowInsets} based on the current source configuration.
165 *
166 * @param frame The frame to calculate the insets relative to.
Jorim Jaggi580aef52020-02-26 18:28:28 +0100167 * @param ignoringVisibilityState {@link InsetsState} used to calculate
168 * {@link WindowInsets#getInsetsIgnoringVisibility(int)} information, or pass
169 * {@code null} to use this state to calculate that information.
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200170 * @return The calculated insets.
171 */
Jorim Jaggi580aef52020-02-26 18:28:28 +0100172 public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
173 boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout,
Jorim Jaggi7f761872020-01-10 18:24:27 +0100174 int legacySoftInputMode, int legacySystemUiFlags,
175 @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
Jorim Jaggibcf99ff2018-12-03 18:04:26 +0100176 Insets[] typeInsetsMap = new Insets[Type.SIZE];
177 Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
Jorim Jaggi90990792019-01-21 23:00:20 +0100178 boolean[] typeVisibilityMap = new boolean[SIZE];
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200179 final Rect relativeFrame = new Rect(frame);
180 final Rect relativeFrameMax = new Rect(frame);
181 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200182 InsetsSource source = mSources[type];
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200183 if (source == null) {
Jorim Jaggi6f2ccea2020-04-01 21:59:18 +0200184 int index = indexOf(toPublicType(type));
185 if (typeInsetsMap[index] == null) {
186 typeInsetsMap[index] = Insets.NONE;
187 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200188 continue;
189 }
Jorim Jaggi648e5882019-01-24 13:24:02 +0100190
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700191 boolean skipNonImeInImeMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_IME
Tiger Huang332793b2019-10-29 23:21:27 +0800192 && source.getType() != ITYPE_IME;
Jorim Jaggi648e5882019-01-24 13:24:02 +0100193 boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
Tiger Huang332793b2019-10-29 23:21:27 +0800194 && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR);
Adrian Roos11dfd272019-03-25 19:21:26 +0100195 boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
Jorim Jaggi761a5ab2020-01-06 23:16:30 +0100196 && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR
197 || type == ITYPE_IME);
198 if (skipSystemBars || skipLegacyTypes || skipNonImeInImeMode) {
Jorim Jaggi90990792019-01-21 23:00:20 +0100199 typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible();
200 continue;
201 }
202
Jorim Jaggibcf99ff2018-12-03 18:04:26 +0100203 processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
Jorim Jaggi90990792019-01-21 23:00:20 +0100204 typeSideMap, typeVisibilityMap);
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200205
206 // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
207 // target.
Tiger Huang332793b2019-10-29 23:21:27 +0800208 if (source.getType() != ITYPE_IME) {
Jorim Jaggi580aef52020-02-26 18:28:28 +0100209 InsetsSource ignoringVisibilitySource = ignoringVisibilityState != null
210 ? ignoringVisibilityState.getSource(type)
211 : source;
212 if (ignoringVisibilitySource == null) {
213 continue;
214 }
215 processSource(ignoringVisibilitySource, relativeFrameMax,
216 true /* ignoreVisibility */, typeMaxInsetsMap, null /* typeSideMap */,
217 null /* typeVisibilityMap */);
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200218 }
219 }
Jorim Jaggi761a5ab2020-01-06 23:16:30 +0100220 final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST;
Jorim Jaggi90990792019-01-21 23:00:20 +0100221 return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
Jorim Jaggi761a5ab2020-01-06 23:16:30 +0100222 alwaysConsumeSystemBars, cutout, softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE
Tiger Huange16645a2020-02-25 22:24:39 +0800223 ? systemBars() | displayCutout() | ime()
224 : systemBars() | displayCutout(),
Jorim Jaggi7f761872020-01-10 18:24:27 +0100225 sNewInsetsMode == NEW_INSETS_MODE_FULL
226 && (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200227 }
228
Jorim Jaggi22488d32020-03-19 01:12:44 +0100229 public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100230 Insets insets = Insets.NONE;
231 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200232 InsetsSource source = mSources[type];
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100233 if (source == null) {
234 continue;
235 }
236 if (sNewInsetsMode != NEW_INSETS_MODE_FULL && type != ITYPE_IME) {
237 continue;
238 }
239
240 // Ignore everything that's not a system bar or IME.
241 int publicType = InsetsState.toPublicType(type);
242 if (!isVisibleInsetsType(publicType, softInputMode)) {
243 continue;
244 }
245 insets = Insets.max(source.calculateVisibleInsets(frame), insets);
246 }
247 return insets.toRect();
248 }
249
Adrian Roos8d04bcb2020-05-29 18:01:04 +0200250 /**
251 * Calculate which insets *cannot* be controlled, because the frame does not cover the
252 * respective side of the inset.
253 *
254 * If the frame of our window doesn't cover the entire inset, the control API makes very
255 * little sense, as we don't deal with negative insets.
256 */
257 @InsetsType
258 public int calculateUncontrollableInsetsFromFrame(Rect frame) {
259 int blocked = 0;
260 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200261 InsetsSource source = mSources[type];
Adrian Roos8d04bcb2020-05-29 18:01:04 +0200262 if (source == null) {
263 continue;
264 }
265 if (!canControlSide(frame, getInsetSide(
266 source.calculateInsets(frame, true /* ignoreVisibility */)))) {
267 blocked |= toPublicType(type);
268 }
269 }
270 return blocked;
271 }
272
273 private boolean canControlSide(Rect frame, int side) {
274 switch (side) {
275 case ISIDE_LEFT:
276 case ISIDE_RIGHT:
277 return frame.left == mDisplayFrame.left && frame.right == mDisplayFrame.right;
278 case ISIDE_TOP:
279 case ISIDE_BOTTOM:
280 return frame.top == mDisplayFrame.top && frame.bottom == mDisplayFrame.bottom;
281 case ISIDE_FLOATING:
282 return true;
283 default:
284 return false;
285 }
286 }
287
Jorim Jaggibcf99ff2018-12-03 18:04:26 +0100288 private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
Tiger Huang332793b2019-10-29 23:21:27 +0800289 Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap,
Jorim Jaggi90990792019-01-21 23:00:20 +0100290 @Nullable boolean[] typeVisibilityMap) {
Jorim Jaggibcf99ff2018-12-03 18:04:26 +0100291 Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility);
292
Adrian Roos11dfd272019-03-25 19:21:26 +0100293 int type = toPublicType(source.getType());
294 processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
295 insets, type);
296
297 if (type == MANDATORY_SYSTEM_GESTURES) {
298 // Mandatory system gestures are also system gestures.
299 // TODO: find a way to express this more generally. One option would be to define
300 // Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the
301 // ability to set systemGestureInsets() independently from
302 // mandatorySystemGestureInsets() in the Builder.
303 processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
304 insets, SYSTEM_GESTURES);
305 }
306 }
307
308 private void processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap,
Tiger Huang332793b2019-10-29 23:21:27 +0800309 @InternalInsetsSide @Nullable SparseIntArray typeSideMap,
Adrian Roos11dfd272019-03-25 19:21:26 +0100310 @Nullable boolean[] typeVisibilityMap, Insets insets, int type) {
311 int index = indexOf(type);
Jorim Jaggibcf99ff2018-12-03 18:04:26 +0100312 Insets existing = typeInsetsMap[index];
313 if (existing == null) {
314 typeInsetsMap[index] = insets;
315 } else {
316 typeInsetsMap[index] = Insets.max(existing, insets);
317 }
318
Jorim Jaggi90990792019-01-21 23:00:20 +0100319 if (typeVisibilityMap != null) {
320 typeVisibilityMap[index] = source.isVisible();
321 }
322
Tarandeep Singh95a3dbf2019-10-22 10:39:24 -0700323 if (typeSideMap != null) {
Tiger Huang332793b2019-10-29 23:21:27 +0800324 @InternalInsetsSide int insetSide = getInsetSide(insets);
325 if (insetSide != ISIDE_UNKNOWN) {
Tarandeep Singh95a3dbf2019-10-22 10:39:24 -0700326 typeSideMap.put(source.getType(), insetSide);
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100327 }
328 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200329 }
330
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100331 /**
332 * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
333 * is set in order that this method returns a meaningful result.
334 */
Tiger Huang332793b2019-10-29 23:21:27 +0800335 private @InternalInsetsSide int getInsetSide(Insets insets) {
Tarandeep Singh95a3dbf2019-10-22 10:39:24 -0700336 if (Insets.NONE.equals(insets)) {
Tiger Huang332793b2019-10-29 23:21:27 +0800337 return ISIDE_FLOATING;
Tarandeep Singh95a3dbf2019-10-22 10:39:24 -0700338 }
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100339 if (insets.left != 0) {
Tiger Huang332793b2019-10-29 23:21:27 +0800340 return ISIDE_LEFT;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100341 }
342 if (insets.top != 0) {
Tiger Huang332793b2019-10-29 23:21:27 +0800343 return ISIDE_TOP;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100344 }
345 if (insets.right != 0) {
Tiger Huang332793b2019-10-29 23:21:27 +0800346 return ISIDE_RIGHT;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100347 }
348 if (insets.bottom != 0) {
Tiger Huang332793b2019-10-29 23:21:27 +0800349 return ISIDE_BOTTOM;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100350 }
Tiger Huang332793b2019-10-29 23:21:27 +0800351 return ISIDE_UNKNOWN;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100352 }
353
Tiger Huang332793b2019-10-29 23:21:27 +0800354 public InsetsSource getSource(@InternalInsetsType int type) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200355 InsetsSource source = mSources[type];
356 if (source != null) {
357 return source;
358 }
359 source = new InsetsSource(type);
360 mSources[type] = source;
361 return source;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200362 }
363
Jorim Jaggi580aef52020-02-26 18:28:28 +0100364 public @Nullable InsetsSource peekSource(@InternalInsetsType int type) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200365 return mSources[type];
366 }
367
368 public boolean hasSources() {
369 for (int i = 0; i < SIZE; i++) {
370 if (mSources[i] != null) {
371 return true;
372 }
373 }
374 return false;
Jorim Jaggi580aef52020-02-26 18:28:28 +0100375 }
376
Tiger Huang017d51d2020-05-27 17:58:17 +0800377 /**
378 * Returns the source visibility or the default visibility if the source doesn't exist. This is
379 * useful if when treating this object as a request.
380 *
381 * @param type The {@link InternalInsetsType} to query.
382 * @return {@code true} if the source is visible or the type is default visible and the source
383 * doesn't exist.
384 */
385 public boolean getSourceOrDefaultVisibility(@InternalInsetsType int type) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200386 final InsetsSource source = mSources[type];
Tiger Huang017d51d2020-05-27 17:58:17 +0800387 return source != null ? source.isVisible() : getDefaultVisibility(type);
388 }
389
Tarandeep Singha6f35612019-01-11 19:50:46 -0800390 public void setDisplayFrame(Rect frame) {
391 mDisplayFrame.set(frame);
392 }
393
394 public Rect getDisplayFrame() {
395 return mDisplayFrame;
396 }
397
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200398 /**
399 * Modifies the state of this class to exclude a certain type to make it ready for dispatching
400 * to the client.
401 *
Tiger Huang332793b2019-10-29 23:21:27 +0800402 * @param type The {@link InternalInsetsType} of the source to remove
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200403 */
Tiger Huang332793b2019-10-29 23:21:27 +0800404 public void removeSource(@InternalInsetsType int type) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200405 mSources[type] = null;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200406 }
407
Jorim Jaggi956ca412019-01-07 14:49:14 +0100408 /**
409 * A shortcut for setting the visibility of the source.
410 *
Tiger Huang332793b2019-10-29 23:21:27 +0800411 * @param type The {@link InternalInsetsType} of the source to set the visibility
Jorim Jaggi956ca412019-01-07 14:49:14 +0100412 * @param visible {@code true} for visible
413 */
Tiger Huang332793b2019-10-29 23:21:27 +0800414 public void setSourceVisible(@InternalInsetsType int type, boolean visible) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200415 InsetsSource source = mSources[type];
Jorim Jaggi956ca412019-01-07 14:49:14 +0100416 if (source != null) {
417 source.setVisible(visible);
418 }
419 }
420
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200421 public void set(InsetsState other) {
422 set(other, false /* copySources */);
423 }
424
425 public void set(InsetsState other, boolean copySources) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800426 mDisplayFrame.set(other.mDisplayFrame);
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200427 if (copySources) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200428 for (int i = 0; i < SIZE; i++) {
429 InsetsSource source = other.mSources[i];
430 if (source == null) continue;
431 mSources[i] = new InsetsSource(source);
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200432 }
433 } else {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200434 for (int i = 0; i < SIZE; i++) {
435 mSources[i] = other.mSources[i];
436 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200437 }
438 }
439
Jorim Jaggie35c0592018-11-06 16:21:08 +0100440 public void addSource(InsetsSource source) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200441 mSources[source.getType()] = source;
Jorim Jaggie35c0592018-11-06 16:21:08 +0100442 }
443
Tiger Huang332793b2019-10-29 23:21:27 +0800444 public static @InternalInsetsType ArraySet<Integer> toInternalType(@InsetsType int types) {
Jorim Jaggib6030952018-10-23 18:31:52 +0200445 final ArraySet<Integer> result = new ArraySet<>();
Tiger Huang332793b2019-10-29 23:21:27 +0800446 if ((types & Type.STATUS_BARS) != 0) {
447 result.add(ITYPE_STATUS_BAR);
Jorim Jaggib6030952018-10-23 18:31:52 +0200448 }
Tiger Huang332793b2019-10-29 23:21:27 +0800449 if ((types & Type.NAVIGATION_BARS) != 0) {
450 result.add(ITYPE_NAVIGATION_BAR);
Jorim Jaggib6030952018-10-23 18:31:52 +0200451 }
Tiger Huang332793b2019-10-29 23:21:27 +0800452 if ((types & Type.CAPTION_BAR) != 0) {
453 result.add(ITYPE_CAPTION_BAR);
454 }
Tiger Huange16645a2020-02-25 22:24:39 +0800455 if ((types & Type.DISPLAY_CUTOUT) != 0) {
456 result.add(ITYPE_LEFT_DISPLAY_CUTOUT);
457 result.add(ITYPE_TOP_DISPLAY_CUTOUT);
458 result.add(ITYPE_RIGHT_DISPLAY_CUTOUT);
459 result.add(ITYPE_BOTTOM_DISPLAY_CUTOUT);
460 }
Tiger Huang332793b2019-10-29 23:21:27 +0800461 if ((types & Type.IME) != 0) {
462 result.add(ITYPE_IME);
Jorim Jaggib6030952018-10-23 18:31:52 +0200463 }
464 return result;
465 }
466
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900467 /**
468 * Converting a internal type to the public type.
469 * @param type internal insets type, {@code InternalInsetsType}.
470 * @return public insets type, {@code Type.InsetsType}.
471 */
472 public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
Jorim Jaggibcf99ff2018-12-03 18:04:26 +0100473 switch (type) {
Tiger Huang332793b2019-10-29 23:21:27 +0800474 case ITYPE_STATUS_BAR:
Heemin Seogd79e4f42020-05-06 15:32:50 -0700475 case ITYPE_CLIMATE_BAR:
Tiger Huang332793b2019-10-29 23:21:27 +0800476 return Type.STATUS_BARS;
477 case ITYPE_NAVIGATION_BAR:
Heemin Seogd79e4f42020-05-06 15:32:50 -0700478 case ITYPE_EXTRA_NAVIGATION_BAR:
Tiger Huang332793b2019-10-29 23:21:27 +0800479 return Type.NAVIGATION_BARS;
480 case ITYPE_CAPTION_BAR:
481 return Type.CAPTION_BAR;
482 case ITYPE_IME:
Jorim Jaggibcf99ff2018-12-03 18:04:26 +0100483 return Type.IME;
Tiger Huang332793b2019-10-29 23:21:27 +0800484 case ITYPE_TOP_GESTURES:
485 case ITYPE_BOTTOM_GESTURES:
Adrian Roos11dfd272019-03-25 19:21:26 +0100486 return Type.MANDATORY_SYSTEM_GESTURES;
Tiger Huang332793b2019-10-29 23:21:27 +0800487 case ITYPE_LEFT_GESTURES:
488 case ITYPE_RIGHT_GESTURES:
Adrian Roos11dfd272019-03-25 19:21:26 +0100489 return Type.SYSTEM_GESTURES;
Tiger Huang332793b2019-10-29 23:21:27 +0800490 case ITYPE_TOP_TAPPABLE_ELEMENT:
491 case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
Adrian Roos11dfd272019-03-25 19:21:26 +0100492 return Type.TAPPABLE_ELEMENT;
Tiger Huange16645a2020-02-25 22:24:39 +0800493 case ITYPE_LEFT_DISPLAY_CUTOUT:
494 case ITYPE_TOP_DISPLAY_CUTOUT:
495 case ITYPE_RIGHT_DISPLAY_CUTOUT:
496 case ITYPE_BOTTOM_DISPLAY_CUTOUT:
497 return Type.DISPLAY_CUTOUT;
Jorim Jaggibcf99ff2018-12-03 18:04:26 +0100498 default:
499 throw new IllegalArgumentException("Unknown type: " + type);
500 }
501 }
502
Tiger Huang017d51d2020-05-27 17:58:17 +0800503 public static boolean getDefaultVisibility(@InternalInsetsType int type) {
Tiger Huang332793b2019-10-29 23:21:27 +0800504 return type != ITYPE_IME;
Jorim Jaggie35c0592018-11-06 16:21:08 +0100505 }
506
Tiger Huang332793b2019-10-29 23:21:27 +0800507 public static boolean containsType(@InternalInsetsType int[] types,
508 @InternalInsetsType int type) {
Jorim Jaggi956ca412019-01-07 14:49:14 +0100509 if (types == null) {
510 return false;
511 }
512 for (int t : types) {
513 if (t == type) {
514 return true;
515 }
516 }
517 return false;
518 }
519
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200520 public void dump(String prefix, PrintWriter pw) {
521 pw.println(prefix + "InsetsState");
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200522 for (int i = 0; i < SIZE; i++) {
523 InsetsSource source = mSources[i];
524 if (source == null) continue;
525 source.dump(prefix + " ", pw);
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200526 }
527 }
528
Tiger Huang332793b2019-10-29 23:21:27 +0800529 public static String typeToString(@InternalInsetsType int type) {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200530 switch (type) {
Tiger Huang332793b2019-10-29 23:21:27 +0800531 case ITYPE_STATUS_BAR:
532 return "ITYPE_STATUS_BAR";
533 case ITYPE_NAVIGATION_BAR:
534 return "ITYPE_NAVIGATION_BAR";
535 case ITYPE_CAPTION_BAR:
536 return "ITYPE_CAPTION_BAR";
537 case ITYPE_TOP_GESTURES:
538 return "ITYPE_TOP_GESTURES";
539 case ITYPE_BOTTOM_GESTURES:
540 return "ITYPE_BOTTOM_GESTURES";
541 case ITYPE_LEFT_GESTURES:
542 return "ITYPE_LEFT_GESTURES";
543 case ITYPE_RIGHT_GESTURES:
544 return "ITYPE_RIGHT_GESTURES";
545 case ITYPE_TOP_TAPPABLE_ELEMENT:
546 return "ITYPE_TOP_TAPPABLE_ELEMENT";
547 case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
548 return "ITYPE_BOTTOM_TAPPABLE_ELEMENT";
Tiger Huange16645a2020-02-25 22:24:39 +0800549 case ITYPE_LEFT_DISPLAY_CUTOUT:
550 return "ITYPE_LEFT_DISPLAY_CUTOUT";
551 case ITYPE_TOP_DISPLAY_CUTOUT:
552 return "ITYPE_TOP_DISPLAY_CUTOUT";
553 case ITYPE_RIGHT_DISPLAY_CUTOUT:
554 return "ITYPE_RIGHT_DISPLAY_CUTOUT";
555 case ITYPE_BOTTOM_DISPLAY_CUTOUT:
556 return "ITYPE_BOTTOM_DISPLAY_CUTOUT";
Tiger Huang332793b2019-10-29 23:21:27 +0800557 case ITYPE_IME:
558 return "ITYPE_IME";
Heemin Seogd79e4f42020-05-06 15:32:50 -0700559 case ITYPE_CLIMATE_BAR:
560 return "ITYPE_CLIMATE_BAR";
561 case ITYPE_EXTRA_NAVIGATION_BAR:
562 return "ITYPE_EXTRA_NAVIGATION_BAR";
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200563 default:
Tiger Huang332793b2019-10-29 23:21:27 +0800564 return "ITYPE_UNKNOWN_" + type;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200565 }
566 }
567
568 @Override
569 public boolean equals(Object o) {
Jorim Jaggied312592020-05-25 16:46:56 +0200570 return equals(o, false, false);
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900571 }
572
573 /**
574 * An equals method can exclude the caption insets. This is useful because we assemble the
575 * caption insets information on the client side, and when we communicate with server, it's
576 * excluded.
577 * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
578 * ignore the caption insets source value.
Jorim Jaggied312592020-05-25 16:46:56 +0200579 * @param excludeInvisibleImeFrames If {@link #ITYPE_IME} frames should be ignored when IME is
580 * not visible.
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900581 * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
582 */
583 @VisibleForTesting
Jorim Jaggied312592020-05-25 16:46:56 +0200584 public boolean equals(Object o, boolean excludingCaptionInsets,
585 boolean excludeInvisibleImeFrames) {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200586 if (this == o) { return true; }
587 if (o == null || getClass() != o.getClass()) { return false; }
588
589 InsetsState state = (InsetsState) o;
590
Tarandeep Singha6f35612019-01-11 19:50:46 -0800591 if (!mDisplayFrame.equals(state.mDisplayFrame)) {
592 return false;
593 }
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200594 for (int i = 0; i < SIZE; i++) {
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900595 if (excludingCaptionInsets) {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200596 if (i == ITYPE_CAPTION_BAR) continue;
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900597 }
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200598 InsetsSource source = mSources[i];
599 InsetsSource otherSource = state.mSources[i];
600 if (source == null && otherSource == null) {
601 continue;
602 }
603 if (source != null && otherSource == null || source == null && otherSource != null) {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200604 return false;
605 }
Jorim Jaggied312592020-05-25 16:46:56 +0200606 if (!otherSource.equals(source, excludeInvisibleImeFrames)) {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200607 return false;
608 }
609 }
610 return true;
611 }
612
613 @Override
614 public int hashCode() {
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200615 return Objects.hash(mDisplayFrame, Arrays.hashCode(mSources));
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200616 }
617
618 public InsetsState(Parcel in) {
619 readFromParcel(in);
620 }
621
622 @Override
623 public int describeContents() {
624 return 0;
625 }
626
627 @Override
628 public void writeToParcel(Parcel dest, int flags) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800629 dest.writeParcelable(mDisplayFrame, flags);
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200630 dest.writeParcelableArray(mSources, 0);
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200631 }
632
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700633 public static final @android.annotation.NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200634
635 public InsetsState createFromParcel(Parcel in) {
636 return new InsetsState(in);
637 }
638
639 public InsetsState[] newArray(int size) {
640 return new InsetsState[size];
641 }
642 };
643
644 public void readFromParcel(Parcel in) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800645 mDisplayFrame.set(in.readParcelable(null /* loader */));
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200646 mSources = in.readParcelableArray(null, InsetsSource.class);
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200647 }
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900648
649 @Override
650 public String toString() {
Taran Singh85661e32020-05-07 14:45:34 -0700651 StringJoiner joiner = new StringJoiner(", ");
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200652 for (int i = 0; i < SIZE; i++) {
653 InsetsSource source = mSources[i];
Taran Singh85661e32020-05-07 14:45:34 -0700654 if (source != null) {
655 joiner.add(source.toString());
656 }
657 }
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900658 return "InsetsState: {"
659 + "mDisplayFrame=" + mDisplayFrame
Taran Singh85661e32020-05-07 14:45:34 -0700660 + ", mSources= { " + joiner
661 + " }";
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900662 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200663}
664