aapt now sorts the strings in the resource string pool.
In our current environment with very many translations, this can save a lot of RAM -- for example over 200K in Gmail just by sorting the strings in the Gmail .apk (not the framework). Also add a new aapt command to print the contents of the resource table string pool. Change-Id: I1da037b3e2c377b890833ff57ab158965314ac48
This commit is contained in:
@@ -347,6 +347,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
|
||||
sb.append(" (no locale)");
|
||||
}
|
||||
switch (textLayoutDirection) {
|
||||
case LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE: /* ltr not interesting */ break;
|
||||
case LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break;
|
||||
default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break;
|
||||
}
|
||||
|
||||
@@ -444,23 +444,31 @@ public:
|
||||
|
||||
void uninit();
|
||||
|
||||
// Return string entry as UTF16; if the pool is UTF8, the string will
|
||||
// be converted before returning.
|
||||
inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const {
|
||||
return stringAt(ref.index, outLen);
|
||||
}
|
||||
const char16_t* stringAt(size_t idx, size_t* outLen) const;
|
||||
|
||||
// Note: returns null if the string pool is not UTF8.
|
||||
const char* string8At(size_t idx, size_t* outLen) const;
|
||||
|
||||
// Return string whether the pool is UTF8 or UTF16. Does not allow you
|
||||
// to distinguish null.
|
||||
const String8 string8ObjectAt(size_t idx) const;
|
||||
|
||||
const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const;
|
||||
const ResStringPool_span* styleAt(size_t idx) const;
|
||||
|
||||
ssize_t indexOfString(const char16_t* str, size_t strLen) const;
|
||||
|
||||
size_t size() const;
|
||||
size_t styleCount() const;
|
||||
size_t bytes() const;
|
||||
|
||||
#ifndef HAVE_ANDROID_OS
|
||||
bool isSorted() const;
|
||||
bool isUTF8() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
status_t mError;
|
||||
@@ -746,7 +754,9 @@ private:
|
||||
/**
|
||||
* Header for a resource table. Its data contains a series of
|
||||
* additional chunks:
|
||||
* * A ResStringPool_header containing all table values.
|
||||
* * A ResStringPool_header containing all table values. This string pool
|
||||
* contains all of the string values in the entire resource table (not
|
||||
* the names of entries or type identifiers however).
|
||||
* * One or more ResTable_package chunks.
|
||||
*
|
||||
* Specific entries within a resource table can be uniquely identified
|
||||
@@ -984,68 +994,15 @@ struct ResTable_config
|
||||
uint32_t screenSizeDp;
|
||||
};
|
||||
|
||||
inline void copyFromDeviceNoSwap(const ResTable_config& o) {
|
||||
const size_t size = dtohl(o.size);
|
||||
if (size >= sizeof(ResTable_config)) {
|
||||
*this = o;
|
||||
} else {
|
||||
memcpy(this, &o, size);
|
||||
memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size);
|
||||
}
|
||||
}
|
||||
void copyFromDeviceNoSwap(const ResTable_config& o);
|
||||
|
||||
inline void copyFromDtoH(const ResTable_config& o) {
|
||||
copyFromDeviceNoSwap(o);
|
||||
size = sizeof(ResTable_config);
|
||||
mcc = dtohs(mcc);
|
||||
mnc = dtohs(mnc);
|
||||
density = dtohs(density);
|
||||
screenWidth = dtohs(screenWidth);
|
||||
screenHeight = dtohs(screenHeight);
|
||||
sdkVersion = dtohs(sdkVersion);
|
||||
minorVersion = dtohs(minorVersion);
|
||||
smallestScreenWidthDp = dtohs(smallestScreenWidthDp);
|
||||
screenWidthDp = dtohs(screenWidthDp);
|
||||
screenHeightDp = dtohs(screenHeightDp);
|
||||
}
|
||||
|
||||
inline void swapHtoD() {
|
||||
size = htodl(size);
|
||||
mcc = htods(mcc);
|
||||
mnc = htods(mnc);
|
||||
density = htods(density);
|
||||
screenWidth = htods(screenWidth);
|
||||
screenHeight = htods(screenHeight);
|
||||
sdkVersion = htods(sdkVersion);
|
||||
minorVersion = htods(minorVersion);
|
||||
smallestScreenWidthDp = htods(smallestScreenWidthDp);
|
||||
screenWidthDp = htods(screenWidthDp);
|
||||
screenHeightDp = htods(screenHeightDp);
|
||||
}
|
||||
|
||||
inline int compare(const ResTable_config& o) const {
|
||||
int32_t diff = (int32_t)(imsi - o.imsi);
|
||||
if (diff != 0) return diff;
|
||||
diff = (int32_t)(locale - o.locale);
|
||||
if (diff != 0) return diff;
|
||||
diff = (int32_t)(screenType - o.screenType);
|
||||
if (diff != 0) return diff;
|
||||
diff = (int32_t)(input - o.input);
|
||||
if (diff != 0) return diff;
|
||||
diff = (int32_t)(screenSize - o.screenSize);
|
||||
if (diff != 0) return diff;
|
||||
diff = (int32_t)(version - o.version);
|
||||
if (diff != 0) return diff;
|
||||
diff = (int32_t)(screenLayout - o.screenLayout);
|
||||
if (diff != 0) return diff;
|
||||
diff = (int32_t)(uiMode - o.uiMode);
|
||||
if (diff != 0) return diff;
|
||||
diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp);
|
||||
if (diff != 0) return diff;
|
||||
diff = (int32_t)(screenSizeDp - o.screenSizeDp);
|
||||
return (int)diff;
|
||||
}
|
||||
void copyFromDtoH(const ResTable_config& o);
|
||||
|
||||
void swapHtoD();
|
||||
|
||||
int compare(const ResTable_config& o) const;
|
||||
int compareLogical(const ResTable_config& o) const;
|
||||
|
||||
// Flags indicating a set of config values. These flag constants must
|
||||
// match the corresponding ones in android.content.pm.ActivityInfo and
|
||||
// attrs_manifest.xml.
|
||||
@@ -1068,158 +1025,10 @@ struct ResTable_config
|
||||
|
||||
// Compare two configuration, returning CONFIG_* flags set for each value
|
||||
// that is different.
|
||||
inline int diff(const ResTable_config& o) const {
|
||||
int diffs = 0;
|
||||
if (mcc != o.mcc) diffs |= CONFIG_MCC;
|
||||
if (mnc != o.mnc) diffs |= CONFIG_MNC;
|
||||
if (locale != o.locale) diffs |= CONFIG_LOCALE;
|
||||
if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION;
|
||||
if (density != o.density) diffs |= CONFIG_DENSITY;
|
||||
if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN;
|
||||
if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0)
|
||||
diffs |= CONFIG_KEYBOARD_HIDDEN;
|
||||
if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD;
|
||||
if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION;
|
||||
if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE;
|
||||
if (version != o.version) diffs |= CONFIG_VERSION;
|
||||
if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT;
|
||||
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;
|
||||
return diffs;
|
||||
}
|
||||
int diff(const ResTable_config& o) const;
|
||||
|
||||
// Return true if 'this' is more specific than 'o'.
|
||||
inline bool
|
||||
isMoreSpecificThan(const ResTable_config& o) const {
|
||||
// The order of the following tests defines the importance of one
|
||||
// configuration parameter over another. Those tests first are more
|
||||
// important, trumping any values in those following them.
|
||||
if (imsi || o.imsi) {
|
||||
if (mcc != o.mcc) {
|
||||
if (!mcc) return false;
|
||||
if (!o.mcc) return true;
|
||||
}
|
||||
|
||||
if (mnc != o.mnc) {
|
||||
if (!mnc) return false;
|
||||
if (!o.mnc) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (locale || o.locale) {
|
||||
if (language[0] != o.language[0]) {
|
||||
if (!language[0]) return false;
|
||||
if (!o.language[0]) return true;
|
||||
}
|
||||
|
||||
if (country[0] != o.country[0]) {
|
||||
if (!country[0]) return false;
|
||||
if (!o.country[0]) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
|
||||
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
|
||||
if (!smallestScreenWidthDp) return false;
|
||||
if (!o.smallestScreenWidthDp) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (screenSizeDp || o.screenSizeDp) {
|
||||
if (screenWidthDp != o.screenWidthDp) {
|
||||
if (!screenWidthDp) return false;
|
||||
if (!o.screenWidthDp) return true;
|
||||
}
|
||||
|
||||
if (screenHeightDp != o.screenHeightDp) {
|
||||
if (!screenHeightDp) return false;
|
||||
if (!o.screenHeightDp) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (screenLayout || o.screenLayout) {
|
||||
if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) {
|
||||
if (!(screenLayout & MASK_SCREENSIZE)) return false;
|
||||
if (!(o.screenLayout & MASK_SCREENSIZE)) return true;
|
||||
}
|
||||
if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) {
|
||||
if (!(screenLayout & MASK_SCREENLONG)) return false;
|
||||
if (!(o.screenLayout & MASK_SCREENLONG)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (orientation != o.orientation) {
|
||||
if (!orientation) return false;
|
||||
if (!o.orientation) return true;
|
||||
}
|
||||
|
||||
if (uiMode || o.uiMode) {
|
||||
if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) {
|
||||
if (!(uiMode & MASK_UI_MODE_TYPE)) return false;
|
||||
if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true;
|
||||
}
|
||||
if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) {
|
||||
if (!(uiMode & MASK_UI_MODE_NIGHT)) return false;
|
||||
if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// density is never 'more specific'
|
||||
// as the default just equals 160
|
||||
|
||||
if (touchscreen != o.touchscreen) {
|
||||
if (!touchscreen) return false;
|
||||
if (!o.touchscreen) return true;
|
||||
}
|
||||
|
||||
if (input || o.input) {
|
||||
if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) {
|
||||
if (!(inputFlags & MASK_KEYSHIDDEN)) return false;
|
||||
if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true;
|
||||
}
|
||||
|
||||
if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) {
|
||||
if (!(inputFlags & MASK_NAVHIDDEN)) return false;
|
||||
if (!(o.inputFlags & MASK_NAVHIDDEN)) return true;
|
||||
}
|
||||
|
||||
if (keyboard != o.keyboard) {
|
||||
if (!keyboard) return false;
|
||||
if (!o.keyboard) return true;
|
||||
}
|
||||
|
||||
if (navigation != o.navigation) {
|
||||
if (!navigation) return false;
|
||||
if (!o.navigation) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (screenSize || o.screenSize) {
|
||||
if (screenWidth != o.screenWidth) {
|
||||
if (!screenWidth) return false;
|
||||
if (!o.screenWidth) return true;
|
||||
}
|
||||
|
||||
if (screenHeight != o.screenHeight) {
|
||||
if (!screenHeight) return false;
|
||||
if (!o.screenHeight) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (version || o.version) {
|
||||
if (sdkVersion != o.sdkVersion) {
|
||||
if (!sdkVersion) return false;
|
||||
if (!o.sdkVersion) return true;
|
||||
}
|
||||
|
||||
if (minorVersion != o.minorVersion) {
|
||||
if (!minorVersion) return false;
|
||||
if (!o.minorVersion) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool isMoreSpecificThan(const ResTable_config& o) const;
|
||||
|
||||
// Return true if 'this' is a better match than 'o' for the 'requested'
|
||||
// configuration. This assumes that match() has already been used to
|
||||
@@ -1231,222 +1040,7 @@ struct ResTable_config
|
||||
// they are not equal then one must be generic because only generic and
|
||||
// '==requested' will pass the match() call. So if this is not generic,
|
||||
// it wins. If this IS generic, o wins (return false).
|
||||
inline bool
|
||||
isBetterThan(const ResTable_config& o,
|
||||
const ResTable_config* requested) const {
|
||||
if (requested) {
|
||||
if (imsi || o.imsi) {
|
||||
if ((mcc != o.mcc) && requested->mcc) {
|
||||
return (mcc);
|
||||
}
|
||||
|
||||
if ((mnc != o.mnc) && requested->mnc) {
|
||||
return (mnc);
|
||||
}
|
||||
}
|
||||
|
||||
if (locale || o.locale) {
|
||||
if ((language[0] != o.language[0]) && requested->language[0]) {
|
||||
return (language[0]);
|
||||
}
|
||||
|
||||
if ((country[0] != o.country[0]) && requested->country[0]) {
|
||||
return (country[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
|
||||
// The configuration closest to the actual size is best.
|
||||
// We assume that larger configs have already been filtered
|
||||
// out at this point. That means we just want the largest one.
|
||||
return smallestScreenWidthDp >= o.smallestScreenWidthDp;
|
||||
}
|
||||
|
||||
if (screenSizeDp || o.screenSizeDp) {
|
||||
// "Better" is based on the sum of the difference between both
|
||||
// width and height from the requested dimensions. We are
|
||||
// assuming the invalid configs (with smaller dimens) have
|
||||
// already been filtered. Note that if a particular dimension
|
||||
// is unspecified, we will end up with a large value (the
|
||||
// difference between 0 and the requested dimension), which is
|
||||
// good since we will prefer a config that has specified a
|
||||
// dimension value.
|
||||
int myDelta = 0, otherDelta = 0;
|
||||
if (requested->screenWidthDp) {
|
||||
myDelta += requested->screenWidthDp - screenWidthDp;
|
||||
otherDelta += requested->screenWidthDp - o.screenWidthDp;
|
||||
}
|
||||
if (requested->screenHeightDp) {
|
||||
myDelta += requested->screenHeightDp - screenHeightDp;
|
||||
otherDelta += requested->screenHeightDp - o.screenHeightDp;
|
||||
}
|
||||
//ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d",
|
||||
// screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp,
|
||||
// requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta);
|
||||
return (myDelta <= otherDelta);
|
||||
}
|
||||
|
||||
if (screenLayout || o.screenLayout) {
|
||||
if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0
|
||||
&& (requested->screenLayout & MASK_SCREENSIZE)) {
|
||||
// A little backwards compatibility here: undefined is
|
||||
// considered equivalent to normal. But only if the
|
||||
// requested size is at least normal; otherwise, small
|
||||
// is better than the default.
|
||||
int mySL = (screenLayout & MASK_SCREENSIZE);
|
||||
int oSL = (o.screenLayout & MASK_SCREENSIZE);
|
||||
int fixedMySL = mySL;
|
||||
int fixedOSL = oSL;
|
||||
if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) {
|
||||
if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL;
|
||||
if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL;
|
||||
}
|
||||
// For screen size, the best match is the one that is
|
||||
// closest to the requested screen size, but not over
|
||||
// (the not over part is dealt with in match() below).
|
||||
if (fixedMySL == fixedOSL) {
|
||||
// If the two are the same, but 'this' is actually
|
||||
// undefined, then the other is really a better match.
|
||||
if (mySL == 0) return false;
|
||||
return true;
|
||||
}
|
||||
return fixedMySL >= fixedOSL;
|
||||
}
|
||||
if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0
|
||||
&& (requested->screenLayout & MASK_SCREENLONG)) {
|
||||
return (screenLayout & MASK_SCREENLONG);
|
||||
}
|
||||
}
|
||||
|
||||
if ((orientation != o.orientation) && requested->orientation) {
|
||||
return (orientation);
|
||||
}
|
||||
|
||||
if (uiMode || o.uiMode) {
|
||||
if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0
|
||||
&& (requested->uiMode & MASK_UI_MODE_TYPE)) {
|
||||
return (uiMode & MASK_UI_MODE_TYPE);
|
||||
}
|
||||
if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0
|
||||
&& (requested->uiMode & MASK_UI_MODE_NIGHT)) {
|
||||
return (uiMode & MASK_UI_MODE_NIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
if (screenType || o.screenType) {
|
||||
if (density != o.density) {
|
||||
// density is tough. Any density is potentially useful
|
||||
// because the system will scale it. Scaling down
|
||||
// is generally better than scaling up.
|
||||
// Default density counts as 160dpi (the system default)
|
||||
// TODO - remove 160 constants
|
||||
int h = (density?density:160);
|
||||
int l = (o.density?o.density:160);
|
||||
bool bImBigger = true;
|
||||
if (l > h) {
|
||||
int t = h;
|
||||
h = l;
|
||||
l = t;
|
||||
bImBigger = false;
|
||||
}
|
||||
|
||||
int reqValue = (requested->density?requested->density:160);
|
||||
if (reqValue >= h) {
|
||||
// requested value higher than both l and h, give h
|
||||
return bImBigger;
|
||||
}
|
||||
if (l >= reqValue) {
|
||||
// requested value lower than both l and h, give l
|
||||
return !bImBigger;
|
||||
}
|
||||
// saying that scaling down is 2x better than up
|
||||
if (((2 * l) - reqValue) * h > reqValue * reqValue) {
|
||||
return !bImBigger;
|
||||
} else {
|
||||
return bImBigger;
|
||||
}
|
||||
}
|
||||
|
||||
if ((touchscreen != o.touchscreen) && requested->touchscreen) {
|
||||
return (touchscreen);
|
||||
}
|
||||
}
|
||||
|
||||
if (input || o.input) {
|
||||
const int keysHidden = inputFlags & MASK_KEYSHIDDEN;
|
||||
const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN;
|
||||
if (keysHidden != oKeysHidden) {
|
||||
const int reqKeysHidden =
|
||||
requested->inputFlags & MASK_KEYSHIDDEN;
|
||||
if (reqKeysHidden) {
|
||||
|
||||
if (!keysHidden) return false;
|
||||
if (!oKeysHidden) return true;
|
||||
// For compatibility, we count KEYSHIDDEN_NO as being
|
||||
// the same as KEYSHIDDEN_SOFT. Here we disambiguate
|
||||
// these by making an exact match more specific.
|
||||
if (reqKeysHidden == keysHidden) return true;
|
||||
if (reqKeysHidden == oKeysHidden) return false;
|
||||
}
|
||||
}
|
||||
|
||||
const int navHidden = inputFlags & MASK_NAVHIDDEN;
|
||||
const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN;
|
||||
if (navHidden != oNavHidden) {
|
||||
const int reqNavHidden =
|
||||
requested->inputFlags & MASK_NAVHIDDEN;
|
||||
if (reqNavHidden) {
|
||||
|
||||
if (!navHidden) return false;
|
||||
if (!oNavHidden) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((keyboard != o.keyboard) && requested->keyboard) {
|
||||
return (keyboard);
|
||||
}
|
||||
|
||||
if ((navigation != o.navigation) && requested->navigation) {
|
||||
return (navigation);
|
||||
}
|
||||
}
|
||||
|
||||
if (screenSize || o.screenSize) {
|
||||
// "Better" is based on the sum of the difference between both
|
||||
// width and height from the requested dimensions. We are
|
||||
// assuming the invalid configs (with smaller sizes) have
|
||||
// already been filtered. Note that if a particular dimension
|
||||
// is unspecified, we will end up with a large value (the
|
||||
// difference between 0 and the requested dimension), which is
|
||||
// good since we will prefer a config that has specified a
|
||||
// size value.
|
||||
int myDelta = 0, otherDelta = 0;
|
||||
if (requested->screenWidth) {
|
||||
myDelta += requested->screenWidth - screenWidth;
|
||||
otherDelta += requested->screenWidth - o.screenWidth;
|
||||
}
|
||||
if (requested->screenHeight) {
|
||||
myDelta += requested->screenHeight - screenHeight;
|
||||
otherDelta += requested->screenHeight - o.screenHeight;
|
||||
}
|
||||
return (myDelta <= otherDelta);
|
||||
}
|
||||
|
||||
if (version || o.version) {
|
||||
if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) {
|
||||
return (sdkVersion > o.sdkVersion);
|
||||
}
|
||||
|
||||
if ((minorVersion != o.minorVersion) &&
|
||||
requested->minorVersion) {
|
||||
return (minorVersion);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return isMoreSpecificThan(o);
|
||||
}
|
||||
bool isBetterThan(const ResTable_config& o, const ResTable_config* requested) const;
|
||||
|
||||
// Return true if 'this' can be considered a match for the parameters in
|
||||
// 'settings'.
|
||||
@@ -1454,150 +1048,11 @@ struct ResTable_config
|
||||
// but a request for the default should not match odd specifics
|
||||
// (ie, request with no mcc should not match a particular mcc's data)
|
||||
// settings is the requested settings
|
||||
inline bool match(const ResTable_config& settings) const {
|
||||
if (imsi != 0) {
|
||||
if (mcc != 0 && mcc != settings.mcc) {
|
||||
return false;
|
||||
}
|
||||
if (mnc != 0 && mnc != settings.mnc) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (locale != 0) {
|
||||
if (language[0] != 0
|
||||
&& (language[0] != settings.language[0]
|
||||
|| language[1] != settings.language[1])) {
|
||||
return false;
|
||||
}
|
||||
if (country[0] != 0
|
||||
&& (country[0] != settings.country[0]
|
||||
|| country[1] != settings.country[1])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (screenConfig != 0) {
|
||||
const int screenSize = screenLayout&MASK_SCREENSIZE;
|
||||
const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
|
||||
// Any screen sizes for larger screens than the setting do not
|
||||
// match.
|
||||
if (screenSize != 0 && screenSize > setScreenSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int screenLong = screenLayout&MASK_SCREENLONG;
|
||||
const int setScreenLong = settings.screenLayout&MASK_SCREENLONG;
|
||||
if (screenLong != 0 && screenLong != setScreenLong) {
|
||||
return false;
|
||||
}
|
||||
bool match(const ResTable_config& settings) const;
|
||||
|
||||
const int uiModeType = uiMode&MASK_UI_MODE_TYPE;
|
||||
const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE;
|
||||
if (uiModeType != 0 && uiModeType != setUiModeType) {
|
||||
return false;
|
||||
}
|
||||
void getLocale(char str[6]) const;
|
||||
|
||||
const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT;
|
||||
const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT;
|
||||
if (uiModeNight != 0 && uiModeNight != setUiModeNight) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (smallestScreenWidthDp != 0
|
||||
&& smallestScreenWidthDp > settings.smallestScreenWidthDp) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (screenSizeDp != 0) {
|
||||
if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) {
|
||||
//ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp);
|
||||
return false;
|
||||
}
|
||||
if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) {
|
||||
//ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (screenType != 0) {
|
||||
if (orientation != 0 && orientation != settings.orientation) {
|
||||
return false;
|
||||
}
|
||||
// density always matches - we can scale it. See isBetterThan
|
||||
if (touchscreen != 0 && touchscreen != settings.touchscreen) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (input != 0) {
|
||||
const int keysHidden = inputFlags&MASK_KEYSHIDDEN;
|
||||
const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN;
|
||||
if (keysHidden != 0 && keysHidden != setKeysHidden) {
|
||||
// For compatibility, we count a request for KEYSHIDDEN_NO as also
|
||||
// matching the more recent KEYSHIDDEN_SOFT. Basically
|
||||
// KEYSHIDDEN_NO means there is some kind of keyboard available.
|
||||
//ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden);
|
||||
if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) {
|
||||
//ALOGI("No match!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const int navHidden = inputFlags&MASK_NAVHIDDEN;
|
||||
const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN;
|
||||
if (navHidden != 0 && navHidden != setNavHidden) {
|
||||
return false;
|
||||
}
|
||||
if (keyboard != 0 && keyboard != settings.keyboard) {
|
||||
return false;
|
||||
}
|
||||
if (navigation != 0 && navigation != settings.navigation) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (screenSize != 0) {
|
||||
if (screenWidth != 0 && screenWidth > settings.screenWidth) {
|
||||
return false;
|
||||
}
|
||||
if (screenHeight != 0 && screenHeight > settings.screenHeight) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (version != 0) {
|
||||
if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) {
|
||||
return false;
|
||||
}
|
||||
if (minorVersion != 0 && minorVersion != settings.minorVersion) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void getLocale(char str[6]) const {
|
||||
memset(str, 0, 6);
|
||||
if (language[0]) {
|
||||
str[0] = language[0];
|
||||
str[1] = language[1];
|
||||
if (country[0]) {
|
||||
str[2] = '_';
|
||||
str[3] = country[0];
|
||||
str[4] = country[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String8 toString() const {
|
||||
char buf[200];
|
||||
sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d "
|
||||
"kbd=%d nav=%d input=%d ssz=%dx%d sw%ddp w%ddp h%ddp sz=%d long=%d "
|
||||
"ui=%d night=%d vers=%d.%d",
|
||||
mcc, mnc,
|
||||
language[0] ? language[0] : '-', language[1] ? language[1] : '-',
|
||||
country[0] ? country[0] : '-', country[1] ? country[1] : '-',
|
||||
orientation, touchscreen, density, keyboard, navigation, inputFlags,
|
||||
screenWidth, screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
|
||||
screenLayout&MASK_SCREENSIZE, screenLayout&MASK_SCREENLONG,
|
||||
uiMode&MASK_UI_MODE_TYPE, uiMode&MASK_UI_MODE_NIGHT,
|
||||
sdkVersion, minorVersion);
|
||||
return String8(buf);
|
||||
}
|
||||
String8 toString() const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2056,8 +1511,14 @@ public:
|
||||
const char16_t* getBasePackageName(size_t idx) const;
|
||||
uint32_t getBasePackageId(size_t idx) const;
|
||||
|
||||
// Return the number of resource tables that the object contains.
|
||||
size_t getTableCount() const;
|
||||
// Return the values string pool for the resource table at the given
|
||||
// index. This string pool contains all of the strings for values
|
||||
// contained in the resource table -- that is the item values themselves,
|
||||
// but not the names their entries or types.
|
||||
const ResStringPool* getTableStringBlock(size_t index) const;
|
||||
// Return unique cookie identifier for the given resource table.
|
||||
void* getTableCookie(size_t index) const;
|
||||
|
||||
// Return the configurations (ResTable_config) that we know about
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -53,17 +53,6 @@ enum {
|
||||
AXIS_END = AXIS_VERSION,
|
||||
};
|
||||
|
||||
enum {
|
||||
SDK_CUPCAKE = 3,
|
||||
SDK_DONUT = 4,
|
||||
SDK_ECLAIR = 5,
|
||||
SDK_ECLAIR_0_1 = 6,
|
||||
SDK_MR1 = 7,
|
||||
SDK_FROYO = 8,
|
||||
SDK_HONEYCOMB_MR2 = 13,
|
||||
SDK_ICE_CREAM_SANDWICH = 14,
|
||||
};
|
||||
|
||||
/**
|
||||
* This structure contains a specific variation of a single file out
|
||||
* of all the variations it can have that we can have.
|
||||
|
||||
@@ -14,6 +14,18 @@
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
enum {
|
||||
SDK_CUPCAKE = 3,
|
||||
SDK_DONUT = 4,
|
||||
SDK_ECLAIR = 5,
|
||||
SDK_ECLAIR_0_1 = 6,
|
||||
SDK_MR1 = 7,
|
||||
SDK_FROYO = 8,
|
||||
SDK_HONEYCOMB_MR2 = 13,
|
||||
SDK_ICE_CREAM_SANDWICH = 14,
|
||||
SDK_ICE_CREAM_SANDWICH_MR1 = 15,
|
||||
};
|
||||
|
||||
/*
|
||||
* Things we can do.
|
||||
*/
|
||||
@@ -82,7 +94,6 @@ public:
|
||||
void setRequireLocalization(bool val) { mRequireLocalization = val; }
|
||||
bool getPseudolocalize(void) const { return mPseudolocalize; }
|
||||
void setPseudolocalize(bool val) { mPseudolocalize = val; }
|
||||
bool getWantUTF16(void) const { return mWantUTF16; }
|
||||
void setWantUTF16(bool val) { mWantUTF16 = val; }
|
||||
bool getValues(void) const { return mValues; }
|
||||
void setValues(bool val) { mValues = val; }
|
||||
@@ -103,6 +114,10 @@ public:
|
||||
bool getGenDependencies() { return mGenDependencies; }
|
||||
void setGenDependencies(bool val) { mGenDependencies = val; }
|
||||
|
||||
bool getUTF16StringsOption() {
|
||||
return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Input options.
|
||||
*/
|
||||
|
||||
@@ -479,6 +479,11 @@ int doDump(Bundle* bundle)
|
||||
#ifndef HAVE_ANDROID_OS
|
||||
res.print(bundle->getValues());
|
||||
#endif
|
||||
|
||||
} else if (strcmp("strings", option) == 0) {
|
||||
const ResStringPool* pool = res.getTableStringBlock(0);
|
||||
printStringPool(pool);
|
||||
|
||||
} else if (strcmp("xmltree", option) == 0) {
|
||||
if (bundle->getFileSpecCount() < 3) {
|
||||
fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
|
||||
@@ -1382,7 +1387,7 @@ int doDump(Bundle* bundle)
|
||||
delete dir;
|
||||
}
|
||||
} else if (strcmp("badger", option) == 0) {
|
||||
printf(CONSOLE_DATA);
|
||||
printf("%s", CONSOLE_DATA);
|
||||
} else if (strcmp("configurations", option) == 0) {
|
||||
Vector<ResTable_config> configs;
|
||||
res.getConfigurations(&configs);
|
||||
|
||||
@@ -847,8 +847,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
* request UTF-16 encoding and the parameters of this package
|
||||
* allow UTF-8 to be used.
|
||||
*/
|
||||
if (!bundle->getWantUTF16()
|
||||
&& bundle->isMinSdkAtLeast(SDK_FROYO)) {
|
||||
if (!bundle->getUTF16StringsOption()) {
|
||||
xmlFlags |= XML_COMPILE_UTF8;
|
||||
}
|
||||
|
||||
|
||||
@@ -2047,7 +2047,8 @@ bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
|
||||
uint32_t attrID,
|
||||
const Vector<StringPool::entry_style_span>* style,
|
||||
String16* outStr, void* accessorCookie,
|
||||
uint32_t attrType)
|
||||
uint32_t attrType, const String8* configTypeName,
|
||||
const ConfigDescription* config)
|
||||
{
|
||||
String16 finalStr;
|
||||
|
||||
@@ -2075,10 +2076,19 @@ bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
|
||||
if (outValue->dataType == outValue->TYPE_STRING) {
|
||||
// Should do better merging styles.
|
||||
if (pool) {
|
||||
if (style != NULL && style->size() > 0) {
|
||||
outValue->data = pool->add(finalStr, *style);
|
||||
String8 configStr;
|
||||
if (config != NULL) {
|
||||
configStr = config->toString();
|
||||
} else {
|
||||
outValue->data = pool->add(finalStr, true);
|
||||
configStr = "(null)";
|
||||
}
|
||||
NOISY(printf("Adding to pool string style #%d config %s: %s\n",
|
||||
style != NULL ? style->size() : 0,
|
||||
configStr.string(), String8(finalStr).string()));
|
||||
if (style != NULL && style->size() > 0) {
|
||||
outValue->data = pool->add(finalStr, *style, configTypeName, config);
|
||||
} else {
|
||||
outValue->data = pool->add(finalStr, true, configTypeName, config);
|
||||
}
|
||||
} else {
|
||||
// Caller will fill this in later.
|
||||
@@ -2537,16 +2547,19 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
return err;
|
||||
}
|
||||
|
||||
const ConfigDescription nullConfig;
|
||||
|
||||
const size_t N = mOrderedPackages.size();
|
||||
size_t pi;
|
||||
|
||||
const static String16 mipmap16("mipmap");
|
||||
|
||||
bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
|
||||
bool useUTF8 = !bundle->getUTF16StringsOption();
|
||||
|
||||
// Iterate through all data, collecting all values (strings,
|
||||
// references, etc).
|
||||
StringPool valueStrings = StringPool(false, useUTF8);
|
||||
Vector<sp<Entry> > allEntries;
|
||||
for (pi=0; pi<N; pi++) {
|
||||
sp<Package> p = mOrderedPackages.itemAt(pi);
|
||||
if (p->getTypes().size() == 0) {
|
||||
@@ -2567,6 +2580,19 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
const String16 typeName(t->getName());
|
||||
typeStrings.add(typeName, false);
|
||||
|
||||
// This is a hack to tweak the sorting order of the final strings,
|
||||
// to put stuff that is generally not language-specific first.
|
||||
String8 configTypeName(typeName);
|
||||
if (configTypeName == "drawable" || configTypeName == "layout"
|
||||
|| configTypeName == "color" || configTypeName == "anim"
|
||||
|| configTypeName == "interpolator" || configTypeName == "animator"
|
||||
|| configTypeName == "xml" || configTypeName == "menu"
|
||||
|| configTypeName == "mipmap" || configTypeName == "raw") {
|
||||
configTypeName = "1complex";
|
||||
} else {
|
||||
configTypeName = "2value";
|
||||
}
|
||||
|
||||
const bool filterable = (typeName != mipmap16);
|
||||
|
||||
const size_t N = t->getOrderedConfigs().size();
|
||||
@@ -2586,10 +2612,21 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
continue;
|
||||
}
|
||||
e->setNameIndex(keyStrings.add(e->getName(), true));
|
||||
status_t err = e->prepareFlatten(&valueStrings, this);
|
||||
|
||||
// If this entry has no values for other configs,
|
||||
// and is the default config, then it is special. Otherwise
|
||||
// we want to add it with the config info.
|
||||
ConfigDescription* valueConfig = NULL;
|
||||
if (N != 1 || config == nullConfig) {
|
||||
valueConfig = &config;
|
||||
}
|
||||
|
||||
status_t err = e->prepareFlatten(&valueStrings, this,
|
||||
&configTypeName, &config);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
allEntries.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2598,6 +2635,17 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
p->setKeyStrings(keyStrings.createStringBlock());
|
||||
}
|
||||
|
||||
if (bundle->getOutputAPKFile() != NULL) {
|
||||
// Now we want to sort the value strings for better locality. This will
|
||||
// cause the positions of the strings to change, so we need to go back
|
||||
// through out resource entries and update them accordingly. Only need
|
||||
// to do this if actually writing the output file.
|
||||
valueStrings.sortByConfig();
|
||||
for (pi=0; pi<allEntries.size(); pi++) {
|
||||
allEntries[pi]->remapStringValue(&valueStrings);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t strAmt = 0;
|
||||
|
||||
// Now build the array of package chunks.
|
||||
@@ -3137,14 +3185,16 @@ status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
|
||||
return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
|
||||
}
|
||||
|
||||
status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
|
||||
status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table,
|
||||
const String8* configTypeName, const ConfigDescription* config)
|
||||
{
|
||||
if (mType == TYPE_ITEM) {
|
||||
Item& it = mItem;
|
||||
AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
|
||||
if (!table->stringToValue(&it.parsedValue, strings,
|
||||
it.value, false, true, 0,
|
||||
&it.style, NULL, &ac, mItemFormat)) {
|
||||
&it.style, NULL, &ac, mItemFormat,
|
||||
configTypeName, config)) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
} else if (mType == TYPE_BAG) {
|
||||
@@ -3155,7 +3205,8 @@ status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable
|
||||
AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
|
||||
if (!table->stringToValue(&it.parsedValue, strings,
|
||||
it.value, false, true, it.bagKeyId,
|
||||
&it.style, NULL, &ac, it.format)) {
|
||||
&it.style, NULL, &ac, it.format,
|
||||
configTypeName, config)) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -3167,6 +3218,29 @@ status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t ResourceTable::Entry::remapStringValue(StringPool* strings)
|
||||
{
|
||||
if (mType == TYPE_ITEM) {
|
||||
Item& it = mItem;
|
||||
if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
|
||||
it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
|
||||
}
|
||||
} else if (mType == TYPE_BAG) {
|
||||
const size_t N = mBag.size();
|
||||
for (size_t i=0; i<N; i++) {
|
||||
Item& it = mBag.editValueAt(i);
|
||||
if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
|
||||
it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mPos.error("Error: entry %s is not a single item or a bag.\n",
|
||||
String8(mName).string());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
|
||||
{
|
||||
size_t amt = 0;
|
||||
|
||||
@@ -76,6 +76,37 @@ public:
|
||||
class Type;
|
||||
class Entry;
|
||||
|
||||
struct ConfigDescription : public ResTable_config {
|
||||
ConfigDescription() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
size = sizeof(ResTable_config);
|
||||
}
|
||||
ConfigDescription(const ResTable_config&o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
size = sizeof(ResTable_config);
|
||||
}
|
||||
ConfigDescription(const ConfigDescription&o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
}
|
||||
|
||||
ConfigDescription& operator=(const ResTable_config& o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
size = sizeof(ResTable_config);
|
||||
return *this;
|
||||
}
|
||||
ConfigDescription& operator=(const ConfigDescription& o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
|
||||
inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
|
||||
inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
|
||||
inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
|
||||
inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
|
||||
inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
|
||||
};
|
||||
|
||||
ResourceTable(Bundle* bundle, const String16& assetsPackage);
|
||||
|
||||
status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets);
|
||||
@@ -183,7 +214,9 @@ public:
|
||||
uint32_t attrID,
|
||||
const Vector<StringPool::entry_style_span>* style = NULL,
|
||||
String16* outStr = NULL, void* accessorCookie = NULL,
|
||||
uint32_t attrType = ResTable_map::TYPE_ANY);
|
||||
uint32_t attrType = ResTable_map::TYPE_ANY,
|
||||
const String8* configTypeName = NULL,
|
||||
const ConfigDescription* config = NULL);
|
||||
|
||||
status_t assignResourceIds();
|
||||
status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL);
|
||||
@@ -305,7 +338,10 @@ public:
|
||||
status_t assignResourceIds(ResourceTable* table,
|
||||
const String16& package);
|
||||
|
||||
status_t prepareFlatten(StringPool* strings, ResourceTable* table);
|
||||
status_t prepareFlatten(StringPool* strings, ResourceTable* table,
|
||||
const String8* configTypeName, const ConfigDescription* config);
|
||||
|
||||
status_t remapStringValue(StringPool* strings);
|
||||
|
||||
ssize_t flatten(Bundle*, const sp<AaptFile>& data, bool isPublic);
|
||||
|
||||
@@ -322,37 +358,6 @@ public:
|
||||
uint32_t mParentId;
|
||||
SourcePos mPos;
|
||||
};
|
||||
|
||||
struct ConfigDescription : public ResTable_config {
|
||||
ConfigDescription() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
size = sizeof(ResTable_config);
|
||||
}
|
||||
ConfigDescription(const ResTable_config&o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
size = sizeof(ResTable_config);
|
||||
}
|
||||
ConfigDescription(const ConfigDescription&o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
}
|
||||
|
||||
ConfigDescription& operator=(const ResTable_config& o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
size = sizeof(ResTable_config);
|
||||
return *this;
|
||||
}
|
||||
ConfigDescription& operator=(const ConfigDescription& o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
|
||||
inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
|
||||
inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
|
||||
inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
|
||||
inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
|
||||
inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
|
||||
};
|
||||
|
||||
class ConfigList : public RefBase {
|
||||
public:
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
//
|
||||
|
||||
#include "StringPool.h"
|
||||
#include "ResourceTable.h"
|
||||
|
||||
#include <utils/ByteOrder.h>
|
||||
#include <utils/SortedVector.h>
|
||||
|
||||
#if HAVE_PRINTF_ZD
|
||||
# define ZD "%zd"
|
||||
@@ -30,31 +32,87 @@ void strcpy16_htod(uint16_t* dst, const uint16_t* src)
|
||||
|
||||
void printStringPool(const ResStringPool* pool)
|
||||
{
|
||||
SortedVector<const void*> uniqueStrings;
|
||||
const size_t N = pool->size();
|
||||
for (size_t i=0; i<N; i++) {
|
||||
size_t len;
|
||||
if (pool->isUTF8()) {
|
||||
uniqueStrings.add(pool->string8At(i, &len));
|
||||
} else {
|
||||
uniqueStrings.add(pool->stringAt(i, &len));
|
||||
}
|
||||
}
|
||||
|
||||
printf("String pool of " ZD " unique %s %s strings, " ZD " entries and "
|
||||
ZD " styles using " ZD " bytes:\n",
|
||||
(ZD_TYPE)uniqueStrings.size(), pool->isUTF8() ? "UTF-8" : "UTF-16",
|
||||
pool->isSorted() ? "sorted" : "non-sorted",
|
||||
(ZD_TYPE)N, (ZD_TYPE)pool->styleCount(), (ZD_TYPE)pool->bytes());
|
||||
|
||||
const size_t NS = pool->size();
|
||||
for (size_t s=0; s<NS; s++) {
|
||||
size_t len;
|
||||
const char *str = (const char*)pool->string8At(s, &len);
|
||||
if (str == NULL) {
|
||||
str = String8(pool->stringAt(s, &len)).string();
|
||||
}
|
||||
|
||||
printf("String #" ZD ": %s\n", (ZD_TYPE) s, str);
|
||||
String8 str = pool->string8ObjectAt(s);
|
||||
printf("String #" ZD ": %s\n", (ZD_TYPE) s, str.string());
|
||||
}
|
||||
}
|
||||
|
||||
String8 StringPool::entry::makeConfigsString() const {
|
||||
String8 configStr(configTypeName);
|
||||
if (configStr.size() > 0) configStr.append(" ");
|
||||
if (configs.size() > 0) {
|
||||
for (size_t j=0; j<configs.size(); j++) {
|
||||
if (j > 0) configStr.append(", ");
|
||||
configStr.append(configs[j].toString());
|
||||
}
|
||||
} else {
|
||||
configStr = "(none)";
|
||||
}
|
||||
return configStr;
|
||||
}
|
||||
|
||||
int StringPool::entry::compare(const entry& o) const {
|
||||
// Strings with styles go first, to reduce the size of the
|
||||
// styles array.
|
||||
if (hasStyles) {
|
||||
return o.hasStyles ? 0 : -1;
|
||||
}
|
||||
if (o.hasStyles) {
|
||||
return 1;
|
||||
}
|
||||
int comp = configTypeName.compare(o.configTypeName);
|
||||
if (comp != 0) {
|
||||
return comp;
|
||||
}
|
||||
const size_t LHN = configs.size();
|
||||
const size_t RHN = o.configs.size();
|
||||
size_t i=0;
|
||||
while (i < LHN && i < RHN) {
|
||||
comp = configs[i].compareLogical(o.configs[i]);
|
||||
if (comp != 0) {
|
||||
return comp;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (LHN < RHN) return -1;
|
||||
else if (LHN > RHN) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
StringPool::StringPool(bool sorted, bool utf8)
|
||||
: mSorted(sorted), mUTF8(utf8), mValues(-1), mIdents(-1)
|
||||
{
|
||||
}
|
||||
|
||||
ssize_t StringPool::add(const String16& value, bool mergeDuplicates)
|
||||
ssize_t StringPool::add(const String16& value, bool mergeDuplicates,
|
||||
const String8* configTypeName, const ResTable_config* config)
|
||||
{
|
||||
return add(String16(), value, mergeDuplicates);
|
||||
return add(String16(), value, mergeDuplicates, configTypeName, config);
|
||||
}
|
||||
|
||||
ssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& spans)
|
||||
ssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& spans,
|
||||
const String8* configTypeName, const ResTable_config* config)
|
||||
{
|
||||
ssize_t res = add(String16(), value, false);
|
||||
ssize_t res = add(String16(), value, false, configTypeName, config);
|
||||
if (res >= 0) {
|
||||
addStyleSpans(res, spans);
|
||||
}
|
||||
@@ -62,7 +120,7 @@ ssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& s
|
||||
}
|
||||
|
||||
ssize_t StringPool::add(const String16& ident, const String16& value,
|
||||
bool mergeDuplicates)
|
||||
bool mergeDuplicates, const String8* configTypeName, const ResTable_config* config)
|
||||
{
|
||||
if (ident.size() > 0) {
|
||||
ssize_t idx = mIdents.valueFor(ident);
|
||||
@@ -84,20 +142,43 @@ ssize_t StringPool::add(const String16& ident, const String16& value,
|
||||
}
|
||||
}
|
||||
|
||||
if (configTypeName != NULL) {
|
||||
entry& ent = mEntries.editItemAt(eidx);
|
||||
NOISY(printf("*** adding config type name %s, was %s\n",
|
||||
configTypeName->string(), ent.configTypeName.string()));
|
||||
if (ent.configTypeName.size() <= 0) {
|
||||
ent.configTypeName = *configTypeName;
|
||||
} else if (ent.configTypeName != *configTypeName) {
|
||||
ent.configTypeName = " ";
|
||||
}
|
||||
}
|
||||
|
||||
if (config != NULL) {
|
||||
// Add this to the set of configs associated with the string.
|
||||
entry& ent = mEntries.editItemAt(eidx);
|
||||
size_t addPos;
|
||||
for (addPos=0; addPos<ent.configs.size(); addPos++) {
|
||||
int cmp = ent.configs.itemAt(addPos).compareLogical(*config);
|
||||
if (cmp >= 0) {
|
||||
if (cmp > 0) {
|
||||
NOISY(printf("*** inserting config: %s\n", config->toString().string()));
|
||||
ent.configs.insertAt(*config, addPos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (addPos >= ent.configs.size()) {
|
||||
NOISY(printf("*** adding config: %s\n", config->toString().string()));
|
||||
ent.configs.add(*config);
|
||||
}
|
||||
}
|
||||
|
||||
const bool first = vidx < 0;
|
||||
if (first || !mergeDuplicates) {
|
||||
pos = mEntryArray.add(eidx);
|
||||
if (first) {
|
||||
vidx = mValues.add(value, pos);
|
||||
const size_t N = mEntryArrayToValues.size();
|
||||
for (size_t i=0; i<N; i++) {
|
||||
size_t& e = mEntryArrayToValues.editItemAt(i);
|
||||
if ((ssize_t)e >= vidx) {
|
||||
e++;
|
||||
}
|
||||
}
|
||||
}
|
||||
mEntryArrayToValues.add(vidx);
|
||||
if (!mSorted) {
|
||||
entry& ent = mEntries.editItemAt(eidx);
|
||||
ent.indices.add(pos);
|
||||
@@ -147,6 +228,7 @@ status_t StringPool::addStyleSpan(size_t idx, const entry_style_span& span)
|
||||
|
||||
entry_style& style = mEntryStyleArray.editItemAt(idx);
|
||||
style.spans.add(span);
|
||||
mEntries.editItemAt(mEntryArray[idx]).hasStyles = true;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -169,6 +251,132 @@ size_t StringPool::countIdentifiers() const
|
||||
return mIdents.size();
|
||||
}
|
||||
|
||||
int StringPool::config_sort(const size_t* lhs, const size_t* rhs, void* state)
|
||||
{
|
||||
StringPool* pool = (StringPool*)state;
|
||||
const entry& lhe = pool->mEntries[pool->mEntryArray[*lhs]];
|
||||
const entry& rhe = pool->mEntries[pool->mEntryArray[*rhs]];
|
||||
return lhe.compare(rhe);
|
||||
}
|
||||
|
||||
void StringPool::sortByConfig()
|
||||
{
|
||||
LOG_ALWAYS_FATAL_IF(mSorted, "Can't sort string pool containing identifiers.");
|
||||
LOG_ALWAYS_FATAL_IF(mIdents.size() > 0, "Can't sort string pool containing identifiers.");
|
||||
LOG_ALWAYS_FATAL_IF(mOriginalPosToNewPos.size() > 0, "Can't sort string pool after already sorted.");
|
||||
|
||||
const size_t N = mEntryArray.size();
|
||||
|
||||
// This is a vector that starts out with a 1:1 mapping to entries
|
||||
// in the array, which we will sort to come up with the desired order.
|
||||
// At that point it maps from the new position in the array to the
|
||||
// original position the entry appeared.
|
||||
Vector<size_t> newPosToOriginalPos;
|
||||
for (size_t i=0; i<mEntryArray.size(); i++) {
|
||||
newPosToOriginalPos.add(i);
|
||||
}
|
||||
|
||||
// Sort the array.
|
||||
NOISY(printf("SORTING STRINGS BY CONFIGURATION...\n"));
|
||||
newPosToOriginalPos.sort(config_sort, this);
|
||||
NOISY(printf("DONE SORTING STRINGS BY CONFIGURATION.\n"));
|
||||
|
||||
// Create the reverse mapping from the original position in the array
|
||||
// to the new position where it appears in the sorted array. This is
|
||||
// so that clients can re-map any positions they had previously stored.
|
||||
mOriginalPosToNewPos = newPosToOriginalPos;
|
||||
for (size_t i=0; i<N; i++) {
|
||||
mOriginalPosToNewPos.editItemAt(newPosToOriginalPos[i]) = i;
|
||||
}
|
||||
|
||||
#if 0
|
||||
SortedVector<entry> entries;
|
||||
|
||||
for (size_t i=0; i<N; i++) {
|
||||
printf("#%d was %d: %s\n", i, newPosToOriginalPos[i],
|
||||
mEntries[mEntryArray[newPosToOriginalPos[i]]].makeConfigsString().string());
|
||||
entries.add(mEntries[mEntryArray[i]]);
|
||||
}
|
||||
|
||||
for (size_t i=0; i<entries.size(); i++) {
|
||||
printf("Sorted config #%d: %s\n", i,
|
||||
entries[i].makeConfigsString().string());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Now we rebuild the arrays.
|
||||
Vector<entry> newEntries;
|
||||
Vector<size_t> newEntryArray;
|
||||
Vector<entry_style> newEntryStyleArray;
|
||||
DefaultKeyedVector<size_t, size_t> origOffsetToNewOffset;
|
||||
|
||||
for (size_t i=0; i<N; i++) {
|
||||
// We are filling in new offset 'i'; oldI is where we can find it
|
||||
// in the original data structure.
|
||||
size_t oldI = newPosToOriginalPos[i];
|
||||
// This is the actual entry associated with the old offset.
|
||||
const entry& oldEnt = mEntries[mEntryArray[oldI]];
|
||||
// This is the same entry the last time we added it to the
|
||||
// new entry array, if any.
|
||||
ssize_t newIndexOfOffset = origOffsetToNewOffset.indexOfKey(oldI);
|
||||
size_t newOffset;
|
||||
if (newIndexOfOffset < 0) {
|
||||
// This is the first time we have seen the entry, so add
|
||||
// it.
|
||||
newOffset = newEntries.add(oldEnt);
|
||||
newEntries.editItemAt(newOffset).indices.clear();
|
||||
} else {
|
||||
// We have seen this entry before, use the existing one
|
||||
// instead of adding it again.
|
||||
newOffset = origOffsetToNewOffset.valueAt(newIndexOfOffset);
|
||||
}
|
||||
// Update the indices to include this new position.
|
||||
newEntries.editItemAt(newOffset).indices.add(i);
|
||||
// And add the offset of the entry to the new entry array.
|
||||
newEntryArray.add(newOffset);
|
||||
// Add any old style to the new style array.
|
||||
if (mEntryStyleArray.size() > 0) {
|
||||
if (oldI < mEntryStyleArray.size()) {
|
||||
newEntryStyleArray.add(mEntryStyleArray[oldI]);
|
||||
} else {
|
||||
newEntryStyleArray.add(entry_style());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now trim any entries at the end of the new style array that are
|
||||
// not needed.
|
||||
for (ssize_t i=newEntryStyleArray.size()-1; i>=0; i--) {
|
||||
const entry_style& style = newEntryStyleArray[i];
|
||||
if (style.spans.size() > 0) {
|
||||
// That's it.
|
||||
break;
|
||||
}
|
||||
// This one is not needed; remove.
|
||||
newEntryStyleArray.removeAt(i);
|
||||
}
|
||||
|
||||
// All done, install the new data structures and upate mValues with
|
||||
// the new positions.
|
||||
mEntries = newEntries;
|
||||
mEntryArray = newEntryArray;
|
||||
mEntryStyleArray = newEntryStyleArray;
|
||||
mValues.clear();
|
||||
for (size_t i=0; i<mEntries.size(); i++) {
|
||||
const entry& ent = mEntries[i];
|
||||
mValues.add(ent.value, ent.indices[0]);
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("FINAL SORTED STRING CONFIGS:\n");
|
||||
for (size_t i=0; i<mEntries.size(); i++) {
|
||||
const entry& ent = mEntries[i];
|
||||
printf("#" ZD " %s: %s\n", (ZD_TYPE)i, ent.makeConfigsString().string(),
|
||||
String8(ent.value).string());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
sp<AaptFile> StringPool::createStringBlock()
|
||||
{
|
||||
sp<AaptFile> pool = new AaptFile(String8(), AaptGroupEntry(),
|
||||
|
||||
@@ -40,12 +40,28 @@ class StringPool
|
||||
public:
|
||||
struct entry {
|
||||
entry() : offset(0) { }
|
||||
entry(const String16& _value) : value(_value), offset(0) { }
|
||||
entry(const entry& o) : value(o.value), offset(o.offset), indices(o.indices) { }
|
||||
entry(const String16& _value) : value(_value), offset(0), hasStyles(false) { }
|
||||
entry(const entry& o) : value(o.value), offset(o.offset),
|
||||
hasStyles(o.hasStyles), indices(o.indices),
|
||||
configTypeName(o.configTypeName), configs(o.configs) { }
|
||||
|
||||
String16 value;
|
||||
size_t offset;
|
||||
bool hasStyles;
|
||||
Vector<size_t> indices;
|
||||
String8 configTypeName;
|
||||
Vector<ResTable_config> configs;
|
||||
|
||||
String8 makeConfigsString() const;
|
||||
|
||||
int compare(const entry& o) const;
|
||||
|
||||
inline bool operator<(const entry& o) const { return compare(o) < 0; }
|
||||
inline bool operator<=(const entry& o) const { return compare(o) <= 0; }
|
||||
inline bool operator==(const entry& o) const { return compare(o) == 0; }
|
||||
inline bool operator!=(const entry& o) const { return compare(o) != 0; }
|
||||
inline bool operator>=(const entry& o) const { return compare(o) >= 0; }
|
||||
inline bool operator>(const entry& o) const { return compare(o) > 0; }
|
||||
};
|
||||
|
||||
struct entry_style_span {
|
||||
@@ -84,12 +100,15 @@ public:
|
||||
* if this string pool is sorted, the returned index will not be valid
|
||||
* when the pool is finally written.
|
||||
*/
|
||||
ssize_t add(const String16& value, bool mergeDuplicates = false);
|
||||
ssize_t add(const String16& value, bool mergeDuplicates = false,
|
||||
const String8* configTypeName = NULL, const ResTable_config* config = NULL);
|
||||
|
||||
ssize_t add(const String16& value, const Vector<entry_style_span>& spans);
|
||||
ssize_t add(const String16& value, const Vector<entry_style_span>& spans,
|
||||
const String8* configTypeName = NULL, const ResTable_config* config = NULL);
|
||||
|
||||
ssize_t add(const String16& ident, const String16& value,
|
||||
bool mergeDuplicates = false);
|
||||
bool mergeDuplicates = false,
|
||||
const String8* configTypeName = NULL, const ResTable_config* config = NULL);
|
||||
|
||||
status_t addStyleSpan(size_t idx, const String16& name,
|
||||
uint32_t start, uint32_t end);
|
||||
@@ -102,6 +121,18 @@ public:
|
||||
|
||||
size_t countIdentifiers() const;
|
||||
|
||||
// Sort the contents of the string block by the configuration associated
|
||||
// with each item. After doing this you can use mapOriginalPosToNewPos()
|
||||
// to find out the new position given the position originall returned by
|
||||
// add().
|
||||
void sortByConfig();
|
||||
|
||||
// For use after sortByConfig() to map from the original position of
|
||||
// a string to its new sorted position.
|
||||
size_t mapOriginalPosToNewPos(size_t originalPos) const {
|
||||
return mOriginalPosToNewPos.itemAt(originalPos);
|
||||
}
|
||||
|
||||
sp<AaptFile> createStringBlock();
|
||||
|
||||
status_t writeStringBlock(const sp<AaptFile>& pool);
|
||||
@@ -125,27 +156,41 @@ public:
|
||||
const Vector<size_t>* offsetsForString(const String16& val) const;
|
||||
|
||||
private:
|
||||
static int config_sort(const size_t* lhs, const size_t* rhs, void* state);
|
||||
|
||||
const bool mSorted;
|
||||
const bool mUTF8;
|
||||
// Raw array of unique strings, in some arbitrary order.
|
||||
|
||||
// The following data structures represent the actual structures
|
||||
// that will be generated for the final string pool.
|
||||
|
||||
// Raw array of unique strings, in some arbitrary order. This is the
|
||||
// actual strings that appear in the final string pool, in the order
|
||||
// that they will be written.
|
||||
Vector<entry> mEntries;
|
||||
// Array of indices into mEntries, in the order they were
|
||||
// added to the pool. This can be different than mEntries
|
||||
// if the same string was added multiple times (it will appear
|
||||
// once in mEntries, with multiple occurrences in this array).
|
||||
// This is the lookup array that will be written for finding
|
||||
// the string for each offset/position in the string pool.
|
||||
Vector<size_t> mEntryArray;
|
||||
// Optional style span information associated with each index of
|
||||
// mEntryArray.
|
||||
Vector<entry_style> mEntryStyleArray;
|
||||
// Mapping from indices in mEntryArray to indices in mValues.
|
||||
Vector<size_t> mEntryArrayToValues;
|
||||
|
||||
// The following data structures are used for book-keeping as the
|
||||
// string pool is constructed.
|
||||
|
||||
// Unique set of all the strings added to the pool, mapped to
|
||||
// the first index of mEntryArray where the value was added.
|
||||
DefaultKeyedVector<String16, ssize_t> mValues;
|
||||
// Unique set of all (optional) identifiers of strings in the
|
||||
// pool, mapping to indices in mEntries.
|
||||
DefaultKeyedVector<String16, ssize_t> mIdents;
|
||||
|
||||
// This array maps from the original position a string was placed at
|
||||
// in mEntryArray to its new position after being sorted with sortByConfig().
|
||||
Vector<size_t> mOriginalPosToNewPos;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user