diff options
Diffstat (limited to 'tools')
29 files changed, 548 insertions, 126 deletions
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index d07e4de..027662d 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -78,7 +78,7 @@ static bool isHidden(const char *root, const char *path) // Skip CVS but don't chatter about it. return true; } else if (strcasecmp(path, "thumbs.db") == 0 - || strcasecmp(path, "picassa.ini") == 0) { + || strcasecmp(path, "picasa.ini") == 0) { // Skip suspected image indexes files. type = "index"; } else if (path[strlen(path)-1] == '~') { @@ -695,6 +695,9 @@ bool AaptGroupEntry::getKeysHiddenName(const char* name, } else if (strcmp(name, "keyshidden") == 0) { mask = out->MASK_KEYSHIDDEN; value = out->KEYSHIDDEN_YES; + } else if (strcmp(name, "keyssoft") == 0) { + mask = out->MASK_KEYSHIDDEN; + value = out->KEYSHIDDEN_SOFT; } if (mask != 0) { diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index 1d7b3ad..99fac2f 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -94,6 +94,8 @@ public: void addPackageInclude(const char* file) { mPackageIncludes.add(file); } const android::Vector<const char*>& getJarFiles() const { return mJarFiles; } void addJarFile(const char* file) { mJarFiles.add(file); } + const android::Vector<const char*>& getNoCompressExtensions() const { return mNoCompressExtensions; } + void addNoCompressExtension(const char* ext) { mNoCompressExtensions.add(ext); } /* * Set and get the file specification. @@ -144,6 +146,7 @@ private: android::String8 mConfigurations; android::Vector<const char*> mPackageIncludes; android::Vector<const char*> mJarFiles; + android::Vector<const char*> mNoCompressExtensions; /* file specification */ int mArgc; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index c02e2c0..9f75d4b 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -586,6 +586,18 @@ int doDump(Bundle* bundle) activityIcon.string()); } } + printf("locales:"); + Vector<String8> locales; + res.getLocales(&locales); + const size_t N = locales.size(); + for (size_t i=0; i<N; i++) { + const char* localeStr = locales[i].string(); + if (localeStr == NULL || strlen(localeStr) == 0) { + localeStr = "--_--"; + } + printf(" '%s'", localeStr); + } + printf("\n"); } else if (strcmp("configurations", option) == 0) { Vector<ResTable_config> configs; res.getConfigurations(&configs); diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index 4a766d1..9d5937c 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -562,10 +562,16 @@ getout: static void checkNinePatchSerialization(Res_png_9patch* inPatch, void * data) { + if (sizeof(void*) != sizeof(int32_t)) { + // can't deserialize on a non-32 bit system + return; + } size_t patchSize = inPatch->serializedSize(); void * newData = malloc(patchSize); memcpy(newData, data, patchSize); Res_png_9patch* outPatch = inPatch->deserialize(newData); + // deserialization is done in place, so outPatch == newData + assert(outPatch == newData); assert(outPatch->numXDivs == inPatch->numXDivs); assert(outPatch->numYDivs == inPatch->numYDivs); assert(outPatch->paddingLeft == inPatch->paddingLeft); @@ -581,6 +587,7 @@ static void checkNinePatchSerialization(Res_png_9patch* inPatch, void * data) for (int i = 0; i < outPatch->numColors; i++) { assert(outPatch->colors[i] == inPatch->colors[i]); } + free(newData); } static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) { diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index d86ad47..a1978da 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -46,6 +46,7 @@ void usage(void) " List contents of Zip-compatible archive.\n\n", gProgName); fprintf(stderr, " %s d[ump] WHAT file.{apk} [asset [asset ...]]\n" + " badging Print the label and icon for the app declared in APK.\n" " permissions Print the permissions from the APK.\n" " resources Print the resource table from the APK.\n" " configurations Print the configurations in the APK.\n" @@ -53,6 +54,7 @@ void usage(void) " xmlstrings Print the strings of the given compiled xml assets.\n\n", gProgName); fprintf(stderr, " %s p[ackage] [-f][-u][-m][-v][-x][-M AndroidManifest.xml] \\\n" + " [-0 extension [-0 extension ...]] \\\n" " [-I base-package [-I base-package ...]] \\\n" " [-A asset-source-dir] [-P public-definitions-file] \\\n" " [-S resource-sources] [-F apk-file] [-J R-file-dir] \\\n" @@ -106,7 +108,9 @@ void usage(void) " -M specify full path to AndroidManifest.xml to include in zip\n" " -P specify where to output public resource definitions\n" " -S directory in which to find resources\n" - " -0 don't compress files we're adding\n"); + " -0 specifies an additional extension for which such files will not\n" + " be stored compressed in the .apk. An empty string means to not\n" + " compress any files at all.\n"); } /* @@ -303,7 +307,18 @@ int main(int argc, char* const argv[]) bundle.setResourceSourceDir(argv[0]); break; case '0': - bundle.setCompressionMethod(ZipEntry::kCompressStored); + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '-e' option\n"); + wantUsage = true; + goto bail; + } + if (argv[0][0] != 0) { + bundle.addNoCompressExtension(argv[0]); + } else { + bundle.setCompressionMethod(ZipEntry::kCompressStored); + } break; default: fprintf(stderr, "ERROR: Unknown flag '-%c'\n", *cp); diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index 23f641a..0df4606 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -34,7 +34,7 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, const AaptGroupEntry& ge); bool processFile(Bundle* bundle, ZipFile* zip, const sp<AaptGroup>& group, const sp<AaptFile>& file); -bool okayToCompress(const String8& pathName); +bool okayToCompress(Bundle* bundle, const String8& pathName); ssize_t processJarFiles(Bundle* bundle, ZipFile* zip); /* @@ -327,7 +327,7 @@ bool processFile(Bundle* bundle, ZipFile* zip, } else if (!hasData) { /* don't compress certain files, e.g. PNGs */ int compressionMethod = bundle->getCompressionMethod(); - if (!okayToCompress(storageName)) { + if (!okayToCompress(bundle, storageName)) { compressionMethod = ZipEntry::kCompressStored; } result = zip->add(file->getSourceFile().string(), storageName.string(), compressionMethod, @@ -365,7 +365,7 @@ bool processFile(Bundle* bundle, ZipFile* zip, * Determine whether or not we want to try to compress this file based * on the file extension. */ -bool okayToCompress(const String8& pathName) +bool okayToCompress(Bundle* bundle, const String8& pathName) { String8 ext = pathName.getPathExtension(); int i; @@ -378,6 +378,19 @@ bool okayToCompress(const String8& pathName) return false; } + const android::Vector<const char*>& others(bundle->getNoCompressExtensions()); + for (i = 0; i < (int)others.size(); i++) { + const char* str = others[i]; + int pos = pathName.length() - strlen(str); + if (pos < 0) { + continue; + } + const char* path = pathName.string(); + if (strcasecmp(path + pos, str) == 0) { + return false; + } + } + return true; } diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 0826b32..fd6ddb5 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -916,6 +916,10 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } } + if (table.validateLocalizations()) { + hasErrors = true; + } + if (hasErrors) { return UNKNOWN_ERROR; } diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 6fe196a..3641458 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -638,6 +638,7 @@ status_t compileResourceFile(Bundle* bundle, const String16 string16("string"); const String16 drawable16("drawable"); const String16 color16("color"); + const String16 bool16("bool"); const String16 integer16("integer"); const String16 dimen16("dimen"); const String16 style16("style"); @@ -671,6 +672,10 @@ status_t compileResourceFile(Bundle* bundle, const String16 many16("many"); const String16 quantityMany16("^many"); + // useful attribute names and special values + const String16 name16("name"); + const String16 translatable16("translatable"); + const String16 false16("false"); const String16 myPackage(assets->getPackage()); @@ -950,6 +955,45 @@ status_t compileResourceFile(Bundle* bundle, } curIsStyled = true; } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) { + // Note the existence and locale of every string we process + char rawLocale[16]; + curParams.getLocale(rawLocale); + String8 locale(rawLocale); + String16 name; + String16 translatable; + + size_t n = block.getAttributeCount(); + for (size_t i = 0; i < n; i++) { + size_t length; + const uint16_t* attr = block.getAttributeName(i, &length); + if (strcmp16(attr, name16.string()) == 0) { + name.setTo(block.getAttributeStringValue(i, &length)); + } else if (strcmp16(attr, translatable16.string()) == 0) { + translatable.setTo(block.getAttributeStringValue(i, &length)); + } + } + + if (name.size() > 0) { + if (translatable == false16) { + // Untranslatable strings must only exist in the default [empty] locale + if (locale.size() > 0) { + fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists" + " in locale '%s'\n", String8(name).string(), + bundle->getResourceSourceDir(), + locale.string()); + // hasErrors = localHasErrors = true; + } else { + // Intentionally empty block: + // + // Don't add untranslatable strings to the localization table; that + // way if we later see localizations of them, they'll be flagged as + // having no default translation. + } + } else { + outTable->addLocalization(name, locale); + } + } + curTag = &string16; curType = string16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; @@ -963,6 +1007,10 @@ status_t compileResourceFile(Bundle* bundle, curTag = &color16; curType = color16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR; + } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) { + curTag = &bool16; + curType = bool16; + curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN; } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) { curTag = &integer16; curType = integer16; @@ -1553,17 +1601,26 @@ inline uint32_t ResourceTable::getResId(const sp<Package>& p, uint32_t ResourceTable::getResId(const String16& package, const String16& type, - const String16& name) const + const String16& name, + bool onlyPublic) const { sp<Package> p = mPackages.valueFor(package); if (p == NULL) return 0; // First look for this in the included resources... + uint32_t specFlags = 0; uint32_t rid = mAssets->getIncludedResources() .identifierForName(name.string(), name.size(), type.string(), type.size(), - package.string(), package.size()); + package.string(), package.size(), + &specFlags); if (rid != 0) { + if (onlyPublic) { + if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) { + return 0; + } + } + if (Res_INTERNALID(rid)) { return rid; } @@ -1584,7 +1641,8 @@ uint32_t ResourceTable::getResId(const String16& package, uint32_t ResourceTable::getResId(const String16& ref, const String16* defType, const String16* defPackage, - const char** outErrorMsg) const + const char** outErrorMsg, + bool onlyPublic) const { String16 package, type, name; if (!ResTable::expandResourceRef( @@ -1603,7 +1661,7 @@ uint32_t ResourceTable::getResId(const String16& ref, String8(name).string())); return 0; } - uint32_t res = getResId(package, type, name); + uint32_t res = getResId(package, type, name, onlyPublic); NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n", String8(package).string(), String8(type).string(), String8(name).string(), res)); @@ -2036,6 +2094,93 @@ status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) { } +void +ResourceTable::addLocalization(const String16& name, const String8& locale) +{ + mLocalizations[name].insert(locale); +} + + +/*! + * Flag various sorts of localization problems. '+' indicates checks already implemented; + * '-' indicates checks that will be implemented in the future. + * + * + A localized string for which no default-locale version exists => warning + * + A string for which no version in an explicitly-requested locale exists => warning + * + A localized translation of an translateable="false" string => warning + * - A localized string not provided in every locale used by the table + */ +status_t +ResourceTable::validateLocalizations(void) +{ + status_t err = NO_ERROR; + const String8 defaultLocale; + + // For all strings... + for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin(); + nameIter != mLocalizations.end(); + nameIter++) { + const set<String8>& configSet = nameIter->second; // naming convenience + + // Look for strings with no default localization + if (configSet.count(defaultLocale) == 0) { + fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:", + String8(nameIter->first).string(), mBundle->getResourceSourceDir()); + for (set<String8>::iterator locales = configSet.begin(); + locales != configSet.end(); + locales++) { + fprintf(stdout, " %s", (*locales).string()); + } + fprintf(stdout, "\n"); + // !!! TODO: throw an error here in some circumstances + } + + // Check that all requested localizations are present for this string + if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) { + const char* allConfigs = mBundle->getConfigurations(); + const char* start = allConfigs; + const char* comma; + + do { + String8 config; + comma = strchr(start, ','); + if (comma != NULL) { + config.setTo(start, comma - start); + start = comma + 1; + } else { + config.setTo(start); + } + + // don't bother with the pseudolocale "zz_ZZ" + if (config != "zz_ZZ") { + if (configSet.find(config) == configSet.end()) { + // okay, no specific localization found. it's possible that we are + // requiring a specific regional localization [e.g. de_DE] but there is an + // available string in the generic language localization [e.g. de]; + // consider that string to have fulfilled the localization requirement. + String8 region(config.string(), 2); + if (configSet.find(region) == configSet.end()) { + // TODO: force an error if there is no default to fall back to + if (configSet.count(defaultLocale) == 0) { + fprintf(stdout, "aapt: warning: " + "*** string '%s' has no default or required localization " + "for '%s' in %s\n", + String8(nameIter->first).string(), + config.string(), + mBundle->getResourceSourceDir()); + //err = UNKNOWN_ERROR; + } + } + } + } + } while (comma != NULL); + } + } + + return err; +} + + status_t ResourceFilter::parse(const char* arg) { @@ -2187,6 +2332,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) } const size_t N = c->getEntries().size(); for (size_t ei=0; ei<N; ei++) { + ConfigDescription config = c->getEntries().keyAt(ei); + if (!filter.match(config)) { + continue; + } sp<Entry> e = c->getEntries().valueAt(ei); if (e == NULL) { continue; diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index b36234d..fff4f49 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -10,6 +10,11 @@ #include "StringPool.h" #include "SourcePos.h" +#include <set> +#include <map> + +using namespace std; + class ResourceTable; enum { @@ -136,12 +141,14 @@ public: uint32_t getResId(const String16& package, const String16& type, - const String16& name) const; + const String16& name, + bool onlyPublic = false) const; uint32_t getResId(const String16& ref, const String16* defType = NULL, const String16* defPackage = NULL, - const char** outErrorMsg = NULL) const; + const char** outErrorMsg = NULL, + bool onlyPublic = false) const; static bool isValidResourceName(const String16& s); @@ -155,6 +162,8 @@ public: status_t assignResourceIds(); status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL); + void addLocalization(const String16& name, const String8& locale); + status_t validateLocalizations(void); status_t flatten(Bundle*, const sp<AaptFile>& dest); @@ -491,7 +500,6 @@ private: String16 mAssetsPackage; sp<AaptAssets> mAssets; - DefaultKeyedVector<String16, DefaultKeyedVector<String16, uint32_t> > mPublicNames; DefaultKeyedVector<String16, sp<Package> > mPackages; Vector<sp<Package> > mOrderedPackages; uint32_t mNextPackageId; @@ -500,6 +508,9 @@ private: size_t mNumLocal; SourcePos mCurrentXmlPos; Bundle* mBundle; + + // key = string resource name, value = set of locales in which that name is defined + map<String16, set<String8> > mLocalizations; }; class ResourceFilter diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index 8f45959..2ea453c 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -21,6 +21,7 @@ const char* const RESOURCES_ROOT_NAMESPACE = "http://schemas.android.com/apk/res/"; const char* const RESOURCES_ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android"; +const char* const RESOURCES_ROOT_PRV_NAMESPACE = "http://schemas.android.com/apk/prv/res/"; const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2"; const char* const ALLOWED_XLIFF_ELEMENTS[] = { @@ -43,15 +44,27 @@ bool isWhitespace(const char16_t* str) } static const String16 RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE); +static const String16 RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE); -String16 getNamespaceResourcePackage(String16 namespaceUri) +String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic) { //printf("%s starts with %s?\n", String8(namespaceUri).string(), // String8(RESOURCES_PREFIX).string()); - if (!namespaceUri.startsWith(RESOURCES_PREFIX)) return String16(); + size_t prefixSize; + bool isPublic = true; + if (namespaceUri.startsWith(RESOURCES_PREFIX)) { + prefixSize = RESOURCES_PREFIX.size(); + } else if (namespaceUri.startsWith(RESOURCES_PRV_PREFIX)) { + isPublic = false; + prefixSize = RESOURCES_PRV_PREFIX.size(); + } else { + if (outIsPublic) *outIsPublic = isPublic; // = true + return String16(); + } + //printf("YES!\n"); - const size_t prefixSize = RESOURCES_PREFIX.size(); //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string()); + if (outIsPublic) *outIsPublic = isPublic; return String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize); } @@ -715,15 +728,18 @@ status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets, for (size_t i=0; i<N; i++) { const attribute_entry& e = mAttributes.itemAt(i); if (e.ns.size() <= 0) continue; - String16 pkg(getNamespaceResourcePackage(e.ns)); - NOISY(printf("Elem %s %s=\"%s\": namespace %s ===> %s\n", + bool nsIsPublic; + String16 pkg(getNamespaceResourcePackage(e.ns, &nsIsPublic)); + NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n", String8(getElementName()).string(), String8(e.name).string(), String8(e.string).string(), - String8(e.ns).string(), String8(pkg).string())); + String8(e.ns).string(), + (nsIsPublic) ? "public" : "private", + String8(pkg).string())); if (pkg.size() <= 0) continue; uint32_t res = table != NULL - ? table->getResId(e.name, &attr, &pkg, &errorMsg) + ? table->getResId(e.name, &attr, &pkg, &errorMsg, nsIsPublic) : assets->getIncludedResources(). identifierForName(e.name.string(), e.name.size(), attr.string(), attr.size(), diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h index 8c4243c..86548a2 100644 --- a/tools/aapt/XMLNode.h +++ b/tools/aapt/XMLNode.h @@ -17,7 +17,7 @@ extern const char* const RESOURCES_ANDROID_NAMESPACE; bool isWhitespace(const char16_t* str); -String16 getNamespaceResourcePackage(String16 namespaceUri); +String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic = NULL); status_t parseStyledString(Bundle* bundle, const char* fileName, diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk index b1bd465..6d606a9 100644 --- a/tools/layoutlib/Android.mk +++ b/tools/layoutlib/Android.mk @@ -35,11 +35,6 @@ built_core_dep := \ built_core_classes := \ $(call intermediates-dir-for,JAVA_LIBRARIES,core)/classes.jar -built_policy_dep := \ - $(call intermediates-dir-for,JAVA_LIBRARIES,android.policy)/javalib.jar -built_policy_classes := \ - $(call intermediates-dir-for,JAVA_LIBRARIES,android.policy)/classes.jar - built_layoutlib_create_jar := $(call intermediates-dir-for, \ JAVA_LIBRARIES,layoutlib_create,HOST)/javalib.jar @@ -56,7 +51,6 @@ include $(BUILD_SYSTEM)/base_rules.mk $(LOCAL_BUILT_MODULE): $(built_core_dep) \ $(built_framework_dep) \ - $(built_policy_dep) \ $(built_layoutlib_create_jar) @echo "host layoutlib_create: $@" @mkdir -p $(dir $@) @@ -64,8 +58,7 @@ $(LOCAL_BUILT_MODULE): $(built_core_dep) \ $(hide) java -jar $(built_layoutlib_create_jar) \ $@ \ $(built_core_classes) \ - $(built_framework_classes) \ - $(built_policy_classes) + $(built_framework_classes) # diff --git a/tools/layoutlib/api/.classpath b/tools/layoutlib/api/.classpath index 14535b7..a09ce5f 100644 --- a/tools/layoutlib/api/.classpath +++ b/tools/layoutlib/api/.classpath @@ -2,7 +2,6 @@ <classpath> <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="var" path="DEVICE_SRC/extlibs/kxml2/lib/xmlpull_1_1_3_1.jar"/> - <classpathentry kind="var" path="DEVICE_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/DEVICE_SRC/dalvik/libcore/xml/src/main/java"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java index d67f0e1..0810d29 100644 --- a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java +++ b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java @@ -21,17 +21,65 @@ import java.util.Map; /** * Entry point of the Layout Lib. Implementations of this interface provide a method to compute * and render a layout. + * <p/> + * <p/>{@link #getApiLevel()} gives the ability to know which methods are available. + * <p/> + * Changes in API level 2: + * <ul> + * <li>{@link #getApiLevel()}</li> + * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}, + * deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, Map, Map, IProjectCallback, ILayoutLog)}.</li> + * </ul> */ public interface ILayoutBridge { + + final int API_CURRENT = 2; + + /** + * Returns the API level of the layout library. + * While no methods will ever be removed, some may become deprecated, and some new ones + * will appear. + */ + int getApiLevel(); /** * Initializes the Bridge object. * @param fontOsLocation the location of the fonts. * @param enumValueMap map attrName => { map enumFlagName => Integer value }. * @return true if success. + * @since 1 */ boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap); + /** + * Computes and renders a layout + * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the + * layout file. + * @param projectKey An Object identifying the project. This is used for the cache mechanism. + * @param screenWidth + * @param screenHeight + * @param themeName The name of the theme to use. + * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme. + * @param projectResources the resources of the project. The map contains (String, map) pairs + * where the string is the type of the resource reference used in the layout file, and the + * map contains (String, {@link IResourceValue}) pairs where the key is the resource name, + * and the value is the resource value. + * @param frameworkResources the framework resources. The map contains (String, map) pairs + * where the string is the type of the resource reference used in the layout file, and the map + * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the + * value is the resource value. + * @param projectCallback The {@link IProjectCallback} object to get information from + * the project. + * @param logger the object responsible for displaying warning/errors to the user. + * @return an {@link ILayoutResult} object that contains the result of the layout. + * @since 2 + */ + ILayoutResult computeLayout(IXmlPullParser layoutDescription, + Object projectKey, + int screenWidth, int screenHeight, String themeName, boolean isProjectTheme, + Map<String, Map<String, IResourceValue>> projectResources, + Map<String, Map<String, IResourceValue>> frameworkResources, + IProjectCallback projectCallback, ILayoutLog logger); /** * Computes and renders a layout @@ -54,7 +102,10 @@ public interface ILayoutBridge { * the project. * @param logger the object responsible for displaying warning/errors to the user. * @return an {@link ILayoutResult} object that contains the result of the layout. + * @deprecated Use {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}. + * @since 1 */ + @Deprecated ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey, int screenWidth, int screenHeight, String themeName, @@ -69,6 +120,7 @@ public interface ILayoutBridge { * <p/>The cache is not configuration dependent and should only be cleared when a * resource changes (at this time only bitmaps and 9 patches go into the cache). * @param objectKey the key for the project. + * @since 1 */ void clearCaches(Object projectKey); } diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath index 946c438..175a98b 100644 --- a/tools/layoutlib/bridge/.classpath +++ b/tools/layoutlib/bridge/.classpath @@ -4,9 +4,9 @@ <classpathentry kind="src" path="tests"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> - <classpathentry kind="var" path="DEVICE_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/DEVICE_SRC/dalvik/libcore/xml/src/main/java"/> <classpathentry combineaccessrules="false" kind="src" path="/layoutlib_api"/> - <classpathentry kind="var" path="DEVICE_FRAMEWORK/layoutlib.jar" sourcepath="/DEVICE_SRC/java/android"/> - <classpathentry kind="var" path="DEVICE_FRAMEWORK/ninepatch.jar" sourcepath="/DEVICE_SRC/tools/ninepatch"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/> + <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_SRC/frameworks/base/core/java"/> + <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/ninepatch.jar" sourcepath="/ANDROID_SRC/development/tools/ninepatch/src"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java index 27276c5..13cc62d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java @@ -45,15 +45,24 @@ public class Paint extends _Original_Paint { new AffineTransform(), true, true); private java.awt.FontMetrics mMetrics; - public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG; - public static final int FILTER_BITMAP_FLAG = _Original_Paint.FILTER_BITMAP_FLAG; - public static final int DITHER_FLAG = _Original_Paint.DITHER_FLAG; - public static final int UNDERLINE_TEXT_FLAG = _Original_Paint.UNDERLINE_TEXT_FLAG; + @SuppressWarnings("hiding") + public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG; + @SuppressWarnings("hiding") + public static final int FILTER_BITMAP_FLAG = _Original_Paint.FILTER_BITMAP_FLAG; + @SuppressWarnings("hiding") + public static final int DITHER_FLAG = _Original_Paint.DITHER_FLAG; + @SuppressWarnings("hiding") + public static final int UNDERLINE_TEXT_FLAG = _Original_Paint.UNDERLINE_TEXT_FLAG; + @SuppressWarnings("hiding") public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG; - public static final int FAKE_BOLD_TEXT_FLAG = _Original_Paint.FAKE_BOLD_TEXT_FLAG; - public static final int LINEAR_TEXT_FLAG = _Original_Paint.LINEAR_TEXT_FLAG; - public static final int SUBPIXEL_TEXT_FLAG = _Original_Paint.SUBPIXEL_TEXT_FLAG; - public static final int DEV_KERN_TEXT_FLAG = _Original_Paint.DEV_KERN_TEXT_FLAG; + @SuppressWarnings("hiding") + public static final int FAKE_BOLD_TEXT_FLAG = _Original_Paint.FAKE_BOLD_TEXT_FLAG; + @SuppressWarnings("hiding") + public static final int LINEAR_TEXT_FLAG = _Original_Paint.LINEAR_TEXT_FLAG; + @SuppressWarnings("hiding") + public static final int SUBPIXEL_TEXT_FLAG = _Original_Paint.SUBPIXEL_TEXT_FLAG; + @SuppressWarnings("hiding") + public static final int DEV_KERN_TEXT_FLAG = _Original_Paint.DEV_KERN_TEXT_FLAG; public static class FontMetrics extends _Original_Paint.FontMetrics { } diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index a0c0c2b..0910d79 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -66,7 +66,6 @@ public final class BridgeInflater extends LayoutInflater { mConstructorArgs[0] = context; } - @SuppressWarnings("unchecked") @Override public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { View view = null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 0afd9f9..b898192 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -16,10 +16,11 @@ package com.android.layoutlib.bridge; -import com.android.layoutlib.api.IProjectCallback; +import com.android.internal.util.XmlUtils; import com.android.layoutlib.api.ILayoutBridge; import com.android.layoutlib.api.ILayoutLog; import com.android.layoutlib.api.ILayoutResult; +import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.api.IStyleResourceValue; import com.android.layoutlib.api.IXmlPullParser; @@ -29,8 +30,6 @@ import com.android.ninepatch.NinePatch; import com.android.tools.layoutlib.create.OverrideMethod; import com.android.tools.layoutlib.create.OverrideMethod.MethodListener; -import org.xmlpull.v1.XmlPullParser; - import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.Region; @@ -39,16 +38,16 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; - -import com.android.internal.util.XmlUtils; - import android.util.TypedValue; import android.view.BridgeInflater; import android.view.IWindow; import android.view.IWindowSession; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; +import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.View.AttachInfo; @@ -65,14 +64,18 @@ import java.util.Map; /** * Main entry point of the LayoutLib Bridge. * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call - * {@link #computeLayout(XmlPullParser, int, int, boolean, Map)}. + * {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}. */ public final class Bridge implements ILayoutBridge { private static final int DEFAULT_TITLE_BAR_HEIGHT = 25; private static final int DEFAULT_STATUS_BAR_HEIGHT = 25; - - private ILayoutLog mLogger; + + public static class StaticMethodNotImplementedException extends RuntimeException { + public StaticMethodNotImplementedException(String msg) { + super(msg); + } + } /** * Maps from id to resource name/type. @@ -100,7 +103,7 @@ public final class Bridge implements ILayoutBridge { private static Map<String, Map<String, Integer>> sEnumValueMap; private final static MethodListener sNullMethodListener = new MethodListener() { - public void onInvoke(String signature, Object caller) { + public void onInvoke(String signature, boolean isNative, Object caller) { // pass } }; @@ -124,6 +127,9 @@ public final class Bridge implements ILayoutBridge { } }; + /** Logger defined during a compute layout operation. */ + private static ILayoutLog sLogger = sDefaultLogger; + private final static String[] IGNORED_STATIC_METHODS = new String[] { "android.content.res.AssetManager#init()V", "android.content.res.AssetManager#deleteTheme(I)V", @@ -135,6 +141,14 @@ public final class Bridge implements ILayoutBridge { "android.view.animation.Transformation#<init>()V", "android.view.animation.Transformation#clear()V", }; + + /* + * (non-Javadoc) + * @see com.android.layoutlib.api.ILayoutBridge#getApiLevel() + */ + public int getApiLevel() { + return API_CURRENT; + } /* * (non-Javadoc) @@ -156,14 +170,25 @@ public final class Bridge implements ILayoutBridge { // set a the default listener for the rest of the static methods. It prints out - // missing stub methods but only if the environment variable DEBUG_LAYOUT is set. - if (System.getenv("DEBUG_LAYOUT") != null) { - OverrideMethod.setDefaultListener(new MethodListener() { - public void onInvoke(String signature, Object caller) { - System.out.println("Missing Stub: " + signature); + // missing stub methods and then throws an exception for native methods if the + // environment variable DEBUG_LAYOUT is not defined. + OverrideMethod.setDefaultListener(new MethodListener() { + public void onInvoke(String signature, boolean isNative, Object caller) { + if (isNative) { + if (sLogger != null) { + sLogger.error("Missing Stub: " + signature + + (isNative ? " (native)" : "")); + } + + if (System.getenv("DEBUG_LAYOUT") == null) { + // TODO throwing this exception doesn't seem that useful. It breaks + // the layout editor yet doesn't display anything meaningful to the + // user. Having the error in the console is just as useful. + throw new StaticMethodNotImplementedException(signature); + } } - }); - } + } + }); // load the fonts. FontLoader fontLoader = FontLoader.create(fontOsLocation); @@ -179,7 +204,7 @@ public final class Bridge implements ILayoutBridge { // the internal version), and put the content in the maps. try { // WARNING: this only works because the class is already loaded, and therefore - // the objects returned by Field.get() are the same as the onea used by + // the objects returned by Field.get() are the same as the ones used by // the code accessing the R class. // int[] does not implement equals/hashCode, and if the parsing used a different class // loader for the R class, this would NOT work. @@ -223,29 +248,48 @@ public final class Bridge implements ILayoutBridge { } /* + * For compatilibty purposes, we implement the old deprecated version of computeLayout. * (non-Javadoc) * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) */ + @Deprecated public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey, int screenWidth, int screenHeight, String themeName, Map<String, Map<String, IResourceValue>> projectResources, Map<String, Map<String, IResourceValue>> frameworkResources, IProjectCallback customViewLoader, ILayoutLog logger) { - // DEBUG - //long time1 = System.currentTimeMillis(); + boolean isProjectTheme = false; + if (themeName.charAt(0) == '*') { + themeName = themeName.substring(1); + isProjectTheme = true; + } + return computeLayout(layoutDescription, projectKey, screenWidth, screenHeight, themeName, isProjectTheme, + projectResources, frameworkResources, customViewLoader, logger); + } + + /* + * (non-Javadoc) + * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) + */ + public ILayoutResult computeLayout(IXmlPullParser layoutDescription, + Object projectKey, + int screenWidth, int screenHeight, String themeName, boolean isProjectTheme, + Map<String, Map<String, IResourceValue>> projectResources, + Map<String, Map<String, IResourceValue>> frameworkResources, + IProjectCallback customViewLoader, ILayoutLog logger) { if (logger == null) { logger = sDefaultLogger; } - mLogger = logger; + sLogger = logger; // find the current theme and compute the style inheritance map Map<IStyleResourceValue, IStyleResourceValue> styleParentMap = new HashMap<IStyleResourceValue, IStyleResourceValue>(); - IStyleResourceValue currentTheme = computeStyleMaps(themeName, + IStyleResourceValue currentTheme = computeStyleMaps(themeName, isProjectTheme, projectResources.get(BridgeConstants.RES_STYLE), frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap); @@ -278,11 +322,11 @@ public final class Bridge implements ILayoutBridge { View view = inflater.inflate(parser, root); // set the AttachInfo on the root view. - AttachInfo info = new AttachInfo(new Handler()); + AttachInfo info = new AttachInfo(new WindowSession(), new Window(), + new Handler(), null); info.mHasWindowFocus = true; info.mWindowVisibility = View.VISIBLE; info.mInTouchMode = false; // this is so that we can display selections. - info.mSession = new WindowSession(); root.dispatchAttachedToWindow(info, 0); // get the background drawable @@ -307,10 +351,6 @@ public final class Bridge implements ILayoutBridge { root.draw(canvas); canvas.dispose(); - // DEBUG - //long time2 = System.currentTimeMillis(); - //System.out.println("Layout: " + (time2 - time1)); - return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context), canvas.getImage()); } catch (Throwable e) { @@ -326,6 +366,9 @@ public final class Bridge implements ILayoutBridge { // then return with an ERROR status and the message from the real exception return new LayoutResult(ILayoutResult.ERROR, t.getClass().getSimpleName() + ": " + t.getMessage()); + } finally { + // Remove the global logger + sLogger = sDefaultLogger; } } @@ -354,7 +397,6 @@ public final class Bridge implements ILayoutBridge { /** * Returns the name of a framework resource whose value is an int array. * @param array - * @return */ public static String resolveResourceValue(int[] array) { return sRArrayMap.get(array); @@ -388,7 +430,6 @@ public final class Bridge implements ILayoutBridge { * bounds of all the views. * @param view the root View * @param context the context. - * @return */ private ILayoutViewInfo visit(View view, BridgeContext context) { if (view == null) { @@ -417,15 +458,15 @@ public final class Bridge implements ILayoutBridge { * @param themeName the name of the current theme. In order to differentiate project and * platform themes sharing the same name, all project themes must be prepended with * a '*' character. + * @param isProjectTheme Is this a project theme * @param inProjectStyleMap the project style map * @param inFrameworkStyleMap the framework style map * @param outInheritanceMap the map of style inheritance. This is filled by the method * @return the {@link IStyleResourceValue} matching <var>themeName</var> */ private IStyleResourceValue computeStyleMaps( - String themeName, - Map<String, IResourceValue> inProjectStyleMap, - Map<String, IResourceValue> inFrameworkStyleMap, + String themeName, boolean isProjectTheme, Map<String, + IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap, Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) { if (inProjectStyleMap != null && inFrameworkStyleMap != null) { @@ -433,12 +474,9 @@ public final class Bridge implements ILayoutBridge { IResourceValue theme = null; // project theme names have been prepended with a * - if (themeName.charAt(0) == '*') { - themeName = themeName.substring(1); + if (isProjectTheme) { theme = inProjectStyleMap.get(themeName); - } - - if (theme == null) { + } else { theme = inFrameworkStyleMap.get(themeName); } @@ -550,15 +588,13 @@ public final class Bridge implements ILayoutBridge { return (IStyleResourceValue)parent; } - mLogger.error(String.format("Unable to resolve parent style name: ", parentName)); + sLogger.error(String.format("Unable to resolve parent style name: ", parentName)); return null; } /** - * Compute the name of the parent style, or <code>null</code> if the style is a root style. - * @param styleName - * @return + * Computes the name of the parent style, or <code>null</code> if the style is a root style. */ private String getParentName(String styleName) { int index = styleName.lastIndexOf('.'); @@ -572,8 +608,6 @@ public final class Bridge implements ILayoutBridge { /** * Returns the top screen offset. This depends on whether the current theme defines the user * of the title and status bars. - * @param currentTheme - * @param styleInheritanceMap * @return the pixel height offset */ private int getScreenOffset(IStyleResourceValue currentTheme, BridgeContext context) { @@ -705,61 +739,135 @@ public final class Bridge implements ILayoutBridge { } /** - * Implementation of IWindowSession so that mSession is not null in the SurfaceView. + * Implementation of {@link IWindowSession} so that mSession is not null in + * the {@link SurfaceView}. */ private static final class WindowSession implements IWindowSession { - public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3) throws RemoteException { + @SuppressWarnings("unused") + public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3) + throws RemoteException { // pass for now. return 0; } + @SuppressWarnings("unused") public void finishDrawing(IWindow arg0) throws RemoteException { // pass for now. } + @SuppressWarnings("unused") public void finishKey(IWindow arg0) throws RemoteException { // pass for now. } + @SuppressWarnings("unused") public boolean getInTouchMode() throws RemoteException { // pass for now. return false; } + @SuppressWarnings("unused") public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException { // pass for now. return null; } + @SuppressWarnings("unused") public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException { // pass for now. return null; } + @SuppressWarnings("unused") public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4, - Rect arg5, Rect arg6, Surface arg7) throws RemoteException { + boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Surface arg8) + throws RemoteException { // pass for now. return 0; } + public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { + // pass for now. + } + + @SuppressWarnings("unused") public void remove(IWindow arg0) throws RemoteException { // pass for now. } + @SuppressWarnings("unused") public void setInTouchMode(boolean arg0) throws RemoteException { // pass for now. } + @SuppressWarnings("unused") public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException { // pass for now. } + public void setInsets(IWindow window, int touchable, Rect contentInsets, + Rect visibleInsets) { + // pass for now. + } + public IBinder asBinder() { // pass for now. return null; } - - }; + } + + /** + * Implementation of {@link IWindow} to pass to the {@link AttachInfo}. + */ + private static final class Window implements IWindow { + + @SuppressWarnings("unused") + public void dispatchAppVisibility(boolean arg0) throws RemoteException { + // pass for now. + } + + @SuppressWarnings("unused") + public void dispatchGetNewSurface() throws RemoteException { + // pass for now. + } + + @SuppressWarnings("unused") + public void dispatchKey(KeyEvent arg0) throws RemoteException { + // pass for now. + } + + @SuppressWarnings("unused") + public void dispatchPointer(MotionEvent arg0, long arg1) throws RemoteException { + // pass for now. + } + + @SuppressWarnings("unused") + public void dispatchTrackball(MotionEvent arg0, long arg1) throws RemoteException { + // pass for now. + } + + @SuppressWarnings("unused") + public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2) + throws RemoteException { + // pass for now. + } + + @SuppressWarnings("unused") + public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4) + throws RemoteException { + // pass for now. + } + + @SuppressWarnings("unused") + public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException { + // pass for now. + } + + public IBinder asBinder() { + // pass for now. + return null; + } + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index 0a90160..cb9509b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -428,8 +428,12 @@ public final class BridgeContext extends Context { * Searches for, and returns a {@link IResourceValue} by its reference. * <p/> * The reference format can be: - * <pre>@[android:]resType/resName</pre> - * <pre>?[android:]resType/resName</pre> + * <pre>@resType/resName</pre> + * <pre>@android:resType/resName</pre> + * <pre>@resType/android:resName</pre> + * <pre>?resType/resName</pre> + * <pre>?android:resType/resName</pre> + * <pre>?resType/android:resName</pre> * Any other string format will return <code>null</code>. * <p/> * The actual format of a reference is <pre>@[namespace:]resType/resName</pre> but this method @@ -478,6 +482,13 @@ public final class BridgeContext extends Context { // it's just an item name. referenceName = segments[0]; } + + // now we look for android: in the referenceName in order to support format + // such as: ?attr/android:name + if (referenceName.startsWith(BridgeConstants.PREFIX_ANDROID)) { + frameworkOnly = true; + referenceName = referenceName.substring(BridgeConstants.PREFIX_ANDROID.length()); + } // Now look for the item in the theme, starting with the current one. if (frameworkOnly) { @@ -503,9 +514,16 @@ public final class BridgeContext extends Context { reference = reference.substring(BridgeConstants.PREFIX_RESOURCE_REF.length()); } - // at this point, value contains type/name (drawable/foo for instance) + // at this point, value contains type/[android:]name (drawable/foo for instance) String[] segments = reference.split("\\/"); + // now we look for android: in the resource name in order to support format + // such as: @drawable/android:name + if (segments[1].startsWith(BridgeConstants.PREFIX_ANDROID)) { + frameworkOnly = true; + segments[1] = segments[1].substring(BridgeConstants.PREFIX_ANDROID.length()); + } + return findResValue(segments[0], segments[1], frameworkOnly); } @@ -518,8 +536,7 @@ public final class BridgeContext extends Context { * @param resType the type of the resource * @param resName the name of the resource * @param frameworkOnly if <code>true</code>, the method does not search in the - * project resources. - * @return + * project resources */ private IResourceValue findResValue(String resType, String resName, boolean frameworkOnly) { // map of IResouceValue for the given type @@ -613,7 +630,6 @@ public final class BridgeContext extends Context { * there's a field com.android.internal.R.styleable.View_xyz and the field value is the index * that is used to reference the attribute later in the TypedArray. * - * @param classNames The parent class name where to look, e.g. "com.android.internal.R" * @param attrs An attribute array reference given to obtainStyledAttributes. * @return A sorted map Attribute-Value to Attribute-Name for all attributes declared by the * attribute array. Returns null if nothing is found. @@ -669,7 +685,7 @@ public final class BridgeContext extends Context { /** * Searches for the attribute referenced by its internal id. * - * @param attrs An attribute reference given to obtainStyledAttributes such as defStyle. + * @param attr An attribute reference given to obtainStyledAttributes such as defStyle. * @return The unique name of the attribute, if found, e.g. "buttonStyle". Returns null * if nothing is found. */ @@ -963,6 +979,7 @@ public final class BridgeContext extends Context { } + @SuppressWarnings("unused") @Override public FileInputStream openFileInput(String arg0) throws FileNotFoundException { @@ -970,6 +987,7 @@ public final class BridgeContext extends Context { return null; } + @SuppressWarnings("unused") @Override public FileOutputStream openFileOutput(String arg0, int arg1) throws FileNotFoundException { @@ -1053,12 +1071,14 @@ public final class BridgeContext extends Context { } + @SuppressWarnings("unused") @Override public void setWallpaper(Bitmap arg0) throws IOException { // TODO Auto-generated method stub } + @SuppressWarnings("unused") @Override public void setWallpaper(InputStream arg0) throws IOException { // TODO Auto-generated method stub diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java index 8bd4ba8..6ab6e32 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java @@ -435,9 +435,8 @@ public final class BridgeResources extends Resources { } /** - * Builds and throws a {@link NotFoundException} based on a resource id and a resource type. + * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource type. * @param id the id of the resource - * @param resType the type of the resource. * @throws NotFoundException */ private void throwException(int id) throws NotFoundException { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java index 1b3d583..1bdd1cc 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java @@ -40,7 +40,8 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; /** - * Implementation of the {@link IFontLoader} to provide {@link Font} object to the layout lib. + * Provides {@link Font} object to the layout lib. + * <p/> * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the * fonts.xml file located alongside the ttf files. */ @@ -247,6 +248,7 @@ public final class FontLoader { /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int) */ + @SuppressWarnings("unused") @Override public void characters(char[] ch, int start, int length) throws SAXException { if (mFontInfo != null) { @@ -257,6 +259,7 @@ public final class FontLoader { /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ + @SuppressWarnings("unused") @Override public void endElement(String uri, String localName, String name) throws SAXException { if (localName.equals(NODE_LEVEL[mDepth-1])) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java index fb885a4..c4c5225 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java @@ -71,7 +71,7 @@ public final class LayoutResult implements ILayoutResult { } /** - * Implementation of {@link ILayoutViewInfo} + * Implementation of {@link ILayoutResult.ILayoutViewInfo} */ public static final class LayoutViewInfo implements ILayoutViewInfo { private final Object mKey; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java index 9c7abcf..fbdf8dc 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java @@ -205,7 +205,7 @@ public final class ResourceHelper { this.unit = unit; this.scale = scale; } - }; + } private final static UnitEntry[] sUnitNames = new UnitEntry[] { new UnitEntry("px", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PX, 1.0f), @@ -222,8 +222,6 @@ public final class ResourceHelper { /** * Returns the raw value from the given string. * This object is only valid until the next call on to {@link ResourceHelper}. - * @param s - * @return */ public static TypedValue getValue(String s) { if (stringToFloat(s, mValue)) { diff --git a/tools/layoutlib/create/.classpath b/tools/layoutlib/create/.classpath index fecb8d0..0c60f6a 100644 --- a/tools/layoutlib/create/.classpath +++ b/tools/layoutlib/create/.classpath @@ -4,6 +4,6 @@ <classpathentry excluding="mock_android/" kind="src" path="tests"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> - <classpathentry kind="var" path="DEVICE_SRC/prebuilt/common/asm/asm-3.1.jar" sourcepath="/DEVICE_SRC/extlibs/asm-3.1/src"/> + <classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/asm/asm-3.1.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index f1da42a..b4e2c2b 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -187,7 +187,7 @@ public class AsmGenerator { for (Entry<String, ClassReader> entry : mDeps.entrySet()) { ClassReader cr = entry.getValue(); - byte[] b = transform(cr, false /* stubNativesOnly */); + byte[] b = transform(cr, true /* stubNativesOnly */); String name = classNameToEntryPath(transformName(cr.getClassName())); all.put(name, b); } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 9202e44..13443d5 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -68,7 +68,7 @@ public class Main { "android.view.SurfaceView", "android.view._Original_SurfaceView", }, new String[] { // methods deleted from their return type. - "android.graphics.Paint", // class to delete method from + "android.graphics.Paint", // class to delete method from "android.graphics.Paint$Align", // list of type identifying methods to delete "android.graphics.Paint$Style", "android.graphics.Paint$Join", diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java index 61dd151..b061a15 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java @@ -34,13 +34,14 @@ public final class OverrideMethod { * * @param signature The signature of the method being invoked, composed of the * binary class name followed by the method descriptor (aka argument - * types). Example: "com/foo/MyClass/InnerClass/printInt(I)V" + * types). Example: "com/foo/MyClass/InnerClass/printInt(I)V". + * @param isNative True if the method was a native method. * @param caller The calling object. Null for static methods, "this" for instance methods. */ - public void onInvoke(String signature, Object caller); + public void onInvoke(String signature, boolean isNative, Object caller); } - /** Map of method overriden. */ + /** Map of method overridden. */ private static HashMap<String, MethodListener> sMethods = new HashMap<String, MethodListener>(); /** Default listener for all method not listed in sMethods. Nothing if null. */ private static MethodListener sDefaultListener = null; @@ -77,15 +78,16 @@ public final class OverrideMethod { * * @param signature The signature of the method being invoked, composed of the * binary class name followed by the method descriptor (aka argument - * types). Example: "com/foo/MyClass/InnerClass/printInt(I)V" + * types). Example: "com/foo/MyClass/InnerClass/printInt(I)V". + * @param isNative True if the method was a native method. * @param caller The calling object. Null for static methods, "this" for instance methods. */ - public static void invoke(String signature, Object caller) { + public static void invoke(String signature, boolean isNative, Object caller) { MethodListener i = sMethods.get(signature); if (i != null) { - i.onInvoke(signature, caller); + i.onInvoke(signature, isNative, caller); } else if (sDefaultListener != null) { - sDefaultListener.onInvoke(signature, caller); + sDefaultListener.onInvoke(signature, isNative, caller); } } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java index 2450d4b..9bb64bd 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java @@ -46,13 +46,15 @@ class StubMethodAdapter implements MethodVisitor { private boolean mMessageGenerated; private final boolean mIsStatic; + private final boolean mIsNative; public StubMethodAdapter(MethodVisitor mv, String methodName, Type returnType, - String invokeSignature, boolean isStatic) { + String invokeSignature, boolean isStatic, boolean isNative) { mParentVisitor = mv; mReturnType = returnType; mInvokeSignature = invokeSignature; mIsStatic = isStatic; + mIsNative = isNative; if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) { mIsInitMethod = true; @@ -61,20 +63,23 @@ class StubMethodAdapter implements MethodVisitor { private void generateInvoke() { /* Generates the code: - * OverrideMethod.invoke("signature", this); + * OverrideMethod.invoke("signature", mIsNative ? true : false, null or this); */ mParentVisitor.visitLdcInsn(mInvokeSignature); + // push true or false + mParentVisitor.visitInsn(mIsNative ? Opcodes.ICONST_1 : Opcodes.ICONST_0); + // push null or this if (mIsStatic) { - mParentVisitor.visitInsn(Opcodes.ACONST_NULL); // push null + mParentVisitor.visitInsn(Opcodes.ACONST_NULL); } else { - mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); // push this + mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); } mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "com/android/tools/layoutlib/create/OverrideMethod", "invoke", - "(Ljava/lang/String;Ljava/lang/Object;)V"); + "(Ljava/lang/String;ZLjava/lang/Object;)V"); } - + private void generateReturn() { /* Generates one of, depending on the return type: * return; diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java index a84353c..e294d56 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java @@ -130,17 +130,19 @@ class TransformClassAdapter extends ClassAdapter { (mStubAll || (access & Opcodes.ACC_NATIVE) != 0) || mStubMethods.contains(methodSignature)) { + + boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; + boolean isNative = (access & Opcodes.ACC_NATIVE) != 0; // remove abstract, final and native access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE); - boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; - String invokeSignature = methodSignature + desc; - mLog.debug(" Stub: %s", invokeSignature); + mLog.debug(" Stub: %s (%s)", invokeSignature, isNative ? "native" : ""); MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); - return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature, isStatic); + return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature, + isStatic, isNative); } else { mLog.debug(" Keep: %s %s", name, desc); |