fix: resource qualifiers (PR #487)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package jadx.core.xmlgen;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -143,6 +144,23 @@ public class ParserStream {
|
||||
input.reset();
|
||||
}
|
||||
|
||||
public void readFully(byte[] b) throws IOException {
|
||||
readFully(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void readFully(byte[] b, int off, int len) throws IOException {
|
||||
readPos += len;
|
||||
if (len < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
int n = 0;
|
||||
while (n < len) {
|
||||
int count = input.read(b, off + n, len - n);
|
||||
if (count < 0)
|
||||
throw new EOFException();
|
||||
n += count;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "pos: 0x" + Long.toHexString(readPos);
|
||||
|
||||
@@ -2,6 +2,7 @@ package jadx.core.xmlgen;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -21,6 +22,8 @@ public class ResTableParser extends CommonBinaryParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ResTableParser.class);
|
||||
|
||||
private static final int KNOWN_CONFIG_BYTES = 56;
|
||||
|
||||
private static final class PackageChunk {
|
||||
private final int id;
|
||||
private final String name;
|
||||
@@ -194,6 +197,11 @@ public class ResTableParser extends CommonBinaryParser {
|
||||
|
||||
EntryConfig config = parseConfig();
|
||||
|
||||
if (config.isInvalid) {
|
||||
String typeName = pkg.getTypeStrings()[id - 1];
|
||||
LOG.warn("Invalid config flags detected: " + typeName + config.getQualifiers());
|
||||
}
|
||||
|
||||
int[] entryIndexes = new int[entryCount];
|
||||
for (int i = 0; i < entryCount; i++) {
|
||||
entryIndexes[i] = is.readInt32();
|
||||
@@ -248,134 +256,134 @@ public class ResTableParser extends CommonBinaryParser {
|
||||
}
|
||||
|
||||
private EntryConfig parseConfig() throws IOException {
|
||||
long start = is.getPos();
|
||||
int size = is.readInt32();
|
||||
int read = 28;
|
||||
|
||||
EntryConfig config = new EntryConfig();
|
||||
if (size < 28) {
|
||||
throw new IOException("Config size < 28");
|
||||
}
|
||||
|
||||
is.readInt16(); //mcc
|
||||
is.readInt16(); //mnc
|
||||
boolean isInvalid = false;
|
||||
|
||||
config.setLanguage(parseLocale());
|
||||
config.setCountry(parseLocale());
|
||||
short mcc = (short) is.readInt16();
|
||||
short mnc = (short) is.readInt16();
|
||||
|
||||
char[] language = unpackLocaleOrRegion((byte) is.readInt8(), (byte) is.readInt8(), 'a');
|
||||
char[] country = unpackLocaleOrRegion((byte) is.readInt8(), (byte) is.readInt8(), '0');
|
||||
|
||||
byte orientation = (byte) is.readInt8();
|
||||
byte touchscreen = (byte) is.readInt8();
|
||||
|
||||
int orientation = is.readInt8();
|
||||
int touchscreen = is.readInt8();
|
||||
int density = is.readInt16();
|
||||
|
||||
if (density != 0) {
|
||||
config.setDensity(parseDensity(density));
|
||||
byte keyboard = (byte) is.readInt8();
|
||||
byte navigation = (byte) is.readInt8();
|
||||
byte inputFlags = (byte) is.readInt8();
|
||||
/* inputPad0 */is.readInt8();
|
||||
|
||||
short screenWidth = (short) is.readInt16();
|
||||
short screenHeight = (short) is.readInt16();
|
||||
|
||||
short sdkVersion = (short) is.readInt16();
|
||||
/* minorVersion, now must always be 0 */is.readInt16();
|
||||
|
||||
byte screenLayout = 0;
|
||||
byte uiMode = 0;
|
||||
short smallestScreenWidthDp = 0;
|
||||
if (size >= 32) {
|
||||
screenLayout = (byte) is.readInt8();
|
||||
uiMode = (byte) is.readInt8();
|
||||
smallestScreenWidthDp = (short) is.readInt16();
|
||||
read = 32;
|
||||
}
|
||||
|
||||
is.readInt8(); // keyboard
|
||||
is.readInt8(); // navigation
|
||||
is.readInt8(); // inputFlags
|
||||
is.readInt8(); // inputPad0
|
||||
|
||||
int screenWidth = is.readInt16();
|
||||
int screenHeight = is.readInt16();
|
||||
|
||||
if (screenWidth != 0 && screenHeight != 0) {
|
||||
config.setScreenSize(screenWidth + "x" + screenHeight);
|
||||
short screenWidthDp = 0;
|
||||
short screenHeightDp = 0;
|
||||
if (size >= 36) {
|
||||
screenWidthDp = (short) is.readInt16();
|
||||
screenHeightDp = (short) is.readInt16();
|
||||
read = 36;
|
||||
}
|
||||
|
||||
int sdkVersion = is.readInt16();
|
||||
|
||||
if (sdkVersion != 0) {
|
||||
config.setSdkVersion("v" + sdkVersion);
|
||||
char[] localeScript = null;
|
||||
char[] localeVariant = null;
|
||||
if (size >= 48) {
|
||||
localeScript = readScriptOrVariantChar(4).toCharArray();
|
||||
localeVariant = readScriptOrVariantChar(8).toCharArray();
|
||||
read = 48;
|
||||
}
|
||||
|
||||
int minorVersion = is.readInt16();
|
||||
|
||||
int screenLayout = is.readInt8();
|
||||
int uiMode = is.readInt8();
|
||||
int smallestScreenWidthDp = is.readInt16();
|
||||
|
||||
int screenWidthDp = is.readInt16();
|
||||
int screenHeightDp = is.readInt16();
|
||||
|
||||
if (screenLayout != 0) {
|
||||
config.setScreenLayout(parseScreenLayout(screenLayout));
|
||||
byte screenLayout2 = 0;
|
||||
byte colorMode = 0;
|
||||
if (size >= 52) {
|
||||
screenLayout2 = (byte) is.readInt8();
|
||||
colorMode = (byte) is.readInt8();
|
||||
is.readInt16(); // reserved padding
|
||||
read = 52;
|
||||
}
|
||||
|
||||
if (smallestScreenWidthDp != 0) {
|
||||
config.setSmallestScreenWidthDp("sw" + smallestScreenWidthDp + "dp");
|
||||
if (size >= 56) {
|
||||
is.readInt32();
|
||||
read = 56;
|
||||
}
|
||||
|
||||
if (orientation != 0) {
|
||||
config.setOrientation(parseOrientation(orientation));
|
||||
}
|
||||
int exceedingSize = size - KNOWN_CONFIG_BYTES;
|
||||
if (exceedingSize > 0) {
|
||||
byte[] buf = new byte[exceedingSize];
|
||||
read += exceedingSize;
|
||||
is.readFully(buf);
|
||||
BigInteger exceedingBI = new BigInteger(1, buf);
|
||||
|
||||
if (screenWidthDp != 0) {
|
||||
config.setScreenWidthDp("w" + screenWidthDp + "dp");
|
||||
}
|
||||
|
||||
if (screenHeightDp != 0) {
|
||||
config.setScreenHeightDp("h" + screenHeightDp + "dp");
|
||||
}
|
||||
|
||||
is.skipToPos(start + size, "Skip config parsing");
|
||||
return config;
|
||||
}
|
||||
|
||||
private String parseOrientation(int orientation) {
|
||||
if (orientation == 1) {
|
||||
return "port";
|
||||
} else if (orientation == 2) {
|
||||
return "land";
|
||||
} else {
|
||||
return "o" + orientation;
|
||||
}
|
||||
}
|
||||
|
||||
private String parseScreenLayout(int screenLayout) {
|
||||
switch (screenLayout) {
|
||||
case 1:
|
||||
return "small";
|
||||
case 2:
|
||||
return "normal";
|
||||
case 3:
|
||||
return "large";
|
||||
case 4:
|
||||
return "xlarge";
|
||||
case 64:
|
||||
return "ldltr";
|
||||
case 128:
|
||||
return "ldrtl";
|
||||
default:
|
||||
return "sl" + screenLayout;
|
||||
}
|
||||
}
|
||||
|
||||
private String parseDensity(int density) {
|
||||
if (density == 120) {
|
||||
return "ldpi";
|
||||
} else if (density == 160) {
|
||||
return "mdpi";
|
||||
} else if (density == 240) {
|
||||
return "hdpi";
|
||||
} else if (density == 320) {
|
||||
return "xhdpi";
|
||||
} else if (density == 480) {
|
||||
return "xxhdpi";
|
||||
} else if (density == 640) {
|
||||
return "xxxhdpi";
|
||||
} else {
|
||||
return density + "dpi";
|
||||
}
|
||||
}
|
||||
|
||||
private String parseLocale() throws IOException {
|
||||
int b1 = is.readInt8();
|
||||
int b2 = is.readInt8();
|
||||
String str = null;
|
||||
if (b1 != 0 && b2 != 0) {
|
||||
if ((b1 & 0x80) == 0) {
|
||||
str = new String(new char[]{(char) b1, (char) b2});
|
||||
if (exceedingBI.equals(BigInteger.ZERO)) {
|
||||
LOG.info(String
|
||||
.format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.",
|
||||
KNOWN_CONFIG_BYTES));
|
||||
} else {
|
||||
LOG.warn("TODO: parse locale: 0x{}{}", Integer.toHexString(b1), Integer.toHexString(b2));
|
||||
LOG.warn(String.format("Config flags size > %d. Size = %d. Exceeding bytes: 0x%X.",
|
||||
KNOWN_CONFIG_BYTES, size, exceedingBI));
|
||||
isInvalid = true;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
|
||||
int remainingSize = size - read;
|
||||
if (remainingSize > 0) {
|
||||
is.skip(remainingSize);
|
||||
}
|
||||
|
||||
return new EntryConfig(mcc, mnc, language, country,
|
||||
orientation, touchscreen, density, keyboard, navigation,
|
||||
inputFlags, screenWidth, screenHeight, sdkVersion,
|
||||
screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
|
||||
screenHeightDp, localeScript, localeVariant, screenLayout2,
|
||||
colorMode, isInvalid, size);
|
||||
}
|
||||
|
||||
private char[] unpackLocaleOrRegion(byte in0, byte in1, char base) throws IOException {
|
||||
// check high bit, if so we have a packed 3 letter code
|
||||
if (((in0 >> 7) & 1) == 1) {
|
||||
int first = in1 & 0x1F;
|
||||
int second = ((in1 & 0xE0) >> 5) + ((in0 & 0x03) << 3);
|
||||
int third = (in0 & 0x7C) >> 2;
|
||||
|
||||
// since this function handles languages & regions, we add the value(s) to the base char
|
||||
// which is usually 'a' or '0' depending on language or region.
|
||||
return new char[] { (char) (first + base), (char) (second + base), (char) (third + base) };
|
||||
}
|
||||
return new char[] { (char) in0, (char) in1 };
|
||||
}
|
||||
|
||||
private String readScriptOrVariantChar(int length) throws IOException {
|
||||
StringBuilder string = new StringBuilder(16);
|
||||
|
||||
while(length-- != 0) {
|
||||
short ch = (short) is.readInt8();
|
||||
if (ch == 0) {
|
||||
break;
|
||||
}
|
||||
string.append((char) ch);
|
||||
}
|
||||
is.skip(length);
|
||||
|
||||
return string.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,10 +202,10 @@ public class ResXmlGen {
|
||||
|
||||
private String getFileName(ResourceEntry ri) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String locale = ri.getConfig().getLocale();
|
||||
String qualifiers = ri.getConfig().getQualifiers();
|
||||
sb.append("res/values");
|
||||
if (!locale.isEmpty()) {
|
||||
sb.append('-').append(locale);
|
||||
if (!qualifiers.isEmpty()) {
|
||||
sb.append(qualifiers);
|
||||
}
|
||||
sb.append('/');
|
||||
sb.append(ri.getTypeName());
|
||||
|
||||
@@ -1,159 +1,649 @@
|
||||
/**
|
||||
* Copyright (C) 2018 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
* Copyright (C) 2018 Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package jadx.core.xmlgen.entry;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Original source code can be found <a href="https://raw.githubusercontent.com/iBotPeaches/Apktool/master/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java">here</a>
|
||||
*/
|
||||
|
||||
public class EntryConfig {
|
||||
private String language;
|
||||
private String country;
|
||||
private String density;
|
||||
private String screenSize;
|
||||
private String sdkVersion;
|
||||
private String screenLayout;
|
||||
private String smallestScreenWidthDp;
|
||||
private String orientation;
|
||||
private String screenWidthDp;
|
||||
private String screenHeightDp;
|
||||
public final short mcc;
|
||||
public final short mnc;
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
public final char[] language;
|
||||
public final char[] region;
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
public final byte orientation;
|
||||
public final byte touchscreen;
|
||||
public final int density;
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
public final byte keyboard;
|
||||
public final byte navigation;
|
||||
public final byte inputFlags;
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
public final short screenWidth;
|
||||
public final short screenHeight;
|
||||
|
||||
public String getLocale() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (screenSize != null) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(screenSize);
|
||||
} else if (screenHeightDp != null) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(screenHeightDp);
|
||||
} else if (screenWidthDp != null) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(screenWidthDp);
|
||||
} else if (screenLayout != null) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(screenLayout);
|
||||
} else if (smallestScreenWidthDp != null) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(smallestScreenWidthDp);
|
||||
} else if (density != null) {
|
||||
sb.append(density);
|
||||
}
|
||||
if (language != null) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(language);
|
||||
}
|
||||
if (country != null) {
|
||||
sb.append("-r").append(country);
|
||||
}
|
||||
if (orientation != null) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(orientation);
|
||||
}
|
||||
if (sdkVersion != null) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(sdkVersion);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
public final short sdkVersion;
|
||||
|
||||
public String getDensity() {
|
||||
return density;
|
||||
}
|
||||
public final byte screenLayout;
|
||||
public final byte uiMode;
|
||||
public final short smallestScreenWidthDp;
|
||||
|
||||
public void setDensity(String density) {
|
||||
this.density = density;
|
||||
}
|
||||
public final short screenWidthDp;
|
||||
public final short screenHeightDp;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getLocale());
|
||||
if (sb.length() != 0) {
|
||||
sb.insert(0, " [");
|
||||
sb.append(']');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
private final char[] localeScript;
|
||||
private final char[] localeVariant;
|
||||
|
||||
public void setScreenSize(String screenSize) {
|
||||
this.screenSize = screenSize;
|
||||
}
|
||||
private final byte screenLayout2;
|
||||
private final byte colorMode;
|
||||
|
||||
public String getScreenSize() {
|
||||
return screenSize;
|
||||
}
|
||||
public final boolean isInvalid;
|
||||
|
||||
public void setSdkVersion(String sdkVersion) {
|
||||
this.sdkVersion = sdkVersion;
|
||||
}
|
||||
private final String mQualifiers;
|
||||
|
||||
public String getSdkVersion() {
|
||||
return sdkVersion;
|
||||
}
|
||||
private final int size;
|
||||
|
||||
public void setScreenLayout(String screenLayout) {
|
||||
this.screenLayout = screenLayout;
|
||||
}
|
||||
public EntryConfig() {
|
||||
mcc = 0;
|
||||
mnc = 0;
|
||||
language = new char[]{'\00', '\00'};
|
||||
region = new char[]{'\00', '\00'};
|
||||
orientation = ORIENTATION_ANY;
|
||||
touchscreen = TOUCHSCREEN_ANY;
|
||||
density = DENSITY_DEFAULT;
|
||||
keyboard = KEYBOARD_ANY;
|
||||
navigation = NAVIGATION_ANY;
|
||||
inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
|
||||
screenWidth = 0;
|
||||
screenHeight = 0;
|
||||
sdkVersion = 0;
|
||||
screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY;
|
||||
uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY;
|
||||
smallestScreenWidthDp = 0;
|
||||
screenWidthDp = 0;
|
||||
screenHeightDp = 0;
|
||||
localeScript = null;
|
||||
localeVariant = null;
|
||||
screenLayout2 = 0;
|
||||
colorMode = COLOR_WIDE_UNDEFINED;
|
||||
isInvalid = false;
|
||||
mQualifiers = "";
|
||||
size = 0;
|
||||
}
|
||||
|
||||
public String getScreenLayout() {
|
||||
return screenLayout;
|
||||
}
|
||||
public EntryConfig(short mcc, short mnc, char[] language,
|
||||
char[] region, byte orientation,
|
||||
byte touchscreen, int density, byte keyboard, byte navigation,
|
||||
byte inputFlags, short screenWidth, short screenHeight,
|
||||
short sdkVersion, byte screenLayout, byte uiMode,
|
||||
short smallestScreenWidthDp, short screenWidthDp,
|
||||
short screenHeightDp, char[] localeScript, char[] localeVariant,
|
||||
byte screenLayout2, byte colorMode, boolean isInvalid, int size) {
|
||||
if (orientation < 0 || orientation > 3) {
|
||||
LOG.warn("Invalid orientation value: " + orientation);
|
||||
orientation = 0;
|
||||
isInvalid = true;
|
||||
}
|
||||
if (touchscreen < 0 || touchscreen > 3) {
|
||||
LOG.warn("Invalid touchscreen value: " + touchscreen);
|
||||
touchscreen = 0;
|
||||
isInvalid = true;
|
||||
}
|
||||
if (density < -1) {
|
||||
LOG.warn("Invalid density value: " + density);
|
||||
density = 0;
|
||||
isInvalid = true;
|
||||
}
|
||||
if (keyboard < 0 || keyboard > 3) {
|
||||
LOG.warn("Invalid keyboard value: " + keyboard);
|
||||
keyboard = 0;
|
||||
isInvalid = true;
|
||||
}
|
||||
if (navigation < 0 || navigation > 4) {
|
||||
LOG.warn("Invalid navigation value: " + navigation);
|
||||
navigation = 0;
|
||||
isInvalid = true;
|
||||
}
|
||||
|
||||
public void setSmallestScreenWidthDp(String smallestScreenWidthDp) {
|
||||
this.smallestScreenWidthDp = smallestScreenWidthDp;
|
||||
}
|
||||
if (localeScript != null && localeScript.length != 0) {
|
||||
if (localeScript[0] == '\00') {
|
||||
localeScript = null;
|
||||
}
|
||||
} else {
|
||||
localeScript = null;
|
||||
}
|
||||
|
||||
public String getSmallestScreenWidthDp() {
|
||||
return smallestScreenWidthDp;
|
||||
}
|
||||
if (localeVariant != null && localeVariant.length != 0) {
|
||||
if (localeVariant[0] == '\00') {
|
||||
localeVariant = null;
|
||||
}
|
||||
} else {
|
||||
localeVariant = null;
|
||||
}
|
||||
|
||||
public void setOrientation(String orientation) {
|
||||
this.orientation = orientation;
|
||||
}
|
||||
this.mcc = mcc;
|
||||
this.mnc = mnc;
|
||||
this.language = language;
|
||||
this.region = region;
|
||||
this.orientation = orientation;
|
||||
this.touchscreen = touchscreen;
|
||||
this.density = density;
|
||||
this.keyboard = keyboard;
|
||||
this.navigation = navigation;
|
||||
this.inputFlags = inputFlags;
|
||||
this.screenWidth = screenWidth;
|
||||
this.screenHeight = screenHeight;
|
||||
this.sdkVersion = sdkVersion;
|
||||
this.screenLayout = screenLayout;
|
||||
this.uiMode = uiMode;
|
||||
this.smallestScreenWidthDp = smallestScreenWidthDp;
|
||||
this.screenWidthDp = screenWidthDp;
|
||||
this.screenHeightDp = screenHeightDp;
|
||||
this.localeScript = localeScript;
|
||||
this.localeVariant = localeVariant;
|
||||
this.screenLayout2 = screenLayout2;
|
||||
this.colorMode = colorMode;
|
||||
this.isInvalid = isInvalid;
|
||||
this.size = size;
|
||||
mQualifiers = generateQualifiers();
|
||||
}
|
||||
|
||||
public String getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
public String getQualifiers() {
|
||||
return mQualifiers;
|
||||
}
|
||||
|
||||
public void setScreenWidthDp(String screenWidthDp) {
|
||||
this.screenWidthDp = screenWidthDp;
|
||||
}
|
||||
private String generateQualifiers() {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
if (mcc != 0) {
|
||||
ret.append("-mcc").append(String.format("%03d", mcc));
|
||||
if (mnc != MNC_ZERO) {
|
||||
if (mnc != 0) {
|
||||
ret.append("-mnc");
|
||||
if (size <= 32) {
|
||||
if (mnc > 0 && mnc < 10) {
|
||||
ret.append(String.format("%02d", mnc));
|
||||
} else {
|
||||
ret.append(String.format("%03d", mnc));
|
||||
}
|
||||
} else {
|
||||
ret.append(mnc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.append("-mnc00");
|
||||
}
|
||||
} else {
|
||||
if (mnc != 0) {
|
||||
ret.append("-mnc").append(mnc);
|
||||
}
|
||||
}
|
||||
ret.append(getLocaleString());
|
||||
|
||||
public String getScreenWidthDp() {
|
||||
return screenWidthDp;
|
||||
}
|
||||
switch (screenLayout & MASK_LAYOUTDIR) {
|
||||
case SCREENLAYOUT_LAYOUTDIR_RTL:
|
||||
ret.append("-ldrtl");
|
||||
break;
|
||||
case SCREENLAYOUT_LAYOUTDIR_LTR:
|
||||
ret.append("-ldltr");
|
||||
break;
|
||||
}
|
||||
if (smallestScreenWidthDp != 0) {
|
||||
ret.append("-sw").append(smallestScreenWidthDp).append("dp");
|
||||
}
|
||||
if (screenWidthDp != 0) {
|
||||
ret.append("-w").append(screenWidthDp).append("dp");
|
||||
}
|
||||
if (screenHeightDp != 0) {
|
||||
ret.append("-h").append(screenHeightDp).append("dp");
|
||||
}
|
||||
switch (screenLayout & MASK_SCREENSIZE) {
|
||||
case SCREENSIZE_SMALL:
|
||||
ret.append("-small");
|
||||
break;
|
||||
case SCREENSIZE_NORMAL:
|
||||
ret.append("-normal");
|
||||
break;
|
||||
case SCREENSIZE_LARGE:
|
||||
ret.append("-large");
|
||||
break;
|
||||
case SCREENSIZE_XLARGE:
|
||||
ret.append("-xlarge");
|
||||
break;
|
||||
}
|
||||
switch (screenLayout & MASK_SCREENLONG) {
|
||||
case SCREENLONG_YES:
|
||||
ret.append("-long");
|
||||
break;
|
||||
case SCREENLONG_NO:
|
||||
ret.append("-notlong");
|
||||
break;
|
||||
}
|
||||
switch (screenLayout2 & MASK_SCREENROUND) {
|
||||
case SCREENLAYOUT_ROUND_NO:
|
||||
ret.append("-notround");
|
||||
break;
|
||||
case SCREENLAYOUT_ROUND_YES:
|
||||
ret.append("-round");
|
||||
break;
|
||||
}
|
||||
switch (colorMode & COLOR_HDR_MASK) {
|
||||
case COLOR_HDR_YES:
|
||||
ret.append("-highdr");
|
||||
break;
|
||||
case COLOR_HDR_NO:
|
||||
ret.append("-lowdr");
|
||||
break;
|
||||
}
|
||||
switch (colorMode & COLOR_WIDE_MASK) {
|
||||
case COLOR_WIDE_YES:
|
||||
ret.append("-widecg");
|
||||
break;
|
||||
case COLOR_WIDE_NO:
|
||||
ret.append("-nowidecg");
|
||||
break;
|
||||
}
|
||||
switch (orientation) {
|
||||
case ORIENTATION_PORT:
|
||||
ret.append("-port");
|
||||
break;
|
||||
case ORIENTATION_LAND:
|
||||
ret.append("-land");
|
||||
break;
|
||||
case ORIENTATION_SQUARE:
|
||||
ret.append("-square");
|
||||
break;
|
||||
}
|
||||
switch (uiMode & MASK_UI_MODE_TYPE) {
|
||||
case UI_MODE_TYPE_CAR:
|
||||
ret.append("-car");
|
||||
break;
|
||||
case UI_MODE_TYPE_DESK:
|
||||
ret.append("-desk");
|
||||
break;
|
||||
case UI_MODE_TYPE_TELEVISION:
|
||||
ret.append("-television");
|
||||
break;
|
||||
case UI_MODE_TYPE_SMALLUI:
|
||||
ret.append("-smallui");
|
||||
break;
|
||||
case UI_MODE_TYPE_MEDIUMUI:
|
||||
ret.append("-mediumui");
|
||||
break;
|
||||
case UI_MODE_TYPE_LARGEUI:
|
||||
ret.append("-largeui");
|
||||
break;
|
||||
case UI_MODE_TYPE_GODZILLAUI:
|
||||
ret.append("-godzillaui");
|
||||
break;
|
||||
case UI_MODE_TYPE_HUGEUI:
|
||||
ret.append("-hugeui");
|
||||
break;
|
||||
case UI_MODE_TYPE_APPLIANCE:
|
||||
ret.append("-appliance");
|
||||
break;
|
||||
case UI_MODE_TYPE_WATCH:
|
||||
ret.append("-watch");
|
||||
break;
|
||||
case UI_MODE_TYPE_VR_HEADSET:
|
||||
ret.append("-vrheadset");
|
||||
break;
|
||||
}
|
||||
switch (uiMode & MASK_UI_MODE_NIGHT) {
|
||||
case UI_MODE_NIGHT_YES:
|
||||
ret.append("-night");
|
||||
break;
|
||||
case UI_MODE_NIGHT_NO:
|
||||
ret.append("-notnight");
|
||||
break;
|
||||
}
|
||||
switch (density) {
|
||||
case DENSITY_DEFAULT:
|
||||
break;
|
||||
case DENSITY_LOW:
|
||||
ret.append("-ldpi");
|
||||
break;
|
||||
case DENSITY_MEDIUM:
|
||||
ret.append("-mdpi");
|
||||
break;
|
||||
case DENSITY_HIGH:
|
||||
ret.append("-hdpi");
|
||||
break;
|
||||
case DENSITY_TV:
|
||||
ret.append("-tvdpi");
|
||||
break;
|
||||
case DENSITY_XHIGH:
|
||||
ret.append("-xhdpi");
|
||||
break;
|
||||
case DENSITY_XXHIGH:
|
||||
ret.append("-xxhdpi");
|
||||
break;
|
||||
case DENSITY_XXXHIGH:
|
||||
ret.append("-xxxhdpi");
|
||||
break;
|
||||
case DENSITY_ANY:
|
||||
ret.append("-anydpi");
|
||||
break;
|
||||
case DENSITY_NONE:
|
||||
ret.append("-nodpi");
|
||||
break;
|
||||
default:
|
||||
ret.append('-').append(density).append("dpi");
|
||||
}
|
||||
switch (touchscreen) {
|
||||
case TOUCHSCREEN_NOTOUCH:
|
||||
ret.append("-notouch");
|
||||
break;
|
||||
case TOUCHSCREEN_STYLUS:
|
||||
ret.append("-stylus");
|
||||
break;
|
||||
case TOUCHSCREEN_FINGER:
|
||||
ret.append("-finger");
|
||||
break;
|
||||
}
|
||||
switch (inputFlags & MASK_KEYSHIDDEN) {
|
||||
case KEYSHIDDEN_NO:
|
||||
ret.append("-keysexposed");
|
||||
break;
|
||||
case KEYSHIDDEN_YES:
|
||||
ret.append("-keyshidden");
|
||||
break;
|
||||
case KEYSHIDDEN_SOFT:
|
||||
ret.append("-keyssoft");
|
||||
break;
|
||||
}
|
||||
switch (keyboard) {
|
||||
case KEYBOARD_NOKEYS:
|
||||
ret.append("-nokeys");
|
||||
break;
|
||||
case KEYBOARD_QWERTY:
|
||||
ret.append("-qwerty");
|
||||
break;
|
||||
case KEYBOARD_12KEY:
|
||||
ret.append("-12key");
|
||||
break;
|
||||
}
|
||||
switch (inputFlags & MASK_NAVHIDDEN) {
|
||||
case NAVHIDDEN_NO:
|
||||
ret.append("-navexposed");
|
||||
break;
|
||||
case NAVHIDDEN_YES:
|
||||
ret.append("-navhidden");
|
||||
break;
|
||||
}
|
||||
switch (navigation) {
|
||||
case NAVIGATION_NONAV:
|
||||
ret.append("-nonav");
|
||||
break;
|
||||
case NAVIGATION_DPAD:
|
||||
ret.append("-dpad");
|
||||
break;
|
||||
case NAVIGATION_TRACKBALL:
|
||||
ret.append("-trackball");
|
||||
break;
|
||||
case NAVIGATION_WHEEL:
|
||||
ret.append("-wheel");
|
||||
break;
|
||||
}
|
||||
if (screenWidth != 0 && screenHeight != 0) {
|
||||
if (screenWidth > screenHeight) {
|
||||
ret.append(String.format("-%dx%d", screenWidth, screenHeight));
|
||||
} else {
|
||||
ret.append(String.format("-%dx%d", screenHeight, screenWidth));
|
||||
}
|
||||
}
|
||||
if (sdkVersion > 0 && sdkVersion >= getNaturalSdkVersionRequirement()) {
|
||||
ret.append("-v").append(sdkVersion);
|
||||
}
|
||||
if (isInvalid) {
|
||||
ret.append("-ERR").append(sErrCounter++);
|
||||
}
|
||||
|
||||
public void setScreenHeightDp(String screenHeightDp) {
|
||||
this.screenHeightDp = screenHeightDp;
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
public String getScreenHeightDp() {
|
||||
return screenHeightDp;
|
||||
}
|
||||
private short getNaturalSdkVersionRequirement() {
|
||||
if ((uiMode & MASK_UI_MODE_TYPE) == UI_MODE_TYPE_VR_HEADSET || (colorMode & COLOR_WIDE_MASK) != 0 || ((colorMode & COLOR_HDR_MASK) != 0)) {
|
||||
return SDK_OREO;
|
||||
}
|
||||
if ((screenLayout2 & MASK_SCREENROUND) != 0) {
|
||||
return SDK_MNC;
|
||||
}
|
||||
if (density == DENSITY_ANY) {
|
||||
return SDK_LOLLIPOP;
|
||||
}
|
||||
if (smallestScreenWidthDp != 0 || screenWidthDp != 0 || screenHeightDp != 0) {
|
||||
return SDK_HONEYCOMB_MR2;
|
||||
}
|
||||
if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != UI_MODE_NIGHT_ANY) {
|
||||
return SDK_FROYO;
|
||||
}
|
||||
if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != SCREENSIZE_ANY || density != DENSITY_DEFAULT) {
|
||||
return SDK_DONUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private String getLocaleString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// check for old style non BCP47 tags
|
||||
// allows values-xx-rXX, values-xx, values-xxx-rXX
|
||||
// denies values-xxx, anything else
|
||||
if (localeVariant == null && localeScript == null && (region[0] != '\00' || language[0] != '\00') &&
|
||||
region.length != 3) {
|
||||
sb.append("-").append(language);
|
||||
if (region[0] != '\00') {
|
||||
sb.append("-r").append(region);
|
||||
}
|
||||
} else { // BCP47
|
||||
if (language[0] == '\00' && region[0] == '\00') {
|
||||
return sb.toString(); // early return, no language or region
|
||||
}
|
||||
sb.append("-b+");
|
||||
if (language[0] != '\00') {
|
||||
sb.append(language);
|
||||
}
|
||||
if (localeScript != null && localeScript.length == 4) {
|
||||
sb.append("+").append(localeScript);
|
||||
}
|
||||
if ((region.length == 2 || region.length == 3) && region[0] != '\00') {
|
||||
sb.append("+").append(region);
|
||||
}
|
||||
if (localeVariant != null && localeVariant.length >= 5) {
|
||||
sb.append("+").append(toUpper(localeVariant));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String toUpper(char[] character) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (char ch : character) {
|
||||
sb.append(Character.toUpperCase(ch));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return !getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final EntryConfig other = (EntryConfig) obj;
|
||||
return this.mQualifiers.equals(other.mQualifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 17;
|
||||
hash = 31 * hash + this.mQualifiers.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
// TODO: Dirty static hack. This counter should be a part of ResPackage,
|
||||
// but it would be hard right now and this feature is very rarely used.
|
||||
private static int sErrCounter = 0;
|
||||
|
||||
public final static byte SDK_BASE = 1;
|
||||
public final static byte SDK_BASE_1_1 = 2;
|
||||
public final static byte SDK_CUPCAKE = 3;
|
||||
public final static byte SDK_DONUT = 4;
|
||||
public final static byte SDK_ECLAIR = 5;
|
||||
public final static byte SDK_ECLAIR_0_1 = 6;
|
||||
public final static byte SDK_ECLAIR_MR1 = 7;
|
||||
public final static byte SDK_FROYO = 8;
|
||||
public final static byte SDK_GINGERBREAD = 9;
|
||||
public final static byte SDK_GINGERBREAD_MR1 = 10;
|
||||
public final static byte SDK_HONEYCOMB = 11;
|
||||
public final static byte SDK_HONEYCOMB_MR1 = 12;
|
||||
public final static byte SDK_HONEYCOMB_MR2 = 13;
|
||||
public final static byte SDK_ICE_CREAM_SANDWICH = 14;
|
||||
public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15;
|
||||
public final static byte SDK_JELLY_BEAN = 16;
|
||||
public final static byte SDK_JELLY_BEAN_MR1 = 17;
|
||||
public final static byte SDK_JELLY_BEAN_MR2 = 18;
|
||||
public final static byte SDK_KITKAT = 19;
|
||||
public final static byte SDK_LOLLIPOP = 21;
|
||||
public final static byte SDK_LOLLIPOP_MR1 = 22;
|
||||
public final static byte SDK_MNC = 23;
|
||||
public final static byte SDK_NOUGAT = 24;
|
||||
public final static byte SDK_NOUGAT_MR1 = 25;
|
||||
public final static byte SDK_OREO = 26;
|
||||
public final static byte SDK_OREO_MR1 = 27;
|
||||
public final static byte SDK_P = 28;
|
||||
|
||||
public final static byte ORIENTATION_ANY = 0;
|
||||
public final static byte ORIENTATION_PORT = 1;
|
||||
public final static byte ORIENTATION_LAND = 2;
|
||||
public final static byte ORIENTATION_SQUARE = 3;
|
||||
|
||||
public final static byte TOUCHSCREEN_ANY = 0;
|
||||
public final static byte TOUCHSCREEN_NOTOUCH = 1;
|
||||
public final static byte TOUCHSCREEN_STYLUS = 2;
|
||||
public final static byte TOUCHSCREEN_FINGER = 3;
|
||||
|
||||
public final static int DENSITY_DEFAULT = 0;
|
||||
public final static int DENSITY_LOW = 120;
|
||||
public final static int DENSITY_MEDIUM = 160;
|
||||
public final static int DENSITY_400 = 190;
|
||||
public final static int DENSITY_TV = 213;
|
||||
public final static int DENSITY_HIGH = 240;
|
||||
public final static int DENSITY_XHIGH = 320;
|
||||
public final static int DENSITY_XXHIGH = 480;
|
||||
public final static int DENSITY_XXXHIGH = 640;
|
||||
public final static int DENSITY_ANY = 0xFFFE;
|
||||
public final static int DENSITY_NONE = 0xFFFF;
|
||||
|
||||
public final static int MNC_ZERO = -1;
|
||||
|
||||
public final static short MASK_LAYOUTDIR = 0xc0;
|
||||
public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00;
|
||||
public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40;
|
||||
public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80;
|
||||
public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06;
|
||||
|
||||
public final static short MASK_SCREENROUND = 0x03;
|
||||
public final static short SCREENLAYOUT_ROUND_ANY = 0;
|
||||
public final static short SCREENLAYOUT_ROUND_NO = 0x1;
|
||||
public final static short SCREENLAYOUT_ROUND_YES = 0x2;
|
||||
|
||||
public final static byte KEYBOARD_ANY = 0;
|
||||
public final static byte KEYBOARD_NOKEYS = 1;
|
||||
public final static byte KEYBOARD_QWERTY = 2;
|
||||
public final static byte KEYBOARD_12KEY = 3;
|
||||
|
||||
public final static byte NAVIGATION_ANY = 0;
|
||||
public final static byte NAVIGATION_NONAV = 1;
|
||||
public final static byte NAVIGATION_DPAD = 2;
|
||||
public final static byte NAVIGATION_TRACKBALL = 3;
|
||||
public final static byte NAVIGATION_WHEEL = 4;
|
||||
|
||||
public final static byte MASK_KEYSHIDDEN = 0x3;
|
||||
public final static byte KEYSHIDDEN_ANY = 0x0;
|
||||
public final static byte KEYSHIDDEN_NO = 0x1;
|
||||
public final static byte KEYSHIDDEN_YES = 0x2;
|
||||
public final static byte KEYSHIDDEN_SOFT = 0x3;
|
||||
|
||||
public final static byte MASK_NAVHIDDEN = 0xc;
|
||||
public final static byte NAVHIDDEN_ANY = 0x0;
|
||||
public final static byte NAVHIDDEN_NO = 0x4;
|
||||
public final static byte NAVHIDDEN_YES = 0x8;
|
||||
|
||||
public final static byte MASK_SCREENSIZE = 0x0f;
|
||||
public final static byte SCREENSIZE_ANY = 0x00;
|
||||
public final static byte SCREENSIZE_SMALL = 0x01;
|
||||
public final static byte SCREENSIZE_NORMAL = 0x02;
|
||||
public final static byte SCREENSIZE_LARGE = 0x03;
|
||||
public final static byte SCREENSIZE_XLARGE = 0x04;
|
||||
|
||||
public final static byte MASK_SCREENLONG = 0x30;
|
||||
public final static byte SCREENLONG_ANY = 0x00;
|
||||
public final static byte SCREENLONG_NO = 0x10;
|
||||
public final static byte SCREENLONG_YES = 0x20;
|
||||
|
||||
public final static byte MASK_UI_MODE_TYPE = 0x0f;
|
||||
public final static byte UI_MODE_TYPE_ANY = 0x00;
|
||||
public final static byte UI_MODE_TYPE_NORMAL = 0x01;
|
||||
public final static byte UI_MODE_TYPE_DESK = 0x02;
|
||||
public final static byte UI_MODE_TYPE_CAR = 0x03;
|
||||
public final static byte UI_MODE_TYPE_TELEVISION = 0x04;
|
||||
public final static byte UI_MODE_TYPE_APPLIANCE = 0x05;
|
||||
public final static byte UI_MODE_TYPE_WATCH = 0x06;
|
||||
public final static byte UI_MODE_TYPE_VR_HEADSET = 0x07;
|
||||
|
||||
// start - miui
|
||||
public final static byte UI_MODE_TYPE_GODZILLAUI = 0x0b;
|
||||
public final static byte UI_MODE_TYPE_SMALLUI = 0x0c;
|
||||
public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d;
|
||||
public final static byte UI_MODE_TYPE_LARGEUI = 0x0e;
|
||||
public final static byte UI_MODE_TYPE_HUGEUI = 0x0f;
|
||||
// end - miui
|
||||
|
||||
public final static byte MASK_UI_MODE_NIGHT = 0x30;
|
||||
public final static byte UI_MODE_NIGHT_ANY = 0x00;
|
||||
public final static byte UI_MODE_NIGHT_NO = 0x10;
|
||||
public final static byte UI_MODE_NIGHT_YES = 0x20;
|
||||
|
||||
public final static byte COLOR_HDR_MASK = 0xC;
|
||||
public final static byte COLOR_HDR_NO = 0x4;
|
||||
public final static byte COLOR_HDR_SHIFT = 0x2;
|
||||
public final static byte COLOR_HDR_UNDEFINED = 0x0;
|
||||
public final static byte COLOR_HDR_YES = 0x8;
|
||||
|
||||
public final static byte COLOR_UNDEFINED = 0x0;
|
||||
|
||||
public final static byte COLOR_WIDE_UNDEFINED = 0x0;
|
||||
public final static byte COLOR_WIDE_NO = 0x1;
|
||||
public final static byte COLOR_WIDE_YES = 0x2;
|
||||
public final static byte COLOR_WIDE_MASK = 0x3;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EntryConfig.class);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user