diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2013-11-22 11:18:57 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2013-11-22 11:18:57 -0800 |
commit | dbccd44a638ae8705a5b14bff8b2dd74abc26045 (patch) | |
tree | 14bfabaf3f3c7be86dfc064e919e00433a0cf2bb /tools | |
parent | ecfae4f899873f224e1aeed076dc8a41f8884487 (diff) | |
parent | b873a17ce7be0a9771c24999adca6964431728f6 (diff) | |
download | frameworks_base-dbccd44a638ae8705a5b14bff8b2dd74abc26045.zip frameworks_base-dbccd44a638ae8705a5b14bff8b2dd74abc26045.tar.gz frameworks_base-dbccd44a638ae8705a5b14bff8b2dd74abc26045.tar.bz2 |
Merge commit 'b873a17ce7be0a9771c24999adca6964431728f6' into HEAD
Change-Id: I938755073e70602cc8f51ce9bd420fdcf870cecd
Diffstat (limited to 'tools')
52 files changed, 1683 insertions, 500 deletions
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index 9b1658a..452c60a 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -7,10 +7,8 @@ # This tool is prebuilt if we're doing an app-only build. ifeq ($(TARGET_BUILD_APPS),) -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ +aapt_src_files := \ AaptAssets.cpp \ Command.cpp \ CrunchCache.cpp \ @@ -24,23 +22,29 @@ LOCAL_SRC_FILES := \ ResourceTable.cpp \ Images.cpp \ Resource.cpp \ + pseudolocalize.cpp \ SourcePos.cpp \ + WorkQueue.cpp \ ZipEntry.cpp \ - ZipFile.cpp + ZipFile.cpp \ + qsort_r_compat.c + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(aapt_src_files) LOCAL_CFLAGS += -Wno-format-y2k ifeq (darwin,$(HOST_OS)) LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS endif +LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS LOCAL_C_INCLUDES += external/libpng LOCAL_C_INCLUDES += external/zlib -LOCAL_C_INCLUDES += build/libs/host/include LOCAL_STATIC_LIBRARIES := \ - libhost \ libandroidfw \ libutils \ libcutils \ @@ -64,4 +68,36 @@ LOCAL_MODULE := aapt include $(BUILD_HOST_EXECUTABLE) +# aapt for running on the device +# ========================================================= +ifneq ($(SDK_ONLY),true) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(aapt_src_files) + +LOCAL_MODULE := aapt + +LOCAL_C_INCLUDES += bionic +LOCAL_C_INCLUDES += bionic/libstdc++/include +LOCAL_C_INCLUDES += external/stlport/stlport +LOCAL_C_INCLUDES += external/libpng +LOCAL_C_INCLUDES += external/zlib + +LOCAL_CFLAGS += -Wno-non-virtual-dtor + +LOCAL_SHARED_LIBRARIES := \ + libandroidfw \ + libutils \ + libcutils \ + libpng \ + liblog \ + libz + +LOCAL_STATIC_LIBRARIES := \ + libstlport_static \ + libexpat_static + +include $(BUILD_EXECUTABLE) +endif + endif # TARGET_BUILD_APPS diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index cadac02..632efe0 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -346,6 +346,8 @@ enum { LABEL_ATTR = 0x01010001, ICON_ATTR = 0x01010002, NAME_ATTR = 0x01010003, + PERMISSION_ATTR = 0x01010006, + RESOURCE_ATTR = 0x01010025, DEBUGGABLE_ATTR = 0x0101000f, VERSION_CODE_ATTR = 0x0101021b, VERSION_NAME_ATTR = 0x0101021c, @@ -372,6 +374,7 @@ enum { COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365, LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366, PUBLIC_KEY_ATTR = 0x010103a6, + CATEGORY_ATTR = 0x010103e8, }; const char *getComponentName(String8 &pkgName, String8 &componentName) { @@ -424,6 +427,61 @@ static void printCompatibleScreens(ResXMLTree& tree) { printf("\n"); } +Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost, + String8 *outError = NULL) +{ + Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER); + if (aidAsset == NULL) { + if (outError != NULL) *outError = "xml resource does not exist"; + return Vector<String8>(); + } + + const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service"); + + bool withinApduService = false; + Vector<String8> categories; + + String8 error; + ResXMLTree tree; + tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength()); + + size_t len; + int depth = 0; + ResXMLTree::event_code_t code; + while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code == ResXMLTree::END_TAG) { + depth--; + String8 tag(tree.getElementName(&len)); + + if (depth == 0 && tag == serviceTagName) { + withinApduService = false; + } + + } else if (code == ResXMLTree::START_TAG) { + depth++; + String8 tag(tree.getElementName(&len)); + + if (depth == 1) { + if (tag == serviceTagName) { + withinApduService = true; + } + } else if (depth == 2 && withinApduService) { + if (tag == "aid-group") { + String8 category = getAttribute(tree, CATEGORY_ATTR, &error); + if (error != "") { + if (outError != NULL) *outError = error; + return Vector<String8>(); + } + + categories.add(category); + } + } + } + } + aidAsset->close(); + return categories; +} + /* * Handle the "dump" command, to extract select data from an archive. */ @@ -621,6 +679,7 @@ int doDump(Bundle* bundle) bool isLauncherActivity = false; bool isSearchable = false; bool withinApplication = false; + bool withinSupportsInput = false; bool withinReceiver = false; bool withinService = false; bool withinIntentFilter = false; @@ -630,12 +689,31 @@ int doDump(Bundle* bundle) bool hasOtherServices = false; bool hasWallpaperService = false; bool hasImeService = false; + bool hasAccessibilityService = false; + bool hasPrintService = false; bool hasWidgetReceivers = false; + bool hasDeviceAdminReceiver = false; bool hasIntentFilter = false; + bool hasPaymentService = false; bool actMainActivity = false; bool actWidgetReceivers = false; + bool actDeviceAdminEnabled = false; bool actImeService = false; bool actWallpaperService = false; + bool actAccessibilityService = false; + bool actPrintService = false; + bool actHostApduService = false; + bool actOffHostApduService = false; + bool hasMetaHostPaymentCategory = false; + bool hasMetaOffHostPaymentCategory = false; + + // These permissions are required by services implementing services + // the system binds to (IME, Accessibility, PrintServices, etc.) + bool hasBindDeviceAdminPermission = false; + bool hasBindInputMethodPermission = false; + bool hasBindAccessibilityServicePermission = false; + bool hasBindPrintServicePermission = false; + bool hasBindNfcServicePermission = false; // These two implement the implicit permissions that are granted // to pre-1.6 applications. @@ -711,11 +789,26 @@ int doDump(Bundle* bundle) String8 activityIcon; String8 receiverName; String8 serviceName; + Vector<String8> supportedInput; while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::END_TAG) { depth--; if (depth < 2) { + if (withinSupportsInput && !supportedInput.isEmpty()) { + printf("supports-input: '"); + const size_t N = supportedInput.size(); + for (size_t i=0; i<N; i++) { + printf("%s", supportedInput[i].string()); + if (i != N - 1) { + printf("' '"); + } else { + printf("'\n"); + } + } + supportedInput.clear(); + } withinApplication = false; + withinSupportsInput = false; } else if (depth < 3) { if (withinActivity && isMainActivity && isLauncherActivity) { const char *aName = getComponentName(pkg, activityName); @@ -731,6 +824,13 @@ int doDump(Bundle* bundle) hasOtherActivities |= withinActivity; hasOtherReceivers |= withinReceiver; hasOtherServices |= withinService; + } else { + if (withinService) { + hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory && + hasBindNfcServicePermission); + hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory && + hasBindNfcServicePermission); + } } withinActivity = false; withinService = false; @@ -744,11 +844,18 @@ int doDump(Bundle* bundle) hasOtherActivities |= !actMainActivity; } else if (withinReceiver) { hasWidgetReceivers |= actWidgetReceivers; - hasOtherReceivers |= !actWidgetReceivers; + hasDeviceAdminReceiver |= (actDeviceAdminEnabled && + hasBindDeviceAdminPermission); + hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled); } else if (withinService) { hasImeService |= actImeService; hasWallpaperService |= actWallpaperService; - hasOtherServices |= (!actImeService && !actWallpaperService); + hasAccessibilityService |= (actAccessibilityService && + hasBindAccessibilityServicePermission); + hasPrintService |= (actPrintService && hasBindPrintServicePermission); + hasOtherServices |= (!actImeService && !actWallpaperService && + !actAccessibilityService && !actPrintService && + !actHostApduService && !actOffHostApduService); } } withinIntentFilter = false; @@ -910,6 +1017,8 @@ int doDump(Bundle* bundle) printf(" reqFiveWayNav='%d'", reqFiveWayNav); } printf("\n"); + } else if (tag == "supports-input") { + withinSupportsInput = true; } else if (tag == "supports-screens") { smallScreen = getIntegerAttribute(tree, SMALL_SCREEN_ATTR, NULL, 1); @@ -1086,73 +1195,175 @@ int doDump(Bundle* bundle) } } } - } else if (depth == 3 && withinApplication) { + } else if (depth == 3) { withinActivity = false; withinReceiver = false; withinService = false; hasIntentFilter = false; - if(tag == "activity") { - withinActivity = true; - activityName = getAttribute(tree, NAME_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); - goto bail; - } + hasMetaHostPaymentCategory = false; + hasMetaOffHostPaymentCategory = false; + hasBindDeviceAdminPermission = false; + hasBindInputMethodPermission = false; + hasBindAccessibilityServicePermission = false; + hasBindPrintServicePermission = false; + hasBindNfcServicePermission = false; + if (withinApplication) { + if(tag == "activity") { + withinActivity = true; + activityName = getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", + error.string()); + goto bail; + } - activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string()); - goto bail; - } + activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", + error.string()); + goto bail; + } - activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); - goto bail; - } + activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", + error.string()); + goto bail; + } + + int32_t orien = getResolvedIntegerAttribute(&res, tree, + SCREEN_ORIENTATION_ATTR, &error); + if (error == "") { + if (orien == 0 || orien == 6 || orien == 8) { + // Requests landscape, sensorLandscape, or reverseLandscape. + reqScreenLandscapeFeature = true; + } else if (orien == 1 || orien == 7 || orien == 9) { + // Requests portrait, sensorPortrait, or reversePortrait. + reqScreenPortraitFeature = true; + } + } + } else if (tag == "uses-library") { + String8 libraryName = getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, + "ERROR getting 'android:name' attribute for uses-library" + " %s\n", error.string()); + goto bail; + } + int req = getIntegerAttribute(tree, + REQUIRED_ATTR, NULL, 1); + printf("uses-library%s:'%s'\n", + req ? "" : "-not-required", libraryName.string()); + } else if (tag == "receiver") { + withinReceiver = true; + receiverName = getAttribute(tree, NAME_ATTR, &error); - int32_t orien = getResolvedIntegerAttribute(&res, tree, - SCREEN_ORIENTATION_ATTR, &error); - if (error == "") { - if (orien == 0 || orien == 6 || orien == 8) { - // Requests landscape, sensorLandscape, or reverseLandscape. - reqScreenLandscapeFeature = true; - } else if (orien == 1 || orien == 7 || orien == 9) { - // Requests portrait, sensorPortrait, or reversePortrait. - reqScreenPortraitFeature = true; + if (error != "") { + fprintf(stderr, + "ERROR getting 'android:name' attribute for receiver:" + " %s\n", error.string()); + goto bail; + } + + String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); + if (error == "") { + if (permission == "android.permission.BIND_DEVICE_ADMIN") { + hasBindDeviceAdminPermission = true; + } + } else { + fprintf(stderr, "ERROR getting 'android:permission' attribute for" + " receiver '%s': %s\n", receiverName.string(), error.string()); + } + } else if (tag == "service") { + withinService = true; + serviceName = getAttribute(tree, NAME_ATTR, &error); + + if (error != "") { + fprintf(stderr, "ERROR getting 'android:name' attribute for" + " service: %s\n", error.string()); + goto bail; + } + + String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); + if (error == "") { + if (permission == "android.permission.BIND_INPUT_METHOD") { + hasBindInputMethodPermission = true; + } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") { + hasBindAccessibilityServicePermission = true; + } else if (permission == "android.permission.BIND_PRINT_SERVICE") { + hasBindPrintServicePermission = true; + } else if (permission == "android.permission.BIND_NFC_SERVICE") { + hasBindNfcServicePermission = true; + } + } else { + fprintf(stderr, "ERROR getting 'android:permission' attribute for" + " service '%s': %s\n", serviceName.string(), error.string()); } } - } else if (tag == "uses-library") { - String8 libraryName = getAttribute(tree, NAME_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string()); + } else if (withinSupportsInput && tag == "input-type") { + String8 name = getAttribute(tree, NAME_ATTR, &error); + if (name != "" && error == "") { + supportedInput.add(name); + } else { + fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", + error.string()); goto bail; } - int req = getIntegerAttribute(tree, - REQUIRED_ATTR, NULL, 1); - printf("uses-library%s:'%s'\n", - req ? "" : "-not-required", libraryName.string()); - } else if (tag == "receiver") { - withinReceiver = true; - receiverName = getAttribute(tree, NAME_ATTR, &error); - + } + } else if (depth == 4) { + if (tag == "intent-filter") { + hasIntentFilter = true; + withinIntentFilter = true; + actMainActivity = false; + actWidgetReceivers = false; + actImeService = false; + actWallpaperService = false; + actAccessibilityService = false; + actPrintService = false; + actDeviceAdminEnabled = false; + actHostApduService = false; + actOffHostApduService = false; + } else if (withinService && tag == "meta-data") { + String8 name = getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:name' attribute for" + " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); goto bail; } - } else if (tag == "service") { - withinService = true; - serviceName = getAttribute(tree, NAME_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string()); - goto bail; + if (name == "android.nfc.cardemulation.host_apdu_service" || + name == "android.nfc.cardemulation.off_host_apdu_service") { + bool offHost = true; + if (name == "android.nfc.cardemulation.host_apdu_service") { + offHost = false; + } + + String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:resource' attribute for" + " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); + goto bail; + } + + Vector<String8> categories = getNfcAidCategories(assets, xmlPath, + offHost, &error); + if (error != "") { + fprintf(stderr, "ERROR getting AID category for service '%s'\n", + serviceName.string()); + goto bail; + } + + const size_t catLen = categories.size(); + for (size_t i = 0; i < catLen; i++) { + bool paymentCategory = (categories[i] == "payment"); + if (offHost) { + hasMetaOffHostPaymentCategory |= paymentCategory; + } else { + hasMetaHostPaymentCategory |= paymentCategory; + } + } } } - } else if ((depth == 4) && (tag == "intent-filter")) { - hasIntentFilter = true; - withinIntentFilter = true; - actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false; } else if ((depth == 5) && withinIntentFilter){ String8 action; if (tag == "action") { @@ -1169,12 +1380,22 @@ int doDump(Bundle* bundle) } else if (withinReceiver) { if (action == "android.appwidget.action.APPWIDGET_UPDATE") { actWidgetReceivers = true; + } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") { + actDeviceAdminEnabled = true; } } else if (withinService) { if (action == "android.view.InputMethod") { actImeService = true; } else if (action == "android.service.wallpaper.WallpaperService") { actWallpaperService = true; + } else if (action == "android.accessibilityservice.AccessibilityService") { + actAccessibilityService = true; + } else if (action == "android.printservice.PrintService") { + actPrintService = true; + } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { + actHostApduService = true; + } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { + actOffHostApduService = true; } } if (action == "android.intent.action.SEARCH") { @@ -1374,12 +1595,24 @@ int doDump(Bundle* bundle) if (hasWidgetReceivers) { printf("app-widget\n"); } + if (hasDeviceAdminReceiver) { + printf("device-admin\n"); + } if (hasImeService) { printf("ime\n"); } if (hasWallpaperService) { printf("wallpaper\n"); } + if (hasAccessibilityService) { + printf("accessibility\n"); + } + if (hasPrintService) { + printf("print\n"); + } + if (hasPaymentService) { + printf("payment\n"); + } if (hasOtherActivities) { printf("other-activities\n"); } diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index 3930117..872d95c 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -8,10 +8,13 @@ #include "ResourceTable.h" #include "ResourceFilter.h" +#include <androidfw/misc.h> + #include <utils/Log.h> #include <utils/threads.h> #include <utils/List.h> #include <utils/Errors.h> +#include <utils/misc.h> #include <sys/types.h> #include <dirent.h> diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 490d8e6..386888b 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -14,7 +14,7 @@ #include "FileFinder.h" #include "CacheUpdater.h" -#include <utils/WorkQueue.h> +#include "WorkQueue.h" #if HAVE_PRINTF_ZD # define ZD "%zd" @@ -172,6 +172,7 @@ private: bool isValidResourceType(const String8& type) { return type == "anim" || type == "animator" || type == "interpolator" + || type == "transition" || type == "drawable" || type == "layout" || type == "values" || type == "xml" || type == "raw" || type == "color" || type == "menu" || type == "mipmap"; @@ -932,6 +933,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) sp<ResourceTypeSet> anims; sp<ResourceTypeSet> animators; sp<ResourceTypeSet> interpolators; + sp<ResourceTypeSet> transitions; sp<ResourceTypeSet> xmls; sp<ResourceTypeSet> raws; sp<ResourceTypeSet> colors; @@ -943,6 +945,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) ASSIGN_IT(anim); ASSIGN_IT(animator); ASSIGN_IT(interpolator); + ASSIGN_IT(transition); ASSIGN_IT(xml); ASSIGN_IT(raw); ASSIGN_IT(color); @@ -965,6 +968,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) !applyFileOverlay(bundle, assets, &anims, "anim") || !applyFileOverlay(bundle, assets, &animators, "animator") || !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || + !applyFileOverlay(bundle, assets, &transitions, "transition") || !applyFileOverlay(bundle, assets, &xmls, "xml") || !applyFileOverlay(bundle, assets, &raws, "raw") || !applyFileOverlay(bundle, assets, &colors, "color") || @@ -1024,6 +1028,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } } + if (transitions != NULL) { + err = makeFileResources(bundle, assets, &table, transitions, "transition"); + if (err != NO_ERROR) { + hasErrors = true; + } + } + if (interpolators != NULL) { err = makeFileResources(bundle, assets, &table, interpolators, "interpolator"); if (err != NO_ERROR) { @@ -1168,6 +1179,21 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) err = NO_ERROR; } + if (transitions != NULL) { + ResourceDirIterator it(transitions, String8("transition")); + while ((err=it.next()) == NO_ERROR) { + err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (err < NO_ERROR) { + hasErrors = true; + } + err = NO_ERROR; + } + if (xmls != NULL) { ResourceDirIterator it(xmls, String8("xml")); while ((err=it.next()) == NO_ERROR) { diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 52ebaf0..f2e5254 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -636,6 +636,30 @@ bool isInProductList(const String16& needle, const String16& haystack) { return false; } +/* + * A simple container that holds a resource type and name. It is ordered first by type then + * by name. + */ +struct type_ident_pair_t { + String16 type; + String16 ident; + + type_ident_pair_t() { }; + type_ident_pair_t(const String16& t, const String16& i) : type(t), ident(i) { } + type_ident_pair_t(const type_ident_pair_t& o) : type(o.type), ident(o.ident) { } + inline bool operator < (const type_ident_pair_t& o) const { + int cmp = compare_type(type, o.type); + if (cmp < 0) { + return true; + } else if (cmp > 0) { + return false; + } else { + return strictly_order_type(ident, o.ident); + } + } +}; + + status_t parseAndAddEntry(Bundle* bundle, const sp<AaptFile>& in, ResXMLTree* block, @@ -650,6 +674,7 @@ status_t parseAndAddEntry(Bundle* bundle, const String16& product, bool pseudolocalize, const bool overwrite, + KeyedVector<type_ident_pair_t, bool>* skippedResourceNames, ResourceTable* outTable) { status_t err; @@ -684,6 +709,13 @@ status_t parseAndAddEntry(Bundle* bundle, if (bundleProduct[0] == '\0') { if (strcmp16(String16("default").string(), product.string()) != 0) { + /* + * This string has a product other than 'default'. Do not add it, + * but record it so that if we do not see the same string with + * product 'default' or no product, then report an error. + */ + skippedResourceNames->replaceValueFor( + type_ident_pair_t(curType, ident), true); return NO_ERROR; } } else { @@ -797,6 +829,11 @@ status_t compileResourceFile(Bundle* bundle, DefaultKeyedVector<String16, uint32_t> nextPublicId(0); + // Stores the resource names that were skipped. Typically this happens when + // AAPT is invoked without a product specified and a resource has no + // 'default' product attribute. + KeyedVector<type_ident_pair_t, bool> skippedResourceNames; + ResXMLTree::event_code_t code; do { code = block.next(); @@ -1544,7 +1581,7 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident, *curTag, curIsStyled, curFormat, curIsFormatted, - product, false, overwrite, outTable); + product, false, overwrite, &skippedResourceNames, outTable); if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR? hasErrors = localHasErrors = true; @@ -1557,7 +1594,7 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, ident, *curTag, curIsStyled, curFormat, curIsFormatted, product, - true, overwrite, outTable); + true, overwrite, &skippedResourceNames, outTable); if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1596,6 +1633,30 @@ status_t compileResourceFile(Bundle* bundle, } } + // For every resource defined, there must be exist one variant with a product attribute + // set to 'default' (or no product attribute at all). + // We check to see that for every resource that was ignored because of a mismatched + // product attribute, some product variant of that resource was processed. + for (size_t i = 0; i < skippedResourceNames.size(); i++) { + if (skippedResourceNames[i]) { + const type_ident_pair_t& p = skippedResourceNames.keyAt(i); + if (!outTable->hasBagOrEntry(myPackage, p.type, p.ident)) { + const char* bundleProduct = + (bundle->getProduct() == NULL) ? "" : bundle->getProduct(); + fprintf(stderr, "In resource file %s: %s\n", + in->getPrintableSource().string(), + curParams.toString().string()); + + fprintf(stderr, "\t%s '%s' does not match product %s.\n" + "\tYou may have forgotten to include a 'default' product variant" + " of the resource.\n", + String8(p.type).string(), String8(p.ident).string(), + bundleProduct[0] == 0 ? "default" : bundleProduct); + return UNKNOWN_ERROR; + } + } + } + return hasErrors ? UNKNOWN_ERROR : NO_ERROR; } @@ -2483,8 +2544,8 @@ status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) { String16 comment(c->getComment()); typeSymbols->appendComment(String8(c->getName()), comment, c->getPos()); - //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(), - // String8(comment).string()); + //printf("Type symbol [%08x] %s comment: %s\n", rid, + // String8(c->getName()).string(), String8(comment).string()); comment = c->getTypeComment(); typeSymbols->appendTypeComment(String8(c->getName()), comment); } else { diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp index 839eda5..158b391 100644 --- a/tools/aapt/StringPool.cpp +++ b/tools/aapt/StringPool.cpp @@ -9,7 +9,7 @@ #include <utils/ByteOrder.h> #include <utils/SortedVector.h> -#include <cutils/qsort_r_compat.h> +#include "qsort_r_compat.h" #if HAVE_PRINTF_ZD # define ZD "%zd" diff --git a/tools/aapt/StringPool.h b/tools/aapt/StringPool.h index 16050b2..1b3abfd 100644 --- a/tools/aapt/StringPool.h +++ b/tools/aapt/StringPool.h @@ -12,7 +12,6 @@ #include <androidfw/ResourceTypes.h> #include <utils/String16.h> -#include <utils/TextOutput.h> #include <utils/TypeHelpers.h> #include <sys/types.h> diff --git a/tools/aapt/WorkQueue.cpp b/tools/aapt/WorkQueue.cpp new file mode 100644 index 0000000..24a962f --- /dev/null +++ b/tools/aapt/WorkQueue.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define LOG_NDEBUG 0 +#define LOG_TAG "WorkQueue" + +#include <utils/Log.h> +#include "WorkQueue.h" + +namespace android { + +// --- WorkQueue --- + +WorkQueue::WorkQueue(size_t maxThreads, bool canCallJava) : + mMaxThreads(maxThreads), mCanCallJava(canCallJava), + mCanceled(false), mFinished(false), mIdleThreads(0) { +} + +WorkQueue::~WorkQueue() { + if (!cancel()) { + finish(); + } +} + +status_t WorkQueue::schedule(WorkUnit* workUnit, size_t backlog) { + AutoMutex _l(mLock); + + if (mFinished || mCanceled) { + return INVALID_OPERATION; + } + + if (mWorkThreads.size() < mMaxThreads + && mIdleThreads < mWorkUnits.size() + 1) { + sp<WorkThread> workThread = new WorkThread(this, mCanCallJava); + status_t status = workThread->run("WorkQueue::WorkThread"); + if (status) { + return status; + } + mWorkThreads.add(workThread); + mIdleThreads += 1; + } else if (backlog) { + while (mWorkUnits.size() >= mMaxThreads * backlog) { + mWorkDequeuedCondition.wait(mLock); + if (mFinished || mCanceled) { + return INVALID_OPERATION; + } + } + } + + mWorkUnits.add(workUnit); + mWorkChangedCondition.broadcast(); + return OK; +} + +status_t WorkQueue::cancel() { + AutoMutex _l(mLock); + + return cancelLocked(); +} + +status_t WorkQueue::cancelLocked() { + if (mFinished) { + return INVALID_OPERATION; + } + + if (!mCanceled) { + mCanceled = true; + + size_t count = mWorkUnits.size(); + for (size_t i = 0; i < count; i++) { + delete mWorkUnits.itemAt(i); + } + mWorkUnits.clear(); + mWorkChangedCondition.broadcast(); + mWorkDequeuedCondition.broadcast(); + } + return OK; +} + +status_t WorkQueue::finish() { + { // acquire lock + AutoMutex _l(mLock); + + if (mFinished) { + return INVALID_OPERATION; + } + + mFinished = true; + mWorkChangedCondition.broadcast(); + } // release lock + + // It is not possible for the list of work threads to change once the mFinished + // flag has been set, so we can access mWorkThreads outside of the lock here. + size_t count = mWorkThreads.size(); + for (size_t i = 0; i < count; i++) { + mWorkThreads.itemAt(i)->join(); + } + mWorkThreads.clear(); + return OK; +} + +bool WorkQueue::threadLoop() { + WorkUnit* workUnit; + { // acquire lock + AutoMutex _l(mLock); + + for (;;) { + if (mCanceled) { + return false; + } + + if (!mWorkUnits.isEmpty()) { + workUnit = mWorkUnits.itemAt(0); + mWorkUnits.removeAt(0); + mIdleThreads -= 1; + mWorkDequeuedCondition.broadcast(); + break; + } + + if (mFinished) { + return false; + } + + mWorkChangedCondition.wait(mLock); + } + } // release lock + + bool shouldContinue = workUnit->run(); + delete workUnit; + + { // acquire lock + AutoMutex _l(mLock); + + mIdleThreads += 1; + + if (!shouldContinue) { + cancelLocked(); + return false; + } + } // release lock + + return true; +} + +// --- WorkQueue::WorkThread --- + +WorkQueue::WorkThread::WorkThread(WorkQueue* workQueue, bool canCallJava) : + Thread(canCallJava), mWorkQueue(workQueue) { +} + +WorkQueue::WorkThread::~WorkThread() { +} + +bool WorkQueue::WorkThread::threadLoop() { + return mWorkQueue->threadLoop(); +} + +}; // namespace android diff --git a/tools/aapt/WorkQueue.h b/tools/aapt/WorkQueue.h new file mode 100644 index 0000000..d38f05d --- /dev/null +++ b/tools/aapt/WorkQueue.h @@ -0,0 +1,119 @@ +/*] + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT_WORK_QUEUE_H +#define AAPT_WORK_QUEUE_H + +#include <utils/Errors.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +namespace android { + +/* + * A threaded work queue. + * + * This class is designed to make it easy to run a bunch of isolated work + * units in parallel, using up to the specified number of threads. + * To use it, write a loop to post work units to the work queue, then synchronize + * on the queue at the end. + */ +class WorkQueue { +public: + class WorkUnit { + public: + WorkUnit() { } + virtual ~WorkUnit() { } + + /* + * Runs the work unit. + * If the result is 'true' then the work queue continues scheduling work as usual. + * If the result is 'false' then the work queue is canceled. + */ + virtual bool run() = 0; + }; + + /* Creates a work queue with the specified maximum number of work threads. */ + WorkQueue(size_t maxThreads, bool canCallJava = true); + + /* Destroys the work queue. + * Cancels pending work and waits for all remaining threads to complete. + */ + ~WorkQueue(); + + /* Posts a work unit to run later. + * If the work queue has been canceled or is already finished, returns INVALID_OPERATION + * and does not take ownership of the work unit (caller must destroy it itself). + * Otherwise, returns OK and takes ownership of the work unit (the work queue will + * destroy it automatically). + * + * For flow control, this method blocks when the size of the pending work queue is more + * 'backlog' times the number of threads. This condition reduces the rate of entry into + * the pending work queue and prevents it from growing much more rapidly than the + * work threads can actually handle. + * + * If 'backlog' is 0, then no throttle is applied. + */ + status_t schedule(WorkUnit* workUnit, size_t backlog = 2); + + /* Cancels all pending work. + * If the work queue is already finished, returns INVALID_OPERATION. + * If the work queue is already canceled, returns OK and does nothing else. + * Otherwise, returns OK, discards all pending work units and prevents additional + * work units from being scheduled. + * + * Call finish() after cancel() to wait for all remaining work to complete. + */ + status_t cancel(); + + /* Waits for all work to complete. + * If the work queue is already finished, returns INVALID_OPERATION. + * Otherwise, waits for all work to complete and returns OK. + */ + status_t finish(); + +private: + class WorkThread : public Thread { + public: + WorkThread(WorkQueue* workQueue, bool canCallJava); + virtual ~WorkThread(); + + private: + virtual bool threadLoop(); + + WorkQueue* const mWorkQueue; + }; + + status_t cancelLocked(); + bool threadLoop(); // called from each work thread + + const size_t mMaxThreads; + const bool mCanCallJava; + + Mutex mLock; + Condition mWorkChangedCondition; + Condition mWorkDequeuedCondition; + + bool mCanceled; + bool mFinished; + size_t mIdleThreads; + Vector<sp<WorkThread> > mWorkThreads; + Vector<WorkUnit*> mWorkUnits; +}; + +}; // namespace android + +#endif // AAPT_WORK_QUEUE_H diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index dcbe7db..a663ad5 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -6,8 +6,8 @@ #include "XMLNode.h" #include "ResourceTable.h" +#include "pseudolocalize.h" -#include <host/pseudolocalize.h> #include <utils/ByteOrder.h> #include <errno.h> #include <string.h> diff --git a/tools/aapt/ZipFile.cpp b/tools/aapt/ZipFile.cpp index 3994c31..8057068 100644 --- a/tools/aapt/ZipFile.cpp +++ b/tools/aapt/ZipFile.cpp @@ -20,8 +20,8 @@ #define LOG_TAG "zip" +#include <androidfw/ZipUtils.h> #include <utils/Log.h> -#include <utils/ZipUtils.h> #include "ZipFile.h" diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp new file mode 100644 index 0000000..9e50c5a --- /dev/null +++ b/tools/aapt/pseudolocalize.cpp @@ -0,0 +1,119 @@ +#include "pseudolocalize.h" + +using namespace std; + +static const char* +pseudolocalize_char(char c) +{ + switch (c) { + case 'a': return "\xc4\x83"; + case 'b': return "\xcf\x84"; + case 'c': return "\xc4\x8b"; + case 'd': return "\xc4\x8f"; + case 'e': return "\xc4\x99"; + case 'f': return "\xc6\x92"; + case 'g': return "\xc4\x9d"; + case 'h': return "\xd1\x9b"; + case 'i': return "\xcf\x8a"; + case 'j': return "\xc4\xb5"; + case 'k': return "\xc4\xb8"; + case 'l': return "\xc4\xba"; + case 'm': return "\xe1\xb8\xbf"; + case 'n': return "\xd0\xb8"; + case 'o': return "\xcf\x8c"; + case 'p': return "\xcf\x81"; + case 'q': return "\x51"; + case 'r': return "\xd2\x91"; + case 's': return "\xc5\xa1"; + case 't': return "\xd1\x82"; + case 'u': return "\xce\xb0"; + case 'v': return "\x56"; + case 'w': return "\xe1\xba\x85"; + case 'x': return "\xd1\x85"; + case 'y': return "\xe1\xbb\xb3"; + case 'z': return "\xc5\xba"; + case 'A': return "\xc3\x85"; + case 'B': return "\xce\xb2"; + case 'C': return "\xc4\x88"; + case 'D': return "\xc4\x90"; + case 'E': return "\xd0\x84"; + case 'F': return "\xce\x93"; + case 'G': return "\xc4\x9e"; + case 'H': return "\xc4\xa6"; + case 'I': return "\xd0\x87"; + case 'J': return "\xc4\xb5"; + case 'K': return "\xc4\xb6"; + case 'L': return "\xc5\x81"; + case 'M': return "\xe1\xb8\xbe"; + case 'N': return "\xc5\x83"; + case 'O': return "\xce\x98"; + case 'P': return "\xcf\x81"; + case 'Q': return "\x71"; + case 'R': return "\xd0\xaf"; + case 'S': return "\xc8\x98"; + case 'T': return "\xc5\xa6"; + case 'U': return "\xc5\xa8"; + case 'V': return "\xce\xbd"; + case 'W': return "\xe1\xba\x84"; + case 'X': return "\xc3\x97"; + case 'Y': return "\xc2\xa5"; + case 'Z': return "\xc5\xbd"; + default: return NULL; + } +} + +/** + * Converts characters so they look like they've been localized. + * + * Note: This leaves escape sequences untouched so they can later be + * processed by ResTable::collectString in the normal way. + */ +string +pseudolocalize_string(const string& source) +{ + const char* s = source.c_str(); + string result; + const size_t I = source.length(); + for (size_t i=0; i<I; i++) { + char c = s[i]; + if (c == '\\') { + if (i<I-1) { + result += '\\'; + i++; + c = s[i]; + switch (c) { + case 'u': + // this one takes up 5 chars + result += string(s+i, 5); + i += 4; + break; + case 't': + case 'n': + case '#': + case '@': + case '?': + case '"': + case '\'': + case '\\': + default: + result += c; + break; + } + } else { + result += c; + } + } else { + const char* p = pseudolocalize_char(c); + if (p != NULL) { + result += p; + } else { + result += c; + } + } + } + + //printf("result=\'%s\'\n", result.c_str()); + return result; +} + + diff --git a/tools/aapt/pseudolocalize.h b/tools/aapt/pseudolocalize.h new file mode 100644 index 0000000..94cb034 --- /dev/null +++ b/tools/aapt/pseudolocalize.h @@ -0,0 +1,9 @@ +#ifndef HOST_PSEUDOLOCALIZE_H +#define HOST_PSEUDOLOCALIZE_H + +#include <string> + +std::string pseudolocalize_string(const std::string& source); + +#endif // HOST_PSEUDOLOCALIZE_H + diff --git a/tools/aapt/qsort_r_compat.c b/tools/aapt/qsort_r_compat.c new file mode 100644 index 0000000..2a8dbe8 --- /dev/null +++ b/tools/aapt/qsort_r_compat.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include "qsort_r_compat.h" + +/* + * Note: This code is only used on the host, and is primarily here for + * Mac OS compatibility. Apparently, glibc and Apple's libc disagree on + * the parameter order for qsort_r. + */ + +#if HAVE_BSD_QSORT_R + +/* + * BSD qsort_r parameter order is as we have defined here. + */ + +void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, + int (*compar)(void*, const void* , const void*)) { + qsort_r(base, nel, width, thunk, compar); +} + +#elif HAVE_GNU_QSORT_R + +/* + * GNU qsort_r parameter order places the thunk parameter last. + */ + +struct compar_data { + void* thunk; + int (*compar)(void*, const void* , const void*); +}; + +static int compar_wrapper(const void* a, const void* b, void* data) { + struct compar_data* compar_data = (struct compar_data*)data; + return compar_data->compar(compar_data->thunk, a, b); +} + +void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, + int (*compar)(void*, const void* , const void*)) { + struct compar_data compar_data; + compar_data.thunk = thunk; + compar_data.compar = compar; + qsort_r(base, nel, width, compar_wrapper, &compar_data); +} + +#else + +/* + * Emulate qsort_r using thread local storage to access the thunk data. + */ + +#include <cutils/threads.h> + +static thread_store_t compar_data_key = THREAD_STORE_INITIALIZER; + +struct compar_data { + void* thunk; + int (*compar)(void*, const void* , const void*); +}; + +static int compar_wrapper(const void* a, const void* b) { + struct compar_data* compar_data = (struct compar_data*)thread_store_get(&compar_data_key); + return compar_data->compar(compar_data->thunk, a, b); +} + +void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, + int (*compar)(void*, const void* , const void*)) { + struct compar_data compar_data; + compar_data.thunk = thunk; + compar_data.compar = compar; + thread_store_set(&compar_data_key, &compar_data, NULL); + qsort(base, nel, width, compar_wrapper); +} + +#endif diff --git a/tools/aapt/qsort_r_compat.h b/tools/aapt/qsort_r_compat.h new file mode 100644 index 0000000..e14f999 --- /dev/null +++ b/tools/aapt/qsort_r_compat.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Provides a portable version of qsort_r, called qsort_r_compat, which is a + * reentrant variant of qsort that passes a user data pointer to its comparator. + * This implementation follows the BSD parameter convention. + */ + +#ifndef ___QSORT_R_COMPAT_H +#define ___QSORT_R_COMPAT_H + +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, + int (*compar)(void*, const void* , const void* )); + +#ifdef __cplusplus +} +#endif + +#endif // ___QSORT_R_COMPAT_H diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp index b5c3da9..a84d743 100644 --- a/tools/aidl/aidl.cpp +++ b/tools/aidl/aidl.cpp @@ -673,6 +673,16 @@ generate_dep_file(const Options& options, const document_item_type* items) fprintf(to, "\n"); + // Output "<imported_file>: " so make won't fail if the imported file has + // been deleted, moved or renamed in incremental build. + import = g_imports; + while (import) { + if (import->filename) { + fprintf(to, "%s :\n", import->filename); + } + import = import->next; + } + fclose(to); } diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk index b27ce0e..4e73568 100644 --- a/tools/layoutlib/Android.mk +++ b/tools/layoutlib/Android.mk @@ -25,8 +25,8 @@ include $(CLEAR_VARS) # We need to process the framework classes.jar file, but we can't # depend directly on it (private vars won't be inherited correctly). # So, we depend on framework's BUILT file. -built_framework_dep := $(call java-lib-deps,framework) -built_framework_classes := $(call java-lib-files,framework) +built_framework_dep := $(call java-lib-deps,framework-base) +built_framework_classes := $(call java-lib-files,framework-base) built_core_dep := $(call java-lib-deps,core) built_core_classes := $(call java-lib-files,core) diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath index 3c124d9..2e4274d 100644 --- a/tools/layoutlib/bridge/.classpath +++ b/tools/layoutlib/bridge/.classpath @@ -7,5 +7,6 @@ <classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/tools-common/tools-common-prebuilt.jar"/> + <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/icu4j/icu4j.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk index 687a91f..e3d48fc 100644 --- a/tools/layoutlib/bridge/Android.mk +++ b/tools/layoutlib/bridge/Android.mk @@ -22,6 +22,7 @@ LOCAL_JAVA_RESOURCE_DIRS := resources LOCAL_JAVA_LIBRARIES := \ kxml2-2.3.0 \ + icu4j \ layoutlib_api-prebuilt \ tools-common-prebuilt diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png Binary files differnew file mode 100644 index 0000000..782ebfe --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png Binary files differnew file mode 100644 index 0000000..677b471 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png Binary files differnew file mode 100644 index 0000000..a1b8062 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png Binary files differnew file mode 100644 index 0000000..fcdbefe --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png Binary files differnew file mode 100644 index 0000000..633d864 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png Binary files differnew file mode 100644 index 0000000..4665e2a --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java new file mode 100644 index 0000000..62d0a0d --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.util.LinkedList; +import java.util.List; + +import com.ibm.icu.lang.UScript; +import com.ibm.icu.lang.UScriptRun; + +import android.graphics.Paint_Delegate.FontInfo; + +/** + * Render the text by breaking it into various scripts and using the right font for each script. + * Can be used to measure the text without actually drawing it. + */ +@SuppressWarnings("deprecation") +public class BidiRenderer { + + /* package */ static class ScriptRun { + int start; + int limit; + boolean isRtl; + int scriptCode; + FontInfo font; + + public ScriptRun(int start, int limit, boolean isRtl) { + this.start = start; + this.limit = limit; + this.isRtl = isRtl; + this.scriptCode = UScript.INVALID_CODE; + } + } + + /* package */ Graphics2D graphics; + /* package */ Paint_Delegate paint; + /* package */ char[] text; + + /** + * @param graphics May be null. + * @param paint The Paint to use to get the fonts. Should not be null. + * @param text Unidirectional text. Should not be null. + */ + /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) { + assert (paint != null); + this.graphics = graphics; + this.paint = paint; + this.text = text; + } + + /** + * Render unidirectional text. + * + * This method can also be used to measure the width of the text without actually drawing it. + * + * @param start index of the first character + * @param limit index of the first character that should not be rendered. + * @param isRtl is the text right-to-left + * @param advances If not null, then advances for each character to be rendered are returned + * here. + * @param advancesIndex index into advances from where the advances need to be filled. + * @param draw If true and {@link graphics} is not null, draw the rendered text on the graphics + * at the given co-ordinates + * @param x The x-coordinate of the left edge of where the text should be drawn on the given + * graphics. + * @param y The y-coordinate at which to draw the text on the given graphics. + * @return The x-coordinate of the right edge of the drawn text. In other words, + * x + the width of the text. + */ + /* package */ float renderText(int start, int limit, boolean isRtl, float advances[], + int advancesIndex, boolean draw, float x, float y) { + // We break the text into scripts and then select font based on it and then render each of + // the script runs. + for (ScriptRun run : getScriptRuns(text, start, limit, isRtl, paint.getFonts())) { + int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT; + flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT; + x = renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw, + x, y); + advancesIndex += run.limit - run.start; + } + return x; + } + + /** + * Render a script run. Use the preferred font to render as much as possible. This also + * implements a fallback mechanism to render characters that cannot be drawn using the + * preferred font. + * + * @return x + width of the text drawn. + */ + private float renderScript(int start, int limit, FontInfo preferredFont, int flag, + float advances[], int advancesIndex, boolean draw, float x, float y) { + List<FontInfo> fonts = paint.getFonts(); + if (fonts == null || preferredFont == null) { + return x; + } + + while (start < limit) { + boolean foundFont = false; + int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(text, start, limit); + if (canDisplayUpTo == -1) { + return render(start, limit, preferredFont, flag, advances, advancesIndex, draw, + x, y); + } else if (canDisplayUpTo > start) { // can draw something + x = render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, + draw, x, y); + advancesIndex += canDisplayUpTo - start; + start = canDisplayUpTo; + } + + int charCount = Character.isHighSurrogate(text[start]) ? 2 : 1; + for (FontInfo font : fonts) { + canDisplayUpTo = font.mFont.canDisplayUpTo(text, start, start + charCount); + if (canDisplayUpTo == -1) { + x = render(start, start+charCount, font, flag, advances, advancesIndex, draw, + x, y); + start += charCount; + advancesIndex += charCount; + foundFont = true; + break; + } + } + if (!foundFont) { + // No font can display this char. Use the preferred font. The char will most + // probably appear as a box or a blank space. We could, probably, use some + // heuristics and break the character into the base character and diacritics and + // then draw it, but it's probably not worth the effort. + x = render(start, start + charCount, preferredFont, flag, advances, advancesIndex, + draw, x, y); + start += charCount; + advancesIndex += charCount; + } + } + return x; + } + + /** + * Render the text with the given font. + */ + private float render(int start, int limit, FontInfo font, int flag, float advances[], + int advancesIndex, boolean draw, float x, float y) { + + float totalAdvance = 0; + // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with + // the anti-aliasing set. + FontRenderContext f = font.mMetrics.getFontRenderContext(); + FontRenderContext frc = new FontRenderContext(f.getTransform(), paint.isAntiAliased(), + f.usesFractionalMetrics()); + GlyphVector gv = font.mFont.layoutGlyphVector(frc, text, start, limit, flag); + int ng = gv.getNumGlyphs(); + int[] ci = gv.getGlyphCharIndices(0, ng, null); + for (int i = 0; i < ng; i++) { + float adv = gv.getGlyphMetrics(i).getAdvanceX(); + if (advances != null) { + int adv_idx = advancesIndex + ci[i]; + advances[adv_idx] += adv; + } + totalAdvance += adv; + } + if (draw && graphics != null) { + graphics.drawGlyphVector(gv, x, y); + } + return x + totalAdvance; + } + + // --- Static helper methods --- + + /* package */ static List<ScriptRun> getScriptRuns(char[] text, int start, int limit, + boolean isRtl, List<FontInfo> fonts) { + LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>(); + + int count = limit - start; + UScriptRun uScriptRun = new UScriptRun(text, start, count); + while (uScriptRun.next()) { + int scriptStart = uScriptRun.getScriptStart(); + int scriptLimit = uScriptRun.getScriptLimit(); + ScriptRun run = new ScriptRun(scriptStart, scriptLimit, isRtl); + run.scriptCode = uScriptRun.getScriptCode(); + setScriptFont(text, run, fonts); + scriptRuns.add(run); + } + + return scriptRuns; + } + + // TODO: Replace this method with one which returns the font based on the scriptCode. + private static void setScriptFont(char[] text, ScriptRun run, + List<FontInfo> fonts) { + for (FontInfo fontInfo : fonts) { + if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) { + run.font = fontInfo; + return; + } + } + run.font = fonts.get(0); + } +} diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java index 5256b58..04ce9d0 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java @@ -24,10 +24,13 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.content.res.BridgeResources.NinePatchInputStream; import android.graphics.BitmapFactory.Options; +import android.graphics.Bitmap_Delegate.BitmapCreateFlags; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; +import java.util.EnumSet; +import java.util.Set; /** * Delegate implementing the native methods of android.graphics.BitmapFactory @@ -98,8 +101,12 @@ import java.io.InputStream; //TODO support rescaling Density density = Density.MEDIUM; + Set<BitmapCreateFlags> bitmapCreateFlags = EnumSet.of(BitmapCreateFlags.MUTABLE); if (opts != null) { density = Density.getEnum(opts.inDensity); + if (opts.inPremultiplied) { + bitmapCreateFlags.add(BitmapCreateFlags.PREMULTIPLIED); + } } try { @@ -112,7 +119,7 @@ import java.io.InputStream; npis, true /*is9Patch*/, false /*convert*/); // get the bitmap and chunk objects. - bm = Bitmap_Delegate.createBitmap(ninePatch.getImage(), true /*isMutable*/, + bm = Bitmap_Delegate.createBitmap(ninePatch.getImage(), bitmapCreateFlags, density); NinePatchChunk chunk = ninePatch.getChunk(); @@ -127,7 +134,7 @@ import java.io.InputStream; padding.bottom = paddingarray[3]; } else { // load the bitmap directly. - bm = Bitmap_Delegate.createBitmap(is, true, density); + bm = Bitmap_Delegate.createBitmap(is, bitmapCreateFlags, density); } } catch (IOException e) { Bridge.getLog().error(null,"Failed to load image" , e, null); diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index 96616aa..ec284ac 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -33,6 +33,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.Buffer; import java.util.Arrays; +import java.util.EnumSet; +import java.util.Set; import javax.imageio.ImageIO; @@ -51,6 +53,10 @@ import javax.imageio.ImageIO; */ public final class Bitmap_Delegate { + public enum BitmapCreateFlags { + PREMULTIPLIED, MUTABLE + } + // ---- delegate manager ---- private static final DelegateManager<Bitmap_Delegate> sManager = new DelegateManager<Bitmap_Delegate>(Bitmap_Delegate.class); @@ -93,10 +99,25 @@ public final class Bitmap_Delegate { */ public static Bitmap createBitmap(File input, boolean isMutable, Density density) throws IOException { + return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density); + } + + /** + * Creates and returns a {@link Bitmap} initialized with the given file content. + * + * @param input the file from which to read the bitmap content + * @param density the density associated with the bitmap + * + * @see Bitmap#isPremultiplied() + * @see Bitmap#isMutable() + * @see Bitmap#getDensity() + */ + public static Bitmap createBitmap(File input, Set<BitmapCreateFlags> createFlags, + Density density) throws IOException { // create a delegate with the content of the file. Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888); - return createBitmap(delegate, isMutable, density.getDpiValue()); + return createBitmap(delegate, createFlags, density.getDpiValue()); } /** @@ -111,10 +132,26 @@ public final class Bitmap_Delegate { */ public static Bitmap createBitmap(InputStream input, boolean isMutable, Density density) throws IOException { + return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density); + } + + /** + * Creates and returns a {@link Bitmap} initialized with the given stream content. + * + * @param input the stream from which to read the bitmap content + * @param createFlags + * @param density the density associated with the bitmap + * + * @see Bitmap#isPremultiplied() + * @see Bitmap#isMutable() + * @see Bitmap#getDensity() + */ + public static Bitmap createBitmap(InputStream input, Set<BitmapCreateFlags> createFlags, + Density density) throws IOException { // create a delegate with the content of the stream. Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888); - return createBitmap(delegate, isMutable, density.getDpiValue()); + return createBitmap(delegate, createFlags, density.getDpiValue()); } /** @@ -129,10 +166,26 @@ public final class Bitmap_Delegate { */ public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density) throws IOException { + return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density); + } + + /** + * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage} + * + * @param image the bitmap content + * @param createFlags + * @param density the density associated with the bitmap + * + * @see Bitmap#isPremultiplied() + * @see Bitmap#isMutable() + * @see Bitmap#getDensity() + */ + public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags, + Density density) throws IOException { // create a delegate with the given image. Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888); - return createBitmap(delegate, isMutable, density.getDpiValue()); + return createBitmap(delegate, createFlags, density.getDpiValue()); } /** @@ -203,7 +256,7 @@ public final class Bitmap_Delegate { @LayoutlibDelegate /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width, - int height, int nativeConfig, boolean mutable) { + int height, int nativeConfig, boolean isMutable) { int imageType = getBufferedImageType(nativeConfig); // create the image @@ -216,7 +269,8 @@ public final class Bitmap_Delegate { // create a delegate with the content of the stream. Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig)); - return createBitmap(delegate, mutable, Bitmap.getDefaultDensity()); + return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable), + Bitmap.getDefaultDensity()); } @LayoutlibDelegate @@ -244,7 +298,8 @@ public final class Bitmap_Delegate { // create a delegate with the content of the stream. Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig)); - return createBitmap(delegate, isMutable, Bitmap.getDefaultDensity()); + return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable), + Bitmap.getDefaultDensity()); } @LayoutlibDelegate @@ -464,7 +519,7 @@ public final class Bitmap_Delegate { Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8); // the density doesn't matter, it's set by the Java method. - return createBitmap(delegate, false /*isMutable*/, + return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE), Density.DEFAULT_DENSITY /*density*/); } @@ -546,13 +601,27 @@ public final class Bitmap_Delegate { mConfig = config; } - private static Bitmap createBitmap(Bitmap_Delegate delegate, boolean isMutable, int density) { + private static Bitmap createBitmap(Bitmap_Delegate delegate, + Set<BitmapCreateFlags> createFlags, int density) { // get its native_int int nativeInt = sManager.addNewDelegate(delegate); + int width = delegate.mImage.getWidth(); + int height = delegate.mImage.getHeight(); + boolean isMutable = createFlags.contains(BitmapCreateFlags.MUTABLE); + boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED); + // and create/return a new Bitmap with it - return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, - density); + return new Bitmap(nativeInt, null /* buffer */, width, height, density, isMutable, + isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */); + } + + private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) { + Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED); + if (isMutable) { + createFlags.add(BitmapCreateFlags.MUTABLE); + } + return createFlags; } /** diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 4171bb5..62b47bd 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -23,7 +23,6 @@ import com.android.layoutlib.bridge.impl.GcSnapshot; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Bitmap.Config; -import android.graphics.Paint_Delegate.FontInfo; import android.text.TextUtils; import java.awt.Color; @@ -35,7 +34,6 @@ import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.image.BufferedImage; -import java.util.List; /** @@ -978,7 +976,8 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, final char[] text, final int index, final int count, - final float startX, final float startY, int flags, int paint) { + final float startX, final float startY, final int flags, int paint) { + draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @Override @@ -988,10 +987,10 @@ public final class Canvas_Delegate { // Paint.TextAlign indicates how the text is positioned relative to X. // LEFT is the default and there's nothing to do. float x = startX; - float y = startY; + int limit = index + count; + boolean isRtl = flags == Canvas.DIRECTION_RTL; if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) { - // TODO: check the value of bidiFlags. - float m = paintDelegate.measureText(text, index, count, 0); + float m = paintDelegate.measureText(text, index, count, isRtl); if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) { x -= m / 2; } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) { @@ -999,87 +998,15 @@ public final class Canvas_Delegate { } } - List<FontInfo> fonts = paintDelegate.getFonts(); - - if (fonts.size() > 0) { - FontInfo mainFont = fonts.get(0); - int i = index; - int lastIndex = index + count; - while (i < lastIndex) { - // always start with the main font. - int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); - if (upTo == -1) { - // draw all the rest and exit. - graphics.setFont(mainFont.mFont); - graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y); - return; - } else if (upTo > 0) { - // draw what's possible - graphics.setFont(mainFont.mFont); - graphics.drawChars(text, i, upTo - i, (int)x, (int)y); - - // compute the width that was drawn to increase x - x += mainFont.mMetrics.charsWidth(text, i, upTo - i); - - // move index to the first non displayed char. - i = upTo; - - // don't call continue at this point. Since it is certain the main font - // cannot display the font a index upTo (now ==i), we move on to the - // fallback fonts directly. - } - - // no char supported, attempt to read the next char(s) with the - // fallback font. In this case we only test the first character - // and then go back to test with the main font. - // Special test for 2-char characters. - boolean foundFont = false; - for (int f = 1 ; f < fonts.size() ; f++) { - FontInfo fontInfo = fonts.get(f); - - // need to check that the font can display the character. We test - // differently if the char is a high surrogate. - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; - upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); - if (upTo == -1) { - // draw that char - graphics.setFont(fontInfo.mFont); - graphics.drawChars(text, i, charCount, (int)x, (int)y); - - // update x - x += fontInfo.mMetrics.charsWidth(text, i, charCount); - - // update the index in the text, and move on - i += charCount; - foundFont = true; - break; - - } - } - - // in case no font can display the char, display it with the main font. - // (it'll put a square probably) - if (foundFont == false) { - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; - - graphics.setFont(mainFont.mFont); - graphics.drawChars(text, i, charCount, (int)x, (int)y); - - // measure it to advance x - x += mainFont.mMetrics.charsWidth(text, i, charCount); - - // and move to the next chars. - i += charCount; - } - } - } + new BidiRenderer(graphics, paintDelegate, text).renderText( + index, limit, isRtl, null, 0, true, x, startY); } }); } @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, String text, - int start, int end, float x, float y, int flags, int paint) { + int start, int end, float x, float y, final int flags, int paint) { int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); @@ -1148,14 +1075,6 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawPicture(int nativeCanvas, - int nativePicture) { - // FIXME - Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Canvas.drawPicture is not supported.", null, null /*data*/); - } - - @LayoutlibDelegate /*package*/ static void finalizer(int nativeCanvas) { // get the delegate from the native int so that it can be disposed. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index c9c9800..41953ed 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -32,7 +32,6 @@ import java.awt.Stroke; import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -576,7 +575,7 @@ public class Paint_Delegate { return 0; } - return delegate.measureText(text, index, count, bidiFlags); + return delegate.measureText(text, index, count, isRtl(bidiFlags)); } @LayoutlibDelegate @@ -615,7 +614,7 @@ public class Paint_Delegate { } // measure from start to end - float res = delegate.measureText(text, start, end - start + 1, bidiFlags); + float res = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags)); if (measuredWidth != null) { measuredWidth[measureIndex] = res; @@ -980,51 +979,27 @@ public class Paint_Delegate { /*package*/ static float native_getTextRunAdvances(int native_object, char[] text, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex) { + + if (advances != null) + for (int i = advancesIndex; i< advancesIndex+count; i++) + advances[i]=0; // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); - if (delegate == null) { + if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { return 0.f; } + boolean isRtl = isRtl(flags); - if (delegate.mFonts.size() > 0) { - // FIXME: handle multi-char characters (see measureText) - float totalAdvance = 0; - for (int i = 0; i < count; i++) { - char c = text[i + index]; - boolean found = false; - for (FontInfo info : delegate.mFonts) { - if (info.mFont.canDisplay(c)) { - float adv = info.mMetrics.charWidth(c); - totalAdvance += adv; - if (advances != null) { - advances[i] = adv; - } - - found = true; - break; - } - } - - if (found == false) { - // no advance for this char. - if (advances != null) { - advances[i] = 0.f; - } - } - } - - return totalAdvance; - } - - return 0; - + int limit = index + count; + return new BidiRenderer(null, delegate, text).renderText( + index, limit, isRtl, advances, advancesIndex, false, 0, 0); } @LayoutlibDelegate /*package*/ static float native_getTextRunAdvances(int native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex) { - // FIXME: support contextStart, contextEnd and direction flag + // FIXME: support contextStart and contextEnd int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); @@ -1080,19 +1055,12 @@ public class Paint_Delegate { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(nativePaint); - if (delegate == null) { + if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { return; } - - // FIXME should test if the main font can display all those characters. - // See MeasureText - if (delegate.mFonts.size() > 0) { - FontInfo mainInfo = delegate.mFonts.get(0); - - Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, - delegate.mFontContext); - bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight()); - } + int w = (int) delegate.measureText(text, index, count, isRtl(bidiFlags)); + int h= delegate.getFonts().get(0).mMetrics.getHeight(); + bounds.set(0, 0, w, h); } @LayoutlibDelegate @@ -1176,6 +1144,7 @@ public class Paint_Delegate { info.mFont = info.mFont.deriveFont(new AffineTransform( mTextScaleX, mTextSkewX, 0, 1, 0, 0)); } + // The metrics here don't have anti-aliasing set. info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); infoList.add(info); @@ -1185,64 +1154,9 @@ public class Paint_Delegate { } } - /*package*/ float measureText(char[] text, int index, int count, int bidiFlags) { - // TODO: find out what bidiFlags actually does. - - // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText - // Any change to this method should be reflected there as well - - if (mFonts.size() > 0) { - FontInfo mainFont = mFonts.get(0); - int i = index; - int lastIndex = index + count; - float total = 0f; - while (i < lastIndex) { - // always start with the main font. - int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); - if (upTo == -1) { - // shortcut to exit - return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); - } else if (upTo > 0) { - total += mainFont.mMetrics.charsWidth(text, i, upTo - i); - i = upTo; - // don't call continue at this point. Since it is certain the main font - // cannot display the font a index upTo (now ==i), we move on to the - // fallback fonts directly. - } - - // no char supported, attempt to read the next char(s) with the - // fallback font. In this case we only test the first character - // and then go back to test with the main font. - // Special test for 2-char characters. - boolean foundFont = false; - for (int f = 1 ; f < mFonts.size() ; f++) { - FontInfo fontInfo = mFonts.get(f); - - // need to check that the font can display the character. We test - // differently if the char is a high surrogate. - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; - upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); - if (upTo == -1) { - total += fontInfo.mMetrics.charsWidth(text, i, charCount); - i += charCount; - foundFont = true; - break; - - } - } - - // in case no font can display the char, measure it with the main font. - if (foundFont == false) { - int size = Character.isHighSurrogate(text[i]) ? 2 : 1; - total += mainFont.mMetrics.charsWidth(text, i, size); - i += size; - } - } - - return total; - } - - return 0; + /*package*/ float measureText(char[] text, int index, int count, boolean isRtl) { + return new BidiRenderer(null, this, text).renderText( + index, index + count, isRtl, null, 0, false, 0, 0); } private float getFontMetrics(FontMetrics metrics) { @@ -1281,4 +1195,14 @@ public class Paint_Delegate { } } + private static boolean isRtl(int flag) { + switch(flag) { + case Paint.BIDI_RTL: + case Paint.BIDI_FORCE_RTL: + case Paint.BIDI_DEFAULT_RTL: + return true; + default: + return false; + } + } } diff --git a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java index 52b8f34..973fa0e 100644 --- a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java @@ -16,7 +16,10 @@ package android.text; +import com.android.ide.common.rendering.api.LayoutLog; +import com.android.layoutlib.bridge.Bridge; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; +import com.ibm.icu.text.Bidi; /** @@ -29,9 +32,29 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; public class AndroidBidi_Delegate { @LayoutlibDelegate - /*package*/ static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) { - // return the equivalent of Layout.DIR_LEFT_TO_RIGHT - // TODO: actually figure the direction. - return 0; + /*package*/ static int runBidi(int dir, char[] chars, byte[] charInfo, int count, + boolean haveInfo) { + + switch (dir) { + case 0: // Layout.DIR_REQUEST_LTR + case 1: // Layout.DIR_REQUEST_RTL + break; // No change. + case -1: + dir = Bidi.LEVEL_DEFAULT_LTR; + break; + case -2: + dir = Bidi.LEVEL_DEFAULT_RTL; + break; + default: + // Invalid code. Log error, assume LEVEL_DEFAULT_LTR and continue. + Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Invalid direction flag", null); + dir = Bidi.LEVEL_DEFAULT_LTR; + } + Bidi bidi = new Bidi(chars, 0, null, 0, count, dir); + if (charInfo != null) { + for (int i = 0; i < count; ++i) + charInfo[i] = bidi.getLevelAt(i); + } + return bidi.getParaLevel(); } } diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 434b131..fd153af 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -49,15 +49,13 @@ public class IWindowManagerImpl implements IWindowManager { private final Configuration mConfig; private final DisplayMetrics mMetrics; private final int mRotation; - private final boolean mHasSystemNavBar; private final boolean mHasNavigationBar; public IWindowManagerImpl(Configuration config, DisplayMetrics metrics, int rotation, - boolean hasSystemNavBar, boolean hasNavigationBar) { + boolean hasNavigationBar) { mConfig = config; mMetrics = metrics; mRotation = rotation; - mHasSystemNavBar = hasSystemNavBar; mHasNavigationBar = hasNavigationBar; } @@ -79,16 +77,11 @@ public class IWindowManagerImpl implements IWindowManager { return mHasNavigationBar; } - @Override - public boolean hasSystemNavBar() throws RemoteException { - return mHasSystemNavBar; - } - // ---- unused implementation of IWindowManager ---- @Override - public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, boolean arg4, - boolean arg5) + public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4, + boolean arg5, boolean arg6, int arg7) throws RemoteException { // TODO Auto-generated method stub @@ -211,24 +204,6 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void moveAppToken(int arg0, IBinder arg1) throws RemoteException { - // TODO Auto-generated method stub - - } - - @Override - public void moveAppTokensToBottom(List<IBinder> arg0) throws RemoteException { - // TODO Auto-generated method stub - - } - - @Override - public void moveAppTokensToTop(List<IBinder> arg0) throws RemoteException { - // TODO Auto-generated method stub - - } - - @Override public IWindowSession openSession(IInputMethodClient arg0, IInputContext arg1) throws RemoteException { // TODO Auto-generated method stub @@ -291,8 +266,8 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public Bitmap screenshotApplications(IBinder arg0, int displayId, int arg1, int arg2) - throws RemoteException { + public Bitmap screenshotApplications(IBinder arg0, int displayId, int arg1, + int arg2, boolean arg3) throws RemoteException { // TODO Auto-generated method stub return null; } @@ -322,7 +297,7 @@ public class IWindowManagerImpl implements IWindowManager { @Override public void setAppStartingWindow(IBinder arg0, String arg1, int arg2, CompatibilityInfo arg3, - CharSequence arg4, int arg5, int arg6, int arg7, IBinder arg8, boolean arg9) + CharSequence arg4, int arg5, int arg6, int arg7, int arg8, IBinder arg9, boolean arg10) throws RemoteException { // TODO Auto-generated method stub } @@ -483,11 +458,6 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void showAssistant() { - - } - - @Override public IBinder getFocusedWindowToken() { // TODO Auto-generated method stub return null; @@ -524,4 +494,8 @@ public class IWindowManagerImpl implements IWindowManager { // TODO Auto-generated method stub return false; } + + @Override + public void setTouchExplorationEnabled(boolean enabled) { + } } diff --git a/tools/layoutlib/bridge/src/android/webkit/WebView.java b/tools/layoutlib/bridge/src/android/webkit/WebView.java index 3b66188..202f204 100644 --- a/tools/layoutlib/bridge/src/android/webkit/WebView.java +++ b/tools/layoutlib/bridge/src/android/webkit/WebView.java @@ -99,14 +99,6 @@ public class WebView extends MockView { public static void disablePlatformNotifications() { } - public WebBackForwardList saveState(Bundle outState) { - return null; - } - - public WebBackForwardList restoreState(Bundle inState) { - return null; - } - public void loadUrl(String url) { } @@ -213,10 +205,6 @@ public class WebView extends MockView { public void clearSslPreferences() { } - public WebBackForwardList copyBackForwardList() { - return null; - } - public static String findAddress(String addr) { return null; } @@ -236,10 +224,6 @@ public class WebView extends MockView { public void addJavascriptInterface(Object obj, String interfaceName) { } - public WebSettings getSettings() { - return null; - } - public View getZoomControls() { return 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 42257c5..ab4be71 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -35,6 +35,7 @@ import com.android.resources.ResourceType; import com.android.tools.layoutlib.create.MethodAdapter; import com.android.tools.layoutlib.create.OverrideMethod; import com.android.util.Pair; +import com.ibm.icu.util.ULocale; import android.content.res.BridgeAssetManager; import android.graphics.Bitmap; @@ -64,6 +65,8 @@ import java.util.concurrent.locks.ReentrantLock; */ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { + private static final String ICU_LOCALE_DIRECTION_RTL = "right-to-left"; + public static class StaticMethodNotImplementedException extends RuntimeException { private static final long serialVersionUID = 1L; @@ -211,7 +214,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { Capability.ANIMATED_VIEW_MANIPULATION, Capability.ADAPTER_BINDING, Capability.EXTENDED_VIEWINFO, - Capability.FIXED_SCALABLE_NINE_PATCH); + Capability.FIXED_SCALABLE_NINE_PATCH, + Capability.RTL); BridgeAssetManager.initSystem(); @@ -411,6 +415,20 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { throw new IllegalArgumentException("viewObject is not a View"); } + @Override + public boolean isRtl(String locale) { + return isLocaleRtl(locale); + } + + public static boolean isLocaleRtl(String locale) { + if (locale == null) { + locale = ""; + } + ULocale uLocale = new ULocale(locale); + return uLocale.getCharacterOrientation().equals(ICU_LOCALE_DIRECTION_RTL) ? + true : false; + } + /** * Returns the lock for the bridge */ diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java index 4aea38f..01740b1 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java @@ -81,14 +81,16 @@ public final class BridgeContentProvider implements IContentProvider { } @Override - public AssetFileDescriptor openAssetFile(String callingPackage, Uri arg0, String arg1) + public AssetFileDescriptor openAssetFile( + String callingPackage, Uri arg0, String arg1, ICancellationSignal signal) throws RemoteException, FileNotFoundException { // TODO Auto-generated method stub return null; } @Override - public ParcelFileDescriptor openFile(String callingPackage, Uri arg0, String arg1) + public ParcelFileDescriptor openFile( + String callingPackage, Uri arg0, String arg1, ICancellationSignal signal) throws RemoteException, FileNotFoundException { // TODO Auto-generated method stub return null; @@ -122,7 +124,7 @@ public final class BridgeContentProvider implements IContentProvider { @Override public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri arg0, String arg1, - Bundle arg2) throws RemoteException, FileNotFoundException { + Bundle arg2, ICancellationSignal signal) throws RemoteException, FileNotFoundException { // TODO Auto-generated method stub return null; } @@ -132,4 +134,14 @@ public final class BridgeContentProvider implements IContentProvider { // TODO Auto-generated method stub return null; } + + @Override + public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException { + return null; + } + + @Override + public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException { + return null; + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 21bef1c..b9294ab 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -67,8 +67,8 @@ import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.BridgeInflater; -import android.view.CompatibilityInfoHolder; import android.view.Display; +import android.view.DisplayAdjustments; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -132,7 +132,8 @@ public final class BridgeContext extends Context { RenderResources renderResources, IProjectCallback projectCallback, Configuration config, - int targetSdkVersion) { + int targetSdkVersion, + boolean hasRtlSupport) { mProjectKey = projectKey; mMetrics = metrics; mProjectCallback = projectCallback; @@ -142,6 +143,9 @@ public final class BridgeContext extends Context { mApplicationInfo = new ApplicationInfo(); mApplicationInfo.targetSdkVersion = targetSdkVersion; + if (hasRtlSupport) { + mApplicationInfo.flags = mApplicationInfo.flags | ApplicationInfo.FLAG_SUPPORTS_RTL; + } mWindowManager = new WindowManagerImpl(mMetrics); } @@ -1086,6 +1090,12 @@ public final class BridgeContext extends Context { } @Override + public String getOpPackageName() { + // pass + return null; + } + + @Override public ApplicationInfo getApplicationInfo() { return mApplicationInfo; } @@ -1394,7 +1404,7 @@ public final class BridgeContext extends Context { } @Override - public CompatibilityInfoHolder getCompatibilityInfo(int displayId) { + public DisplayAdjustments getDisplayAdjustments(int displayId) { // pass return null; } @@ -1406,4 +1416,22 @@ public final class BridgeContext extends Context { public int getUserId() { return 0; // not used } + + @Override + public File[] getExternalFilesDirs(String type) { + // pass + return new File[0]; + } + + @Override + public File[] getObbDirs() { + // pass + return new File[0]; + } + + @Override + public File[] getExternalCacheDirs() { + // pass + return new File[0]; + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java index d6abbaa..3cf5ed5 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java @@ -63,7 +63,7 @@ public class BridgeIInputMethodManager implements IInputMethodManager { } @Override - public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo arg0, + public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String arg0, boolean arg1) throws RemoteException { // TODO Auto-generated method stub return null; @@ -203,6 +203,12 @@ public class BridgeIInputMethodManager implements IInputMethodManager { } @Override + public boolean shouldOfferSwitchingToNextInputMethod(IBinder arg0) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + @Override public void updateStatusIcon(IBinder arg0, String arg1, int arg2) throws RemoteException { // TODO Auto-generated method stub diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java index 1ccbc40..a8b58aa 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java @@ -39,7 +39,13 @@ public class BridgePowerManager implements IPowerManager { } @Override - public void acquireWakeLock(IBinder arg0, int arg1, String arg2, WorkSource arg3) + public void acquireWakeLock(IBinder arg0, int arg1, String arg2, String arg2_5, WorkSource arg3) + throws RemoteException { + // pass for now. + } + + @Override + public void acquireWakeLockWithUid(IBinder arg0, int arg1, String arg2, String arg2_5, int arg3) throws RemoteException { // pass for now. } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index ea9d8d9..17b0eb6 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -25,6 +25,7 @@ import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.impl.ParserFactory; import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.resources.Density; +import com.android.resources.LayoutDirection; import com.android.resources.ResourceType; import org.xmlpull.v1.XmlPullParser; @@ -86,38 +87,53 @@ abstract class CustomBar extends LinearLayout { } } - private InputStream getIcon(String iconName, Density[] densityInOut, String[] pathOut, - boolean tryOtherDensities) { + private InputStream getIcon(String iconName, Density[] densityInOut, LayoutDirection direction, + String[] pathOut, boolean tryOtherDensities) { // current density Density density = densityInOut[0]; // bitmap url relative to this class - pathOut[0] = "/bars/" + density.getResourceValue() + "/" + iconName; + if (direction != null) { + pathOut[0] = "/bars/" + direction.getResourceValue() + "-" + density.getResourceValue() + + "/" + iconName; + } else { + pathOut[0] = "/bars/" + density.getResourceValue() + "/" + iconName; + } InputStream stream = getClass().getResourceAsStream(pathOut[0]); if (stream == null && tryOtherDensities) { for (Density d : Density.values()) { if (d != density) { densityInOut[0] = d; - stream = getIcon(iconName, densityInOut, pathOut, false /*tryOtherDensities*/); + stream = getIcon(iconName, densityInOut, direction, pathOut, + false /*tryOtherDensities*/); if (stream != null) { return stream; } } } + // couldn't find resource with direction qualifier. try without. + if (direction != null) { + return getIcon(iconName, densityInOut, null, pathOut, true); + } } return stream; } protected void loadIcon(int index, String iconName, Density density) { + loadIcon(index, iconName, density, false); + } + + protected void loadIcon(int index, String iconName, Density density, boolean isRtl) { View child = getChildAt(index); if (child instanceof ImageView) { ImageView imageView = (ImageView) child; String[] pathOut = new String[1]; Density[] densityInOut = new Density[] { density }; - InputStream stream = getIcon(iconName, densityInOut, pathOut, + LayoutDirection dir = isRtl ? LayoutDirection.RTL : LayoutDirection.LTR; + InputStream stream = getIcon(iconName, densityInOut, dir, pathOut, true /*tryOtherDensities*/); density = densityInOut[0]; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java index cc90d6b..84e676e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java @@ -17,6 +17,7 @@ package com.android.layoutlib.bridge.bars; import com.android.resources.Density; +import com.android.layoutlib.bridge.Bridge; import org.xmlpull.v1.XmlPullParserException; @@ -26,7 +27,8 @@ import android.widget.TextView; public class NavigationBar extends CustomBar { - public NavigationBar(Context context, Density density, int orientation) throws XmlPullParserException { + public NavigationBar(Context context, Density density, int orientation, boolean isRtl, + boolean rtlEnabled) throws XmlPullParserException { super(context, density, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml"); setBackgroundColor(0xFF000000); @@ -37,14 +39,15 @@ public class NavigationBar extends CustomBar { // 0 is a spacer. int back = 1; int recent = 3; - if (orientation == LinearLayout.VERTICAL) { + if (orientation == LinearLayout.VERTICAL || (isRtl && !rtlEnabled)) { + // If RTL is enabled, then layoutlib mirrors the layout for us. back = 3; recent = 1; } - loadIcon(back, "ic_sysbar_back.png", density); - loadIcon(2, "ic_sysbar_home.png", density); - loadIcon(recent, "ic_sysbar_recent.png", density); + loadIcon(back, "ic_sysbar_back.png", density, isRtl); + loadIcon(2, "ic_sysbar_home.png", density, isRtl); + loadIcon(recent, "ic_sysbar_recent.png", density, isRtl); } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index 5c08412..baa956d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -30,7 +30,10 @@ import android.widget.TextView; public class StatusBar extends CustomBar { - public StatusBar(Context context, Density density) throws XmlPullParserException { + public StatusBar(Context context, Density density, int direction, boolean RtlEnabled) + throws XmlPullParserException { + // FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar. + super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml"); // FIXME: use FILL_H? diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java index 081ce67..108b651 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java @@ -52,6 +52,8 @@ public final class FontLoader { private static final String NODE_NAME = "name"; private static final String NODE_FILE = "file"; + private static final String ATTRIBUTE_VARIANT = "variant"; + private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant"; private static final String FONT_SUFFIX_NONE = ".ttf"; private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf"; private static final String FONT_SUFFIX_BOLD = "-Bold.ttf"; @@ -189,6 +191,7 @@ public final class FontLoader { private FontInfo mFontInfo = null; private final StringBuilder mBuilder = new StringBuilder(); private List<FontInfo> mFontList = new ArrayList<FontInfo>(); + private boolean isCompactFont = true; private FontHandler(String osFontsLocation) { super(); @@ -209,8 +212,21 @@ public final class FontLoader { mFontList = new ArrayList<FontInfo>(); } else if (NODE_FAMILY.equals(localName)) { if (mFontList != null) { + mFontInfo = null; + } + } else if (NODE_NAME.equals(localName)) { + if (mFontList != null && mFontInfo == null) { + mFontInfo = new FontInfo(); + } + } else if (NODE_FILE.equals(localName)) { + if (mFontList != null && mFontInfo == null) { mFontInfo = new FontInfo(); } + if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) { + isCompactFont = false; + } else { + isCompactFont = true; + } } mBuilder.setLength(0); @@ -223,7 +239,9 @@ public final class FontLoader { */ @Override public void characters(char[] ch, int start, int length) throws SAXException { - mBuilder.append(ch, start, length); + if (isCompactFont) { + mBuilder.append(ch, start, length); + } } /* (non-Javadoc) @@ -259,7 +277,7 @@ public final class FontLoader { } } else if (NODE_FILE.equals(localName)) { // handle a new file for an existing Font Info - if (mFontInfo != null) { + if (isCompactFont && mFontInfo != null) { String fileName = trimXmlWhitespaces(mBuilder.toString()); Font font = getFont(fileName); if (font != null) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java index b909bec..87047b3 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java @@ -121,7 +121,8 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso // build the context mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources, - mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion()); + mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion(), + mParams.isRtlSupported()); setUp(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index c14af4a..57771e3 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -192,11 +192,10 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { findNavigationBar(resources, metrics); // FIXME: find those out, and possibly add them to the render params - boolean hasSystemNavBar = true; boolean hasNavigationBar = true; IWindowManager iwm = new IWindowManagerImpl(getContext().getConfiguration(), metrics, Surface.ROTATION_0, - hasSystemNavBar, hasNavigationBar); + hasNavigationBar); WindowManagerGlobal_Delegate.setWindowManagerService(iwm); // build the inflater and parser. @@ -225,13 +224,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { SessionParams params = getParams(); HardwareConfig hardwareConfig = params.getHardwareConfig(); BridgeContext context = getContext(); - + boolean isRtl = Bridge.isLocaleRtl(params.getLocale()); + int direction = isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR; // the view group that receives the window background. ViewGroup backgroundView = null; if (mWindowIsFloating || params.isForceNoDecor()) { backgroundView = mViewRoot = mContentRoot = new FrameLayout(context); + mViewRoot.setLayoutDirection(direction); } else { if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) { /* @@ -253,12 +254,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { the bottom */ LinearLayout topLayout = new LinearLayout(context); + topLayout.setLayoutDirection(direction); mViewRoot = topLayout; topLayout.setOrientation(LinearLayout.HORIZONTAL); try { NavigationBar navigationBar = new NavigationBar(context, - hardwareConfig.getDensity(), LinearLayout.VERTICAL); + hardwareConfig.getDensity(), LinearLayout.VERTICAL, isRtl, + params.isRtlSupported()); navigationBar.setLayoutParams( new LinearLayout.LayoutParams( mNavigationBarSize, @@ -290,6 +293,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { LinearLayout topLayout = new LinearLayout(context); topLayout.setOrientation(LinearLayout.VERTICAL); + topLayout.setLayoutDirection(direction); // if we don't already have a view root this is it if (mViewRoot == null) { mViewRoot = topLayout; @@ -301,13 +305,22 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // this is the case of soft buttons + vertical bar. // this top layout is the first layout in the horizontal layout. see above) - mViewRoot.addView(topLayout, 0); + if (isRtl && params.isRtlSupported()) { + // If RTL is enabled, layoutlib will mirror the layouts. So, add the + // topLayout to the right of Navigation Bar and layoutlib will draw it + // to the left. + mViewRoot.addView(topLayout); + } else { + // Add the top layout to the left of the Navigation Bar. + mViewRoot.addView(topLayout, 0); + } } if (mStatusBarSize > 0) { // system bar try { - StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity()); + StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity(), + direction, params.isRtlSupported()); systemBar.setLayoutParams( new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, mStatusBarSize)); @@ -366,7 +379,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // system bar try { NavigationBar navigationBar = new NavigationBar(context, - hardwareConfig.getDensity(), LinearLayout.HORIZONTAL); + hardwareConfig.getDensity(), LinearLayout.HORIZONTAL, isRtl, + params.isRtlSupported()); navigationBar.setLayoutParams( new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, mNavigationBarSize)); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java index e0414fe..6c998af 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package com.android.layoutlib.bridge.impl.binding; -import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.DataBindingItem; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.LayoutLog; @@ -27,7 +26,6 @@ import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.impl.RenderAction; import com.android.util.Pair; -import android.database.DataSetObserver; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; @@ -35,124 +33,27 @@ import android.widget.Checkable; import android.widget.ImageView; import android.widget.TextView; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - /** - * Base adapter to do fake data binding in {@link AdapterView} objects. + * A Helper class to do fake data binding in {@link AdapterView} objects. */ -public class BaseAdapter { - - /** - * This is the items provided by the adapter. They are dynamically generated. - */ - protected final static class AdapterItem { - private final DataBindingItem mItem; - private final int mType; - private final int mFullPosition; - private final int mPositionPerType; - private List<AdapterItem> mChildren; - - protected AdapterItem(DataBindingItem item, int type, int fullPosition, - int positionPerType) { - mItem = item; - mType = type; - mFullPosition = fullPosition; - mPositionPerType = positionPerType; - } - - void addChild(AdapterItem child) { - if (mChildren == null) { - mChildren = new ArrayList<AdapterItem>(); - } - - mChildren.add(child); - } - - List<AdapterItem> getChildren() { - if (mChildren != null) { - return mChildren; - } - - return Collections.emptyList(); - } - - int getType() { - return mType; - } - - int getFullPosition() { - return mFullPosition; - } - - int getPositionPerType() { - return mPositionPerType; - } - - DataBindingItem getDataBindingItem() { - return mItem; - } - } - - private final AdapterBinding mBinding; - private final IProjectCallback mCallback; - private final ResourceReference mAdapterRef; - private boolean mSkipCallbackParser = false; - - protected final List<AdapterItem> mItems = new ArrayList<AdapterItem>(); - - protected BaseAdapter(ResourceReference adapterRef, AdapterBinding binding, - IProjectCallback callback) { - mAdapterRef = adapterRef; - mBinding = binding; - mCallback = callback; - } - - // ------- Some Adapter method used by all children classes. - - public boolean areAllItemsEnabled() { - return true; - } - - public boolean hasStableIds() { - return true; - } - - public boolean isEmpty() { - return mItems.size() == 0; - } - - public void registerDataSetObserver(DataSetObserver observer) { - // pass - } - - public void unregisterDataSetObserver(DataSetObserver observer) { - // pass - } - - // ------- - - - protected AdapterBinding getBinding() { - return mBinding; - } +@SuppressWarnings("deprecation") +public class AdapterHelper { - protected View getView(AdapterItem item, AdapterItem parentItem, View convertView, - ViewGroup parent) { + static Pair<View, Boolean> getView(AdapterItem item, AdapterItem parentItem, ViewGroup parent, + IProjectCallback callback, ResourceReference adapterRef, boolean skipCallbackParser) { // we don't care about recycling here because we never scroll. DataBindingItem dataBindingItem = item.getDataBindingItem(); BridgeContext context = RenderAction.getCurrentContext(); Pair<View, Boolean> pair = context.inflateView(dataBindingItem.getViewReference(), - parent, false /*attachToRoot*/, mSkipCallbackParser); + parent, false /*attachToRoot*/, skipCallbackParser); View view = pair.getFirst(); - mSkipCallbackParser |= pair.getSecond(); + skipCallbackParser |= pair.getSecond(); if (view != null) { - fillView(context, view, item, parentItem); + fillView(context, view, item, parentItem, callback, adapterRef); } else { // create a text view to display an error. TextView tv = new TextView(context); @@ -160,16 +61,16 @@ public class BaseAdapter { view = tv; } - return view; + return Pair.of(view, skipCallbackParser); } - private void fillView(BridgeContext context, View view, AdapterItem item, - AdapterItem parentItem) { + private static void fillView(BridgeContext context, View view, AdapterItem item, + AdapterItem parentItem, IProjectCallback callback, ResourceReference adapterRef) { if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); for (int i = 0 ; i < count ; i++) { - fillView(context, group.getChildAt(i), item, parentItem); + fillView(context, group.getChildAt(i), item, parentItem, callback, adapterRef); } } else { int id = view.getId(); @@ -184,8 +85,8 @@ public class BaseAdapter { if (view instanceof TextView) { TextView tv = (TextView) view; - Object value = mCallback.getAdapterItemValue( - mAdapterRef, context.getViewKey(view), + Object value = callback.getAdapterItemValue( + adapterRef, context.getViewKey(view), item.getDataBindingItem().getViewReference(), fullPosition, positionPerType, fullParentPosition, parentPositionPerType, @@ -204,8 +105,8 @@ public class BaseAdapter { if (view instanceof Checkable) { Checkable cb = (Checkable) view; - Object value = mCallback.getAdapterItemValue( - mAdapterRef, context.getViewKey(view), + Object value = callback.getAdapterItemValue( + adapterRef, context.getViewKey(view), item.getDataBindingItem().getViewReference(), fullPosition, positionPerType, fullParentPosition, parentPositionPerType, @@ -224,8 +125,8 @@ public class BaseAdapter { if (view instanceof ImageView) { ImageView iv = (ImageView) view; - Object value = mCallback.getAdapterItemValue( - mAdapterRef, context.getViewKey(view), + Object value = callback.getAdapterItemValue( + adapterRef, context.getViewKey(view), item.getDataBindingItem().getViewReference(), fullPosition, positionPerType, fullParentPosition, parentPositionPerType, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterItem.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterItem.java new file mode 100644 index 0000000..8e28dba --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterItem.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.layoutlib.bridge.impl.binding; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.android.ide.common.rendering.api.DataBindingItem; + +/** + * This is the items provided by the adapter. They are dynamically generated. + */ +final class AdapterItem { + private final DataBindingItem mItem; + private final int mType; + private final int mFullPosition; + private final int mPositionPerType; + private List<AdapterItem> mChildren; + + protected AdapterItem(DataBindingItem item, int type, int fullPosition, + int positionPerType) { + mItem = item; + mType = type; + mFullPosition = fullPosition; + mPositionPerType = positionPerType; + } + + void addChild(AdapterItem child) { + if (mChildren == null) { + mChildren = new ArrayList<AdapterItem>(); + } + + mChildren.add(child); + } + + List<AdapterItem> getChildren() { + if (mChildren != null) { + return mChildren; + } + + return Collections.emptyList(); + } + + int getType() { + return mType; + } + + int getFullPosition() { + return mFullPosition; + } + + int getPositionPerType() { + return mPositionPerType; + } + + DataBindingItem getDataBindingItem() { + return mItem; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java index 22570b9..9a13f5a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java @@ -20,10 +20,12 @@ import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.DataBindingItem; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.ResourceReference; +import com.android.util.Pair; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; +import android.widget.BaseAdapter; import android.widget.ListAdapter; import android.widget.SpinnerAdapter; @@ -35,17 +37,23 @@ import java.util.List; * and {@link SpinnerAdapter}. * */ -public class FakeAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter { +@SuppressWarnings("deprecation") +public class FakeAdapter extends BaseAdapter { // don't use a set because the order is important. private final List<ResourceReference> mTypes = new ArrayList<ResourceReference>(); + private final IProjectCallback mCallback; + private final ResourceReference mAdapterRef; + private final List<AdapterItem> mItems = new ArrayList<AdapterItem>(); + private boolean mSkipCallbackParser = false; public FakeAdapter(ResourceReference adapterRef, AdapterBinding binding, IProjectCallback callback) { - super(adapterRef, binding, callback); + mAdapterRef = adapterRef; + mCallback = callback; - final int repeatCount = getBinding().getRepeatCount(); - final int itemCount = getBinding().getItemCount(); + final int repeatCount = binding.getRepeatCount(); + final int itemCount = binding.getItemCount(); // Need an array to count for each type. // This is likely too big, but is the max it can be. @@ -54,7 +62,7 @@ public class FakeAdapter extends BaseAdapter implements ListAdapter, SpinnerAdap // We put several repeating sets. for (int r = 0 ; r < repeatCount ; r++) { // loop on the type of list items, and add however many for each type. - for (DataBindingItem dataBindingItem : getBinding()) { + for (DataBindingItem dataBindingItem : binding) { ResourceReference viewRef = dataBindingItem.getViewReference(); int typeIndex = mTypes.indexOf(viewRef); if (typeIndex == -1) { @@ -103,7 +111,11 @@ public class FakeAdapter extends BaseAdapter implements ListAdapter, SpinnerAdap public View getView(int position, View convertView, ViewGroup parent) { // we don't care about recycling here because we never scroll. AdapterItem item = mItems.get(position); - return getView(item, null /*parentGroup*/, convertView, parent); + Pair<View, Boolean> pair = AdapterHelper.getView(item, null /*parentGroup*/, parent, + mCallback, mAdapterRef, mSkipCallbackParser); + mSkipCallbackParser = pair.getSecond(); + return pair.getFirst(); + } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java index 199e040..e539579 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java @@ -20,7 +20,9 @@ import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.DataBindingItem; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.ResourceReference; +import com.android.util.Pair; +import android.database.DataSetObserver; import android.view.View; import android.view.ViewGroup; import android.widget.ExpandableListAdapter; @@ -29,8 +31,14 @@ import android.widget.HeterogeneousExpandableList; import java.util.ArrayList; import java.util.List; -public class FakeExpandableAdapter extends BaseAdapter implements ExpandableListAdapter, - HeterogeneousExpandableList { +@SuppressWarnings("deprecation") +public class FakeExpandableAdapter implements ExpandableListAdapter, HeterogeneousExpandableList { + + private final IProjectCallback mCallback; + private final ResourceReference mAdapterRef; + private boolean mSkipCallbackParser = false; + + protected final List<AdapterItem> mItems = new ArrayList<AdapterItem>(); // don't use a set because the order is important. private final List<ResourceReference> mGroupTypes = new ArrayList<ResourceReference>(); @@ -38,7 +46,8 @@ public class FakeExpandableAdapter extends BaseAdapter implements ExpandableList public FakeExpandableAdapter(ResourceReference adapterRef, AdapterBinding binding, IProjectCallback callback) { - super(adapterRef, binding, callback); + mAdapterRef = adapterRef; + mCallback = callback; createItems(binding, binding.getItemCount(), binding.getRepeatCount(), mGroupTypes, 1); } @@ -125,7 +134,10 @@ public class FakeExpandableAdapter extends BaseAdapter implements ExpandableList ViewGroup parent) { // we don't care about recycling here because we never scroll. AdapterItem item = mItems.get(groupPosition); - return getView(item, null /*parentItem*/, convertView, parent); + Pair<View, Boolean> pair = AdapterHelper.getView(item, null /*parentItem*/, parent, + mCallback, mAdapterRef, mSkipCallbackParser); + mSkipCallbackParser = pair.getSecond(); + return pair.getFirst(); } @Override @@ -134,7 +146,10 @@ public class FakeExpandableAdapter extends BaseAdapter implements ExpandableList // we don't care about recycling here because we never scroll. AdapterItem parentItem = mItems.get(groupPosition); AdapterItem item = getChildItem(groupPosition, childPosition); - return getView(item, parentItem, convertView, parent); + Pair<View, Boolean> pair = AdapterHelper.getView(item, parentItem, parent, mCallback, + mAdapterRef, mSkipCallbackParser); + mSkipCallbackParser = pair.getSecond(); + return pair.getFirst(); } @Override @@ -172,6 +187,31 @@ public class FakeExpandableAdapter extends BaseAdapter implements ExpandableList // pass } + @Override + public void registerDataSetObserver(DataSetObserver observer) { + // pass + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + // pass + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public boolean areAllItemsEnabled() { + return true; + } + + @Override + public boolean isEmpty() { + return mItems.isEmpty(); + } + // ---- HeterogeneousExpandableList @Override diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk index ad8de69..9ff56d6 100644 --- a/tools/obbtool/Android.mk +++ b/tools/obbtool/Android.mk @@ -18,8 +18,8 @@ LOCAL_CFLAGS := -Wall -Werror #LOCAL_C_INCLUDES += LOCAL_STATIC_LIBRARIES := \ - libutils \ libandroidfw \ + libutils \ libcutils \ liblog diff --git a/tools/preload/Policy.java b/tools/preload/Policy.java index ca0291b..af46820 100644 --- a/tools/preload/Policy.java +++ b/tools/preload/Policy.java @@ -81,8 +81,9 @@ public class Policy { return SERVICES.contains(processName); } - /**Reports if the given class should be preloaded. */ + /** Reports if the given class should be preloaded. */ public static boolean isPreloadable(LoadedClass clazz) { - return clazz.systemClass && !EXCLUDED_CLASSES.contains(clazz.name); + return clazz.systemClass && !EXCLUDED_CLASSES.contains(clazz.name) + && !clazz.name.endsWith("$NoPreloadHolder"); } } diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk index 90fbc08..9af721d 100644 --- a/tools/validatekeymaps/Android.mk +++ b/tools/validatekeymaps/Android.mk @@ -15,10 +15,8 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS := -Wall -Werror -#LOCAL_C_INCLUDES += - LOCAL_STATIC_LIBRARIES := \ - libandroidfw \ + libinput \ libutils \ libcutils \ liblog diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp index 91e4fda..5b45c55 100644 --- a/tools/validatekeymaps/Main.cpp +++ b/tools/validatekeymaps/Main.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include <androidfw/KeyCharacterMap.h> -#include <androidfw/KeyLayoutMap.h> -#include <androidfw/VirtualKeyMap.h> +#include <input/KeyCharacterMap.h> +#include <input/KeyLayoutMap.h> +#include <input/VirtualKeyMap.h> #include <utils/PropertyMap.h> #include <utils/String8.h> |