Add -round and -notround qualifier to android runtime/aapt

The round qualifier denotes a device with a screen shape that
is round. The qualifier shows up after the 'long/notlong' qualifier
and before the orientation 'port/land/square' qualifiers.

Change-Id: I3044258b2703a9165694b79725bade770fa6cea1
This commit is contained in:
Adam Lesinski
2015-05-14 14:25:36 -07:00
parent 038959e851
commit 2738c96d99
9 changed files with 208 additions and 1 deletions

View File

@@ -615,6 +615,10 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c
const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
// Constants duplicated from Java class android.content.res.Configuration.
static const jint kScreenLayoutRoundMask = 0x300;
static const jint kScreenLayoutRoundShift = 8;
config.mcc = (uint16_t)mcc;
config.mnc = (uint16_t)mnc;
config.orientation = (uint8_t)orientation;
@@ -632,6 +636,13 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c
config.uiMode = (uint8_t)uiMode;
config.sdkVersion = (uint16_t)sdkVersion;
config.minorVersion = 0;
// In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
// in C++. We must extract the round qualifier out of the Java screenLayout and put it
// into screenLayout2.
config.screenLayout2 =
(uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
am->setConfiguration(config, locale8);
if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);

View File

@@ -1132,6 +1132,24 @@ struct ResTable_config
// chars. Interpreted in conjunction with the locale field.
char localeVariant[8];
enum {
// screenLayout2 bits for round/notround.
MASK_SCREENROUND = 0x03,
SCREENROUND_ANY = ACONFIGURATION_SCREENROUND_ANY,
SCREENROUND_NO = ACONFIGURATION_SCREENROUND_NO,
SCREENROUND_YES = ACONFIGURATION_SCREENROUND_YES,
};
// An extension of screenConfig.
union {
struct {
uint8_t screenLayout2; // Contains round/notround qualifier.
uint8_t screenConfigPad1; // Reserved padding.
uint16_t screenConfigPad2; // Reserved padding.
};
uint32_t screenConfig2;
};
void copyFromDeviceNoSwap(const ResTable_config& o);
void copyFromDtoH(const ResTable_config& o);
@@ -1160,6 +1178,7 @@ struct ResTable_config
CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
CONFIG_UI_MODE = ACONFIGURATION_UI_MODE,
CONFIG_LAYOUTDIR = ACONFIGURATION_LAYOUTDIR,
CONFIG_SCREEN_ROUND = ACONFIGURATION_SCREEN_ROUND,
};
// Compare two configuration, returning CONFIG_* flags set for each value

View File

@@ -1894,6 +1894,8 @@ int ResTable_config::compare(const ResTable_config& o) const {
if (diff != 0) return diff;
diff = (int32_t)(screenLayout - o.screenLayout);
if (diff != 0) return diff;
diff = (int32_t)(screenLayout2 - o.screenLayout2);
if (diff != 0) return diff;
diff = (int32_t)(uiMode - o.uiMode);
if (diff != 0) return diff;
diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp);
@@ -1951,6 +1953,9 @@ int ResTable_config::compareLogical(const ResTable_config& o) const {
if (screenLayout != o.screenLayout) {
return screenLayout < o.screenLayout ? -1 : 1;
}
if (screenLayout2 != o.screenLayout2) {
return screenLayout2 < o.screenLayout2 ? -1 : 1;
}
if (uiMode != o.uiMode) {
return uiMode < o.uiMode ? -1 : 1;
}
@@ -1975,6 +1980,7 @@ int ResTable_config::diff(const ResTable_config& o) const {
if (version != o.version) diffs |= CONFIG_VERSION;
if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) diffs |= CONFIG_LAYOUTDIR;
if ((screenLayout & ~MASK_LAYOUTDIR) != (o.screenLayout & ~MASK_LAYOUTDIR)) diffs |= CONFIG_SCREEN_LAYOUT;
if ((screenLayout2 & MASK_SCREENROUND) != (o.screenLayout2 & MASK_SCREENROUND)) diffs |= CONFIG_SCREEN_ROUND;
if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE;
if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE;
@@ -2080,6 +2086,13 @@ bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const {
}
}
if (screenLayout2 || o.screenLayout2) {
if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0) {
if (!(screenLayout2 & MASK_SCREENROUND)) return false;
if (!(o.screenLayout2 & MASK_SCREENROUND)) return true;
}
}
if (orientation != o.orientation) {
if (!orientation) return false;
if (!o.orientation) return true;
@@ -2267,6 +2280,13 @@ bool ResTable_config::isBetterThan(const ResTable_config& o,
}
}
if (screenLayout2 || o.screenLayout2) {
if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0 &&
(requested->screenLayout2 & MASK_SCREENROUND)) {
return screenLayout2 & MASK_SCREENROUND;
}
}
if ((orientation != o.orientation) && requested->orientation) {
return (orientation);
}
@@ -2480,6 +2500,15 @@ bool ResTable_config::match(const ResTable_config& settings) const {
return false;
}
}
if (screenConfig2 != 0) {
const int screenRound = screenLayout2 & MASK_SCREENROUND;
const int setScreenRound = settings.screenLayout2 & MASK_SCREENROUND;
if (screenRound != 0 && screenRound != setScreenRound) {
return false;
}
}
if (screenSizeDp != 0) {
if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) {
if (kDebugTableSuperNoisy) {
@@ -2770,6 +2799,20 @@ String8 ResTable_config::toString() const {
break;
}
}
if ((screenLayout2&MASK_SCREENROUND) != 0) {
if (res.size() > 0) res.append("-");
switch (screenLayout2&MASK_SCREENROUND) {
case SCREENROUND_NO:
res.append("notround");
break;
case SCREENROUND_YES:
res.append("round");
break;
default:
res.appendFormat("screenRound=%d", dtohs(screenLayout2&MASK_SCREENROUND));
break;
}
}
if (orientation != ORIENTATION_ANY) {
if (res.size() > 0) res.append("-");
switch (orientation) {

View File

@@ -100,4 +100,82 @@ TEST(ConfigTest, shouldSelectBestDensityWhenNoneSpecified) {
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
}
TEST(ConfigTest, shouldMatchRoundQualifier) {
ResTable_config deviceConfig;
memset(&deviceConfig, 0, sizeof(deviceConfig));
ResTable_config roundConfig;
memset(&roundConfig, 0, sizeof(roundConfig));
roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
EXPECT_FALSE(roundConfig.match(deviceConfig));
deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
EXPECT_TRUE(roundConfig.match(deviceConfig));
deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_NO;
EXPECT_FALSE(roundConfig.match(deviceConfig));
ResTable_config notRoundConfig;
memset(&notRoundConfig, 0, sizeof(notRoundConfig));
notRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_NO;
EXPECT_TRUE(notRoundConfig.match(deviceConfig));
}
TEST(ConfigTest, RoundQualifierShouldHaveStableSortOrder) {
ResTable_config defaultConfig;
memset(&defaultConfig, 0, sizeof(defaultConfig));
ResTable_config longConfig = defaultConfig;
longConfig.screenLayout = ResTable_config::SCREENLONG_YES;
ResTable_config longRoundConfig = longConfig;
longRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
ResTable_config longRoundPortConfig = longConfig;
longRoundPortConfig.orientation = ResTable_config::ORIENTATION_PORT;
EXPECT_TRUE(longConfig.compare(longRoundConfig) < 0);
EXPECT_TRUE(longConfig.compareLogical(longRoundConfig) < 0);
EXPECT_TRUE(longRoundConfig.compare(longConfig) > 0);
EXPECT_TRUE(longRoundConfig.compareLogical(longConfig) > 0);
EXPECT_TRUE(longRoundConfig.compare(longRoundPortConfig) < 0);
EXPECT_TRUE(longRoundConfig.compareLogical(longRoundPortConfig) < 0);
EXPECT_TRUE(longRoundPortConfig.compare(longRoundConfig) > 0);
EXPECT_TRUE(longRoundPortConfig.compareLogical(longRoundConfig) > 0);
}
TEST(ConfigTest, ScreenShapeHasCorrectDiff) {
ResTable_config defaultConfig;
memset(&defaultConfig, 0, sizeof(defaultConfig));
ResTable_config roundConfig = defaultConfig;
roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
EXPECT_EQ(defaultConfig.diff(roundConfig), ResTable_config::CONFIG_SCREEN_ROUND);
}
TEST(ConfigTest, RoundIsMoreSpecific) {
ResTable_config deviceConfig;
memset(&deviceConfig, 0, sizeof(deviceConfig));
deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
deviceConfig.screenLayout = ResTable_config::SCREENLONG_YES;
ResTable_config targetConfigA;
memset(&targetConfigA, 0, sizeof(targetConfigA));
ResTable_config targetConfigB = targetConfigA;
targetConfigB.screenLayout = ResTable_config::SCREENLONG_YES;
ResTable_config targetConfigC = targetConfigB;
targetConfigC.screenLayout2 = ResTable_config::SCREENROUND_YES;
EXPECT_TRUE(targetConfigB.isBetterThan(targetConfigA, &deviceConfig));
EXPECT_TRUE(targetConfigC.isBetterThan(targetConfigB, &deviceConfig));
}
} // namespace android.

View File

@@ -101,6 +101,10 @@ int32_t AConfiguration_getScreenLong(AConfiguration* config) {
>> ResTable_config::SHIFT_SCREENLONG;
}
int32_t AConfiguration_getScreenRound(AConfiguration* config) {
return (config->screenLayout2&ResTable_config::MASK_SCREENROUND);
}
int32_t AConfiguration_getUiModeType(AConfiguration* config) {
return config->uiMode&ResTable_config::MASK_UI_MODE_TYPE;
}
@@ -192,6 +196,11 @@ void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong) {
| ((screenLong<<ResTable_config::SHIFT_SCREENLONG)&ResTable_config::MASK_SCREENLONG);
}
void AConfiguration_setScreenRound(AConfiguration* config, int32_t screenRound) {
config->screenLayout2 = (config->screenLayout2&~ResTable_config::MASK_SCREENROUND)
| (screenRound&ResTable_config::MASK_SCREENROUND);
}
void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType) {
config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
| (uiModeType&ResTable_config::MASK_UI_MODE_TYPE);

View File

@@ -123,6 +123,14 @@ bool parse(const String8& str, ConfigDescription* out) {
part = parts[index].string();
}
if (parseScreenRound(part, &config)) {
index++;
if (index == N) {
goto success;
}
part = parts[index].string();
}
if (parseOrientation(part, &config)) {
index++;
if (index == N) {
@@ -241,7 +249,9 @@ void applyVersionForCompatibility(ConfigDescription* config) {
}
uint16_t minSdk = 0;
if (config->density == ResTable_config::DENSITY_ANY) {
if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
minSdk = SDK_MNC;
} else if (config->density == ResTable_config::DENSITY_ANY) {
minSdk = SDK_LOLLIPOP;
} else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
|| config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
@@ -395,7 +405,26 @@ bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
| ResTable_config::SCREENLONG_NO;
return true;
}
return false;
}
bool parseScreenRound(const char* name, ResTable_config* out) {
if (strcmp(name, kWildcardName) == 0) {
if (out) out->screenLayout2 =
(out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
| ResTable_config::SCREENROUND_ANY;
return true;
} else if (strcmp(name, "round") == 0) {
if (out) out->screenLayout2 =
(out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
| ResTable_config::SCREENROUND_YES;
return true;
} else if (strcmp(name, "notround") == 0) {
if (out) out->screenLayout2 =
(out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
| ResTable_config::SCREENROUND_NO;
return true;
}
return false;
}

View File

@@ -60,6 +60,7 @@ bool parseScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
bool parseScreenHeightDp(const char* str, android::ResTable_config* out = NULL);
bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL);
bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL);
bool parseScreenRound(const char* name, android::ResTable_config* out = NULL);
bool parseOrientation(const char* str, android::ResTable_config* out = NULL);
bool parseUiModeType(const char* str, android::ResTable_config* out = NULL);
bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL);

View File

@@ -38,6 +38,7 @@ enum {
SDK_KITKAT_WATCH = 20,
SDK_LOLLIPOP = 21,
SDK_LOLLIPOP_MR1 = 22,
SDK_MNC = 23,
};
#endif // H_AAPT_SDK_CONSTANTS

View File

@@ -19,6 +19,7 @@
#include "AaptConfig.h"
#include "ConfigDescription.h"
#include "SdkConstants.h"
#include "TestHelper.h"
using android::String8;
@@ -82,3 +83,18 @@ TEST(AaptConfigTest, TestParsingOfCarAttribute) {
EXPECT_TRUE(TestParse("car", &config));
EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode);
}
TEST(AaptConfigTest, TestParsingRoundQualifier) {
ConfigDescription config;
EXPECT_TRUE(TestParse("round", &config));
EXPECT_EQ(android::ResTable_config::SCREENROUND_YES,
config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
EXPECT_EQ(SDK_MNC, config.sdkVersion);
EXPECT_EQ(String8("round-v23"), config.toString());
EXPECT_TRUE(TestParse("notround", &config));
EXPECT_EQ(android::ResTable_config::SCREENROUND_NO,
config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
EXPECT_EQ(SDK_MNC, config.sdkVersion);
EXPECT_EQ(String8("notround-v23"), config.toString());
}