Merge pull request #100 from iBotPeaches/bcp47-support

[WIP] Initial support for BCP47 tags
This commit is contained in:
Connor Tumbleson 2015-02-25 16:46:22 -06:00
commit 4638c06de4
12 changed files with 180 additions and 23 deletions

View File

@ -26,9 +26,7 @@ public class ResConfigFlags {
public final short mnc;
public final char[] language;
public final char[] country;
public final short layoutDirection;
public final char[] region;
public final byte orientation;
public final byte touchscreen;
@ -50,6 +48,9 @@ public class ResConfigFlags {
public final short screenWidthDp;
public final short screenHeightDp;
private final char[] localeScript;
private final char[] localeVariant;
public final boolean isInvalid;
private final String mQualifiers;
@ -58,8 +59,7 @@ public class ResConfigFlags {
mcc = 0;
mnc = 0;
language = new char[] { '\00', '\00' };
country = new char[] { '\00', '\00' };
layoutDirection = SCREENLAYOUT_LAYOUTDIR_ANY;
region = new char[] { '\00', '\00' };
orientation = ORIENTATION_ANY;
touchscreen = TOUCHSCREEN_ANY;
density = DENSITY_DEFAULT;
@ -74,17 +74,20 @@ public class ResConfigFlags {
smallestScreenWidthDp = 0;
screenWidthDp = 0;
screenHeightDp = 0;
localeScript = new char[] { '\00', '\00', '\00', '\00' };
localeVariant = new char[] { '\00', '\00', '\00', '\00', '\00', '\00', '\00', '\00' };
isInvalid = false;
mQualifiers = "";
}
public ResConfigFlags(short mcc, short mnc, char[] language,
char[] country, short layoutDirection, byte orientation,
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, boolean isInvalid) {
short screenHeightDp, char[] localeScript, char[] localeVariant,
boolean isInvalid) {
if (orientation < 0 || orientation > 3) {
LOGGER.warning("Invalid orientation value: " + orientation);
orientation = 0;
@ -114,8 +117,7 @@ public class ResConfigFlags {
this.mcc = mcc;
this.mnc = mnc;
this.language = language;
this.country = country;
this.layoutDirection = layoutDirection;
this.region = region;
this.orientation = orientation;
this.touchscreen = touchscreen;
this.density = density;
@ -130,6 +132,8 @@ public class ResConfigFlags {
this.smallestScreenWidthDp = smallestScreenWidthDp;
this.screenWidthDp = screenWidthDp;
this.screenHeightDp = screenHeightDp;
this.localeScript = localeScript;
this.localeVariant = localeVariant;
this.isInvalid = isInvalid;
mQualifiers = generateQualifiers();
}
@ -155,12 +159,8 @@ public class ResConfigFlags {
ret.append("-mnc00");
}
}
if (language[0] != '\00') {
ret.append('-').append(language);
if (country[0] != '\00') {
ret.append("-r").append(country);
}
}
ret.append(getLocaleString());
switch (screenLayout & MASK_LAYOUTDIR) {
case SCREENLAYOUT_LAYOUTDIR_RTL:
ret.append("-ldrtl");
@ -369,6 +369,51 @@ public class ResConfigFlags {
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 (language[0] != '\00' && localeScript.length == 0 && localeVariant.length == 0 &&
(region.length != 3 && language.length != 3) ||
(language.length == 3 && region.length == 2 && region[0] != '\00' &&
localeScript.length == 0 && localeVariant.length == 0)) {
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.length == 4) {
sb.append("+").append(localeScript);
}
if ((region.length == 2 || region.length == 3) && region[0] != '\00') {
sb.append("+").append(region);
}
if (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]";

View File

@ -256,8 +256,8 @@ public class ARSCDecoder {
short mcc = mIn.readShort();
short mnc = mIn.readShort();
char[] language = new char[] { (char) mIn.readByte(), (char) mIn.readByte() };
char[] country = new char[] { (char) mIn.readByte(), (char) mIn.readByte() };
char[] language = this.unpackLanguageOrRegion(mIn.readByte(), mIn.readByte(), 'a');
char[] country = this.unpackLanguageOrRegion(mIn.readByte(), mIn.readByte(), '0');
byte orientation = mIn.readByte();
byte touchscreen = mIn.readByte();
@ -291,9 +291,11 @@ public class ARSCDecoder {
screenHeightDp = mIn.readShort();
}
short layoutDirection = 0;
if (size >= 38) {
layoutDirection = mIn.readShort();
char[] localeScript = {'\00'};
char[] localeVariant = {'\00'};
if (size >= 48) {
localeScript = this.readScriptOrVariantChar(4).toCharArray();
localeVariant = this.readScriptOrVariantChar(8).toCharArray();
}
int exceedingSize = size - KNOWN_CONFIG_BYTES;
@ -313,11 +315,40 @@ public class ARSCDecoder {
}
}
return new ResConfigFlags(mcc, mnc, language, country, layoutDirection,
return new ResConfigFlags(mcc, mnc, language, country,
orientation, touchscreen, density, keyboard, navigation,
inputFlags, screenWidth, screenHeight, sdkVersion,
screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
screenHeightDp, isInvalid);
screenHeightDp, localeScript, localeVariant, isInvalid);
}
private char[] unpackLanguageOrRegion(byte in0, byte in1, char base) throws AndrolibException {
// 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 AndrolibException, IOException {
StringBuilder string = new StringBuilder(16);
while(length-- != 0) {
short ch = mIn.readByte();
if (ch == 0) {
break;
}
string.append((char) ch);
}
mIn.skipBytes(length);
return string.toString();
}
private void addMissingResSpecs() throws AndrolibException {
@ -416,7 +447,7 @@ public class ARSCDecoder {
}
private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class.getName());
private static final int KNOWN_CONFIG_BYTES = 38;
private static final int KNOWN_CONFIG_BYTES = 48;
public static class ARSCData {

View File

@ -182,6 +182,51 @@ public class BuildAndDecodeTest {
compareValuesFiles("values-watch/strings.xml");
}
@Test
public void packed3CharsTest() throws BrutException, IOException {
compareValuesFiles("values-ast-rES/strings.xml");
}
@Test
public void rightToLeftTest() throws BrutException, IOException {
compareValuesFiles("values-ldrtl/strings.xml");
}
@Test
public void scriptBcp47Test() throws BrutException, IOException {
compareValuesFiles("values-b+en+Latn+US/strings.xml");
}
@Test
public void threeLetterLangBcp47Test() throws BrutException, IOException {
compareValuesFiles("values-b+ast/strings.xml");
}
@Test
public void twoLetterLangBcp47Test() throws BrutException, IOException {
compareValuesFiles("values-en-rUS/strings.xml");
}
@Test
public void variantBcp47Test() throws BrutException, IOException {
compareValuesFiles("values-b+en+US+POSIX/strings.xml");
}
@Test
public void fourpartBcp47Test() throws BrutException, IOException {
compareValuesFiles("values-b+ast+Latn+IT+AREVELA/strings.xml");
}
@Test
public void RegionLocaleBcp47Test() throws BrutException, IOException {
compareValuesFiles("values-b+en+Latn+419/strings.xml");
}
@Test
public void numericalRegionBcp47Test() throws BrutException, IOException {
compareValuesFiles("values-b+eng+419/strings.xml");
}
@Test
public void drawableNoDpiTest() throws BrutException, IOException {
compareResFolder("drawable-nodpi");

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="test1">test1</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="test1">test1</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="test1">test1</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="test1">test1</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="test1">test1</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="test1">test1</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="test1">test1</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="test1">test1</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="test1">test1</string>
</resources>