| package org.robolectric.res.android; |
| |
| import static org.robolectric.res.android.Util.isTruthy; |
| |
| import com.google.common.base.Charsets; |
| import com.google.common.collect.Iterators; |
| import com.google.common.collect.PeekingIterator; |
| import java.util.Arrays; |
| import java.util.Objects; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * transliterated from |
| * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r22/tools/aapt2/ConfigDescription.cpp |
| */ |
| public class ConfigDescription { |
| public static final int SDK_CUPCAKE = 3; |
| public static final int SDK_DONUT = 4; |
| public static final int SDK_ECLAIR = 5; |
| public static final int SDK_ECLAIR_0_1 = 6; |
| public static final int SDK_ECLAIR_MR1 = 7; |
| public static final int SDK_FROYO = 8; |
| public static final int SDK_GINGERBREAD = 9; |
| public static final int SDK_GINGERBREAD_MR1 = 10; |
| public static final int SDK_HONEYCOMB = 11; |
| public static final int SDK_HONEYCOMB_MR1 = 12; |
| public static final int SDK_HONEYCOMB_MR2 = 13; |
| public static final int SDK_ICE_CREAM_SANDWICH = 14; |
| public static final int SDK_ICE_CREAM_SANDWICH_MR1 = 15; |
| public static final int SDK_JELLY_BEAN = 16; |
| public static final int SDK_JELLY_BEAN_MR1 = 17; |
| public static final int SDK_JELLY_BEAN_MR2 = 18; |
| public static final int SDK_KITKAT = 19; |
| public static final int SDK_KITKAT_WATCH = 20; |
| public static final int SDK_LOLLIPOP = 21; |
| public static final int SDK_LOLLIPOP_MR1 = 22; |
| public static final int SDK_MNC = 23; |
| public static final int SDK_NOUGAT = 24; |
| public static final int SDK_NOUGAT_MR1 = 25; |
| public static final int SDK_O = 26; |
| |
| /** |
| * Constant used to to represent MNC (Mobile Network Code) zero. |
| * 0 cannot be used, since it is used to represent an undefined MNC. |
| */ |
| private static final int ACONFIGURATION_MNC_ZERO = 0xffff; |
| |
| private static final String kWildcardName = "any"; |
| |
| private static final Pattern MCC_PATTERN = Pattern.compile("mcc([\\d]+)"); |
| private static final Pattern MNC_PATTERN = Pattern.compile("mnc([\\d]+)"); |
| private static final Pattern SMALLEST_SCREEN_WIDTH_PATTERN = Pattern.compile("^sw([0-9]+)dp"); |
| private static final Pattern SCREEN_WIDTH_PATTERN = Pattern.compile("^w([0-9]+)dp"); |
| private static final Pattern SCREEN_HEIGHT_PATTERN = Pattern.compile("^h([0-9]+)dp"); |
| private static final Pattern DENSITY_PATTERN = Pattern.compile("^([0-9]+)dpi"); |
| private static final Pattern HEIGHT_WIDTH_PATTERN = Pattern.compile("^([0-9]+)x([0-9]+)"); |
| private static final Pattern VERSION_QUALIFIER_PATTERN = Pattern.compile("v([0-9]+)$"); |
| |
| public static class LocaleValue { |
| |
| String language; |
| String region; |
| String script; |
| String variant; |
| |
| void set_language(String language_chars) { |
| language = language_chars.trim().toLowerCase(); |
| } |
| |
| void set_region(String region_chars) { |
| region = region_chars.trim().toUpperCase(); |
| } |
| |
| void set_script(String script_chars) { |
| script = String.valueOf(Character.toUpperCase(script_chars.charAt(0))) + |
| script_chars.substring(1).toLowerCase(); |
| } |
| |
| void set_variant(String variant_chars) { |
| variant = variant_chars.trim(); |
| } |
| |
| |
| static boolean is_alpha(final String str) { |
| for (int i = 0; i < str.length(); i++) { |
| if (!Character.isAlphabetic(str.charAt(i))) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| int initFromParts(PeekingIterator<String> iter) { |
| |
| String part = iter.peek(); |
| if (part.startsWith("b+")) { |
| // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags, |
| // except that the separator is "+" and not "-". |
| String[] subtags = part.substring(2).toLowerCase().split("\\+"); |
| if (subtags.length == 1) { |
| set_language(subtags[0]); |
| } else if (subtags.length == 2) { |
| set_language(subtags[0]); |
| |
| // The second tag can either be a region, a variant or a script. |
| switch (subtags[1].length()) { |
| case 2: |
| case 3: |
| set_region(subtags[1]); |
| break; |
| case 4: |
| if ('0' <= subtags[1].charAt(0) && subtags[1].charAt(0) <= '9') { |
| // This is a variant: fall through |
| } else { |
| set_script(subtags[1]); |
| break; |
| } |
| // fall through |
| case 5: |
| case 6: |
| case 7: |
| case 8: |
| set_variant(subtags[1]); |
| break; |
| default: |
| return -1; |
| } |
| } else if (subtags.length == 3) { |
| // The language is always the first subtag. |
| set_language(subtags[0]); |
| |
| // The second subtag can either be a script or a region code. |
| // If its size is 4, it's a script code, else it's a region code. |
| if (subtags[1].length() == 4) { |
| set_script(subtags[1]); |
| } else if (subtags[1].length() == 2 || subtags[1].length() == 3) { |
| set_region(subtags[1]); |
| } else { |
| return -1; |
| } |
| |
| // The third tag can either be a region code (if the second tag was |
| // a script), else a variant code. |
| if (subtags[2].length() >= 4) { |
| set_variant(subtags[2]); |
| } else { |
| set_region(subtags[2]); |
| } |
| } else if (subtags.length == 4) { |
| set_language(subtags[0]); |
| set_script(subtags[1]); |
| set_region(subtags[2]); |
| set_variant(subtags[3]); |
| } else { |
| return -1; |
| } |
| |
| iter.next(); |
| |
| } else { |
| if ((part.length() == 2 || part.length() == 3) && is_alpha(part) && |
| !Objects.equals(part, "car")) { |
| set_language(part); |
| iter.next(); |
| |
| if (iter.hasNext()) { |
| final String region_part = iter.peek(); |
| if (region_part.charAt(0) == 'r' && region_part.length() == 3) { |
| set_region(region_part.substring(1)); |
| iter.next(); |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| public void writeTo(ResTable_config out) { |
| out.packLanguage(language); |
| out.packRegion(region); |
| |
| Arrays.fill(out.localeScript, (byte) 0); |
| byte[] scriptBytes = script == null ? new byte[4] : script.getBytes(Charsets.UTF_8); |
| System.arraycopy(scriptBytes, 0, out.localeScript, 0, scriptBytes.length); |
| |
| Arrays.fill(out.localeVariant, (byte) 0); |
| byte[] variantBytes = variant == null ? new byte[8] : variant.getBytes(Charsets.UTF_8); |
| System.arraycopy(variantBytes, 0, out.localeVariant, 0, variantBytes.length); |
| } |
| } |
| |
| public static boolean parse(final String str, ResTable_config out) { |
| return parse(str, out, true); |
| } |
| |
| public static boolean parse(final String str, ResTable_config out, boolean applyVersionForCompat) { |
| PeekingIterator<String> part_iter = Iterators |
| .peekingIterator(Arrays.asList(str.toLowerCase().split("-")).iterator()); |
| |
| LocaleValue locale = new LocaleValue(); |
| |
| boolean success = !part_iter.hasNext(); |
| if (part_iter.hasNext() && parseMcc(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseMnc(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext()) { |
| // Locale spans a few '-' separators, so we let it |
| // control the index. |
| int parts_consumed = locale.initFromParts(part_iter); |
| if (parts_consumed < 0) { |
| return false; |
| } else { |
| locale.writeTo(out); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| } |
| |
| if (part_iter.hasNext() && parseLayoutDirection(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseSmallestScreenWidthDp(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseScreenWidthDp(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseScreenHeightDp(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseScreenLayoutSize(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseScreenLayoutLong(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseScreenRound(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseWideColorGamut(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseHdr(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseOrientation(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseUiModeType(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseUiModeNight(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseDensity(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseTouchscreen(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseKeysHidden(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseKeyboard(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseNavHidden(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseNavigation(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseScreenSize(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (part_iter.hasNext() && parseVersion(part_iter.peek(), out)) { |
| part_iter.next(); |
| if (!part_iter.hasNext()) { |
| success = !part_iter.hasNext(); |
| } |
| } |
| |
| if (!success) { |
| // Unrecognized. |
| return false; |
| } |
| |
| if (out != null && applyVersionForCompat) { |
| applyVersionForCompatibility(out); |
| } |
| return true; |
| } |
| |
| private static boolean parseLayoutDirection(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.screenLayout = |
| (out.screenLayout & ~ResTable_config.MASK_LAYOUTDIR) | |
| ResTable_config.LAYOUTDIR_ANY; |
| } |
| return true; |
| } else if (Objects.equals(name, "ldltr")) { |
| if (out != null) { |
| out.screenLayout = |
| (out.screenLayout & ~ResTable_config.MASK_LAYOUTDIR) | |
| ResTable_config.LAYOUTDIR_LTR; |
| } |
| return true; |
| } else if (Objects.equals(name, "ldrtl")) { |
| if (out != null) { |
| out.screenLayout = |
| (out.screenLayout & ~ResTable_config.MASK_LAYOUTDIR) | |
| ResTable_config.LAYOUTDIR_RTL; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean parseSmallestScreenWidthDp(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.smallestScreenWidthDp = ResTable_config.SCREENWIDTH_ANY; |
| } |
| return true; |
| } |
| |
| Matcher matcher = SMALLEST_SCREEN_WIDTH_PATTERN.matcher(name); |
| if (matcher.matches()) { |
| out.smallestScreenWidthDp = Integer.parseInt(matcher.group(1)); |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseScreenWidthDp(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.screenWidthDp = ResTable_config.SCREENWIDTH_ANY; |
| } |
| return true; |
| } |
| |
| Matcher matcher = SCREEN_WIDTH_PATTERN.matcher(name); |
| if (matcher.matches()) { |
| out.screenWidthDp = Integer.parseInt(matcher.group(1)); |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseScreenHeightDp(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.screenHeightDp = ResTable_config.SCREENWIDTH_ANY; |
| } |
| return true; |
| } |
| |
| Matcher matcher = SCREEN_HEIGHT_PATTERN.matcher(name); |
| if (matcher.matches()) { |
| out.screenHeightDp = Integer.parseInt(matcher.group(1)); |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseScreenLayoutSize(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.screenLayout = |
| (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | |
| ResTable_config.SCREENSIZE_ANY; |
| } |
| return true; |
| } else if (Objects.equals(name, "small")) { |
| if (out != null) { |
| out.screenLayout = |
| (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | |
| ResTable_config.SCREENSIZE_SMALL; |
| } |
| return true; |
| } else if (Objects.equals(name, "normal")) { |
| if (out != null) { |
| out.screenLayout = |
| (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | |
| ResTable_config.SCREENSIZE_NORMAL; |
| } |
| return true; |
| } else if (Objects.equals(name, "large")) { |
| if (out != null) { |
| out.screenLayout = |
| (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | |
| ResTable_config.SCREENSIZE_LARGE; |
| } |
| return true; |
| } else if (Objects.equals(name, "xlarge")) { |
| if (out != null) { |
| out.screenLayout = |
| (out.screenLayout & ~ResTable_config.MASK_SCREENSIZE) | |
| ResTable_config.SCREENSIZE_XLARGE; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static boolean parseScreenLayoutLong(final String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.screenLayout = |
| (out.screenLayout&~ResTable_config.MASK_SCREENLONG) |
| | ResTable_config.SCREENLONG_ANY; |
| } |
| return true; |
| } else if (Objects.equals(name, "long")) { |
| if (out != null) out.screenLayout = |
| (out.screenLayout&~ResTable_config.MASK_SCREENLONG) |
| | ResTable_config.SCREENLONG_YES; |
| return true; |
| } else if (Objects.equals(name, "notlong")) { |
| if (out != null) out.screenLayout = |
| (out.screenLayout&~ResTable_config.MASK_SCREENLONG) |
| | ResTable_config.SCREENLONG_NO; |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseScreenRound(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.screenLayout2 = |
| (byte) ((out.screenLayout2 & ~ResTable_config.MASK_SCREENROUND) | |
| ResTable_config.SCREENROUND_ANY); |
| } |
| return true; |
| } else if (Objects.equals(name, "round")) { |
| if (out != null) { |
| out.screenLayout2 = |
| (byte) ((out.screenLayout2 & ~ResTable_config.MASK_SCREENROUND) | |
| ResTable_config.SCREENROUND_YES); |
| } |
| return true; |
| } else if (Objects.equals(name, "notround")) { |
| if (out != null) { |
| out.screenLayout2 = |
| (byte) ((out.screenLayout2 & ~ResTable_config.MASK_SCREENROUND) | |
| ResTable_config.SCREENROUND_NO); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseWideColorGamut(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) |
| out.colorMode = |
| (byte) ((out.colorMode & ~ResTable_config.MASK_WIDE_COLOR_GAMUT) | |
| ResTable_config.WIDE_COLOR_GAMUT_ANY); |
| return true; |
| } else if (Objects.equals(name, "widecg")) { |
| if (out != null) |
| out.colorMode = |
| (byte) ((out.colorMode & ~ResTable_config.MASK_WIDE_COLOR_GAMUT) | |
| ResTable_config.WIDE_COLOR_GAMUT_YES); |
| return true; |
| } else if (Objects.equals(name, "nowidecg")) { |
| if (out != null) |
| out.colorMode = |
| (byte) ((out.colorMode & ~ResTable_config.MASK_WIDE_COLOR_GAMUT) | |
| ResTable_config.WIDE_COLOR_GAMUT_NO); |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseHdr(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) |
| out.colorMode = |
| (byte) ((out.colorMode & ~ResTable_config.MASK_HDR) | |
| ResTable_config.HDR_ANY); |
| return true; |
| } else if (Objects.equals(name, "highdr")) { |
| if (out != null) |
| out.colorMode = |
| (byte) ((out.colorMode & ~ResTable_config.MASK_HDR) | |
| ResTable_config.HDR_YES); |
| return true; |
| } else if (Objects.equals(name, "lowdr")) { |
| if (out != null) |
| out.colorMode = |
| (byte) ((out.colorMode & ~ResTable_config.MASK_HDR) | |
| ResTable_config.HDR_NO); |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseOrientation(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.orientation = ResTable_config.ORIENTATION_ANY; |
| } |
| return true; |
| } else if (Objects.equals(name, "port")) { |
| if (out != null) { |
| out.orientation = ResTable_config.ORIENTATION_PORT; |
| } |
| return true; |
| } else if (Objects.equals(name, "land")) { |
| if (out != null) { |
| out.orientation = ResTable_config.ORIENTATION_LAND; |
| } |
| return true; |
| } else if (Objects.equals(name, "square")) { |
| if (out != null) { |
| out.orientation = ResTable_config.ORIENTATION_SQUARE; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean parseUiModeType(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | |
| ResTable_config.UI_MODE_TYPE_ANY; |
| } |
| return true; |
| } else if (Objects.equals(name, "desk")) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | |
| ResTable_config.UI_MODE_TYPE_DESK; |
| } |
| return true; |
| } else if (Objects.equals(name, "car")) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | |
| ResTable_config.UI_MODE_TYPE_CAR; |
| } |
| return true; |
| } else if (Objects.equals(name, "television")) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | |
| ResTable_config.UI_MODE_TYPE_TELEVISION; |
| } |
| return true; |
| } else if (Objects.equals(name, "appliance")) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | |
| ResTable_config.UI_MODE_TYPE_APPLIANCE; |
| } |
| return true; |
| } else if (Objects.equals(name, "watch")) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | |
| ResTable_config.UI_MODE_TYPE_WATCH; |
| } |
| return true; |
| } else if (Objects.equals(name, "vrheadset")) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_TYPE) | |
| ResTable_config.UI_MODE_TYPE_VR_HEADSET; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean parseUiModeNight(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_NIGHT) | |
| ResTable_config.UI_MODE_NIGHT_ANY; |
| } |
| return true; |
| } else if (Objects.equals(name, "night")) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_NIGHT) | |
| ResTable_config.UI_MODE_NIGHT_YES; |
| } |
| return true; |
| } else if (Objects.equals(name, "notnight")) { |
| if (out != null) { |
| out.uiMode = (out.uiMode & ~ResTable_config.MASK_UI_MODE_NIGHT) | |
| ResTable_config.UI_MODE_NIGHT_NO; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean parseDensity(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_DEFAULT; |
| } |
| return true; |
| } |
| |
| if (Objects.equals(name, "anydpi")) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_ANY; |
| } |
| return true; |
| } |
| |
| if (Objects.equals(name, "nodpi")) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_NONE; |
| } |
| return true; |
| } |
| |
| if (Objects.equals(name, "ldpi")) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_LOW; |
| } |
| return true; |
| } |
| |
| if (Objects.equals(name, "mdpi")) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_MEDIUM; |
| } |
| return true; |
| } |
| |
| if (Objects.equals(name, "tvdpi")) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_TV; |
| } |
| return true; |
| } |
| |
| if (Objects.equals(name, "hdpi")) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_HIGH; |
| } |
| return true; |
| } |
| |
| if (Objects.equals(name, "xhdpi")) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_XHIGH; |
| } |
| return true; |
| } |
| |
| if (Objects.equals(name, "xxhdpi")) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_XXHIGH; |
| } |
| return true; |
| } |
| |
| if (Objects.equals(name, "xxxhdpi")) { |
| if (out != null) { |
| out.density = ResTable_config.DENSITY_XXXHIGH; |
| } |
| return true; |
| } |
| |
| // check that we have 'dpi' after the last digit. |
| Matcher matcher = DENSITY_PATTERN.matcher(name); |
| if (matcher.matches()) { |
| out.density = Integer.parseInt(matcher.group(1)); |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseTouchscreen(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.touchscreen = ResTable_config.TOUCHSCREEN_ANY; |
| } |
| return true; |
| } else if (Objects.equals(name, "notouch")) { |
| if (out != null) { |
| out.touchscreen = ResTable_config.TOUCHSCREEN_NOTOUCH; |
| } |
| return true; |
| } else if (Objects.equals(name, "stylus")) { |
| if (out != null) { |
| out.touchscreen = ResTable_config.TOUCHSCREEN_STYLUS; |
| } |
| return true; |
| } else if (Objects.equals(name, "finger")) { |
| if (out != null) { |
| out.touchscreen = ResTable_config.TOUCHSCREEN_FINGER; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean parseKeysHidden(String name, ResTable_config out) { |
| byte mask = 0; |
| byte value = 0; |
| if (Objects.equals(name, kWildcardName)) { |
| mask = ResTable_config.MASK_KEYSHIDDEN; |
| value = ResTable_config.KEYSHIDDEN_ANY; |
| } else if (Objects.equals(name, "keysexposed")) { |
| mask = ResTable_config.MASK_KEYSHIDDEN; |
| value = ResTable_config.KEYSHIDDEN_NO; |
| } else if (Objects.equals(name, "keyshidden")) { |
| mask = ResTable_config.MASK_KEYSHIDDEN; |
| value = ResTable_config.KEYSHIDDEN_YES; |
| } else if (Objects.equals(name, "keyssoft")) { |
| mask = ResTable_config.MASK_KEYSHIDDEN; |
| value = ResTable_config.KEYSHIDDEN_SOFT; |
| } |
| |
| if (mask != 0) { |
| if (out != null) { |
| out.inputFlags = (out.inputFlags & ~mask) | value; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean parseKeyboard(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.keyboard = ResTable_config.KEYBOARD_ANY; |
| } |
| return true; |
| } else if (Objects.equals(name, "nokeys")) { |
| if (out != null) { |
| out.keyboard = ResTable_config.KEYBOARD_NOKEYS; |
| } |
| return true; |
| } else if (Objects.equals(name, "qwerty")) { |
| if (out != null) { |
| out.keyboard = ResTable_config.KEYBOARD_QWERTY; |
| } |
| return true; |
| } else if (Objects.equals(name, "12key")) { |
| if (out != null) { |
| out.keyboard = ResTable_config.KEYBOARD_12KEY; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean parseNavHidden(String name, ResTable_config out) { |
| byte mask = 0; |
| byte value = 0; |
| if (Objects.equals(name, kWildcardName)) { |
| mask = ResTable_config.MASK_NAVHIDDEN; |
| value = ResTable_config.NAVHIDDEN_ANY; |
| } else if (Objects.equals(name, "navexposed")) { |
| mask = ResTable_config.MASK_NAVHIDDEN; |
| value = ResTable_config.NAVHIDDEN_NO; |
| } else if (Objects.equals(name, "navhidden")) { |
| mask = ResTable_config.MASK_NAVHIDDEN; |
| value = ResTable_config.NAVHIDDEN_YES; |
| } |
| |
| if (mask != 0) { |
| if (out != null) { |
| out.inputFlags = (out.inputFlags & ~mask) | value; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean parseNavigation(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.navigation = ResTable_config.NAVIGATION_ANY; |
| } |
| return true; |
| } else if (Objects.equals(name, "nonav")) { |
| if (out != null) { |
| out.navigation = ResTable_config.NAVIGATION_NONAV; |
| } |
| return true; |
| } else if (Objects.equals(name, "dpad")) { |
| if (out != null) { |
| out.navigation = ResTable_config.NAVIGATION_DPAD; |
| } |
| return true; |
| } else if (Objects.equals(name, "trackball")) { |
| if (out != null) { |
| out.navigation = ResTable_config.NAVIGATION_TRACKBALL; |
| } |
| return true; |
| } else if (Objects.equals(name, "wheel")) { |
| if (out != null) { |
| out.navigation = ResTable_config.NAVIGATION_WHEEL; |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean parseScreenSize(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.screenWidth = ResTable_config.SCREENWIDTH_ANY; |
| out.screenHeight = ResTable_config.SCREENHEIGHT_ANY; |
| } |
| return true; |
| } |
| |
| Matcher matcher = HEIGHT_WIDTH_PATTERN.matcher(name); |
| if (matcher.matches()) { |
| int w = Integer.parseInt(matcher.group(1)); |
| int h = Integer.parseInt(matcher.group(2)); |
| if (w < h) { |
| return false; |
| } |
| out.screenWidth = w; |
| out.screenHeight = h; |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseVersion(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.sdkVersion = ResTable_config.SDKVERSION_ANY; |
| out.minorVersion = ResTable_config.MINORVERSION_ANY; |
| } |
| return true; |
| } |
| |
| Matcher matcher = VERSION_QUALIFIER_PATTERN.matcher(name); |
| if (matcher.matches()) { |
| out.sdkVersion = Integer.parseInt(matcher.group(1)); |
| out.minorVersion = 0; |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseMnc(String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.mnc = 0; |
| } |
| return true; |
| } |
| |
| Matcher matcher = MNC_PATTERN.matcher(name); |
| if (matcher.matches()) { |
| out.mnc = Integer.parseInt(matcher.group(1)); |
| if (out.mnc == 0) { |
| out.mnc = ACONFIGURATION_MNC_ZERO; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean parseMcc(final String name, ResTable_config out) { |
| if (Objects.equals(name, kWildcardName)) { |
| if (out != null) { |
| out.mcc = 0; |
| } |
| return true; |
| } |
| |
| Matcher matcher = MCC_PATTERN.matcher(name); |
| if (matcher.matches()) { |
| out.mcc = Integer.parseInt(matcher.group(1)); |
| return true; |
| } |
| return false; |
| } |
| |
| // transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r22/tools/aapt/AaptConfig.cpp |
| private static void applyVersionForCompatibility(ResTable_config config) { |
| if (config == null) { |
| return; |
| } |
| int min_sdk = 0; |
| if (((config.uiMode & ResTable_config.MASK_UI_MODE_TYPE) |
| == ResTable_config.UI_MODE_TYPE_VR_HEADSET) || |
| (config.colorMode & ResTable_config.MASK_WIDE_COLOR_GAMUT) != 0 || |
| (config.colorMode & ResTable_config.MASK_HDR) != 0) { |
| min_sdk = SDK_O; |
| } else if (isTruthy(config.screenLayout2 & ResTable_config.MASK_SCREENROUND)) { |
| min_sdk = SDK_MNC; |
| } else if (config.density == ResTable_config.DENSITY_ANY) { |
| min_sdk = SDK_LOLLIPOP; |
| } else if (config.smallestScreenWidthDp != ResTable_config.SCREENWIDTH_ANY |
| || config.screenWidthDp != ResTable_config.SCREENWIDTH_ANY |
| || config.screenHeightDp != ResTable_config.SCREENHEIGHT_ANY) { |
| min_sdk = SDK_HONEYCOMB_MR2; |
| } else if ((config.uiMode & ResTable_config.MASK_UI_MODE_TYPE) |
| != ResTable_config.UI_MODE_TYPE_ANY |
| || (config.uiMode & ResTable_config.MASK_UI_MODE_NIGHT) |
| != ResTable_config.UI_MODE_NIGHT_ANY) { |
| min_sdk = SDK_FROYO; |
| } else if ((config.screenLayout & ResTable_config.MASK_SCREENSIZE) |
| != ResTable_config.SCREENSIZE_ANY |
| || (config.screenLayout & ResTable_config.MASK_SCREENLONG) |
| != ResTable_config.SCREENLONG_ANY |
| || config.density != ResTable_config.DENSITY_DEFAULT) { |
| min_sdk = SDK_DONUT; |
| } |
| if (min_sdk > config.sdkVersion) { |
| config.sdkVersion = min_sdk; |
| } |
| } |
| } |