1 package org.robolectric.res.android; 2 3 import static org.robolectric.res.android.Util.isTruthy; 4 5 import com.google.common.base.Charsets; 6 import com.google.common.collect.Iterators; 7 import com.google.common.collect.PeekingIterator; 8 import java.util.Arrays; 9 import java.util.Objects; 10 import java.util.regex.Matcher; 11 import java.util.regex.Pattern; 12 13 /** 14 * transliterated from 15 * https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/tools/aapt2/ConfigDescription.cpp 16 */ 17 public class ConfigDescription { 18 public static final int SDK_CUPCAKE = 3; 19 public static final int SDK_DONUT = 4; 20 public static final int SDK_ECLAIR = 5; 21 public static final int SDK_ECLAIR_0_1 = 6; 22 public static final int SDK_ECLAIR_MR1 = 7; 23 public static final int SDK_FROYO = 8; 24 public static final int SDK_GINGERBREAD = 9; 25 public static final int SDK_GINGERBREAD_MR1 = 10; 26 public static final int SDK_HONEYCOMB = 11; 27 public static final int SDK_HONEYCOMB_MR1 = 12; 28 public static final int SDK_HONEYCOMB_MR2 = 13; 29 public static final int SDK_ICE_CREAM_SANDWICH = 14; 30 public static final int SDK_ICE_CREAM_SANDWICH_MR1 = 15; 31 public static final int SDK_JELLY_BEAN = 16; 32 public static final int SDK_JELLY_BEAN_MR1 = 17; 33 public static final int SDK_JELLY_BEAN_MR2 = 18; 34 public static final int SDK_KITKAT = 19; 35 public static final int SDK_KITKAT_WATCH = 20; 36 public static final int SDK_LOLLIPOP = 21; 37 public static final int SDK_LOLLIPOP_MR1 = 22; 38 public static final int SDK_MNC = 23; 39 public static final int SDK_NOUGAT = 24; 40 public static final int SDK_NOUGAT_MR1 = 25; 41 public static final int SDK_O = 26; 42 43 /** 44 * Constant used to to represent MNC (Mobile Network Code) zero. 45 * 0 cannot be used, since it is used to represent an undefined MNC. 46 */ 47 private static final int ACONFIGURATION_MNC_ZERO = 0xffff; 48 49 private static final String kWildcardName = "any"; 50 51 private static final Pattern MCC_PATTERN = Pattern.compile("mcc([\\d]+)"); 52 private static final Pattern MNC_PATTERN = Pattern.compile("mnc([\\d]+)"); 53 private static final Pattern SMALLEST_SCREEN_WIDTH_PATTERN = Pattern.compile("^sw([0-9]+)dp"); 54 private static final Pattern SCREEN_WIDTH_PATTERN = Pattern.compile("^w([0-9]+)dp"); 55 private static final Pattern SCREEN_HEIGHT_PATTERN = Pattern.compile("^h([0-9]+)dp"); 56 private static final Pattern DENSITY_PATTERN = Pattern.compile("^([0-9]+)dpi"); 57 private static final Pattern HEIGHT_WIDTH_PATTERN = Pattern.compile("^([0-9]+)x([0-9]+)"); 58 private static final Pattern VERSION_QUALIFIER_PATTERN = Pattern.compile("v([0-9]+)$"); 59 60 public static class LocaleValue { 61 62 String language; 63 String region; 64 String script; 65 String variant; 66 set_language(String language_chars)67 void set_language(String language_chars) { 68 language = language_chars.trim().toLowerCase(); 69 } 70 set_region(String region_chars)71 void set_region(String region_chars) { 72 region = region_chars.trim().toUpperCase(); 73 } 74 set_script(String script_chars)75 void set_script(String script_chars) { 76 script = String.valueOf(Character.toUpperCase(script_chars.charAt(0))) + 77 script_chars.substring(1).toLowerCase(); 78 } 79 set_variant(String variant_chars)80 void set_variant(String variant_chars) { 81 variant = variant_chars.trim(); 82 } 83 84 is_alpha(final String str)85 static boolean is_alpha(final String str) { 86 for (int i = 0; i < str.length(); i++) { 87 if (!Character.isAlphabetic(str.charAt(i))) { 88 return false; 89 } 90 } 91 92 return true; 93 } 94 initFromParts(PeekingIterator<String> iter)95 int initFromParts(PeekingIterator<String> iter) { 96 97 String part = iter.peek(); 98 if (part.startsWith("b+")) { 99 // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags, 100 // except that the separator is "+" and not "-". 101 String[] subtags = part.substring(2).toLowerCase().split("\\+", 0); 102 if (subtags.length == 1) { 103 set_language(subtags[0]); 104 } else if (subtags.length == 2) { 105 set_language(subtags[0]); 106 107 // The second tag can either be a region, a variant or a script. 108 switch (subtags[1].length()) { 109 case 2: 110 case 3: 111 set_region(subtags[1]); 112 break; 113 case 4: 114 if ('0' <= subtags[1].charAt(0) && subtags[1].charAt(0) <= '9') { 115 // This is a variant: fall through 116 } else { 117 set_script(subtags[1]); 118 break; 119 } 120 // fall through 121 case 5: 122 case 6: 123 case 7: 124 case 8: 125 set_variant(subtags[1]); 126 break; 127 default: 128 return -1; 129 } 130 } else if (subtags.length == 3) { 131 // The language is always the first subtag. 132 set_language(subtags[0]); 133 134 // The second subtag can either be a script or a region code. 135 // If its size is 4, it's a script code, else it's a region code. 136 if (subtags[1].length() == 4) { 137 set_script(subtags[1]); 138 } else if (subtags[1].length() == 2 || subtags[1].length() == 3) { 139 set_region(subtags[1]); 140 } else { 141 return -1; 142 } 143 144 // The third tag can either be a region code (if the second tag was 145 // a script), else a variant code. 146 if (subtags[2].length() >= 4) { 147 set_variant(subtags[2]); 148 } else { 149 set_region(subtags[2]); 150 } 151 } else if (subtags.length == 4) { 152 set_language(subtags[0]); 153 set_script(subtags[1]); 154 set_region(subtags[2]); 155 set_variant(subtags[3]); 156 } else { 157 return -1; 158 } 159 160 iter.next(); 161 162 } else { 163 if ((part.length() == 2 || part.length() == 3) && is_alpha(part) && 164 !Objects.equals(part, "car")) { 165 set_language(part); 166 iter.next(); 167 168 if (iter.hasNext()) { 169 final String region_part = iter.peek(); 170 if (region_part.charAt(0) == 'r' && region_part.length() == 3) { 171 set_region(region_part.substring(1)); 172 iter.next(); 173 } 174 } 175 } 176 } 177 178 return 0; 179 } 180 writeTo(ResTable_config out)181 public void writeTo(ResTable_config out) { 182 out.packLanguage(language); 183 out.packRegion(region); 184 185 Arrays.fill(out.localeScript, (byte) 0); 186 byte[] scriptBytes = script == null ? new byte[4] : script.getBytes(Charsets.UTF_8); 187 System.arraycopy(scriptBytes, 0, out.localeScript, 0, scriptBytes.length); 188 189 Arrays.fill(out.localeVariant, (byte) 0); 190 byte[] variantBytes = variant == null ? new byte[8] : variant.getBytes(Charsets.UTF_8); 191 System.arraycopy(variantBytes, 0, out.localeVariant, 0, variantBytes.length); 192 } 193 } 194 parse(final String str, ResTable_config out)195 public static boolean parse(final String str, ResTable_config out) { 196 return parse(str, out, true); 197 } 198 parse(final String str, ResTable_config out, boolean applyVersionForCompat)199 public static boolean parse(final String str, ResTable_config out, boolean applyVersionForCompat) { 200 PeekingIterator<String> part_iter = Iterators 201 .peekingIterator(Arrays.asList(str.toLowerCase().split("-")).iterator()); 202 203 LocaleValue locale = new LocaleValue(); 204 205 boolean success = !part_iter.hasNext(); 206 if (part_iter.hasNext() && parseMcc(part_iter.peek(), out)) { 207 part_iter.next(); 208 if (!part_iter.hasNext()) { 209 success = !part_iter.hasNext(); 210 } 211 } 212 213 if (part_iter.hasNext() && parseMnc(part_iter.peek(), out)) { 214 part_iter.next(); 215 if (!part_iter.hasNext()) { 216 success = !part_iter.hasNext(); 217 } 218 } 219 220 if (part_iter.hasNext()) { 221 // Locale spans a few '-' separators, so we let it 222 // control the index. 223 int parts_consumed = locale.initFromParts(part_iter); 224 if (parts_consumed < 0) { 225 return false; 226 } else { 227 locale.writeTo(out); 228 if (!part_iter.hasNext()) { 229 success = !part_iter.hasNext(); 230 } 231 } 232 } 233 234 if (part_iter.hasNext() && parseLayoutDirection(part_iter.peek(), out)) { 235 part_iter.next(); 236 if (!part_iter.hasNext()) { 237 success = !part_iter.hasNext(); 238 } 239 } 240 241 if (part_iter.hasNext() && parseSmallestScreenWidthDp(part_iter.peek(), out)) { 242 part_iter.next(); 243 if (!part_iter.hasNext()) { 244 success = !part_iter.hasNext(); 245 } 246 } 247 248 if (part_iter.hasNext() && parseScreenWidthDp(part_iter.peek(), out)) { 249 part_iter.next(); 250 if (!part_iter.hasNext()) { 251 success = !part_iter.hasNext(); 252 } 253 } 254 255 if (part_iter.hasNext() && parseScreenHeightDp(part_iter.peek(), out)) { 256 part_iter.next(); 257 if (!part_iter.hasNext()) { 258 success = !part_iter.hasNext(); 259 } 260 } 261 262 if (part_iter.hasNext() && parseScreenLayoutSize(part_iter.peek(), out)) { 263 part_iter.next(); 264 if (!part_iter.hasNext()) { 265 success = !part_iter.hasNext(); 266 } 267 } 268 269 if (part_iter.hasNext() && parseScreenLayoutLong(part_iter.peek(), out)) { 270 part_iter.next(); 271 if (!part_iter.hasNext()) { 272 success = !part_iter.hasNext(); 273 } 274 } 275 276 if (part_iter.hasNext() && parseScreenRound(part_iter.peek(), out)) { 277 part_iter.next(); 278 if (!part_iter.hasNext()) { 279 success = !part_iter.hasNext(); 280 } 281 } 282 283 if (part_iter.hasNext() && parseWideColorGamut(part_iter.peek(), out)) { 284 part_iter.next(); 285 if (!part_iter.hasNext()) { 286 success = !part_iter.hasNext(); 287 } 288 } 289 290 if (part_iter.hasNext() && parseHdr(part_iter.peek(), out)) { 291 part_iter.next(); 292 if (!part_iter.hasNext()) { 293 success = !part_iter.hasNext(); 294 } 295 } 296 297 if (part_iter.hasNext() && parseOrientation(part_iter.peek(), out)) { 298 part_iter.next(); 299 if (!part_iter.hasNext()) { 300 success = !part_iter.hasNext(); 301 } 302 } 303 304 if (part_iter.hasNext() && parseUiModeType(part_iter.peek(), out)) { 305 part_iter.next(); 306 if (!part_iter.hasNext()) { 307 success = !part_iter.hasNext(); 308 } 309 } 310 311 if (part_iter.hasNext() && parseUiModeNight(part_iter.peek(), out)) { 312 part_iter.next(); 313 if (!part_iter.hasNext()) { 314 success = !part_iter.hasNext(); 315 } 316 } 317 318 if (part_iter.hasNext() && parseDensity(part_iter.peek(), out)) { 319 part_iter.next(); 320 if (!part_iter.hasNext()) { 321 success = !part_iter.hasNext(); 322 } 323 } 324 325 if (part_iter.hasNext() && parseTouchscreen(part_iter.peek(), out)) { 326 part_iter.next(); 327 if (!part_iter.hasNext()) { 328 success = !part_iter.hasNext(); 329 } 330 } 331 332 if (part_iter.hasNext() && parseKeysHidden(part_iter.peek(), out)) { 333 part_iter.next(); 334 if (!part_iter.hasNext()) { 335 success = !part_iter.hasNext(); 336 } 337 } 338 339 if (part_iter.hasNext() && parseKeyboard(part_iter.peek(), out)) { 340 part_iter.next(); 341 if (!part_iter.hasNext()) { 342 success = !part_iter.hasNext(); 343 } 344 } 345 346 if (part_iter.hasNext() && parseNavHidden(part_iter.peek(), out)) { 347 part_iter.next(); 348 if (!part_iter.hasNext()) { 349 success = !part_iter.hasNext(); 350 } 351 } 352 353 if (part_iter.hasNext() && parseNavigation(part_iter.peek(), out)) { 354 part_iter.next(); 355 if (!part_iter.hasNext()) { 356 success = !part_iter.hasNext(); 357 } 358 } 359 360 if (part_iter.hasNext() && parseScreenSize(part_iter.peek(), out)) { 361 part_iter.next(); 362 if (!part_iter.hasNext()) { 363 success = !part_iter.hasNext(); 364 } 365 } 366 367 if (part_iter.hasNext() && parseVersion(part_iter.peek(), out)) { 368 part_iter.next(); 369 if (!part_iter.hasNext()) { 370 success = !part_iter.hasNext(); 371 } 372 } 373 374 if (!success) { 375 // Unrecognized. 376 return false; 377 } 378 379 if (out != null && applyVersionForCompat) { 380 applyVersionForCompatibility(out); 381 } 382 return true; 383 } 384 parseLayoutDirection(String name, ResTable_config out)385 private static boolean parseLayoutDirection(String name, ResTable_config out) { 386 if (Objects.equals(name, kWildcardName)) { 387 if (out != null) { 388 out.screenLayout = 389 (out.screenLayout & ~ResTable_config.MASK_LAYOUTDIR) | 390 ResTable_config.LAYOUTDIR_ANY; 391 } 392 return true; 393 } else if (Objects.equals(name, "ldltr")) { 394 if (out != null) { 395 out.screenLayout = 396 (out.screenLayout & ~ResTable_config.MASK_LAYOUTDIR) | 397 ResTable_config.LAYOUTDIR_LTR; 398 } 399 return true; 400 } else if (Objects.equals(name, "ldrtl")) { 401 if (out != null) { 402 out.screenLayout = 403 (out.screenLayout & ~ResTable_config.MASK_LAYOUTDIR) | 404 ResTable_config.LAYOUTDIR_RTL; 405 } 406 return true; 407 } 408 409 return false; 410 } 411 parseSmallestScreenWidthDp(String name, ResTable_config out)412 private static boolean parseSmallestScreenWidthDp(String name, ResTable_config out) { 413 if (Objects.equals(name, kWildcardName)) { 414 if (out != null) { 415 out.smallestScreenWidthDp = ResTable_config.SCREENWIDTH_ANY; 416 } 417 return true; 418 } 419 420 Matcher matcher = SMALLEST_SCREEN_WIDTH_PATTERN.matcher(name); 421 if (matcher.matches()) { 422 out.smallestScreenWidthDp = Integer.parseInt(matcher.group(1)); 423 return true; 424 } 425 return false; 426 } 427 parseScreenWidthDp(String name, ResTable_config out)428 private static boolean parseScreenWidthDp(String name, ResTable_config out) { 429 if (Objects.equals(name, kWildcardName)) { 430 if (out != null) { 431 out.screenWidthDp = ResTable_config.SCREENWIDTH_ANY; 432 } 433 return true; 434 } 435 436 Matcher matcher = SCREEN_WIDTH_PATTERN.matcher(name); 437 if (matcher.matches()) { 438 out.screenWidthDp = Integer.parseInt(matcher.group(1)); 439 return true; 440 } 441 return false; 442 } 443 parseScreenHeightDp(String name, ResTable_config out)444 private static boolean parseScreenHeightDp(String name, ResTable_config out) { 445 if (Objects.equals(name, kWildcardName)) { 446 if (out != null) { 447 out.screenHeightDp = ResTable_config.SCREENWIDTH_ANY; 448 } 449 return true; 450 } 451 452 Matcher matcher = SCREEN_HEIGHT_PATTERN.matcher(name); 453 if (matcher.matches()) { 454 out.screenHeightDp = Integer.parseInt(matcher.group(1)); 455 return true; 456 } 457 return false; 458 } 459 parseScreenLayoutSize(String name, ResTable_config out)460 private static boolean parseScreenLayoutSize(String name, ResTable_config out) { 461 if (Objects.equals(name, kWildcardName)) { 462 if (out != null) { 463 out.screenLayout = 464 (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | 465 ResTable_config.SCREENSIZE_ANY; 466 } 467 return true; 468 } else if (Objects.equals(name, "small")) { 469 if (out != null) { 470 out.screenLayout = 471 (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | 472 ResTable_config.SCREENSIZE_SMALL; 473 } 474 return true; 475 } else if (Objects.equals(name, "normal")) { 476 if (out != null) { 477 out.screenLayout = 478 (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | 479 ResTable_config.SCREENSIZE_NORMAL; 480 } 481 return true; 482 } else if (Objects.equals(name, "large")) { 483 if (out != null) { 484 out.screenLayout = 485 (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | 486 ResTable_config.SCREENSIZE_LARGE; 487 } 488 return true; 489 } else if (Objects.equals(name, "xlarge")) { 490 if (out != null) { 491 out.screenLayout = 492 (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | 493 ResTable_config.SCREENSIZE_XLARGE; 494 } 495 return true; 496 } 497 498 return false; 499 } 500 parseScreenLayoutLong(final String name, ResTable_config out)501 static boolean parseScreenLayoutLong(final String name, ResTable_config out) { 502 if (Objects.equals(name, kWildcardName)) { 503 if (out != null) { 504 out.screenLayout = 505 (out.screenLayout&~ResTable_config.MASK_SCREENLONG) 506 | ResTable_config.SCREENLONG_ANY; 507 } 508 return true; 509 } else if (Objects.equals(name, "long")) { 510 if (out != null) out.screenLayout = 511 (out.screenLayout&~ResTable_config.MASK_SCREENLONG) 512 | ResTable_config.SCREENLONG_YES; 513 return true; 514 } else if (Objects.equals(name, "notlong")) { 515 if (out != null) out.screenLayout = 516 (out.screenLayout&~ResTable_config.MASK_SCREENLONG) 517 | ResTable_config.SCREENLONG_NO; 518 return true; 519 } 520 return false; 521 } 522 parseScreenRound(String name, ResTable_config out)523 private static boolean parseScreenRound(String name, ResTable_config out) { 524 if (Objects.equals(name, kWildcardName)) { 525 if (out != null) { 526 out.screenLayout2 = 527 (byte) ((out.screenLayout2 & ~ResTable_config.MASK_SCREENROUND) | 528 ResTable_config.SCREENROUND_ANY); 529 } 530 return true; 531 } else if (Objects.equals(name, "round")) { 532 if (out != null) { 533 out.screenLayout2 = 534 (byte) ((out.screenLayout2 & ~ResTable_config.MASK_SCREENROUND) | 535 ResTable_config.SCREENROUND_YES); 536 } 537 return true; 538 } else if (Objects.equals(name, "notround")) { 539 if (out != null) { 540 out.screenLayout2 = 541 (byte) ((out.screenLayout2 & ~ResTable_config.MASK_SCREENROUND) | 542 ResTable_config.SCREENROUND_NO); 543 } 544 return true; 545 } 546 return false; 547 } 548 parseWideColorGamut(String name, ResTable_config out)549 private static boolean parseWideColorGamut(String name, ResTable_config out) { 550 if (Objects.equals(name, kWildcardName)) { 551 if (out != null) 552 out.colorMode = 553 (byte) ((out.colorMode & ~ResTable_config.MASK_WIDE_COLOR_GAMUT) | 554 ResTable_config.WIDE_COLOR_GAMUT_ANY); 555 return true; 556 } else if (Objects.equals(name, "widecg")) { 557 if (out != null) 558 out.colorMode = 559 (byte) ((out.colorMode & ~ResTable_config.MASK_WIDE_COLOR_GAMUT) | 560 ResTable_config.WIDE_COLOR_GAMUT_YES); 561 return true; 562 } else if (Objects.equals(name, "nowidecg")) { 563 if (out != null) 564 out.colorMode = 565 (byte) ((out.colorMode & ~ResTable_config.MASK_WIDE_COLOR_GAMUT) | 566 ResTable_config.WIDE_COLOR_GAMUT_NO); 567 return true; 568 } 569 return false; 570 } 571 parseHdr(String name, ResTable_config out)572 private static boolean parseHdr(String name, ResTable_config out) { 573 if (Objects.equals(name, kWildcardName)) { 574 if (out != null) 575 out.colorMode = 576 (byte) ((out.colorMode & ~ResTable_config.MASK_HDR) | 577 ResTable_config.HDR_ANY); 578 return true; 579 } else if (Objects.equals(name, "highdr")) { 580 if (out != null) 581 out.colorMode = 582 (byte) ((out.colorMode & ~ResTable_config.MASK_HDR) | 583 ResTable_config.HDR_YES); 584 return true; 585 } else if (Objects.equals(name, "lowdr")) { 586 if (out != null) 587 out.colorMode = 588 (byte) ((out.colorMode & ~ResTable_config.MASK_HDR) | 589 ResTable_config.HDR_NO); 590 return true; 591 } 592 return false; 593 } 594 parseOrientation(String name, ResTable_config out)595 private static boolean parseOrientation(String name, ResTable_config out) { 596 if (Objects.equals(name, kWildcardName)) { 597 if (out != null) { 598 out.orientation = ResTable_config.ORIENTATION_ANY; 599 } 600 return true; 601 } else if (Objects.equals(name, "port")) { 602 if (out != null) { 603 out.orientation = ResTable_config.ORIENTATION_PORT; 604 } 605 return true; 606 } else if (Objects.equals(name, "land")) { 607 if (out != null) { 608 out.orientation = ResTable_config.ORIENTATION_LAND; 609 } 610 return true; 611 } else if (Objects.equals(name, "square")) { 612 if (out != null) { 613 out.orientation = ResTable_config.ORIENTATION_SQUARE; 614 } 615 return true; 616 } 617 618 return false; 619 } 620 parseUiModeType(String name, ResTable_config out)621 private static boolean parseUiModeType(String name, ResTable_config out) { 622 if (Objects.equals(name, kWildcardName)) { 623 if (out != null) { 624 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | 625 ResTable_config.UI_MODE_TYPE_ANY; 626 } 627 return true; 628 } else if (Objects.equals(name, "desk")) { 629 if (out != null) { 630 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | 631 ResTable_config.UI_MODE_TYPE_DESK; 632 } 633 return true; 634 } else if (Objects.equals(name, "car")) { 635 if (out != null) { 636 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | 637 ResTable_config.UI_MODE_TYPE_CAR; 638 } 639 return true; 640 } else if (Objects.equals(name, "television")) { 641 if (out != null) { 642 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | 643 ResTable_config.UI_MODE_TYPE_TELEVISION; 644 } 645 return true; 646 } else if (Objects.equals(name, "appliance")) { 647 if (out != null) { 648 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | 649 ResTable_config.UI_MODE_TYPE_APPLIANCE; 650 } 651 return true; 652 } else if (Objects.equals(name, "watch")) { 653 if (out != null) { 654 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | 655 ResTable_config.UI_MODE_TYPE_WATCH; 656 } 657 return true; 658 } else if (Objects.equals(name, "vrheadset")) { 659 if (out != null) { 660 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | 661 ResTable_config.UI_MODE_TYPE_VR_HEADSET; 662 } 663 return true; 664 } 665 666 return false; 667 } 668 parseUiModeNight(String name, ResTable_config out)669 private static boolean parseUiModeNight(String name, ResTable_config out) { 670 if (Objects.equals(name, kWildcardName)) { 671 if (out != null) { 672 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_NIGHT) | 673 ResTable_config.UI_MODE_NIGHT_ANY; 674 } 675 return true; 676 } else if (Objects.equals(name, "night")) { 677 if (out != null) { 678 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_NIGHT) | 679 ResTable_config.UI_MODE_NIGHT_YES; 680 } 681 return true; 682 } else if (Objects.equals(name, "notnight")) { 683 if (out != null) { 684 out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_NIGHT) | 685 ResTable_config.UI_MODE_NIGHT_NO; 686 } 687 return true; 688 } 689 690 return false; 691 } 692 parseDensity(String name, ResTable_config out)693 private static boolean parseDensity(String name, ResTable_config out) { 694 if (Objects.equals(name, kWildcardName)) { 695 if (out != null) { 696 out.density = ResTable_config.DENSITY_DEFAULT; 697 } 698 return true; 699 } 700 701 if (Objects.equals(name, "anydpi")) { 702 if (out != null) { 703 out.density = ResTable_config.DENSITY_ANY; 704 } 705 return true; 706 } 707 708 if (Objects.equals(name, "nodpi")) { 709 if (out != null) { 710 out.density = ResTable_config.DENSITY_NONE; 711 } 712 return true; 713 } 714 715 if (Objects.equals(name, "ldpi")) { 716 if (out != null) { 717 out.density = ResTable_config.DENSITY_LOW; 718 } 719 return true; 720 } 721 722 if (Objects.equals(name, "mdpi")) { 723 if (out != null) { 724 out.density = ResTable_config.DENSITY_MEDIUM; 725 } 726 return true; 727 } 728 729 if (Objects.equals(name, "tvdpi")) { 730 if (out != null) { 731 out.density = ResTable_config.DENSITY_TV; 732 } 733 return true; 734 } 735 736 if (Objects.equals(name, "hdpi")) { 737 if (out != null) { 738 out.density = ResTable_config.DENSITY_HIGH; 739 } 740 return true; 741 } 742 743 if (Objects.equals(name, "xhdpi")) { 744 if (out != null) { 745 out.density = ResTable_config.DENSITY_XHIGH; 746 } 747 return true; 748 } 749 750 if (Objects.equals(name, "xxhdpi")) { 751 if (out != null) { 752 out.density = ResTable_config.DENSITY_XXHIGH; 753 } 754 return true; 755 } 756 757 if (Objects.equals(name, "xxxhdpi")) { 758 if (out != null) { 759 out.density = ResTable_config.DENSITY_XXXHIGH; 760 } 761 return true; 762 } 763 764 // check that we have 'dpi' after the last digit. 765 Matcher matcher = DENSITY_PATTERN.matcher(name); 766 if (matcher.matches()) { 767 out.density = Integer.parseInt(matcher.group(1)); 768 return true; 769 } 770 return false; 771 } 772 parseTouchscreen(String name, ResTable_config out)773 private static boolean parseTouchscreen(String name, ResTable_config out) { 774 if (Objects.equals(name, kWildcardName)) { 775 if (out != null) { 776 out.touchscreen = ResTable_config.TOUCHSCREEN_ANY; 777 } 778 return true; 779 } else if (Objects.equals(name, "notouch")) { 780 if (out != null) { 781 out.touchscreen = ResTable_config.TOUCHSCREEN_NOTOUCH; 782 } 783 return true; 784 } else if (Objects.equals(name, "stylus")) { 785 if (out != null) { 786 out.touchscreen = ResTable_config.TOUCHSCREEN_STYLUS; 787 } 788 return true; 789 } else if (Objects.equals(name, "finger")) { 790 if (out != null) { 791 out.touchscreen = ResTable_config.TOUCHSCREEN_FINGER; 792 } 793 return true; 794 } 795 796 return false; 797 } 798 parseKeysHidden(String name, ResTable_config out)799 private static boolean parseKeysHidden(String name, ResTable_config out) { 800 byte mask = 0; 801 byte value = 0; 802 if (Objects.equals(name, kWildcardName)) { 803 mask = ResTable_config.MASK_KEYSHIDDEN; 804 value = ResTable_config.KEYSHIDDEN_ANY; 805 } else if (Objects.equals(name, "keysexposed")) { 806 mask = ResTable_config.MASK_KEYSHIDDEN; 807 value = ResTable_config.KEYSHIDDEN_NO; 808 } else if (Objects.equals(name, "keyshidden")) { 809 mask = ResTable_config.MASK_KEYSHIDDEN; 810 value = ResTable_config.KEYSHIDDEN_YES; 811 } else if (Objects.equals(name, "keyssoft")) { 812 mask = ResTable_config.MASK_KEYSHIDDEN; 813 value = ResTable_config.KEYSHIDDEN_SOFT; 814 } 815 816 if (mask != 0) { 817 if (out != null) { 818 out.inputFlags = (out.inputFlags & ~mask) | value; 819 } 820 return true; 821 } 822 823 return false; 824 } 825 parseKeyboard(String name, ResTable_config out)826 private static boolean parseKeyboard(String name, ResTable_config out) { 827 if (Objects.equals(name, kWildcardName)) { 828 if (out != null) { 829 out.keyboard = ResTable_config.KEYBOARD_ANY; 830 } 831 return true; 832 } else if (Objects.equals(name, "nokeys")) { 833 if (out != null) { 834 out.keyboard = ResTable_config.KEYBOARD_NOKEYS; 835 } 836 return true; 837 } else if (Objects.equals(name, "qwerty")) { 838 if (out != null) { 839 out.keyboard = ResTable_config.KEYBOARD_QWERTY; 840 } 841 return true; 842 } else if (Objects.equals(name, "12key")) { 843 if (out != null) { 844 out.keyboard = ResTable_config.KEYBOARD_12KEY; 845 } 846 return true; 847 } 848 849 return false; 850 } 851 parseNavHidden(String name, ResTable_config out)852 private static boolean parseNavHidden(String name, ResTable_config out) { 853 byte mask = 0; 854 byte value = 0; 855 if (Objects.equals(name, kWildcardName)) { 856 mask = ResTable_config.MASK_NAVHIDDEN; 857 value = ResTable_config.NAVHIDDEN_ANY; 858 } else if (Objects.equals(name, "navexposed")) { 859 mask = ResTable_config.MASK_NAVHIDDEN; 860 value = ResTable_config.NAVHIDDEN_NO; 861 } else if (Objects.equals(name, "navhidden")) { 862 mask = ResTable_config.MASK_NAVHIDDEN; 863 value = ResTable_config.NAVHIDDEN_YES; 864 } 865 866 if (mask != 0) { 867 if (out != null) { 868 out.inputFlags = (out.inputFlags & ~mask) | value; 869 } 870 return true; 871 } 872 873 return false; 874 } 875 parseNavigation(String name, ResTable_config out)876 private static boolean parseNavigation(String name, ResTable_config out) { 877 if (Objects.equals(name, kWildcardName)) { 878 if (out != null) { 879 out.navigation = ResTable_config.NAVIGATION_ANY; 880 } 881 return true; 882 } else if (Objects.equals(name, "nonav")) { 883 if (out != null) { 884 out.navigation = ResTable_config.NAVIGATION_NONAV; 885 } 886 return true; 887 } else if (Objects.equals(name, "dpad")) { 888 if (out != null) { 889 out.navigation = ResTable_config.NAVIGATION_DPAD; 890 } 891 return true; 892 } else if (Objects.equals(name, "trackball")) { 893 if (out != null) { 894 out.navigation = ResTable_config.NAVIGATION_TRACKBALL; 895 } 896 return true; 897 } else if (Objects.equals(name, "wheel")) { 898 if (out != null) { 899 out.navigation = ResTable_config.NAVIGATION_WHEEL; 900 } 901 return true; 902 } 903 904 return false; 905 } 906 parseScreenSize(String name, ResTable_config out)907 private static boolean parseScreenSize(String name, ResTable_config out) { 908 if (Objects.equals(name, kWildcardName)) { 909 if (out != null) { 910 out.screenWidth = ResTable_config.SCREENWIDTH_ANY; 911 out.screenHeight = ResTable_config.SCREENHEIGHT_ANY; 912 } 913 return true; 914 } 915 916 Matcher matcher = HEIGHT_WIDTH_PATTERN.matcher(name); 917 if (matcher.matches()) { 918 int w = Integer.parseInt(matcher.group(1)); 919 int h = Integer.parseInt(matcher.group(2)); 920 if (w < h) { 921 return false; 922 } 923 out.screenWidth = w; 924 out.screenHeight = h; 925 return true; 926 } 927 return false; 928 } 929 parseVersion(String name, ResTable_config out)930 private static boolean parseVersion(String name, ResTable_config out) { 931 if (Objects.equals(name, kWildcardName)) { 932 if (out != null) { 933 out.sdkVersion = ResTable_config.SDKVERSION_ANY; 934 out.minorVersion = ResTable_config.MINORVERSION_ANY; 935 } 936 return true; 937 } 938 939 Matcher matcher = VERSION_QUALIFIER_PATTERN.matcher(name); 940 if (matcher.matches()) { 941 out.sdkVersion = Integer.parseInt(matcher.group(1)); 942 out.minorVersion = 0; 943 return true; 944 } 945 return false; 946 } 947 parseMnc(String name, ResTable_config out)948 private static boolean parseMnc(String name, ResTable_config out) { 949 if (Objects.equals(name, kWildcardName)) { 950 if (out != null) { 951 out.mnc = 0; 952 } 953 return true; 954 } 955 956 Matcher matcher = MNC_PATTERN.matcher(name); 957 if (matcher.matches()) { 958 out.mnc = Integer.parseInt(matcher.group(1)); 959 if (out.mnc == 0) { 960 out.mnc = ACONFIGURATION_MNC_ZERO; 961 } 962 return true; 963 } 964 return false; 965 } 966 parseMcc(final String name, ResTable_config out)967 private static boolean parseMcc(final String name, ResTable_config out) { 968 if (Objects.equals(name, kWildcardName)) { 969 if (out != null) { 970 out.mcc = 0; 971 } 972 return true; 973 } 974 975 Matcher matcher = MCC_PATTERN.matcher(name); 976 if (matcher.matches()) { 977 out.mcc = Integer.parseInt(matcher.group(1)); 978 return true; 979 } 980 return false; 981 } 982 983 // transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/tools/aapt/AaptConfig.cpp applyVersionForCompatibility(ResTable_config config)984 private static void applyVersionForCompatibility(ResTable_config config) { 985 if (config == null) { 986 return; 987 } 988 int min_sdk = 0; 989 if (((config.uiMode & ResTable_config.MASK_UI_MODE_TYPE) 990 == ResTable_config.UI_MODE_TYPE_VR_HEADSET) || 991 (config.colorMode & ResTable_config.MASK_WIDE_COLOR_GAMUT) != 0 || 992 (config.colorMode & ResTable_config.MASK_HDR) != 0) { 993 min_sdk = SDK_O; 994 } else if (isTruthy(config.screenLayout2 & ResTable_config.MASK_SCREENROUND)) { 995 min_sdk = SDK_MNC; 996 } else if (config.density == ResTable_config.DENSITY_ANY) { 997 min_sdk = SDK_LOLLIPOP; 998 } else if (config.smallestScreenWidthDp != ResTable_config.SCREENWIDTH_ANY 999 || config.screenWidthDp != ResTable_config.SCREENWIDTH_ANY 1000 || config.screenHeightDp != ResTable_config.SCREENHEIGHT_ANY) { 1001 min_sdk = SDK_HONEYCOMB_MR2; 1002 } else if ((config.uiMode & ResTable_config.MASK_UI_MODE_TYPE) 1003 != ResTable_config.UI_MODE_TYPE_ANY 1004 || (config.uiMode & ResTable_config.MASK_UI_MODE_NIGHT) 1005 != ResTable_config.UI_MODE_NIGHT_ANY) { 1006 min_sdk = SDK_FROYO; 1007 } else if ((config.screenLayout & ResTable_config.MASK_SCREENSIZE) 1008 != ResTable_config.SCREENSIZE_ANY 1009 || (config.screenLayout & ResTable_config.MASK_SCREENLONG) 1010 != ResTable_config.SCREENLONG_ANY 1011 || config.density != ResTable_config.DENSITY_DEFAULT) { 1012 min_sdk = SDK_DONUT; 1013 } 1014 if (min_sdk > config.sdkVersion) { 1015 config.sdkVersion = min_sdk; 1016 } 1017 } 1018 } 1019