blob: f5b7068998d6de79a6091a4d83e45a83bcf08e86 [file] [log] [blame]
Adrian Roosd4970af2017-11-10 15:48:01 +01001/*
2 * Copyright 2017 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
Vishnu Nair1d0fa072018-01-04 07:53:00 -080019import static android.view.DisplayCutoutProto.BOUNDS;
20import static android.view.DisplayCutoutProto.INSETS;
Adrian Roosd4970af2017-11-10 15:48:01 +010021import static android.view.Surface.ROTATION_0;
22import static android.view.Surface.ROTATION_180;
23import static android.view.Surface.ROTATION_270;
24import static android.view.Surface.ROTATION_90;
25
Adrian Roos8d13bf12018-02-21 15:17:07 +010026import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
27
Adrian Roos16693f32018-01-18 17:45:52 +010028import android.content.res.Resources;
29import android.graphics.Matrix;
Adrian Roosd07bafd2017-12-11 17:30:56 +010030import android.graphics.Path;
Adrian Roosd4970af2017-11-10 15:48:01 +010031import android.graphics.Rect;
Adrian Roosd07bafd2017-12-11 17:30:56 +010032import android.graphics.RectF;
33import android.graphics.Region;
Adrian Roosd4970af2017-11-10 15:48:01 +010034import android.os.Parcel;
35import android.os.Parcelable;
Adrian Roos16693f32018-01-18 17:45:52 +010036import android.text.TextUtils;
37import android.util.Log;
38import android.util.PathParser;
Adrian Roos24264212018-02-19 16:26:15 +010039import android.util.Size;
Vishnu Nair1d0fa072018-01-04 07:53:00 -080040import android.util.proto.ProtoOutputStream;
Adrian Roosd4970af2017-11-10 15:48:01 +010041
Adrian Roos16693f32018-01-18 17:45:52 +010042import com.android.internal.R;
Adrian Roos8d13bf12018-02-21 15:17:07 +010043import com.android.internal.annotations.GuardedBy;
Adrian Roosd4970af2017-11-10 15:48:01 +010044import com.android.internal.annotations.VisibleForTesting;
45
Adrian Roos24264212018-02-19 16:26:15 +010046import java.util.Objects;
Adrian Roosd4970af2017-11-10 15:48:01 +010047
48/**
49 * Represents a part of the display that is not functional for displaying content.
50 *
51 * <p>{@code DisplayCutout} is immutable.
Adrian Roosd4970af2017-11-10 15:48:01 +010052 */
53public final class DisplayCutout {
54
Adrian Roos16693f32018-01-18 17:45:52 +010055 private static final String TAG = "DisplayCutout";
56 private static final String DP_MARKER = "@dp";
57
Adrian Roosc84df772018-01-19 21:20:22 +010058 /**
59 * Category for overlays that allow emulating a display cutout on devices that don't have
60 * one.
61 *
62 * @see android.content.om.IOverlayManager
63 * @hide
64 */
65 public static final String EMULATION_OVERLAY_CATEGORY =
66 "com.android.internal.display_cutout_emulation";
67
Adrian Roosd07bafd2017-12-11 17:30:56 +010068 private static final Rect ZERO_RECT = new Rect();
69 private static final Region EMPTY_REGION = new Region();
Adrian Roosd4970af2017-11-10 15:48:01 +010070
71 /**
Adrian Roosd07bafd2017-12-11 17:30:56 +010072 * An instance where {@link #isEmpty()} returns {@code true}.
Adrian Roosd4970af2017-11-10 15:48:01 +010073 *
74 * @hide
75 */
Adrian Roos24264212018-02-19 16:26:15 +010076 public static final DisplayCutout NO_CUTOUT = new DisplayCutout(ZERO_RECT, EMPTY_REGION,
77 new Size(0, 0));
Adrian Roosd4970af2017-11-10 15:48:01 +010078
Adrian Roos8d13bf12018-02-21 15:17:07 +010079
80 private static final Object CACHE_LOCK = new Object();
81 @GuardedBy("CACHE_LOCK")
82 private static String sCachedSpec;
83 @GuardedBy("CACHE_LOCK")
84 private static int sCachedDisplayWidth;
85 @GuardedBy("CACHE_LOCK")
86 private static float sCachedDensity;
87 @GuardedBy("CACHE_LOCK")
88 private static DisplayCutout sCachedCutout;
89
Adrian Roosd4970af2017-11-10 15:48:01 +010090 private final Rect mSafeInsets;
Adrian Roosd07bafd2017-12-11 17:30:56 +010091 private final Region mBounds;
Adrian Roos24264212018-02-19 16:26:15 +010092 private final Size mFrameSize;
Adrian Roosd4970af2017-11-10 15:48:01 +010093
94 /**
95 * Creates a DisplayCutout instance.
96 *
97 * NOTE: the Rects passed into this instance are not copied and MUST remain unchanged.
98 *
99 * @hide
100 */
101 @VisibleForTesting
Adrian Roos24264212018-02-19 16:26:15 +0100102 public DisplayCutout(Rect safeInsets, Region bounds, Size frameSize) {
Adrian Roosd4970af2017-11-10 15:48:01 +0100103 mSafeInsets = safeInsets != null ? safeInsets : ZERO_RECT;
Adrian Roosd07bafd2017-12-11 17:30:56 +0100104 mBounds = bounds != null ? bounds : Region.obtain();
Adrian Roos24264212018-02-19 16:26:15 +0100105 mFrameSize = frameSize;
Adrian Roosd4970af2017-11-10 15:48:01 +0100106 }
107
108 /**
Adrian Roosd07bafd2017-12-11 17:30:56 +0100109 * Returns true if there is no cutout or it is outside of the content view.
Adrian Roosd4970af2017-11-10 15:48:01 +0100110 *
Adrian Roosd07bafd2017-12-11 17:30:56 +0100111 * @hide
Adrian Roosd4970af2017-11-10 15:48:01 +0100112 */
Adrian Roosd07bafd2017-12-11 17:30:56 +0100113 public boolean isEmpty() {
114 return mSafeInsets.equals(ZERO_RECT);
Adrian Roosd4970af2017-11-10 15:48:01 +0100115 }
116
117 /** Returns the inset from the top which avoids the display cutout. */
118 public int getSafeInsetTop() {
119 return mSafeInsets.top;
120 }
121
122 /** Returns the inset from the bottom which avoids the display cutout. */
123 public int getSafeInsetBottom() {
124 return mSafeInsets.bottom;
125 }
126
127 /** Returns the inset from the left which avoids the display cutout. */
128 public int getSafeInsetLeft() {
129 return mSafeInsets.left;
130 }
131
132 /** Returns the inset from the right which avoids the display cutout. */
133 public int getSafeInsetRight() {
134 return mSafeInsets.right;
135 }
136
137 /**
Adrian Roosd07bafd2017-12-11 17:30:56 +0100138 * Returns the safe insets in a rect.
Adrian Roosd4970af2017-11-10 15:48:01 +0100139 *
Adrian Roosd07bafd2017-12-11 17:30:56 +0100140 * @return a rect which is set to the safe insets.
Adrian Roosd4970af2017-11-10 15:48:01 +0100141 * @hide
142 */
Adrian Roosd07bafd2017-12-11 17:30:56 +0100143 public Rect getSafeInsets() {
144 return new Rect(mSafeInsets);
Adrian Roosd4970af2017-11-10 15:48:01 +0100145 }
146
147 /**
Adrian Roosd07bafd2017-12-11 17:30:56 +0100148 * Returns the bounding region of the cutout.
Adrian Roosd4970af2017-11-10 15:48:01 +0100149 *
Adrian Roosd07bafd2017-12-11 17:30:56 +0100150 * @return the bounding region of the cutout. Coordinates are relative
Adrian Roosd4970af2017-11-10 15:48:01 +0100151 * to the top-left corner of the content view.
152 */
Adrian Roosd07bafd2017-12-11 17:30:56 +0100153 public Region getBounds() {
154 return Region.obtain(mBounds);
Adrian Roosd4970af2017-11-10 15:48:01 +0100155 }
156
157 /**
Adrian Roosd07bafd2017-12-11 17:30:56 +0100158 * Returns the bounding rect of the cutout.
Adrian Roosd4970af2017-11-10 15:48:01 +0100159 *
Adrian Roosd07bafd2017-12-11 17:30:56 +0100160 * @return the bounding rect of the cutout. Coordinates are relative
161 * to the top-left corner of the content view.
162 * @hide
Adrian Roosd4970af2017-11-10 15:48:01 +0100163 */
Adrian Roosd07bafd2017-12-11 17:30:56 +0100164 public Rect getBoundingRect() {
165 // TODO(roosa): Inline.
166 return mBounds.getBounds();
Adrian Roosd4970af2017-11-10 15:48:01 +0100167 }
168
169 @Override
170 public int hashCode() {
171 int result = mSafeInsets.hashCode();
Adrian Roosd07bafd2017-12-11 17:30:56 +0100172 result = result * 31 + mBounds.getBounds().hashCode();
Adrian Roosd4970af2017-11-10 15:48:01 +0100173 return result;
174 }
175
176 @Override
177 public boolean equals(Object o) {
178 if (o == this) {
179 return true;
180 }
181 if (o instanceof DisplayCutout) {
182 DisplayCutout c = (DisplayCutout) o;
183 return mSafeInsets.equals(c.mSafeInsets)
Adrian Roos24264212018-02-19 16:26:15 +0100184 && mBounds.equals(c.mBounds)
185 && Objects.equals(mFrameSize, c.mFrameSize);
Adrian Roosd4970af2017-11-10 15:48:01 +0100186 }
187 return false;
188 }
189
190 @Override
191 public String toString() {
192 return "DisplayCutout{insets=" + mSafeInsets
Adrian Roos1cf585052018-01-03 18:43:27 +0100193 + " boundingRect=" + getBoundingRect()
Adrian Roosd4970af2017-11-10 15:48:01 +0100194 + "}";
195 }
196
197 /**
Vishnu Nair1d0fa072018-01-04 07:53:00 -0800198 * @hide
199 */
200 public void writeToProto(ProtoOutputStream proto, long fieldId) {
201 final long token = proto.start(fieldId);
202 mSafeInsets.writeToProto(proto, INSETS);
203 mBounds.getBounds().writeToProto(proto, BOUNDS);
204 proto.end(token);
205 }
206
207 /**
Adrian Roosd4970af2017-11-10 15:48:01 +0100208 * Insets the reference frame of the cutout in the given directions.
209 *
210 * @return a copy of this instance which has been inset
211 * @hide
212 */
213 public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
Adrian Roosd07bafd2017-12-11 17:30:56 +0100214 if (mBounds.isEmpty()
Adrian Roosd4970af2017-11-10 15:48:01 +0100215 || insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) {
216 return this;
217 }
218
219 Rect safeInsets = new Rect(mSafeInsets);
Adrian Roosd07bafd2017-12-11 17:30:56 +0100220 Region bounds = Region.obtain(mBounds);
Adrian Roosd4970af2017-11-10 15:48:01 +0100221
222 // Note: it's not really well defined what happens when the inset is negative, because we
223 // don't know if the safe inset needs to expand in general.
224 if (insetTop > 0 || safeInsets.top > 0) {
225 safeInsets.top = atLeastZero(safeInsets.top - insetTop);
226 }
227 if (insetBottom > 0 || safeInsets.bottom > 0) {
228 safeInsets.bottom = atLeastZero(safeInsets.bottom - insetBottom);
229 }
230 if (insetLeft > 0 || safeInsets.left > 0) {
231 safeInsets.left = atLeastZero(safeInsets.left - insetLeft);
232 }
233 if (insetRight > 0 || safeInsets.right > 0) {
234 safeInsets.right = atLeastZero(safeInsets.right - insetRight);
235 }
236
Adrian Roosd07bafd2017-12-11 17:30:56 +0100237 bounds.translate(-insetLeft, -insetTop);
Adrian Roos24264212018-02-19 16:26:15 +0100238 Size frame = mFrameSize == null ? null : new Size(
239 mFrameSize.getWidth() - insetLeft - insetRight,
240 mFrameSize.getHeight() - insetTop - insetBottom);
Adrian Roosd4970af2017-11-10 15:48:01 +0100241
Adrian Roos24264212018-02-19 16:26:15 +0100242 return new DisplayCutout(safeInsets, bounds, frame);
Adrian Roosd4970af2017-11-10 15:48:01 +0100243 }
244
245 /**
Adrian Roos24264212018-02-19 16:26:15 +0100246 * Recalculates the cutout relative to the given reference frame.
247 *
248 * The safe insets must already have been computed, e.g. with {@link #computeSafeInsets}.
249 *
250 * @return a copy of this instance with the safe insets recalculated
251 * @hide
252 */
253 public DisplayCutout calculateRelativeTo(Rect frame) {
254 return inset(frame.left, frame.top,
255 mFrameSize.getWidth() - frame.right, mFrameSize.getHeight() - frame.bottom);
256 }
257
258 /**
259 * Calculates the safe insets relative to the given display size.
Adrian Roosd4970af2017-11-10 15:48:01 +0100260 *
261 * @return a copy of this instance with the safe insets calculated
262 * @hide
263 */
Adrian Roos24264212018-02-19 16:26:15 +0100264 public DisplayCutout computeSafeInsets(int width, int height) {
265 if (this == NO_CUTOUT || mBounds.isEmpty()) {
Adrian Roosd4970af2017-11-10 15:48:01 +0100266 return NO_CUTOUT;
267 }
268
Adrian Roos24264212018-02-19 16:26:15 +0100269 return computeSafeInsets(new Size(width, height), mBounds);
Adrian Roosd4970af2017-11-10 15:48:01 +0100270 }
271
Adrian Roos24264212018-02-19 16:26:15 +0100272 private static DisplayCutout computeSafeInsets(Size displaySize, Region bounds) {
Adrian Roosd07bafd2017-12-11 17:30:56 +0100273 Rect boundingRect = bounds.getBounds();
Adrian Roosd4970af2017-11-10 15:48:01 +0100274 Rect safeRect = new Rect();
Adrian Roosd07bafd2017-12-11 17:30:56 +0100275
Adrian Roosd4970af2017-11-10 15:48:01 +0100276 int bestArea = 0;
277 int bestVariant = 0;
278 for (int variant = ROTATION_0; variant <= ROTATION_270; variant++) {
Adrian Roos24264212018-02-19 16:26:15 +0100279 int area = calculateInsetVariantArea(displaySize, boundingRect, variant, safeRect);
Adrian Roosd4970af2017-11-10 15:48:01 +0100280 if (bestArea < area) {
281 bestArea = area;
282 bestVariant = variant;
283 }
284 }
Adrian Roos24264212018-02-19 16:26:15 +0100285 calculateInsetVariantArea(displaySize, boundingRect, bestVariant, safeRect);
Adrian Roosd4970af2017-11-10 15:48:01 +0100286 if (safeRect.isEmpty()) {
Adrian Roos24264212018-02-19 16:26:15 +0100287 // The entire displaySize overlaps with the cutout.
288 safeRect.set(0, displaySize.getHeight(), 0, 0);
Adrian Roosd4970af2017-11-10 15:48:01 +0100289 } else {
Adrian Roos24264212018-02-19 16:26:15 +0100290 // Convert safeRect to insets relative to displaySize. We're reusing the rect here to
291 // avoid an allocation.
Adrian Roosd4970af2017-11-10 15:48:01 +0100292 safeRect.set(
Adrian Roos24264212018-02-19 16:26:15 +0100293 Math.max(0, safeRect.left),
294 Math.max(0, safeRect.top),
295 Math.max(0, displaySize.getWidth() - safeRect.right),
296 Math.max(0, displaySize.getHeight() - safeRect.bottom));
Adrian Roosd4970af2017-11-10 15:48:01 +0100297 }
298
Adrian Roos24264212018-02-19 16:26:15 +0100299 return new DisplayCutout(safeRect, bounds, displaySize);
Adrian Roosd4970af2017-11-10 15:48:01 +0100300 }
301
Adrian Roos24264212018-02-19 16:26:15 +0100302 private static int calculateInsetVariantArea(Size display, Rect boundingRect, int variant,
Adrian Roosd4970af2017-11-10 15:48:01 +0100303 Rect outSafeRect) {
304 switch (variant) {
305 case ROTATION_0:
Adrian Roos24264212018-02-19 16:26:15 +0100306 outSafeRect.set(0, 0, display.getWidth(), boundingRect.top);
Adrian Roosd4970af2017-11-10 15:48:01 +0100307 break;
308 case ROTATION_90:
Adrian Roos24264212018-02-19 16:26:15 +0100309 outSafeRect.set(0, 0, boundingRect.left, display.getHeight());
Adrian Roosd4970af2017-11-10 15:48:01 +0100310 break;
311 case ROTATION_180:
Adrian Roos24264212018-02-19 16:26:15 +0100312 outSafeRect.set(0, boundingRect.bottom, display.getWidth(), display.getHeight());
Adrian Roosd4970af2017-11-10 15:48:01 +0100313 break;
314 case ROTATION_270:
Adrian Roos24264212018-02-19 16:26:15 +0100315 outSafeRect.set(boundingRect.right, 0, display.getWidth(), display.getHeight());
Adrian Roosd4970af2017-11-10 15:48:01 +0100316 break;
317 }
318
319 return outSafeRect.isEmpty() ? 0 : outSafeRect.width() * outSafeRect.height();
320 }
321
322 private static int atLeastZero(int value) {
323 return value < 0 ? 0 : value;
324 }
325
Adrian Roosd4970af2017-11-10 15:48:01 +0100326
327 /**
Adrian Roos24264212018-02-19 16:26:15 +0100328 * Creates an instance from a bounding rect.
Adrian Roosd4970af2017-11-10 15:48:01 +0100329 *
330 * @hide
331 */
Adrian Roos24264212018-02-19 16:26:15 +0100332 public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
Adrian Roosd07bafd2017-12-11 17:30:56 +0100333 Path path = new Path();
Adrian Roosd07bafd2017-12-11 17:30:56 +0100334 path.reset();
Adrian Roos24264212018-02-19 16:26:15 +0100335 path.moveTo(left, top);
336 path.lineTo(left, bottom);
337 path.lineTo(right, bottom);
338 path.lineTo(right, top);
Adrian Roosd07bafd2017-12-11 17:30:56 +0100339 path.close();
Adrian Roos1cf585052018-01-03 18:43:27 +0100340 return fromBounds(path);
341 }
Adrian Roosd4970af2017-11-10 15:48:01 +0100342
Adrian Roos1cf585052018-01-03 18:43:27 +0100343 /**
344 * Creates an instance from a bounding {@link Path}.
345 *
346 * @hide
347 */
348 public static DisplayCutout fromBounds(Path path) {
Adrian Roosd07bafd2017-12-11 17:30:56 +0100349 RectF clipRect = new RectF();
350 path.computeBounds(clipRect, false /* unused */);
351 Region clipRegion = Region.obtain();
352 clipRegion.set((int) clipRect.left, (int) clipRect.top,
353 (int) clipRect.right, (int) clipRect.bottom);
354
Adrian Roos1cf585052018-01-03 18:43:27 +0100355 Region bounds = new Region();
Adrian Roosd07bafd2017-12-11 17:30:56 +0100356 bounds.setPath(path, clipRegion);
Adrian Roos1cf585052018-01-03 18:43:27 +0100357 clipRegion.recycle();
Adrian Roos24264212018-02-19 16:26:15 +0100358 return new DisplayCutout(ZERO_RECT, bounds, null /* frameSize */);
Adrian Roosd4970af2017-11-10 15:48:01 +0100359 }
360
361 /**
Adrian Roos16693f32018-01-18 17:45:52 +0100362 * Creates an instance according to @android:string/config_mainBuiltInDisplayCutout.
363 *
364 * @hide
365 */
366 public static DisplayCutout fromResources(Resources res, int displayWidth) {
Adrian Roos8d13bf12018-02-21 15:17:07 +0100367 return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
368 displayWidth, res.getDisplayMetrics().density);
369 }
370
371 /**
372 * Creates an instance according to the supplied {@link android.util.PathParser.PathData} spec.
373 *
374 * @hide
375 */
376 @VisibleForTesting(visibility = PRIVATE)
377 public static DisplayCutout fromSpec(String spec, int displayWidth, float density) {
Adrian Roos16693f32018-01-18 17:45:52 +0100378 if (TextUtils.isEmpty(spec)) {
379 return null;
380 }
Adrian Roos8d13bf12018-02-21 15:17:07 +0100381 synchronized (CACHE_LOCK) {
382 if (spec.equals(sCachedSpec) && sCachedDisplayWidth == displayWidth
383 && sCachedDensity == density) {
384 return sCachedCutout;
385 }
386 }
Adrian Roos16693f32018-01-18 17:45:52 +0100387 spec = spec.trim();
388 final boolean inDp = spec.endsWith(DP_MARKER);
389 if (inDp) {
390 spec = spec.substring(0, spec.length() - DP_MARKER.length());
391 }
392
393 Path p;
394 try {
395 p = PathParser.createPathFromPathData(spec);
396 } catch (Throwable e) {
397 Log.wtf(TAG, "Could not inflate cutout: ", e);
398 return null;
399 }
400
401 final Matrix m = new Matrix();
402 if (inDp) {
Adrian Roos8d13bf12018-02-21 15:17:07 +0100403 m.postScale(density, density);
Adrian Roos16693f32018-01-18 17:45:52 +0100404 }
405 m.postTranslate(displayWidth / 2f, 0);
406 p.transform(m);
Adrian Roos8d13bf12018-02-21 15:17:07 +0100407
408 final DisplayCutout result = fromBounds(p);
409 synchronized (CACHE_LOCK) {
410 sCachedSpec = spec;
411 sCachedDisplayWidth = displayWidth;
412 sCachedDensity = density;
413 sCachedCutout = result;
414 }
415 return result;
Adrian Roos16693f32018-01-18 17:45:52 +0100416 }
417
418 /**
Adrian Roosd4970af2017-11-10 15:48:01 +0100419 * Helper class for passing {@link DisplayCutout} through binder.
420 *
421 * Needed, because {@code readFromParcel} cannot be used with immutable classes.
422 *
423 * @hide
424 */
425 public static final class ParcelableWrapper implements Parcelable {
426
427 private DisplayCutout mInner;
428
429 public ParcelableWrapper() {
430 this(NO_CUTOUT);
431 }
432
433 public ParcelableWrapper(DisplayCutout cutout) {
434 mInner = cutout;
435 }
436
437 @Override
438 public int describeContents() {
439 return 0;
440 }
441
442 @Override
443 public void writeToParcel(Parcel out, int flags) {
Adrian Roos1cf585052018-01-03 18:43:27 +0100444 writeCutoutToParcel(mInner, out, flags);
445 }
446
447 /**
448 * Writes a DisplayCutout to a {@link Parcel}.
449 *
450 * @see #readCutoutFromParcel(Parcel)
451 */
452 public static void writeCutoutToParcel(DisplayCutout cutout, Parcel out, int flags) {
453 if (cutout == null) {
454 out.writeInt(-1);
455 } else if (cutout == NO_CUTOUT) {
Adrian Roosd4970af2017-11-10 15:48:01 +0100456 out.writeInt(0);
457 } else {
458 out.writeInt(1);
Adrian Roos1cf585052018-01-03 18:43:27 +0100459 out.writeTypedObject(cutout.mSafeInsets, flags);
460 out.writeTypedObject(cutout.mBounds, flags);
Adrian Roos24264212018-02-19 16:26:15 +0100461 if (cutout.mFrameSize != null) {
462 out.writeInt(cutout.mFrameSize.getWidth());
463 out.writeInt(cutout.mFrameSize.getHeight());
464 } else {
465 out.writeInt(-1);
466 }
Adrian Roosd4970af2017-11-10 15:48:01 +0100467 }
468 }
469
470 /**
471 * Similar to {@link Creator#createFromParcel(Parcel)}, but reads into an existing
472 * instance.
473 *
474 * Needed for AIDL out parameters.
475 */
476 public void readFromParcel(Parcel in) {
Adrian Roos1cf585052018-01-03 18:43:27 +0100477 mInner = readCutoutFromParcel(in);
Adrian Roosd4970af2017-11-10 15:48:01 +0100478 }
479
480 public static final Creator<ParcelableWrapper> CREATOR = new Creator<ParcelableWrapper>() {
481 @Override
482 public ParcelableWrapper createFromParcel(Parcel in) {
Adrian Roos1cf585052018-01-03 18:43:27 +0100483 return new ParcelableWrapper(readCutoutFromParcel(in));
Adrian Roosd4970af2017-11-10 15:48:01 +0100484 }
485
486 @Override
487 public ParcelableWrapper[] newArray(int size) {
488 return new ParcelableWrapper[size];
489 }
490 };
491
Adrian Roos1cf585052018-01-03 18:43:27 +0100492 /**
493 * Reads a DisplayCutout from a {@link Parcel}.
494 *
495 * @see #writeCutoutToParcel(DisplayCutout, Parcel, int)
496 */
497 public static DisplayCutout readCutoutFromParcel(Parcel in) {
498 int variant = in.readInt();
499 if (variant == -1) {
500 return null;
501 }
502 if (variant == 0) {
Adrian Roosd4970af2017-11-10 15:48:01 +0100503 return NO_CUTOUT;
504 }
505
Adrian Roosd4970af2017-11-10 15:48:01 +0100506 Rect safeInsets = in.readTypedObject(Rect.CREATOR);
Adrian Roosd07bafd2017-12-11 17:30:56 +0100507 Region bounds = in.readTypedObject(Region.CREATOR);
Adrian Roosd4970af2017-11-10 15:48:01 +0100508
Adrian Roos24264212018-02-19 16:26:15 +0100509 int width = in.readInt();
510 Size frameSize = width >= 0 ? new Size(width, in.readInt()) : null;
511
512 return new DisplayCutout(safeInsets, bounds, frameSize);
Adrian Roosd4970af2017-11-10 15:48:01 +0100513 }
514
515 public DisplayCutout get() {
516 return mInner;
517 }
518
519 public void set(ParcelableWrapper cutout) {
520 mInner = cutout.get();
521 }
522
523 public void set(DisplayCutout cutout) {
524 mInner = cutout;
525 }
526
527 @Override
528 public int hashCode() {
529 return mInner.hashCode();
530 }
531
532 @Override
533 public boolean equals(Object o) {
534 return o instanceof ParcelableWrapper
535 && mInner.equals(((ParcelableWrapper) o).mInner);
536 }
537
538 @Override
539 public String toString() {
540 return String.valueOf(mInner);
541 }
542 }
543}