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 // For devices with round displays (e.g. watches) that don't otherwise provide the rounded 202 // corner radius via resource overlays, we can infer the corner radius directly from the 203 // display size. 204 if (radius == 0 && res.getConfiguration().isScreenRound()) { 205 radius = res.getDisplayMetrics().widthPixels / 2; 206 } 207 return radius; 208 } 209 210 /** 211 * Gets the top rounded corner radius of a display which is determined by the 212 * given display unique id. 213 * 214 * Loads the default dimen{@link R.dimen#rounded_corner_radius_top} if 215 * {@link R.array#config_displayUniqueIdArray} is not set. 216 * 217 * @hide 218 */ getRoundedCornerTopRadius(Resources res, String displayUniqueId)219 public static int getRoundedCornerTopRadius(Resources res, String displayUniqueId) { 220 final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId); 221 final TypedArray array = res.obtainTypedArray(R.array.config_roundedCornerTopRadiusArray); 222 int radius; 223 if (index >= 0 && index < array.length()) { 224 radius = array.getDimensionPixelSize(index, 0); 225 } else { 226 radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_top); 227 } 228 array.recycle(); 229 return radius; 230 } 231 232 /** 233 * Gets the bottom rounded corner radius of a display which is determined by the 234 * given display unique id. 235 * 236 * Loads the default dimen{@link R.dimen#rounded_corner_radius_bottom} if 237 * {@link R.array#config_displayUniqueIdArray} is not set. 238 * 239 * @hide 240 */ getRoundedCornerBottomRadius(Resources res, String displayUniqueId)241 public static int getRoundedCornerBottomRadius(Resources res, String displayUniqueId) { 242 final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId); 243 final TypedArray array = res.obtainTypedArray( 244 R.array.config_roundedCornerBottomRadiusArray); 245 int radius; 246 if (index >= 0 && index < array.length()) { 247 radius = array.getDimensionPixelSize(index, 0); 248 } else { 249 radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_bottom); 250 } 251 array.recycle(); 252 return radius; 253 } 254 255 /** 256 * Gets the rounded corner radius adjustment of a display which is determined by the 257 * given display unique id. 258 * 259 * Loads the default dimen{@link R.dimen#rounded_corner_radius_adjustment} if 260 * {@link R.array#config_displayUniqueIdArray} is not set. 261 * 262 * @hide 263 */ getRoundedCornerRadiusAdjustment(Resources res, String displayUniqueId)264 public static int getRoundedCornerRadiusAdjustment(Resources res, String displayUniqueId) { 265 final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId); 266 final TypedArray array = res.obtainTypedArray( 267 R.array.config_roundedCornerRadiusAdjustmentArray); 268 int radius; 269 if (index >= 0 && index < array.length()) { 270 radius = array.getDimensionPixelSize(index, 0); 271 } else { 272 radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_adjustment); 273 } 274 array.recycle(); 275 return radius; 276 } 277 278 /** 279 * Gets the rounded corner top radius adjustment of a display which is determined by the 280 * given display unique id. 281 * 282 * Loads the default dimen{@link R.dimen#rounded_corner_radius_top_adjustment} if 283 * {@link R.array#config_displayUniqueIdArray} is not set. 284 * 285 * @hide 286 */ getRoundedCornerRadiusTopAdjustment(Resources res, String displayUniqueId)287 public static int getRoundedCornerRadiusTopAdjustment(Resources res, String displayUniqueId) { 288 final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId); 289 final TypedArray array = res.obtainTypedArray( 290 R.array.config_roundedCornerTopRadiusAdjustmentArray); 291 int radius; 292 if (index >= 0 && index < array.length()) { 293 radius = array.getDimensionPixelSize(index, 0); 294 } else { 295 radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_top_adjustment); 296 } 297 array.recycle(); 298 return radius; 299 } 300 301 /** 302 * Gets the rounded corner bottom radius adjustment of a display which is determined by the 303 * given display unique id. 304 * 305 * Loads the default dimen{@link R.dimen#rounded_corner_radius_bottom_adjustment} if 306 * {@link R.array#config_displayUniqueIdArray} is not set. 307 * 308 * @hide 309 */ getRoundedCornerRadiusBottomAdjustment( Resources res, String displayUniqueId)310 public static int getRoundedCornerRadiusBottomAdjustment( 311 Resources res, String displayUniqueId) { 312 final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId); 313 final TypedArray array = res.obtainTypedArray( 314 R.array.config_roundedCornerBottomRadiusAdjustmentArray); 315 int radius; 316 if (index >= 0 && index < array.length()) { 317 radius = array.getDimensionPixelSize(index, 0); 318 } else { 319 radius = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_bottom_adjustment); 320 } 321 array.recycle(); 322 return radius; 323 } 324 325 /** 326 * Gets whether a built-in display is round. 327 * 328 * Loads the default config{@link R.bool#config_mainBuiltInDisplayIsRound} if 329 * {@link R.array#config_displayUniqueIdArray} is not set. 330 * 331 * @hide 332 */ getBuiltInDisplayIsRound(Resources res, String displayUniqueId)333 public static boolean getBuiltInDisplayIsRound(Resources res, String displayUniqueId) { 334 final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId); 335 final TypedArray array = res.obtainTypedArray(R.array.config_builtInDisplayIsRoundArray); 336 boolean isRound; 337 if (index >= 0 && index < array.length()) { 338 isRound = array.getBoolean(index, false); 339 } else { 340 isRound = res.getBoolean(R.bool.config_mainBuiltInDisplayIsRound); 341 } 342 array.recycle(); 343 return isRound; 344 } 345 346 /** 347 * Insets the reference frame of the rounded corners. 348 * 349 * @param frame the frame of a window or any rectangle bounds 350 * @param roundedCornerFrame the frame that used to calculate relative {@link RoundedCorner} 351 * @return a copy of this instance which has been inset 352 */ insetWithFrame(Rect frame, Rect roundedCornerFrame)353 public RoundedCorners insetWithFrame(Rect frame, Rect roundedCornerFrame) { 354 int insetLeft = frame.left - roundedCornerFrame.left; 355 int insetTop = frame.top - roundedCornerFrame.top; 356 int insetRight = roundedCornerFrame.right - frame.right; 357 int insetBottom = roundedCornerFrame.bottom - frame.bottom; 358 final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH]; 359 int centerX, centerY; 360 for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) { 361 if (mRoundedCorners[i].isEmpty()) { 362 roundedCorners[i] = new RoundedCorner(i); 363 continue; 364 } 365 final int radius = mRoundedCorners[i].getRadius(); 366 switch (i) { 367 case POSITION_TOP_LEFT: 368 centerX = radius; 369 centerY = radius; 370 break; 371 case POSITION_TOP_RIGHT: 372 centerX = roundedCornerFrame.width() - radius; 373 centerY = radius; 374 break; 375 case POSITION_BOTTOM_RIGHT: 376 centerX = roundedCornerFrame.width() - radius; 377 centerY = roundedCornerFrame.height() - radius; 378 break; 379 case POSITION_BOTTOM_LEFT: 380 centerX = radius; 381 centerY = roundedCornerFrame.height() - radius; 382 break; 383 default: 384 throw new IllegalArgumentException( 385 "The position is not one of the RoundedCornerPosition =" + i); 386 } 387 roundedCorners[i] = insetRoundedCorner(i, radius, centerX, centerY, insetLeft, insetTop, 388 insetRight, insetBottom); 389 } 390 return new RoundedCorners(roundedCorners); 391 } 392 393 /** 394 * Insets the reference frame of the rounded corners. 395 * 396 * @return a copy of this instance which has been inset 397 */ inset(int insetLeft, int insetTop, int insetRight, int insetBottom)398 public RoundedCorners inset(int insetLeft, int insetTop, int insetRight, int insetBottom) { 399 final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH]; 400 for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) { 401 roundedCorners[i] = insetRoundedCorner(i, mRoundedCorners[i].getRadius(), 402 mRoundedCorners[i].getCenter().x, mRoundedCorners[i].getCenter().y, insetLeft, 403 insetTop, insetRight, insetBottom); 404 } 405 return new RoundedCorners(roundedCorners); 406 } 407 insetRoundedCorner(@osition int position, int radius, int centerX, int centerY, int insetLeft, int insetTop, int insetRight, int insetBottom)408 private RoundedCorner insetRoundedCorner(@Position int position, int radius, int centerX, 409 int centerY, int insetLeft, int insetTop, int insetRight, int insetBottom) { 410 if (mRoundedCorners[position].isEmpty()) { 411 return new RoundedCorner(position); 412 } 413 414 boolean hasRoundedCorner; 415 switch (position) { 416 case POSITION_TOP_LEFT: 417 hasRoundedCorner = radius > insetTop && radius > insetLeft; 418 break; 419 case POSITION_TOP_RIGHT: 420 hasRoundedCorner = radius > insetTop && radius > insetRight; 421 break; 422 case POSITION_BOTTOM_RIGHT: 423 hasRoundedCorner = radius > insetBottom && radius > insetRight; 424 break; 425 case POSITION_BOTTOM_LEFT: 426 hasRoundedCorner = radius > insetBottom && radius > insetLeft; 427 break; 428 default: 429 throw new IllegalArgumentException( 430 "The position is not one of the RoundedCornerPosition =" + position); 431 } 432 return new RoundedCorner( 433 position, radius, 434 hasRoundedCorner ? centerX - insetLeft : 0, 435 hasRoundedCorner ? centerY - insetTop : 0); 436 } 437 438 /** 439 * Returns the {@link RoundedCorner} of the given position if there is one. 440 * 441 * @param position the position of the rounded corner on the display. 442 * @return the rounded corner of the given position. Returns {@code null} if 443 * {@link RoundedCorner#isEmpty()} is {@code true}. 444 */ 445 @Nullable getRoundedCorner(@osition int position)446 public RoundedCorner getRoundedCorner(@Position int position) { 447 return mRoundedCorners[position].isEmpty() 448 ? null : new RoundedCorner(mRoundedCorners[position]); 449 } 450 451 /** 452 * Sets the rounded corner of given position. 453 * 454 * @param position the position of this rounded corner 455 * @param roundedCorner the rounded corner or null if there is none 456 */ setRoundedCorner(@osition int position, @Nullable RoundedCorner roundedCorner)457 public void setRoundedCorner(@Position int position, @Nullable RoundedCorner roundedCorner) { 458 mRoundedCorners[position] = roundedCorner == null 459 ? new RoundedCorner(position) : roundedCorner; 460 } 461 462 /** 463 * Returns an array of {@link RoundedCorner}s. Ordinal value of RoundedCornerPosition is used 464 * as an index of the array. 465 * 466 * @return an array of {@link RoundedCorner}s, one for each rounded corner area. 467 */ getAllRoundedCorners()468 public RoundedCorner[] getAllRoundedCorners() { 469 RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH]; 470 for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) { 471 roundedCorners[i] = new RoundedCorner(roundedCorners[i]); 472 } 473 return roundedCorners; 474 } 475 476 /** 477 * Returns a scaled RoundedCorners. 478 */ scale(float scale)479 public RoundedCorners scale(float scale) { 480 if (scale == 1f) { 481 return this; 482 } 483 484 RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH]; 485 for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) { 486 final RoundedCorner roundedCorner = mRoundedCorners[i]; 487 roundedCorners[i] = new RoundedCorner( 488 i, 489 (int) (roundedCorner.getRadius() * scale), 490 (int) (roundedCorner.getCenter().x * scale), 491 (int) (roundedCorner.getCenter().y * scale)); 492 } 493 return new RoundedCorners(roundedCorners); 494 } 495 496 /** 497 * Returns a rotated RoundedCorners. 498 */ rotate(@urface.Rotation int rotation, int initialDisplayWidth, int initialDisplayHeight)499 public RoundedCorners rotate(@Surface.Rotation int rotation, int initialDisplayWidth, 500 int initialDisplayHeight) { 501 if (rotation == ROTATION_0) { 502 return this; 503 } 504 final boolean isSizeFlipped = rotation == ROTATION_90 || rotation == ROTATION_270; 505 RoundedCorner[] newCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH]; 506 int newPosistion; 507 for (int i = 0; i < mRoundedCorners.length; i++) { 508 newPosistion = getRotatedIndex(i, rotation); 509 newCorners[newPosistion] = createRoundedCorner( 510 newPosistion, 511 mRoundedCorners[i].getRadius(), 512 isSizeFlipped ? initialDisplayHeight : initialDisplayWidth, 513 isSizeFlipped ? initialDisplayWidth : initialDisplayHeight); 514 } 515 return new RoundedCorners(newCorners); 516 } 517 createRoundedCorner(@osition int position, int radius, int displayWidth, int displayHeight)518 private static RoundedCorner createRoundedCorner(@Position int position, 519 int radius, int displayWidth, int displayHeight) { 520 switch (position) { 521 case POSITION_TOP_LEFT: 522 return new RoundedCorner( 523 POSITION_TOP_LEFT, 524 radius, 525 radius > 0 ? radius : 0, 526 radius > 0 ? radius : 0); 527 case POSITION_TOP_RIGHT: 528 return new RoundedCorner( 529 POSITION_TOP_RIGHT, 530 radius, 531 radius > 0 ? displayWidth - radius : 0, 532 radius > 0 ? radius : 0); 533 case POSITION_BOTTOM_RIGHT: 534 return new RoundedCorner( 535 POSITION_BOTTOM_RIGHT, 536 radius, 537 radius > 0 ? displayWidth - radius : 0, 538 radius > 0 ? displayHeight - radius : 0); 539 case POSITION_BOTTOM_LEFT: 540 return new RoundedCorner( 541 POSITION_BOTTOM_LEFT, 542 radius, 543 radius > 0 ? radius : 0, 544 radius > 0 ? displayHeight - radius : 0); 545 default: 546 throw new IllegalArgumentException( 547 "The position is not one of the RoundedCornerPosition =" + position); 548 } 549 } 550 getRotatedIndex(int position, int rotation)551 private static int getRotatedIndex(int position, int rotation) { 552 return (position - rotation + ROUNDED_CORNER_POSITION_LENGTH) % 4; 553 } 554 555 @Override hashCode()556 public int hashCode() { 557 int result = 0; 558 for (RoundedCorner roundedCorner : mRoundedCorners) { 559 result = result * 31 + roundedCorner.hashCode(); 560 } 561 return result; 562 } 563 564 @Override equals(Object o)565 public boolean equals(Object o) { 566 if (o == this) { 567 return true; 568 } 569 if (o instanceof RoundedCorners) { 570 RoundedCorners r = (RoundedCorners) o; 571 return Arrays.deepEquals(mRoundedCorners, r.mRoundedCorners); 572 } 573 return false; 574 } 575 576 @Override toString()577 public String toString() { 578 return "RoundedCorners{" + Arrays.toString(mRoundedCorners) + "}"; 579 } 580 581 @Override describeContents()582 public int describeContents() { 583 return 0; 584 } 585 586 @Override writeToParcel(Parcel dest, int flags)587 public void writeToParcel(Parcel dest, int flags) { 588 if (equals(NO_ROUNDED_CORNERS)) { 589 dest.writeInt(0); 590 } else { 591 dest.writeInt(1); 592 dest.writeTypedArray(mRoundedCorners, flags); 593 } 594 } 595 596 public static final @NonNull Creator<RoundedCorners> CREATOR = new Creator<RoundedCorners>() { 597 @Override 598 public RoundedCorners createFromParcel(Parcel in) { 599 int variant = in.readInt(); 600 if (variant == 0) { 601 return NO_ROUNDED_CORNERS; 602 } 603 RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH]; 604 in.readTypedArray(roundedCorners, RoundedCorner.CREATOR); 605 return new RoundedCorners(roundedCorners); 606 } 607 608 @Override 609 public RoundedCorners[] newArray(int size) { 610 return new RoundedCorners[size]; 611 } 612 }; 613 } 614