• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package android.view;
18 
19 import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
20 import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
21 import static android.view.RoundedCorner.POSITION_TOP_LEFT;
22 import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
23 import static android.view.Surface.ROTATION_0;
24 import static android.view.Surface.ROTATION_270;
25 import static android.view.Surface.ROTATION_90;
26 
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.content.res.Resources;
30 import android.content.res.TypedArray;
31 import android.graphics.Rect;
32 import android.os.Parcel;
33 import android.os.Parcelable;
34 import android.util.DisplayUtils;
35 import android.util.Pair;
36 import android.view.RoundedCorner.Position;
37 
38 import com.android.internal.R;
39 import com.android.internal.annotations.GuardedBy;
40 import com.android.internal.annotations.VisibleForTesting;
41 
42 import java.util.Arrays;
43 
44 /**
45  * A class to create & manage all the {@link RoundedCorner} on the display.
46  *
47  * @hide
48  */
49 public class RoundedCorners implements Parcelable {
50 
51     public static final RoundedCorners NO_ROUNDED_CORNERS = new RoundedCorners(
52             new RoundedCorner(POSITION_TOP_LEFT), new RoundedCorner(POSITION_TOP_RIGHT),
53             new RoundedCorner(POSITION_BOTTOM_RIGHT), new RoundedCorner(POSITION_BOTTOM_LEFT));
54 
55     /**
56      * The number of possible positions at which rounded corners can be located.
57      */
58     public static final int ROUNDED_CORNER_POSITION_LENGTH = 4;
59 
60     private static final Object CACHE_LOCK = new Object();
61 
62     @GuardedBy("CACHE_LOCK")
63     private static int sCachedDisplayWidth;
64     @GuardedBy("CACHE_LOCK")
65     private static int sCachedDisplayHeight;
66     @GuardedBy("CACHE_LOCK")
67     private static Pair<Integer, Integer> sCachedRadii;
68     @GuardedBy("CACHE_LOCK")
69     private static RoundedCorners sCachedRoundedCorners;
70     @GuardedBy("CACHE_LOCK")
71     private static float sCachedPhysicalPixelDisplaySizeRatio;
72 
73     @VisibleForTesting
74     public final RoundedCorner[] mRoundedCorners;
75 
RoundedCorners(RoundedCorner[] roundedCorners)76     public RoundedCorners(RoundedCorner[] roundedCorners) {
77         mRoundedCorners = roundedCorners;
78     }
79 
RoundedCorners(RoundedCorner topLeft, RoundedCorner topRight, RoundedCorner bottomRight, RoundedCorner bottomLeft)80     public RoundedCorners(RoundedCorner topLeft, RoundedCorner topRight, RoundedCorner bottomRight,
81             RoundedCorner bottomLeft) {
82         mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
83         mRoundedCorners[POSITION_TOP_LEFT] = topLeft;
84         mRoundedCorners[POSITION_TOP_RIGHT] = topRight;
85         mRoundedCorners[POSITION_BOTTOM_RIGHT] = bottomRight;
86         mRoundedCorners[POSITION_BOTTOM_LEFT] = bottomLeft;
87     }
88 
RoundedCorners(RoundedCorners roundedCorners)89     public RoundedCorners(RoundedCorners roundedCorners) {
90         mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
91         for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
92             mRoundedCorners[i] = new RoundedCorner(roundedCorners.mRoundedCorners[i]);
93         }
94     }
95 
96     /**
97      * Creates the rounded corners according to @android:dimen/rounded_corner_radius,
98      * @android:dimen/rounded_corner_radius_top and @android:dimen/rounded_corner_radius_bottom
99      */
fromResources( Resources res, String displayUniqueId, int physicalDisplayWidth, int physicalDisplayHeight, int displayWidth, int displayHeight)100     public static RoundedCorners fromResources(
101             Resources res, String displayUniqueId, int physicalDisplayWidth,
102             int physicalDisplayHeight, int displayWidth, int displayHeight) {
103         return fromRadii(loadRoundedCornerRadii(res, displayUniqueId), physicalDisplayWidth,
104                 physicalDisplayHeight, displayWidth, displayHeight);
105     }
106 
107     /**
108      * Creates the rounded corners from radius
109      */
110     @VisibleForTesting
fromRadii(Pair<Integer, Integer> radii, int displayWidth, int displayHeight)111     public static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int displayWidth,
112             int displayHeight) {
113         return fromRadii(radii, displayWidth, displayHeight, displayWidth, displayHeight);
114     }
115 
fromRadii(Pair<Integer, Integer> radii, int physicalDisplayWidth, int physicalDisplayHeight, int displayWidth, int displayHeight)116     private static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int physicalDisplayWidth,
117             int physicalDisplayHeight, int displayWidth, int displayHeight) {
118         if (radii == null) {
119             return null;
120         }
121 
122         final float physicalPixelDisplaySizeRatio = DisplayUtils.getPhysicalPixelDisplaySizeRatio(
123                 physicalDisplayWidth, physicalDisplayHeight, displayWidth, displayHeight);
124 
125         synchronized (CACHE_LOCK) {
126             if (radii.equals(sCachedRadii) && sCachedDisplayWidth == displayWidth
127                     && sCachedDisplayHeight == displayHeight
128                     && sCachedPhysicalPixelDisplaySizeRatio == physicalPixelDisplaySizeRatio) {
129                 return sCachedRoundedCorners;
130             }
131         }
132 
133         final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
134         int topRadius = radii.first > 0 ? radii.first : 0;
135         int bottomRadius = radii.second > 0 ? radii.second : 0;
136         if (physicalPixelDisplaySizeRatio != 1f) {
137             topRadius = (int) (topRadius * physicalPixelDisplaySizeRatio + 0.5);
138             bottomRadius = (int) (bottomRadius * physicalPixelDisplaySizeRatio + 0.5);
139         }
140         for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
141             roundedCorners[i] = createRoundedCorner(
142                     i,
143                     i <= POSITION_TOP_RIGHT ? topRadius : bottomRadius,
144                     displayWidth,
145                     displayHeight);
146         }
147 
148         final RoundedCorners result = new RoundedCorners(roundedCorners);
149         synchronized (CACHE_LOCK) {
150             sCachedDisplayWidth = displayWidth;
151             sCachedDisplayHeight = displayHeight;
152             sCachedRadii = radii;
153             sCachedRoundedCorners = result;
154             sCachedPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
155         }
156         return result;
157     }
158 
159     /**
160      * Loads the rounded corner radii from resources.
161      *
162      * @param res
163      * @param displayUniqueId the display unique id.
164      * @return a Pair of radius. The first is the top rounded corner radius and second is the
165      * bottom corner radius.
166      */
167     @Nullable
loadRoundedCornerRadii( Resources res, String displayUniqueId)168     private static Pair<Integer, Integer> loadRoundedCornerRadii(
169             Resources res, String displayUniqueId) {
170         final int radiusDefault = getRoundedCornerRadius(res, displayUniqueId);
171         final int radiusTop = getRoundedCornerTopRadius(res, displayUniqueId);
172         final int radiusBottom = getRoundedCornerBottomRadius(res, displayUniqueId);
173         if (radiusDefault == 0 && radiusTop == 0 && radiusBottom == 0) {
174             return null;
175         }
176         final Pair<Integer, Integer> radii = new Pair<>(
177                         radiusTop > 0 ? radiusTop : radiusDefault,
178                         radiusBottom > 0 ? radiusBottom : radiusDefault);
179         return radii;
180     }
181 
182     /**
183      * Gets the default rounded corner radius of a display which is determined by the
184      * given display unique id.
185      *
186      * Loads the default dimen{@link R.dimen#rounded_corner_radius} if
187      * {@link R.array#config_displayUniqueIdArray} is not set.
188      *
189      * @hide
190      */
getRoundedCornerRadius(Resources res, String displayUniqueId)191     public static int getRoundedCornerRadius(Resources res, String displayUniqueId) {
192         final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
193         final TypedArray array = res.obtainTypedArray(R.array.config_roundedCornerRadiusArray);
194         int radius;
195         if (index >= 0 && index < array.length()) {
196             radius = array.getDimensionPixelSize(index, 0);
197         } else {
198             radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius);
199         }
200         array.recycle();
201         return radius;
202     }
203 
204     /**
205      * Gets the top rounded corner radius of a display which is determined by the
206      * given display unique id.
207      *
208      * Loads the default dimen{@link R.dimen#rounded_corner_radius_top} if
209      * {@link R.array#config_displayUniqueIdArray} is not set.
210      *
211      * @hide
212      */
getRoundedCornerTopRadius(Resources res, String displayUniqueId)213     public static int getRoundedCornerTopRadius(Resources res, String displayUniqueId) {
214         final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
215         final TypedArray array = res.obtainTypedArray(R.array.config_roundedCornerTopRadiusArray);
216         int radius;
217         if (index >= 0 && index < array.length()) {
218             radius = array.getDimensionPixelSize(index, 0);
219         } else {
220             radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_top);
221         }
222         array.recycle();
223         return radius;
224     }
225 
226     /**
227      * Gets the bottom rounded corner radius of a display which is determined by the
228      * given display unique id.
229      *
230      * Loads the default dimen{@link R.dimen#rounded_corner_radius_bottom} if
231      * {@link R.array#config_displayUniqueIdArray} is not set.
232      *
233      * @hide
234      */
getRoundedCornerBottomRadius(Resources res, String displayUniqueId)235     public static int getRoundedCornerBottomRadius(Resources res, String displayUniqueId) {
236         final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
237         final TypedArray array = res.obtainTypedArray(
238                 R.array.config_roundedCornerBottomRadiusArray);
239         int radius;
240         if (index >= 0 && index < array.length()) {
241             radius = array.getDimensionPixelSize(index, 0);
242         } else {
243             radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_bottom);
244         }
245         array.recycle();
246         return radius;
247     }
248 
249     /**
250      * Gets the rounded corner radius adjustment of a display which is determined by the
251      * given display unique id.
252      *
253      * Loads the default dimen{@link R.dimen#rounded_corner_radius_adjustment} if
254      * {@link R.array#config_displayUniqueIdArray} is not set.
255      *
256      * @hide
257      */
getRoundedCornerRadiusAdjustment(Resources res, String displayUniqueId)258     public static int getRoundedCornerRadiusAdjustment(Resources res, String displayUniqueId) {
259         final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
260         final TypedArray array = res.obtainTypedArray(
261                 R.array.config_roundedCornerRadiusAdjustmentArray);
262         int radius;
263         if (index >= 0 && index < array.length()) {
264             radius = array.getDimensionPixelSize(index, 0);
265         } else {
266             radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_adjustment);
267         }
268         array.recycle();
269         return radius;
270     }
271 
272     /**
273      * Gets the rounded corner top radius adjustment of a display which is determined by the
274      * given display unique id.
275      *
276      * Loads the default dimen{@link R.dimen#rounded_corner_radius_top_adjustment} if
277      * {@link R.array#config_displayUniqueIdArray} is not set.
278      *
279      * @hide
280      */
getRoundedCornerRadiusTopAdjustment(Resources res, String displayUniqueId)281     public static int getRoundedCornerRadiusTopAdjustment(Resources res, String displayUniqueId) {
282         final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
283         final TypedArray array = res.obtainTypedArray(
284                 R.array.config_roundedCornerTopRadiusAdjustmentArray);
285         int radius;
286         if (index >= 0 && index < array.length()) {
287             radius = array.getDimensionPixelSize(index, 0);
288         } else {
289             radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_top_adjustment);
290         }
291         array.recycle();
292         return radius;
293     }
294 
295     /**
296      * Gets the rounded corner bottom radius adjustment of a display which is determined by the
297      * given display unique id.
298      *
299      * Loads the default dimen{@link R.dimen#rounded_corner_radius_bottom_adjustment} if
300      * {@link R.array#config_displayUniqueIdArray} is not set.
301      *
302      * @hide
303      */
getRoundedCornerRadiusBottomAdjustment( Resources res, String displayUniqueId)304     public static int getRoundedCornerRadiusBottomAdjustment(
305             Resources res, String displayUniqueId) {
306         final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
307         final TypedArray array = res.obtainTypedArray(
308                 R.array.config_roundedCornerBottomRadiusAdjustmentArray);
309         int radius;
310         if (index >= 0 && index < array.length()) {
311             radius = array.getDimensionPixelSize(index, 0);
312         } else {
313             radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_bottom_adjustment);
314         }
315         array.recycle();
316         return radius;
317     }
318 
319     /**
320      * Gets whether a built-in display is round.
321      *
322      * Loads the default config{@link R.bool#config_mainBuiltInDisplayIsRound} if
323      * {@link R.array#config_displayUniqueIdArray} is not set.
324      *
325      * @hide
326      */
getBuiltInDisplayIsRound(Resources res, String displayUniqueId)327     public static boolean getBuiltInDisplayIsRound(Resources res, String displayUniqueId) {
328         final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
329         final TypedArray array = res.obtainTypedArray(R.array.config_builtInDisplayIsRoundArray);
330         boolean isRound;
331         if (index >= 0 && index < array.length()) {
332             isRound = array.getBoolean(index, false);
333         } else {
334             isRound = res.getBoolean(R.bool.config_mainBuiltInDisplayIsRound);
335         }
336         array.recycle();
337         return isRound;
338     }
339 
340     /**
341      * Insets the reference frame of the rounded corners.
342      *
343      * @param frame the frame of a window or any rectangle bounds
344      * @param roundedCornerFrame the frame that used to calculate relative {@link RoundedCorner}
345      * @return a copy of this instance which has been inset
346      */
insetWithFrame(Rect frame, Rect roundedCornerFrame)347     public RoundedCorners insetWithFrame(Rect frame, Rect roundedCornerFrame) {
348         int insetLeft = frame.left - roundedCornerFrame.left;
349         int insetTop = frame.top - roundedCornerFrame.top;
350         int insetRight = roundedCornerFrame.right - frame.right;
351         int insetBottom = roundedCornerFrame.bottom - frame.bottom;
352         final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
353         int centerX, centerY;
354         for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
355             if (mRoundedCorners[i].isEmpty()) {
356                 roundedCorners[i] = new RoundedCorner(i);
357                 continue;
358             }
359             final int radius = mRoundedCorners[i].getRadius();
360             switch (i) {
361                 case POSITION_TOP_LEFT:
362                     centerX = radius;
363                     centerY = radius;
364                     break;
365                 case POSITION_TOP_RIGHT:
366                     centerX = roundedCornerFrame.width() - radius;
367                     centerY = radius;
368                     break;
369                 case POSITION_BOTTOM_RIGHT:
370                     centerX = roundedCornerFrame.width() - radius;
371                     centerY = roundedCornerFrame.height() - radius;
372                     break;
373                 case POSITION_BOTTOM_LEFT:
374                     centerX = radius;
375                     centerY = roundedCornerFrame.height() - radius;
376                     break;
377                 default:
378                     throw new IllegalArgumentException(
379                             "The position is not one of the RoundedCornerPosition =" + i);
380             }
381             roundedCorners[i] = insetRoundedCorner(i, radius, centerX, centerY, insetLeft, insetTop,
382                     insetRight, insetBottom);
383         }
384         return new RoundedCorners(roundedCorners);
385     }
386 
387     /**
388      * Insets the reference frame of the rounded corners.
389      *
390      * @return a copy of this instance which has been inset
391      */
inset(int insetLeft, int insetTop, int insetRight, int insetBottom)392     public RoundedCorners inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
393         final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
394         for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
395             roundedCorners[i] = insetRoundedCorner(i, mRoundedCorners[i].getRadius(),
396                     mRoundedCorners[i].getCenter().x, mRoundedCorners[i].getCenter().y, insetLeft,
397                     insetTop, insetRight, insetBottom);
398         }
399         return new RoundedCorners(roundedCorners);
400     }
401 
insetRoundedCorner(@osition int position, int radius, int centerX, int centerY, int insetLeft, int insetTop, int insetRight, int insetBottom)402     private RoundedCorner insetRoundedCorner(@Position int position, int radius, int centerX,
403             int centerY, int insetLeft, int insetTop, int insetRight, int insetBottom) {
404         if (mRoundedCorners[position].isEmpty()) {
405             return new RoundedCorner(position);
406         }
407 
408         boolean hasRoundedCorner;
409         switch (position) {
410             case POSITION_TOP_LEFT:
411                 hasRoundedCorner = radius > insetTop && radius > insetLeft;
412                 break;
413             case POSITION_TOP_RIGHT:
414                 hasRoundedCorner = radius > insetTop && radius > insetRight;
415                 break;
416             case POSITION_BOTTOM_RIGHT:
417                 hasRoundedCorner = radius > insetBottom && radius > insetRight;
418                 break;
419             case POSITION_BOTTOM_LEFT:
420                 hasRoundedCorner = radius > insetBottom && radius > insetLeft;
421                 break;
422             default:
423                 throw new IllegalArgumentException(
424                         "The position is not one of the RoundedCornerPosition =" + position);
425         }
426         return new RoundedCorner(
427                 position, radius,
428                 hasRoundedCorner ? centerX - insetLeft : 0,
429                 hasRoundedCorner ? centerY - insetTop : 0);
430     }
431 
432     /**
433      * Returns the {@link RoundedCorner} of the given position if there is one.
434      *
435      * @param position the position of the rounded corner on the display.
436      * @return the rounded corner of the given position. Returns {@code null} if
437      * {@link RoundedCorner#isEmpty()} is {@code true}.
438      */
439     @Nullable
getRoundedCorner(@osition int position)440     public RoundedCorner getRoundedCorner(@Position int position) {
441         return mRoundedCorners[position].isEmpty()
442                 ? null : new RoundedCorner(mRoundedCorners[position]);
443     }
444 
445     /**
446      * Sets the rounded corner of given position.
447      *
448      * @param position the position of this rounded corner
449      * @param roundedCorner the rounded corner or null if there is none
450      */
setRoundedCorner(@osition int position, @Nullable RoundedCorner roundedCorner)451     public void setRoundedCorner(@Position int position, @Nullable RoundedCorner roundedCorner) {
452         mRoundedCorners[position] = roundedCorner == null
453                 ? new RoundedCorner(position) : roundedCorner;
454     }
455 
456     /**
457      * Returns an array of {@link RoundedCorner}s. Ordinal value of RoundedCornerPosition is used
458      * as an index of the array.
459      *
460      * @return an array of {@link RoundedCorner}s, one for each rounded corner area.
461      */
getAllRoundedCorners()462     public RoundedCorner[] getAllRoundedCorners() {
463         RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
464         for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
465             roundedCorners[i] = new RoundedCorner(roundedCorners[i]);
466         }
467         return roundedCorners;
468     }
469 
470     /**
471      * Returns a scaled RoundedCorners.
472      */
scale(float scale)473     public RoundedCorners scale(float scale) {
474         if (scale == 1f) {
475             return this;
476         }
477 
478         RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
479         for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
480             final RoundedCorner roundedCorner = mRoundedCorners[i];
481             roundedCorners[i] = new RoundedCorner(
482                     i,
483                     (int) (roundedCorner.getRadius() * scale),
484                     (int) (roundedCorner.getCenter().x * scale),
485                     (int) (roundedCorner.getCenter().y * scale));
486         }
487         return new RoundedCorners(roundedCorners);
488     }
489 
490     /**
491      * Returns a rotated RoundedCorners.
492      */
rotate(@urface.Rotation int rotation, int initialDisplayWidth, int initialDisplayHeight)493     public RoundedCorners rotate(@Surface.Rotation int rotation, int initialDisplayWidth,
494             int initialDisplayHeight) {
495         if (rotation == ROTATION_0) {
496             return this;
497         }
498         final boolean isSizeFlipped = rotation == ROTATION_90 || rotation == ROTATION_270;
499         RoundedCorner[] newCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
500         int newPosistion;
501         for (int i = 0; i < mRoundedCorners.length; i++) {
502             newPosistion = getRotatedIndex(i, rotation);
503             newCorners[newPosistion] = createRoundedCorner(
504                     newPosistion,
505                     mRoundedCorners[i].getRadius(),
506                     isSizeFlipped ? initialDisplayHeight : initialDisplayWidth,
507                     isSizeFlipped ? initialDisplayWidth : initialDisplayHeight);
508         }
509         return new RoundedCorners(newCorners);
510     }
511 
createRoundedCorner(@osition int position, int radius, int displayWidth, int displayHeight)512     private static RoundedCorner createRoundedCorner(@Position int position,
513             int radius, int displayWidth, int displayHeight) {
514         switch (position) {
515             case POSITION_TOP_LEFT:
516                 return new RoundedCorner(
517                         POSITION_TOP_LEFT,
518                         radius,
519                         radius > 0 ? radius : 0,
520                         radius > 0 ? radius : 0);
521             case POSITION_TOP_RIGHT:
522                 return new RoundedCorner(
523                         POSITION_TOP_RIGHT,
524                         radius,
525                         radius > 0 ? displayWidth - radius : 0,
526                         radius > 0 ? radius : 0);
527             case POSITION_BOTTOM_RIGHT:
528                 return new RoundedCorner(
529                         POSITION_BOTTOM_RIGHT,
530                         radius,
531                         radius > 0 ? displayWidth - radius : 0,
532                         radius > 0 ? displayHeight - radius : 0);
533             case POSITION_BOTTOM_LEFT:
534                 return new RoundedCorner(
535                         POSITION_BOTTOM_LEFT,
536                         radius,
537                         radius > 0 ? radius : 0,
538                         radius > 0 ? displayHeight - radius  : 0);
539             default:
540                 throw new IllegalArgumentException(
541                         "The position is not one of the RoundedCornerPosition =" + position);
542         }
543     }
544 
getRotatedIndex(int position, int rotation)545     private static int getRotatedIndex(int position, int rotation) {
546         return (position - rotation + ROUNDED_CORNER_POSITION_LENGTH) % 4;
547     }
548 
549     @Override
hashCode()550     public int hashCode() {
551         int result = 0;
552         for (RoundedCorner roundedCorner : mRoundedCorners) {
553             result = result * 31 + roundedCorner.hashCode();
554         }
555         return result;
556     }
557 
558     @Override
equals(Object o)559     public boolean equals(Object o) {
560         if (o == this) {
561             return true;
562         }
563         if (o instanceof RoundedCorners) {
564             RoundedCorners r = (RoundedCorners) o;
565             return Arrays.deepEquals(mRoundedCorners, r.mRoundedCorners);
566         }
567         return false;
568     }
569 
570     @Override
toString()571     public String toString() {
572         return "RoundedCorners{" + Arrays.toString(mRoundedCorners) + "}";
573     }
574 
575     @Override
describeContents()576     public int describeContents() {
577         return 0;
578     }
579 
580     @Override
writeToParcel(Parcel dest, int flags)581     public void writeToParcel(Parcel dest, int flags) {
582         if (equals(NO_ROUNDED_CORNERS)) {
583             dest.writeInt(0);
584         } else {
585             dest.writeInt(1);
586             dest.writeTypedArray(mRoundedCorners, flags);
587         }
588     }
589 
590     public static final @NonNull Creator<RoundedCorners> CREATOR = new Creator<RoundedCorners>() {
591         @Override
592         public RoundedCorners createFromParcel(Parcel in) {
593             int variant = in.readInt();
594             if (variant == 0) {
595                 return NO_ROUNDED_CORNERS;
596             }
597             RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
598             in.readTypedArray(roundedCorners, RoundedCorner.CREATOR);
599             return new RoundedCorners(roundedCorners);
600         }
601 
602         @Override
603         public RoundedCorners[] newArray(int size) {
604             return new RoundedCorners[size];
605         }
606     };
607 }
608