Home | History | Annotate | Download | only in android
      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 
     67     void set_language(String language_chars) {
     68       language = language_chars.trim().toLowerCase();
     69     }
     70 
     71     void set_region(String region_chars) {
     72       region = region_chars.trim().toUpperCase();
     73     }
     74 
     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 
     80     void set_variant(String variant_chars) {
     81       variant = variant_chars.trim();
     82     }
     83 
     84 
     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 
     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 
    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 
    195   public static boolean parse(final String str, ResTable_config out) {
    196     return parse(str, out, true);
    197   }
    198 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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