diff options
Diffstat (limited to 'tools')
55 files changed, 2504 insertions, 1160 deletions
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index b7f64f63fdbc..871e04f7aa9d 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -15,7 +15,6 @@ #include <dirent.h> #include <errno.h> -static const char* kDefaultLocale = "default"; static const char* kAssetDir = "assets"; static const char* kResourceDir = "res"; static const char* kValuesDir = "values"; @@ -176,7 +175,7 @@ inline bool isNumber(const String8& string) { void AaptLocaleValue::setLanguage(const char* languageChars) { size_t i = 0; - while ((*languageChars) != '\0') { + while ((*languageChars) != '\0' && i < sizeof(language)/sizeof(language[0])) { language[i++] = tolower(*languageChars); languageChars++; } @@ -184,7 +183,7 @@ void AaptLocaleValue::setLanguage(const char* languageChars) { void AaptLocaleValue::setRegion(const char* regionChars) { size_t i = 0; - while ((*regionChars) != '\0') { + while ((*regionChars) != '\0' && i < sizeof(region)/sizeof(region[0])) { region[i++] = toupper(*regionChars); regionChars++; } @@ -192,7 +191,7 @@ void AaptLocaleValue::setRegion(const char* regionChars) { void AaptLocaleValue::setScript(const char* scriptChars) { size_t i = 0; - while ((*scriptChars) != '\0') { + while ((*scriptChars) != '\0' && i < sizeof(script)/sizeof(script[0])) { if (i == 0) { script[i++] = toupper(*scriptChars); } else { @@ -204,7 +203,7 @@ void AaptLocaleValue::setScript(const char* scriptChars) { void AaptLocaleValue::setVariant(const char* variantChars) { size_t i = 0; - while ((*variantChars) != '\0') { + while ((*variantChars) != '\0' && i < sizeof(variant)/sizeof(variant[0])) { variant[i++] = *variantChars; variantChars++; } @@ -346,7 +345,8 @@ int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int sta return ++currentIndex; } else { - if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) { + if ((part.length() == 2 || part.length() == 3) + && isAlpha(part) && strcmp("car", part.string())) { setLanguage(part); if (++currentIndex == size) { return size; @@ -1241,7 +1241,7 @@ bail: } ssize_t -AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename) +AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename) { int count = 0; SortedVector<AaptGroupEntry> entries; diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index bc9c1f70b8c6..c5495a55848d 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -36,7 +36,6 @@ aaptSources := \ Images.cpp \ Package.cpp \ pseudolocalize.cpp \ - qsort_r_compat.c \ Resource.cpp \ ResourceFilter.cpp \ ResourceIdCache.cpp \ @@ -68,6 +67,7 @@ aaptHostStaticLibs := \ libziparchive-host aaptCFlags := -DAAPT_VERSION=\"$(BUILD_NUMBER)\" +aaptCFLAGS += -Wall -Werror ifeq ($(HOST_OS),linux) aaptHostLdLibs += -lrt -ldl -lpthread @@ -118,6 +118,7 @@ include $(BUILD_HOST_EXECUTABLE) # Build the host tests: libaapt_tests # ========================================================== include $(CLEAR_VARS) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE := libaapt_tests LOCAL_CFLAGS += $(aaptCFlags) @@ -139,10 +140,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := aapt LOCAL_CFLAGS += $(aaptCFlags) LOCAL_SRC_FILES := $(aaptSources) $(aaptMain) -LOCAL_C_INCLUDES += \ - $(aaptCIncludes) \ - bionic \ - external/stlport/stlport +LOCAL_C_INCLUDES += $(aaptCIncludes) LOCAL_SHARED_LIBRARIES := \ libandroidfw \ libutils \ @@ -151,7 +149,6 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libz LOCAL_STATIC_LIBRARIES := \ - libstlport_static \ libexpat_static include $(BUILD_EXECUTABLE) diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 3fa131edd873..8a0a39cfb445 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -215,14 +215,15 @@ int doList(Bundle* bundle) goto bail; } +#ifdef HAVE_ANDROID_OS + static const bool kHaveAndroidOs = true; +#else + static const bool kHaveAndroidOs = false; +#endif const ResTable& res = assets.getResources(false); - if (&res == NULL) { - printf("\nNo resource table found.\n"); - } else { -#ifndef HAVE_ANDROID_OS + if (!kHaveAndroidOs) { printf("\nResource table:\n"); res.print(false); -#endif } Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", @@ -620,10 +621,7 @@ int doDump(Bundle* bundle) assets.setConfiguration(config); const ResTable& res = assets.getResources(false); - if (&res == NULL) { - fprintf(stderr, "ERROR: dump failed because no resource table was found\n"); - return 1; - } else if (res.getError() != NO_ERROR) { + if (res.getError() != NO_ERROR) { fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n"); return 1; } diff --git a/tools/aapt/CrunchCache.cpp b/tools/aapt/CrunchCache.cpp index c4cf6bc8494f..6c39d1d7753c 100644 --- a/tools/aapt/CrunchCache.cpp +++ b/tools/aapt/CrunchCache.cpp @@ -101,4 +101,4 @@ bool CrunchCache::needsUpdating(String8 relativePath) const time_t sourceDate = mSourceFiles.valueFor(mSourcePath.appendPathCopy(relativePath)); time_t destDate = mDestFiles.valueFor(mDestPath.appendPathCopy(relativePath)); return sourceDate > destDate; -}
\ No newline at end of file +} diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index 5d4a6ac8e0c6..063b4e6801a0 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -14,7 +14,8 @@ #include <png.h> #include <zlib.h> -#define NOISY(x) //x +// Change this to true for noisy debug output. +static const bool kIsDebug = false; static void png_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length) @@ -28,7 +29,7 @@ png_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length) static void -png_flush_aapt_file(png_structp png_ptr) +png_flush_aapt_file(png_structp /* png_ptr */) { } @@ -156,11 +157,13 @@ static void read_png(const char* imageName, png_read_end(read_ptr, read_info); - NOISY(printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n", - imageName, - (int)outImageInfo->width, (int)outImageInfo->height, - bit_depth, color_type, - interlace_type, compression_type)); + if (kIsDebug) { + printf("Image %s: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n", + imageName, + (int)outImageInfo->width, (int)outImageInfo->height, + bit_depth, color_type, + interlace_type, compression_type); + } png_get_IHDR(read_ptr, read_info, &outImageInfo->width, &outImageInfo->height, &bit_depth, &color_type, @@ -330,7 +333,7 @@ static status_t get_vertical_ticks( } static status_t get_horizontal_layout_bounds_ticks( - png_bytep row, int width, bool transparent, bool required, + png_bytep row, int width, bool transparent, bool /* required */, int32_t* outLeft, int32_t* outRight, const char** outError) { int i; @@ -368,7 +371,7 @@ static status_t get_horizontal_layout_bounds_ticks( } static status_t get_vertical_layout_bounds_ticks( - png_bytepp rows, int offset, int height, bool transparent, bool required, + png_bytepp rows, int offset, int height, bool transparent, bool /* required */, int32_t* outTop, int32_t* outBottom, const char** outError) { int i; @@ -478,8 +481,9 @@ static void get_outline(image_info* image) // assuming the image is a round rect, compute the radius by marching // diagonally from the top left corner towards the center - image->outlineAlpha = max(max_alpha_over_row(image->rows[innerMidY], innerStartX, innerEndX), - max_alpha_over_col(image->rows, innerMidX, innerStartY, innerStartY)); + image->outlineAlpha = std::max( + max_alpha_over_row(image->rows[innerMidY], innerStartX, innerEndX), + max_alpha_over_col(image->rows, innerMidX, innerStartY, innerStartY)); int diagonalInset = 0; find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1, @@ -493,13 +497,15 @@ static void get_outline(image_info* image) */ image->outlineRadius = 3.4142f * diagonalInset; - NOISY(printf("outline insets %d %d %d %d, rad %f, alpha %x\n", - image->outlineInsetsLeft, - image->outlineInsetsTop, - image->outlineInsetsRight, - image->outlineInsetsBottom, - image->outlineRadius, - image->outlineAlpha)); + if (kIsDebug) { + printf("outline insets %d %d %d %d, rad %f, alpha %x\n", + image->outlineInsetsLeft, + image->outlineInsetsTop, + image->outlineInsetsRight, + image->outlineInsetsBottom, + image->outlineRadius, + image->outlineAlpha); + } } @@ -533,41 +539,6 @@ static uint32_t get_color( return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2]; } -static void select_patch( - int which, int front, int back, int size, int* start, int* end) -{ - switch (which) { - case 0: - *start = 0; - *end = front-1; - break; - case 1: - *start = front; - *end = back-1; - break; - case 2: - *start = back; - *end = size-1; - break; - } -} - -static uint32_t get_color(image_info* image, int hpatch, int vpatch) -{ - int left, right, top, bottom; - select_patch( - hpatch, image->xDivs[0], image->xDivs[1], - image->width, &left, &right); - select_patch( - vpatch, image->yDivs[0], image->yDivs[1], - image->height, &top, &bottom); - //printf("Selecting h=%d v=%d: (%d,%d)-(%d,%d)\n", - // hpatch, vpatch, left, top, right, bottom); - const uint32_t c = get_color(image->rows, left, top, right, bottom); - NOISY(printf("Color in (%d,%d)-(%d,%d): #%08x\n", left, top, right, bottom, c)); - return c; -} - static status_t do_9patch(const char* imageName, image_info* image) { image->is9Patch = true; @@ -672,8 +643,10 @@ static status_t do_9patch(const char* imageName, image_info* image) || image->layoutBoundsBottom != 0; if (image->haveLayoutBounds) { - NOISY(printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop, - image->layoutBoundsRight, image->layoutBoundsBottom)); + if (kIsDebug) { + printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop, + image->layoutBoundsRight, image->layoutBoundsBottom); + } } // use opacity of pixels to estimate the round rect outline @@ -695,12 +668,14 @@ static status_t do_9patch(const char* imageName, image_info* image) image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom; } - NOISY(printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName, - xDivs[0], xDivs[1], - yDivs[0], yDivs[1])); - NOISY(printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName, - image->info9Patch.paddingLeft, image->info9Patch.paddingRight, - image->info9Patch.paddingTop, image->info9Patch.paddingBottom)); + if (kIsDebug) { + printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName, + xDivs[0], xDivs[1], + yDivs[0], yDivs[1]); + printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName, + image->info9Patch.paddingLeft, image->info9Patch.paddingRight, + image->info9Patch.paddingTop, image->info9Patch.paddingBottom); + } // Remove frame from image. image->rows = (png_bytepp)malloc((H-2) * sizeof(png_bytep)); @@ -782,7 +757,10 @@ static status_t do_9patch(const char* imageName, image_info* image) } c = get_color(image->rows, left, top, right - 1, bottom - 1); image->colors[colorIndex++] = c; - NOISY(if (c != Res_png_9patch::NO_COLOR) hasColor = true); + if (kIsDebug) { + if (c != Res_png_9patch::NO_COLOR) + hasColor = true; + } left = right; } top = bottom; @@ -885,7 +863,7 @@ static void dump_image(int w, int h, png_bytepp rows, int color_type) break; } if (i == (w - 1)) { - NOISY(printf("\n")); + printf("\n"); } } } @@ -915,8 +893,10 @@ static void analyze_image(const char *imageName, image_info &imageInfo, int gray // 2. Every pixel has A == 255 (opaque) // 3. There are no more than 256 distinct RGBA colors - // NOISY(printf("Initial image data:\n")); - // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA); + if (kIsDebug) { + printf("Initial image data:\n"); + dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA); + } for (j = 0; j < h; j++) { png_bytep row = imageInfo.rows[j]; @@ -932,15 +912,19 @@ static void analyze_image(const char *imageName, image_info &imageInfo, int gray maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation); maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation); if (maxGrayDeviation > odev) { - NOISY(printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n", - maxGrayDeviation, i, j, rr, gg, bb, aa)); + if (kIsDebug) { + printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n", + maxGrayDeviation, i, j, rr, gg, bb, aa); + } } // Check if image is really grayscale if (isGrayscale) { if (rr != gg || rr != bb) { - NOISY(printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n", - i, j, rr, gg, bb, aa)); + if (kIsDebug) { + printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n", + i, j, rr, gg, bb, aa); + } isGrayscale = false; } } @@ -948,8 +932,10 @@ static void analyze_image(const char *imageName, image_info &imageInfo, int gray // Check if image is really opaque if (isOpaque) { if (aa != 0xff) { - NOISY(printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", - i, j, rr, gg, bb, aa)); + if (kIsDebug) { + printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", + i, j, rr, gg, bb, aa); + } isOpaque = false; } } @@ -971,7 +957,9 @@ static void analyze_image(const char *imageName, image_info &imageInfo, int gray *out++ = idx; if (!match) { if (num_colors == 256) { - NOISY(printf("Found 257th color at %d, %d\n", i, j)); + if (kIsDebug) { + printf("Found 257th color at %d, %d\n", i, j); + } isPalette = false; } else { colors[num_colors++] = col; @@ -986,12 +974,14 @@ static void analyze_image(const char *imageName, image_info &imageInfo, int gray int bpp = isOpaque ? 3 : 4; int paletteSize = w * h + bpp * num_colors; - NOISY(printf("isGrayscale = %s\n", isGrayscale ? "true" : "false")); - NOISY(printf("isOpaque = %s\n", isOpaque ? "true" : "false")); - NOISY(printf("isPalette = %s\n", isPalette ? "true" : "false")); - NOISY(printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n", - paletteSize, 2 * w * h, bpp * w * h)); - NOISY(printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance)); + if (kIsDebug) { + printf("isGrayscale = %s\n", isGrayscale ? "true" : "false"); + printf("isOpaque = %s\n", isOpaque ? "true" : "false"); + printf("isPalette = %s\n", isPalette ? "true" : "false"); + printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n", + paletteSize, 2 * w * h, bpp * w * h); + printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance); + } // Choose the best color type for the image. // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel @@ -1068,7 +1058,6 @@ static void write_png(const char* imageName, png_structp write_ptr, png_infop write_info, image_info& imageInfo, int grayscaleTolerance) { - bool optimize = true; png_uint_32 width, height; int color_type; int bit_depth, interlace_type, compression_type; @@ -1094,8 +1083,10 @@ static void write_png(const char* imageName, png_set_compression_level(write_ptr, Z_BEST_COMPRESSION); - NOISY(printf("Writing image %s: w = %d, h = %d\n", imageName, - (int) imageInfo.width, (int) imageInfo.height)); + if (kIsDebug) { + printf("Writing image %s: w = %d, h = %d\n", imageName, + (int) imageInfo.width, (int) imageInfo.height); + } png_color rgbPalette[256]; png_byte alphaPalette[256]; @@ -1112,24 +1103,26 @@ static void write_png(const char* imageName, color_type = PNG_COLOR_TYPE_RGB_ALPHA; } - switch (color_type) { - case PNG_COLOR_TYPE_PALETTE: - NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n", - imageName, paletteEntries, - hasTransparency ? " (with alpha)" : "")); - break; - case PNG_COLOR_TYPE_GRAY: - NOISY(printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName)); - break; - case PNG_COLOR_TYPE_GRAY_ALPHA: - NOISY(printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName)); - break; - case PNG_COLOR_TYPE_RGB: - NOISY(printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName)); - break; - case PNG_COLOR_TYPE_RGB_ALPHA: - NOISY(printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName)); - break; + if (kIsDebug) { + switch (color_type) { + case PNG_COLOR_TYPE_PALETTE: + printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n", + imageName, paletteEntries, + hasTransparency ? " (with alpha)" : ""); + break; + case PNG_COLOR_TYPE_GRAY: + printf("Image %s is opaque gray, using PNG_COLOR_TYPE_GRAY\n", imageName); + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + printf("Image %s is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA\n", imageName); + break; + case PNG_COLOR_TYPE_RGB: + printf("Image %s is opaque RGB, using PNG_COLOR_TYPE_RGB\n", imageName); + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + printf("Image %s is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA\n", imageName); + break; + } } png_set_IHDR(write_ptr, write_info, imageInfo.width, imageInfo.height, @@ -1158,7 +1151,9 @@ static void write_png(const char* imageName, : (png_byte*)"npOl\0npTc"; // base 9 patch data - NOISY(printf("Adding 9-patch info...\n")); + if (kIsDebug) { + printf("Adding 9-patch info...\n"); + } strcpy((char*)unknowns[p_index].name, "npTc"); unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch(); unknowns[p_index].size = imageInfo.info9Patch.serializedSize(); @@ -1214,8 +1209,10 @@ static void write_png(const char* imageName, } png_write_image(write_ptr, rows); -// NOISY(printf("Final image data:\n")); -// dump_image(imageInfo.width, imageInfo.height, rows, color_type); + if (kIsDebug) { + printf("Final image data:\n"); + dump_image(imageInfo.width, imageInfo.height, rows, color_type); + } png_write_end(write_ptr, write_info); @@ -1231,13 +1228,50 @@ static void write_png(const char* imageName, &bit_depth, &color_type, &interlace_type, &compression_type, NULL); - NOISY(printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n", - (int)width, (int)height, bit_depth, color_type, interlace_type, - compression_type)); + if (kIsDebug) { + printf("Image written: w=%d, h=%d, d=%d, colors=%d, inter=%d, comp=%d\n", + (int)width, (int)height, bit_depth, color_type, interlace_type, + compression_type); + } } -status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets, - const sp<AaptFile>& file, String8* outNewLeafName) +static bool read_png_protected(png_structp read_ptr, String8& printableName, png_infop read_info, + const sp<AaptFile>& file, FILE* fp, image_info* imageInfo) { + if (setjmp(png_jmpbuf(read_ptr))) { + return false; + } + + png_init_io(read_ptr, fp); + + read_png(printableName.string(), read_ptr, read_info, imageInfo); + + const size_t nameLen = file->getPath().length(); + if (nameLen > 6) { + const char* name = file->getPath().string(); + if (name[nameLen-5] == '9' && name[nameLen-6] == '.') { + if (do_9patch(printableName.string(), imageInfo) != NO_ERROR) { + return false; + } + } + } + + return true; +} + +static bool write_png_protected(png_structp write_ptr, String8& printableName, png_infop write_info, + image_info* imageInfo, const Bundle* bundle) { + if (setjmp(png_jmpbuf(write_ptr))) { + return false; + } + + write_png(printableName.string(), write_ptr, write_info, *imageInfo, + bundle->getGrayscaleTolerance()); + + return true; +} + +status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets */, + const sp<AaptFile>& file, String8* /* outNewLeafName */) { String8 ext(file->getPath().getPathExtension()); @@ -1267,8 +1301,6 @@ status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets, status_t error = UNKNOWN_ERROR; - const size_t nameLen = file->getPath().length(); - fp = fopen(file->getSourceFile().string(), "rb"); if (fp == NULL) { fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string()); @@ -1286,23 +1318,10 @@ status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets, goto bail; } - if (setjmp(png_jmpbuf(read_ptr))) { + if (!read_png_protected(read_ptr, printableName, read_info, file, fp, &imageInfo)) { goto bail; } - png_init_io(read_ptr, fp); - - read_png(printableName.string(), read_ptr, read_info, &imageInfo); - - if (nameLen > 6) { - const char* name = file->getPath().string(); - if (name[nameLen-5] == '9' && name[nameLen-6] == '.') { - if (do_9patch(printableName.string(), &imageInfo) != NO_ERROR) { - goto bail; - } - } - } - write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL, (png_error_ptr)NULL); if (!write_ptr) @@ -1319,14 +1338,10 @@ status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets, png_set_write_fn(write_ptr, (void*)file.get(), png_write_aapt_file, png_flush_aapt_file); - if (setjmp(png_jmpbuf(write_ptr))) - { + if (!write_png_protected(write_ptr, printableName, write_info, &imageInfo, bundle)) { goto bail; } - write_png(printableName.string(), write_ptr, write_info, imageInfo, - bundle->getGrayscaleTolerance()); - error = NO_ERROR; if (bundle->getVerbose()) { diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 18b8e1ecfe49..8b416aa82fdb 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -69,7 +69,7 @@ void usage(void) " [-S resource-sources [-S resource-sources ...]] \\\n" " [-F apk-file] [-J R-file-dir] \\\n" " [--product product1,product2,...] \\\n" - " [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n" + " [-c CONFIGS] [--preferred-density DENSITY] \\\n" " [--split CONFIGS [--split CONFIGS]] \\\n" " [--feature-of package [--feature-after package]] \\\n" " [raw-files-dir [raw-files-dir] ...] \\\n" diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index dc16e354b584..cb244eccfe21 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -402,7 +402,6 @@ bool endsWith(const char* haystack, const char* needle) ssize_t processJarFile(ZipFile* jar, ZipFile* out) { - status_t err; size_t N = jar->getNumEntries(); size_t count = 0; for (size_t i=0; i<N; i++) { diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index e2e83e493e7d..36299c2c3a5b 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -20,15 +20,20 @@ #include <algorithm> +// STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary. + #if HAVE_PRINTF_ZD # define ZD "%zd" # define ZD_TYPE ssize_t +# define STATUST(x) x #else # define ZD "%ld" # define ZD_TYPE long +# define STATUST(x) (status_t)x #endif -#define NOISY(x) // x +// Set to true for noisy debug output. +static const bool kIsDebug = false; // Number of threads to use for preprocessing images. static const size_t MAX_THREADS = 4; @@ -129,15 +134,17 @@ public: String8 leaf(group->getLeaf()); mLeafName = String8(leaf); mParams = file->getGroupEntry().toParams(); - NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n", - group->getPath().string(), mParams.mcc, mParams.mnc, - mParams.language[0] ? mParams.language[0] : '-', - mParams.language[1] ? mParams.language[1] : '-', - mParams.country[0] ? mParams.country[0] : '-', - mParams.country[1] ? mParams.country[1] : '-', - mParams.orientation, mParams.uiMode, - mParams.density, mParams.touchscreen, mParams.keyboard, - mParams.inputFlags, mParams.navigation)); + if (kIsDebug) { + printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n", + group->getPath().string(), mParams.mcc, mParams.mnc, + mParams.language[0] ? mParams.language[0] : '-', + mParams.language[1] ? mParams.language[1] : '-', + mParams.country[0] ? mParams.country[0] : '-', + mParams.country[1] ? mParams.country[1] : '-', + mParams.orientation, mParams.uiMode, + mParams.density, mParams.touchscreen, mParams.keyboard, + mParams.inputFlags, mParams.navigation); + } mPath = "res"; mPath.appendPath(file->getGroupEntry().toDirName(mResType)); mPath.appendPath(leaf); @@ -148,7 +155,9 @@ public: return UNKNOWN_ERROR; } - NOISY(printf("file name=%s\n", mBaseName.string())); + if (kIsDebug) { + printf("file name=%s\n", mBaseName.string()); + } return NO_ERROR; } @@ -326,7 +335,7 @@ static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets, assets->addResource(it.getLeafName(), resPath, it.getFile(), type8); } - return hasErrors ? UNKNOWN_ERROR : NO_ERROR; + return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; } class PreProcessImageWorkUnit : public WorkQueue::WorkUnit { @@ -376,7 +385,7 @@ static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& ass hasErrors = true; } } - return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; + return (hasErrors || (res < NO_ERROR)) ? STATUST(UNKNOWN_ERROR) : NO_ERROR; } static void collect_files(const sp<AaptDir>& dir, @@ -401,27 +410,35 @@ static void collect_files(const sp<AaptDir>& dir, if (index < 0) { sp<ResourceTypeSet> set = new ResourceTypeSet(); - NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n", - leafName.string(), group->getPath().string(), group.get())); + if (kIsDebug) { + printf("Creating new resource type set for leaf %s with group %s (%p)\n", + leafName.string(), group->getPath().string(), group.get()); + } set->add(leafName, group); resources->add(resType, set); } else { sp<ResourceTypeSet> set = resources->valueAt(index); index = set->indexOfKey(leafName); if (index < 0) { - NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n", - leafName.string(), group->getPath().string(), group.get())); + if (kIsDebug) { + printf("Adding to resource type set for leaf %s group %s (%p)\n", + leafName.string(), group->getPath().string(), group.get()); + } set->add(leafName, group); } else { sp<AaptGroup> existingGroup = set->valueAt(index); - NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n", - leafName.string(), group->getPath().string(), group.get())); + if (kIsDebug) { + printf("Extending to resource type set for leaf %s group %s (%p)\n", + leafName.string(), group->getPath().string(), group.get()); + } for (size_t j=0; j<files.size(); j++) { - NOISY(printf("Adding file %s in group %s resType %s\n", - files.valueAt(j)->getSourceFile().string(), - files.keyAt(j).toDirName(String8()).string(), - resType.string())); - status_t err = existingGroup->addFile(files.valueAt(j)); + if (kIsDebug) { + printf("Adding file %s in group %s resType %s\n", + files.valueAt(j)->getSourceFile().string(), + files.keyAt(j).toDirName(String8()).string(), + resType.string()); + } + existingGroup->addFile(files.valueAt(j)); } } } @@ -436,12 +453,16 @@ static void collect_files(const sp<AaptAssets>& ass, for (int i=0; i<N; i++) { sp<AaptDir> d = dirs.itemAt(i); - NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(), - d->getLeaf().string())); + if (kIsDebug) { + printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(), + d->getLeaf().string()); + } collect_files(d, resources); // don't try to include the res dir - NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string())); + if (kIsDebug) { + printf("Removing dir leaf %s\n", d->getLeaf().string()); + } ass->removeDir(d->getLeaf()); } } @@ -536,7 +557,7 @@ static int validateAttr(const String8& path, const ResTable& table, String8(parser.getElementName(&len)).string(), attr); return ATTR_LEADING_SPACES; } - if (str[len-1] == ' ') { + if (len != 0 && str[len-1] == ' ') { fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n", path.string(), parser.getLineNumber(), String8(parser.getElementName(&len)).string(), attr); @@ -699,9 +720,11 @@ bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, XMLNode::attribute_entry* existingEntry = node->editAttribute(ns, attr); if (existingEntry != NULL) { if (replaceExisting) { - NOISY(printf("Info: AndroidManifest.xml already defines %s (in %s);" - " overwriting existing value from manifest.\n", - String8(attr).string(), String8(ns).string())); + if (kIsDebug) { + printf("Info: AndroidManifest.xml already defines %s (in %s);" + " overwriting existing value from manifest.\n", + String8(attr).string(), String8(ns).string()); + } existingEntry->string = String16(value); return true; } @@ -760,7 +783,9 @@ static void fullyQualifyClassName(const String8& package, sp<XMLNode> node, } else { className += name; } - NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string())); + if (kIsDebug) { + printf("Qualifying class '%s' to '%s'", name.string(), className.string()); + } attr->string.setTo(String16(className)); } } @@ -864,7 +889,10 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) } String8 origPackage(attr->string); attr->string.setTo(String16(manifestPackageNameOverride)); - NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride)); + if (kIsDebug) { + printf("Overriding package '%s' to be '%s'\n", origPackage.string(), + manifestPackageNameOverride); + } // Make class names fully qualified sp<XMLNode> application = root->getChildElement(String16(), String16("application")); @@ -1129,8 +1157,9 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil return err; } - NOISY(printf("Creating resources for package %s\n", - assets->getPackage().string())); + if (kIsDebug) { + printf("Creating resources for package %s\n", assets->getPackage().string()); + } ResourceTable::PackageType packageType = ResourceTable::App; if (bundle->getBuildSharedLibrary()) { @@ -1147,7 +1176,9 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil return err; } - NOISY(printf("Found %d included resource packages\n", (int)table.size())); + if (kIsDebug) { + printf("Found %d included resource packages\n", (int)table.size()); + } // Standard flags for compiled XML and optional UTF-8 encoding int xmlFlags = XML_COMPILE_STANDARD_RESOURCE; @@ -2052,7 +2083,7 @@ static String16 getAttributeComment(const sp<AaptAssets>& assets, static status_t writeResourceLoadedCallbackForLayoutClasses( FILE* fp, const sp<AaptAssets>& assets, - const sp<AaptSymbols>& symbols, int indent, bool includePrivate) + const sp<AaptSymbols>& symbols, int indent, bool /* includePrivate */) { String16 attr16("attr"); String16 package16(assets->getPackage()); @@ -2356,7 +2387,7 @@ static status_t writeLayoutClasses( indent--; fprintf(fp, "%s};\n", getIndentSpace(indent)); - return hasErrors ? UNKNOWN_ERROR : NO_ERROR; + return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; } static status_t writeTextLayoutClasses( @@ -2442,7 +2473,7 @@ static status_t writeTextLayoutClasses( package16.string(), package16.size(), &typeSpecFlags); //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), // String8(attr16).string(), String8(name16).string(), typeSpecFlags); - const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; + //const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; fprintf(fp, "int styleable %s_%s %d\n", @@ -2452,7 +2483,7 @@ static status_t writeTextLayoutClasses( } } - return hasErrors ? UNKNOWN_ERROR : NO_ERROR; + return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; } static status_t writeSymbolClass( @@ -2783,7 +2814,7 @@ addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName, void addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName, - const char* pkg, const String8& srcName, int line) + const char* /* pkg */, const String8& srcName, int line) { String8 rule("-keepclassmembers class * { *** "); rule += memberName; @@ -3123,7 +3154,7 @@ status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp) } status_t -writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw) +writeDependencyPreReqs(Bundle* /* bundle */, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw) { status_t deps = -1; deps += writePathsToFile(assets->getFullResPaths(), fp); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index e000a1dc1feb..941a2884e2a6 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -18,7 +18,24 @@ #include <utils/TypeHelpers.h> #include <stdarg.h> -#define NOISY(x) //x +// SSIZE: mingw does not have signed size_t == ssize_t. +// STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary. +#if HAVE_PRINTF_ZD +# define SSIZE(x) x +# define STATUST(x) x +#else +# define SSIZE(x) (signed size_t)x +# define STATUST(x) (status_t)x +#endif + +// Set to true for noisy debug output. +static const bool kIsDebug = false; + +#if PRINT_STRING_METRICS +static const bool kPrintStringMetrics = true; +#else +static const bool kPrintStringMetrics = false; +#endif static const char* kAttrPrivateType = "^attr-private"; @@ -92,9 +109,11 @@ status_t compileXmlFile(const Bundle* bundle, if (table->modifyForCompat(bundle, resourceName, target, root) != NO_ERROR) { return UNKNOWN_ERROR; } - - NOISY(printf("Input XML Resource:\n")); - NOISY(root->print()); + + if (kIsDebug) { + printf("Input XML Resource:\n"); + root->print(); + } err = root->flatten(target, (options&XML_COMPILE_STRIP_COMMENTS) != 0, (options&XML_COMPILE_STRIP_RAW_VALUES) != 0); @@ -102,19 +121,18 @@ status_t compileXmlFile(const Bundle* bundle, return err; } - NOISY(printf("Output XML Resource:\n")); - NOISY(ResXMLTree tree; + if (kIsDebug) { + printf("Output XML Resource:\n"); + ResXMLTree tree; tree.setTo(target->getData(), target->getSize()); - printXMLBlock(&tree)); + printXMLBlock(&tree); + } target->setCompressionMethod(ZipEntry::kCompressDeflated); return err; } -#undef NOISY -#define NOISY(x) //x - struct flag_entry { const char16_t* name; @@ -583,7 +601,7 @@ status_t parseAndAddBag(Bundle* bundle, const String16& itemIdent, int32_t curFormat, bool isFormatted, - const String16& product, + const String16& /* product */, PseudolocalizationMethod pseudolocalize, const bool overwrite, ResourceTable* outTable) @@ -599,16 +617,18 @@ status_t parseAndAddBag(Bundle* bundle, if (err != NO_ERROR) { return err; } - - NOISY(printf("Adding resource bag entry l=%c%c c=%c%c orien=%d d=%d " - " pid=%s, bag=%s, id=%s: %s\n", - config.language[0], config.language[1], - config.country[0], config.country[1], - config.orientation, config.density, - String8(parentIdent).string(), - String8(ident).string(), - String8(itemIdent).string(), - String8(str).string())); + + if (kIsDebug) { + printf("Adding resource bag entry l=%c%c c=%c%c orien=%d d=%d " + " pid=%s, bag=%s, id=%s: %s\n", + config.language[0], config.language[1], + config.country[0], config.country[1], + config.orientation, config.density, + String8(parentIdent).string(), + String8(ident).string(), + String8(itemIdent).string(), + String8(str).string()); + } err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()), myPackage, curType, ident, parentIdent, itemIdent, str, @@ -744,11 +764,13 @@ status_t parseAndAddEntry(Bundle* bundle, } } - NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n", - config.language[0], config.language[1], - config.country[0], config.country[1], - config.orientation, config.density, - String8(ident).string(), String8(str).string())); + if (kIsDebug) { + printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n", + config.language[0], config.language[1], + config.country[0], config.country[1], + config.orientation, config.density, + String8(ident).string(), String8(str).string()); + } err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()), myPackage, curType, ident, str, &spans, &config, @@ -1717,7 +1739,7 @@ status_t compileResourceFile(Bundle* bundle, } } - return hasErrors ? UNKNOWN_ERROR : NO_ERROR; + return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; } ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage, ResourceTable::PackageType type) @@ -1785,7 +1807,7 @@ status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets } const ResTable& featureTable = featureAssetManager.getResources(false); - mTypeIdOffset = max(mTypeIdOffset, + mTypeIdOffset = std::max(mTypeIdOffset, findLargestTypeIdForPackage(featureTable, mAssetsPackage)); } @@ -1856,7 +1878,7 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, const String16& bagParent, const ResTable_config* params, bool overlay, - bool replace, bool isId) + bool replace, bool /* isId */) { status_t result = NO_ERROR; @@ -2163,22 +2185,25 @@ uint32_t ResourceTable::getResId(const String16& ref, ref.string(), ref.size(), &package, &type, &name, defType, defPackage ? defPackage:&mAssetsPackage, outErrorMsg, &refOnlyPublic)) { - NOISY(printf("Expanding resource: ref=%s\n", - String8(ref).string())); - NOISY(printf("Expanding resource: defType=%s\n", - defType ? String8(*defType).string() : "NULL")); - NOISY(printf("Expanding resource: defPackage=%s\n", - defPackage ? String8(*defPackage).string() : "NULL")); - NOISY(printf("Expanding resource: ref=%s\n", String8(ref).string())); - NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n", - String8(package).string(), String8(type).string(), - String8(name).string())); + if (kIsDebug) { + printf("Expanding resource: ref=%s\n", String8(ref).string()); + printf("Expanding resource: defType=%s\n", + defType ? String8(*defType).string() : "NULL"); + printf("Expanding resource: defPackage=%s\n", + defPackage ? String8(*defPackage).string() : "NULL"); + printf("Expanding resource: ref=%s\n", String8(ref).string()); + printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n", + String8(package).string(), String8(type).string(), + String8(name).string()); + } return 0; } uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic); - NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n", - String8(package).string(), String8(type).string(), - String8(name).string(), res)); + if (kIsDebug) { + printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n", + String8(package).string(), String8(type).string(), + String8(name).string(), res); + } if (res == 0) { if (outErrorMsg) *outErrorMsg = "No resource found that matches the given name"; @@ -2245,9 +2270,11 @@ bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool, } else { configStr = "(null)"; } - NOISY(printf("Adding to pool string style #%d config %s: %s\n", - style != NULL ? style->size() : 0, - configStr.string(), String8(finalStr).string())); + if (kIsDebug) { + printf("Adding to pool string style #%zu config %s: %s\n", + style != NULL ? style->size() : 0U, + configStr.string(), String8(finalStr).string()); + } if (style != NULL && style->size() > 0) { outValue->data = pool->add(finalStr, *style, configTypeName, config); } else { @@ -2676,20 +2703,16 @@ ResourceTable::validateLocalizations(void) const String8 defaultLocale; // For all strings... - for (map<String16, map<String8, SourcePos> >::iterator nameIter = mLocalizations.begin(); - nameIter != mLocalizations.end(); - nameIter++) { - const map<String8, SourcePos>& configSrcMap = nameIter->second; + for (const auto& nameIter : mLocalizations) { + const std::map<String8, SourcePos>& configSrcMap = nameIter.second; // Look for strings with no default localization if (configSrcMap.count(defaultLocale) == 0) { SourcePos().warning("string '%s' has no default translation.", - String8(nameIter->first).string()); + String8(nameIter.first).string()); if (mBundle->getVerbose()) { - for (map<String8, SourcePos>::const_iterator locales = configSrcMap.begin(); - locales != configSrcMap.end(); - locales++) { - locales->second.printf("locale %s found", locales->first.string()); + for (const auto& locale : configSrcMap) { + locale.second.printf("locale %s found", locale.first.string()); } } // !!! TODO: throw an error here in some circumstances @@ -2700,8 +2723,8 @@ ResourceTable::validateLocalizations(void) const char* allConfigs = mBundle->getConfigurations().string(); const char* start = allConfigs; const char* comma; - - set<String8> missingConfigs; + + std::set<String8> missingConfigs; AaptLocaleValue locale; do { String8 config; @@ -2735,13 +2758,11 @@ ResourceTable::validateLocalizations(void) if (!missingConfigs.empty()) { String8 configStr; - for (set<String8>::iterator iter = missingConfigs.begin(); - iter != missingConfigs.end(); - iter++) { - configStr.appendFormat(" %s", iter->string()); + for (const auto& iter : missingConfigs) { + configStr.appendFormat(" %s", iter.string()); } SourcePos().warning("string '%s' is missing %u required localizations:%s", - String8(nameIter->first).string(), + String8(nameIter.first).string(), (unsigned int)missingConfigs.size(), configStr.string()); } @@ -2914,9 +2935,9 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& const size_t typeStringsStart = data->getSize(); sp<AaptFile> strFile = p->getTypeStringsData(); ssize_t amt = data->writeData(strFile->getData(), strFile->getSize()); - #if PRINT_STRING_METRICS - fprintf(stderr, "**** type strings: %d\n", amt); - #endif + if (kPrintStringMetrics) { + fprintf(stderr, "**** type strings: %zd\n", SSIZE(amt)); + } strAmt += amt; if (amt < 0) { return amt; @@ -2924,9 +2945,9 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& const size_t keyStringsStart = data->getSize(); strFile = p->getKeyStringsData(); amt = data->writeData(strFile->getData(), strFile->getSize()); - #if PRINT_STRING_METRICS - fprintf(stderr, "**** key strings: %d\n", amt); - #endif + if (kPrintStringMetrics) { + fprintf(stderr, "**** key strings: %zd\n", SSIZE(amt)); + } strAmt += amt; if (amt < 0) { return amt; @@ -3022,36 +3043,41 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& // We need to write one type chunk for each configuration for // which we have entries in this type. - const SortedVector<ConfigDescription> uniqueConfigs(t->getUniqueConfigs()); - const size_t NC = uniqueConfigs.size(); + SortedVector<ConfigDescription> uniqueConfigs; + if (t != NULL) { + uniqueConfigs = t->getUniqueConfigs(); + } const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N; + const size_t NC = uniqueConfigs.size(); for (size_t ci=0; ci<NC; ci++) { const ConfigDescription& config = uniqueConfigs[ci]; - NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " - "sw%ddp w%ddp h%ddp dir:%d\n", - ti+1, - config.mcc, config.mnc, - config.language[0] ? config.language[0] : '-', - config.language[1] ? config.language[1] : '-', - config.country[0] ? config.country[0] : '-', - config.country[1] ? config.country[1] : '-', - config.orientation, - config.uiMode, - config.touchscreen, - config.density, - config.keyboard, - config.inputFlags, - config.navigation, - config.screenWidth, - config.screenHeight, - config.smallestScreenWidthDp, - config.screenWidthDp, - config.screenHeightDp, - config.layoutDirection)); + if (kIsDebug) { + printf("Writing config %zu config: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " + "sw%ddp w%ddp h%ddp layout:%d\n", + ti + 1, + config.mcc, config.mnc, + config.language[0] ? config.language[0] : '-', + config.language[1] ? config.language[1] : '-', + config.country[0] ? config.country[0] : '-', + config.country[1] ? config.country[1] : '-', + config.orientation, + config.uiMode, + config.touchscreen, + config.density, + config.keyboard, + config.inputFlags, + config.navigation, + config.screenWidth, + config.screenHeight, + config.smallestScreenWidthDp, + config.screenWidthDp, + config.screenHeightDp, + config.screenLayout); + } if (filterable && !filter->match(config)) { continue; @@ -3073,28 +3099,30 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& tHeader->entryCount = htodl(N); tHeader->entriesStart = htodl(typeSize); tHeader->config = config; - NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " - "sw%ddp w%ddp h%ddp dir:%d\n", - ti+1, - tHeader->config.mcc, tHeader->config.mnc, - tHeader->config.language[0] ? tHeader->config.language[0] : '-', - tHeader->config.language[1] ? tHeader->config.language[1] : '-', - tHeader->config.country[0] ? tHeader->config.country[0] : '-', - tHeader->config.country[1] ? tHeader->config.country[1] : '-', - tHeader->config.orientation, - tHeader->config.uiMode, - tHeader->config.touchscreen, - tHeader->config.density, - tHeader->config.keyboard, - tHeader->config.inputFlags, - tHeader->config.navigation, - tHeader->config.screenWidth, - tHeader->config.screenHeight, - tHeader->config.smallestScreenWidthDp, - tHeader->config.screenWidthDp, - tHeader->config.screenHeightDp, - tHeader->config.layoutDirection)); + if (kIsDebug) { + printf("Writing type %zu config: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " + "sw%ddp w%ddp h%ddp layout:%d\n", + ti + 1, + tHeader->config.mcc, tHeader->config.mnc, + tHeader->config.language[0] ? tHeader->config.language[0] : '-', + tHeader->config.language[1] ? tHeader->config.language[1] : '-', + tHeader->config.country[0] ? tHeader->config.country[0] : '-', + tHeader->config.country[1] ? tHeader->config.country[1] : '-', + tHeader->config.orientation, + tHeader->config.uiMode, + tHeader->config.touchscreen, + tHeader->config.density, + tHeader->config.keyboard, + tHeader->config.inputFlags, + tHeader->config.navigation, + tHeader->config.screenWidth, + tHeader->config.screenHeight, + tHeader->config.smallestScreenWidthDp, + tHeader->config.screenWidthDp, + tHeader->config.screenHeightDp, + tHeader->config.screenLayout); + } tHeader->config.swapHtoD(); // Build the entries inside of this type. @@ -3189,10 +3217,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& ssize_t amt = (dest->getSize()-strStart); strAmt += amt; - #if PRINT_STRING_METRICS - fprintf(stderr, "**** value strings: %d\n", amt); - fprintf(stderr, "**** total strings: %d\n", strAmt); - #endif + if (kPrintStringMetrics) { + fprintf(stderr, "**** value strings: %zd\n", SSIZE(amt)); + fprintf(stderr, "**** total strings: %zd\n", SSIZE(strAmt)); + } for (pi=0; pi<flatPackages.size(); pi++) { err = dest->writeData(flatPackages[pi]->getData(), @@ -3207,13 +3235,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& (((uint8_t*)dest->getData()) + dataStart); header->header.size = htodl(dest->getSize() - dataStart); - NOISY(aout << "Resource table:" - << HexDump(dest->getData(), dest->getSize()) << endl); - - #if PRINT_STRING_METRICS - fprintf(stderr, "**** total resource table size: %d / %d%% strings\n", - dest->getSize(), (strAmt*100)/dest->getSize()); - #endif + if (kPrintStringMetrics) { + fprintf(stderr, "**** total resource table size: %zu / %zu%% strings\n", + dest->getSize(), (size_t)(strAmt*100)/dest->getSize()); + } return NO_ERROR; } @@ -3221,7 +3246,9 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs) { // Write out the library table if necessary if (libs.size() > 0) { - NOISY(fprintf(stderr, "Writing library reference table\n")); + if (kIsDebug) { + fprintf(stderr, "Writing library reference table\n"); + } const size_t libStart = dest->getSize(); const size_t count = libs.size(); @@ -3238,9 +3265,11 @@ status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vect for (size_t i = 0; i < count; i++) { const size_t entryStart = dest->getSize(); sp<Package> libPackage = libs[i]; - NOISY(fprintf(stderr, " Entry %s -> 0x%02x\n", + if (kIsDebug) { + fprintf(stderr, " Entry %s -> 0x%02x\n", String8(libPackage->getName()).string(), - (uint8_t)libPackage->getAssignedId())); + (uint8_t)libPackage->getAssignedId()); + } ResTable_lib_entry* entry = (ResTable_lib_entry*) dest->editDataInRange( entryStart, sizeof(ResTable_lib_entry)); @@ -3488,9 +3517,11 @@ status_t ResourceTable::Entry::generateAttributes(ResourceTable* table, if (it.isId) { if (!table->hasBagOrEntry(key, &id16, &package)) { String16 value("false"); - NOISY(fprintf(stderr, "Generating %s:id/%s\n", - String8(package).string(), - String8(key).string())); + if (kIsDebug) { + fprintf(stderr, "Generating %s:id/%s\n", + String8(package).string(), + String8(key).string()); + } status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package, id16, key, value); if (err != NO_ERROR) { @@ -3523,7 +3554,7 @@ status_t ResourceTable::Entry::generateAttributes(ResourceTable* table, } status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table, - const String16& package) + const String16& /* package */) { bool hasErrors = false; @@ -3556,7 +3587,7 @@ status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table, } } } - return hasErrors ? UNKNOWN_ERROR : NO_ERROR; + return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; } status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table, @@ -3615,22 +3646,20 @@ status_t ResourceTable::Entry::remapStringValue(StringPool* strings) return NO_ERROR; } -ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic) +ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp<AaptFile>& data, bool isPublic) { size_t amt = 0; ResTable_entry header; memset(&header, 0, sizeof(header)); header.size = htods(sizeof(header)); - const type ty = this != NULL ? mType : TYPE_ITEM; - if (this != NULL) { - if (ty == TYPE_BAG) { - header.flags |= htods(header.FLAG_COMPLEX); - } - if (isPublic) { - header.flags |= htods(header.FLAG_PUBLIC); - } - header.key.index = htodl(mNameIndex); + const type ty = mType; + if (ty == TYPE_BAG) { + header.flags |= htods(header.FLAG_COMPLEX); + } + if (isPublic) { + header.flags |= htods(header.FLAG_PUBLIC); } + header.key.index = htodl(mNameIndex); if (ty != TYPE_BAG) { status_t err = data->writeData(&header, sizeof(header)); if (err != NO_ERROR) { @@ -3805,10 +3834,11 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry, sp<Entry> e = c->getEntries().valueFor(cdesc); if (e == NULL) { - if (config != NULL) { - NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c " + if (kIsDebug) { + if (config != NULL) { + printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c " "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " - "sw%ddp w%ddp h%ddp dir:%d\n", + "sw%ddp w%ddp h%ddp layout:%d\n", sourcePos.file.string(), sourcePos.line, config->mcc, config->mnc, config->language[0] ? config->language[0] : '-', @@ -3826,10 +3856,11 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry, config->smallestScreenWidthDp, config->screenWidthDp, config->screenHeightDp, - config->layoutDirection)); - } else { - NOISY(printf("New entry at %s:%d: NULL config\n", - sourcePos.file.string(), sourcePos.line)); + config->screenLayout); + } else { + printf("New entry at %s:%d: NULL config\n", + sourcePos.file.string(), sourcePos.line); + } } e = new Entry(entry, sourcePos); c->addEntry(cdesc, e); @@ -3968,7 +3999,7 @@ status_t ResourceTable::Type::applyPublicEntryOrder() j++; } - return hasError ? UNKNOWN_ERROR : NO_ERROR; + return hasError ? STATUST(UNKNOWN_ERROR) : NO_ERROR; } ResourceTable::Package::Package(const String16& name, size_t packageId) @@ -4032,9 +4063,6 @@ status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data, return UNKNOWN_ERROR; } - NOISY(aout << "Setting restable string pool: " - << HexDump(data->getData(), data->getSize()) << endl); - status_t err = strings->setTo(data->getData(), data->getSize()); if (err == NO_ERROR) { const size_t N = strings->size(); @@ -4313,7 +4341,7 @@ bool ResourceTable::getItemValue( } item->evaluating = true; res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId); - NOISY( + if (kIsDebug) { if (res) { printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n", resID, attrID, String8(getEntry(resID)->getName()).string(), @@ -4322,7 +4350,7 @@ bool ResourceTable::getItemValue( printf("getItemValue of #%08x[#%08x]: failed\n", resID, attrID); } - ); + } item->evaluating = false; } return res; diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index eef0ae10e60d..96442248d296 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -17,8 +17,6 @@ #include "StringPool.h" #include "Symbol.h" -using namespace std; - class XMLNode; class ResourceTable; @@ -29,7 +27,7 @@ enum { XML_COMPILE_STRIP_WHITESPACE = 1<<3, XML_COMPILE_STRIP_RAW_VALUES = 1<<4, XML_COMPILE_UTF8 = 1<<5, - + XML_COMPILE_STANDARD_RESOURCE = XML_COMPILE_STRIP_COMMENTS | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES @@ -116,7 +114,7 @@ public: * and would mess up iteration order for the existing * resources. */ - queue<CompileResourceWorkItem>& getWorkQueue() { + std::queue<CompileResourceWorkItem>& getWorkQueue() { return mWorkQueue; } @@ -587,10 +585,10 @@ private: size_t mNumLocal; SourcePos mCurrentXmlPos; Bundle* mBundle; - + // key = string resource name, value = set of locales in which that name is defined - map<String16, map<String8, SourcePos> > mLocalizations; - queue<CompileResourceWorkItem> mWorkQueue; + std::map<String16, std::map<String8, SourcePos>> mLocalizations; + std::queue<CompileResourceWorkItem> mWorkQueue; }; #endif diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp index 2727b3dc9252..a18e9f191941 100644 --- a/tools/aapt/StringPool.cpp +++ b/tools/aapt/StringPool.cpp @@ -3,23 +3,28 @@ // // Build resource files from raw assets. // - #include "StringPool.h" -#include "ResourceTable.h" #include <utils/ByteOrder.h> #include <utils/SortedVector.h> -#include "qsort_r_compat.h" +#include <algorithm> + +#include "ResourceTable.h" + +// SSIZE: mingw does not have signed size_t == ssize_t. #if HAVE_PRINTF_ZD # define ZD "%zd" # define ZD_TYPE ssize_t +# define SSIZE(x) x #else # define ZD "%ld" # define ZD_TYPE long +# define SSIZE(x) (signed size_t)x #endif -#define NOISY(x) //x +// Set to true for noisy debug output. +static const bool kIsDebug = false; #if __cplusplus >= 201103L void strcpy16_htod(char16_t* dst, const char16_t* src) @@ -152,8 +157,10 @@ ssize_t StringPool::add(const String16& value, if (configTypeName != NULL) { entry& ent = mEntries.editItemAt(eidx); - NOISY(printf("*** adding config type name %s, was %s\n", - configTypeName->string(), ent.configTypeName.string())); + if (kIsDebug) { + printf("*** adding config type name %s, was %s\n", + configTypeName->string(), ent.configTypeName.string()); + } if (ent.configTypeName.size() <= 0) { ent.configTypeName = *configTypeName; } else if (ent.configTypeName != *configTypeName) { @@ -169,14 +176,18 @@ ssize_t StringPool::add(const String16& value, int cmp = ent.configs.itemAt(addPos).compareLogical(*config); if (cmp >= 0) { if (cmp > 0) { - NOISY(printf("*** inserting config: %s\n", config->toString().string())); + if (kIsDebug) { + printf("*** inserting config: %s\n", config->toString().string()); + } ent.configs.insertAt(*config, addPos); } break; } } if (addPos >= ent.configs.size()) { - NOISY(printf("*** adding config: %s\n", config->toString().string())); + if (kIsDebug) { + printf("*** adding config: %s\n", config->toString().string()); + } ent.configs.add(*config); } } @@ -193,9 +204,11 @@ ssize_t StringPool::add(const String16& value, ent.indices.add(pos); } - NOISY(printf("Adding string %s to pool: pos=%d eidx=%d vidx=%d\n", - String8(value).string(), pos, eidx, vidx)); - + if (kIsDebug) { + printf("Adding string %s to pool: pos=%zd eidx=%zd vidx=%zd\n", + String8(value).string(), SSIZE(pos), SSIZE(eidx), SSIZE(vidx)); + } + return pos; } @@ -234,12 +247,15 @@ status_t StringPool::addStyleSpan(size_t idx, const entry_style_span& span) return NO_ERROR; } -int StringPool::config_sort(void* state, const void* lhs, const void* rhs) +StringPool::ConfigSorter::ConfigSorter(const StringPool& pool) : pool(pool) +{ +} + +bool StringPool::ConfigSorter::operator()(size_t l, size_t r) { - StringPool* pool = (StringPool*)state; - const entry& lhe = pool->mEntries[pool->mEntryArray[*static_cast<const size_t*>(lhs)]]; - const entry& rhe = pool->mEntries[pool->mEntryArray[*static_cast<const size_t*>(rhs)]]; - return lhe.compare(rhe); + const StringPool::entry& lhe = pool.mEntries[pool.mEntryArray[l]]; + const StringPool::entry& rhe = pool.mEntries[pool.mEntryArray[r]]; + return lhe.compare(rhe) < 0; } void StringPool::sortByConfig() @@ -259,12 +275,14 @@ void StringPool::sortByConfig() } // Sort the array. - NOISY(printf("SORTING STRINGS BY CONFIGURATION...\n")); - // Vector::sort uses insertion sort, which is very slow for this data set. - // Use quicksort instead because we don't need a stable sort here. - qsort_r_compat(newPosToOriginalPos.editArray(), N, sizeof(size_t), this, config_sort); - //newPosToOriginalPos.sort(config_sort, this); - NOISY(printf("DONE SORTING STRINGS BY CONFIGURATION.\n")); + if (kIsDebug) { + printf("SORTING STRINGS BY CONFIGURATION...\n"); + } + ConfigSorter sorter(*this); + std::sort(newPosToOriginalPos.begin(), newPosToOriginalPos.end(), sorter); + if (kIsDebug) { + printf("DONE SORTING STRINGS BY CONFIGURATION.\n"); + } // Create the reverse mapping from the original position in the array // to the new position where it appears in the sorted array. This is @@ -467,9 +485,9 @@ status_t StringPool::writeStringBlock(const sp<AaptFile>& pool) strncpy((char*)strings, encStr, encSize+1); } else { - uint16_t* strings = (uint16_t*)dat; + char16_t* strings = (char16_t*)dat; - ENCODE_LENGTH(strings, sizeof(uint16_t), strSize) + ENCODE_LENGTH(strings, sizeof(char16_t), strSize) strcpy16_htod(strings, ent.value); } @@ -561,9 +579,13 @@ status_t StringPool::writeStringBlock(const sp<AaptFile>& pool) for (i=0; i<ENTRIES; i++) { entry& ent = mEntries.editItemAt(mEntryArray[i]); *index++ = htodl(ent.offset); - NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i, - String8(ent.value).string(), - mEntryArray[i], ent.offset)); + if (kIsDebug) { + printf("Writing entry #%zu: \"%s\" ent=%zu off=%zu\n", + i, + String8(ent.value).string(), + mEntryArray[i], + ent.offset); + } } // Write style index array. @@ -579,8 +601,10 @@ ssize_t StringPool::offsetForString(const String16& val) const { const Vector<size_t>* indices = offsetsForString(val); ssize_t res = indices != NULL && indices->size() > 0 ? indices->itemAt(0) : -1; - NOISY(printf("Offset for string %s: %d (%s)\n", String8(val).string(), res, - res >= 0 ? String8(mEntries[mEntryArray[res]].value).string() : String8())); + if (kIsDebug) { + printf("Offset for string %s: %zd (%s)\n", String8(val).string(), SSIZE(res), + res >= 0 ? String8(mEntries[mEntryArray[res]].value).string() : String8()); + } return res; } diff --git a/tools/aapt/StringPool.h b/tools/aapt/StringPool.h index a9c7becbe700..dbe8c8542185 100644 --- a/tools/aapt/StringPool.h +++ b/tools/aapt/StringPool.h @@ -141,7 +141,14 @@ public: const Vector<size_t>* offsetsForString(const String16& val) const; private: - static int config_sort(void* state, const void* lhs, const void* rhs); + class ConfigSorter + { + public: + explicit ConfigSorter(const StringPool&); + bool operator()(size_t l, size_t r); + private: + const StringPool& pool; + }; const bool mUTF8; diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index 899fb63e2a4c..b38b2eda4f73 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -16,8 +16,26 @@ #define O_BINARY 0 #endif -#define NOISY(x) //x -#define NOISY_PARSE(x) //x +// SSIZE: mingw does not have signed size_t == ssize_t. +// STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary. +#if HAVE_PRINTF_ZD +# define SSIZE(x) x +# define STATUST(x) x +#else +# define SSIZE(x) (signed size_t)x +# define STATUST(x) (status_t)x +#endif + +// Set to true for noisy debug output. +static const bool kIsDebug = false; +// Set to true for noisy debug output of parsing. +static const bool kIsDebugParse = false; + +#if PRINT_STRING_METRICS +static const bool kPrintStringMetrics = true; +#else +static const bool kPrintStringMetrics = false; +#endif const char* const RESOURCES_ROOT_NAMESPACE = "http://schemas.android.com/apk/res/"; const char* const RESOURCES_ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android"; @@ -56,7 +74,10 @@ String16 getNamespaceResourcePackage(String16 appPackage, String16 namespaceUri, size_t prefixSize; bool isPublic = true; if(namespaceUri.startsWith(RESOURCES_PREFIX_AUTO_PACKAGE)) { - NOISY(printf("Using default application package: %s -> %s\n", String8(namespaceUri).string(), String8(appPackage).string())); + if (kIsDebug) { + printf("Using default application package: %s -> %s\n", String8(namespaceUri).string(), + String8(appPackage).string()); + } isPublic = true; return appPackage; } else if (namespaceUri.startsWith(RESOURCES_PREFIX)) { @@ -180,7 +201,7 @@ status_t hasSubstitutionErrors(const char* fileName, return NO_ERROR; } -status_t parseStyledString(Bundle* bundle, +status_t parseStyledString(Bundle* /* bundle */, const char* fileName, ResXMLTree* inXml, const String16& endTag, @@ -557,8 +578,10 @@ status_t parseXMLResource(const sp<AaptFile>& file, ResXMLTree* outTree, } root->removeWhitespace(stripAll, cDataTags); - NOISY(printf("Input XML from %s:\n", (const char*)file->getPrintableSource())); - NOISY(root->print()); + if (kIsDebug) { + printf("Input XML from %s:\n", (const char*)file->getPrintableSource()); + root->print(); + } sp<AaptFile> rsc = new AaptFile(String8(), AaptGroupEntry(), String8()); status_t err = root->flatten(rsc, !keepComments, false); if (err != NO_ERROR) { @@ -569,8 +592,10 @@ status_t parseXMLResource(const sp<AaptFile>& file, ResXMLTree* outTree, return err; } - NOISY(printf("Output XML:\n")); - NOISY(printXMLBlock(outTree)); + if (kIsDebug) { + printf("Output XML:\n"); + printXMLBlock(outTree); + } return NO_ERROR; } @@ -850,11 +875,13 @@ void XMLNode::setAttributeResID(size_t attrIdx, uint32_t resId) } else { mAttributeOrder.removeItem(e.index); } - NOISY(printf("Elem %s %s=\"%s\": set res id = 0x%08x\n", - String8(getElementName()).string(), - String8(mAttributes.itemAt(attrIdx).name).string(), - String8(mAttributes.itemAt(attrIdx).string).string(), - resId)); + if (kIsDebug) { + printf("Elem %s %s=\"%s\": set res id = 0x%08x\n", + String8(getElementName()).string(), + String8(mAttributes.itemAt(attrIdx).name).string(), + String8(mAttributes.itemAt(attrIdx).string).string(), + resId); + } mAttributes.editItemAt(attrIdx).nameResId = resId; mAttributeOrder.add(resId, attrIdx); } @@ -965,9 +992,11 @@ status_t XMLNode::parseValues(const sp<AaptAssets>& assets, e.nameResId, NULL, &defPackage, table, &ac)) { hasErrors = true; } - NOISY(printf("Attr %s: type=0x%x, str=%s\n", - String8(e.name).string(), e.value.dataType, - String8(e.string).string())); + if (kIsDebug) { + printf("Attr %s: type=0x%x, str=%s\n", + String8(e.name).string(), e.value.dataType, + String8(e.string).string()); + } } } const size_t N = mChildren.size(); @@ -977,7 +1006,7 @@ status_t XMLNode::parseValues(const sp<AaptAssets>& assets, hasErrors = true; } } - return hasErrors ? UNKNOWN_ERROR : NO_ERROR; + return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; } status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets, @@ -992,15 +1021,17 @@ status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets, for (size_t i=0; i<N; i++) { const attribute_entry& e = mAttributes.itemAt(i); if (e.ns.size() <= 0) continue; - bool nsIsPublic; + bool nsIsPublic = true; String16 pkg(getNamespaceResourcePackage(String16(assets->getPackage()), e.ns, &nsIsPublic)); - NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n", - String8(getElementName()).string(), - String8(e.name).string(), - String8(e.string).string(), - String8(e.ns).string(), - (nsIsPublic) ? "public" : "private", - String8(pkg).string())); + if (kIsDebug) { + printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n", + String8(getElementName()).string(), + String8(e.name).string(), + String8(e.string).string(), + String8(e.ns).string(), + (nsIsPublic) ? "public" : "private", + String8(pkg).string()); + } if (pkg.size() <= 0) continue; uint32_t res = table != NULL ? table->getResId(e.name, &attr, &pkg, &errorMsg, nsIsPublic) @@ -1009,8 +1040,10 @@ status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets, attr.string(), attr.size(), pkg.string(), pkg.size()); if (res != 0) { - NOISY(printf("XML attribute name %s: resid=0x%08x\n", - String8(e.name).string(), res)); + if (kIsDebug) { + printf("XML attribute name %s: resid=0x%08x\n", + String8(e.name).string(), res); + } setAttributeResID(i, res); } else { SourcePos(mFilename, getStartLineNumber()).error( @@ -1028,7 +1061,7 @@ status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets, } } - return hasErrors ? UNKNOWN_ERROR : NO_ERROR; + return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; } sp<XMLNode> XMLNode::clone() const { @@ -1070,18 +1103,7 @@ status_t XMLNode::flatten(const sp<AaptFile>& dest, // Next collect all remainibng strings. collect_strings(&strings, &resids, stripComments, stripRawValues); -#if 0 // No longer compiles - NOISY(printf("Found strings:\n"); - const size_t N = strings.size(); - for (size_t i=0; i<N; i++) { - printf("%s\n", String8(strings.entryAt(i).string).string()); - } - ); -#endif - sp<AaptFile> stringPool = strings.createStringBlock(); - NOISY(aout << "String pool:" - << HexDump(stringPool->getData(), stringPool->getSize()) << endl); ResXMLTree_header header; memset(&header, 0, sizeof(header)); @@ -1112,17 +1134,13 @@ status_t XMLNode::flatten(const sp<AaptFile>& dest, void* data = dest->editData(); ResXMLTree_header* hd = (ResXMLTree_header*)(((uint8_t*)data)+basePos); - size_t size = dest->getSize()-basePos; hd->header.size = htodl(dest->getSize()-basePos); - NOISY(aout << "XML resource:" - << HexDump(dest->getData(), dest->getSize()) << endl); - - #if PRINT_STRING_METRICS - fprintf(stderr, "**** total xml size: %d / %d%% strings (in %s)\n", - dest->getSize(), (stringPool->getSize()*100)/dest->getSize(), - dest->getPath().string()); - #endif + if (kPrintStringMetrics) { + fprintf(stderr, "**** total xml size: %zu / %zu%% strings (in %s)\n", + dest->getSize(), (stringPool->getSize()*100)/dest->getSize(), + dest->getPath().string()); + } return NO_ERROR; } @@ -1195,7 +1213,9 @@ static void splitName(const char* name, String16* outNs, String16* outName) void XMLCALL XMLNode::startNamespace(void *userData, const char *prefix, const char *uri) { - NOISY_PARSE(printf("Start Namespace: %s %s\n", prefix, uri)); + if (kIsDebugParse) { + printf("Start Namespace: %s %s\n", prefix, uri); + } ParseState* st = (ParseState*)userData; sp<XMLNode> node = XMLNode::newNamespace(st->filename, String16(prefix != NULL ? prefix : ""), String16(uri)); @@ -1211,7 +1231,9 @@ XMLNode::startNamespace(void *userData, const char *prefix, const char *uri) void XMLCALL XMLNode::startElement(void *userData, const char *name, const char **atts) { - NOISY_PARSE(printf("Start Element: %s\n", name)); + if (kIsDebugParse) { + printf("Start Element: %s\n", name); + } ParseState* st = (ParseState*)userData; String16 ns16, name16; splitName(name, &ns16, &name16); @@ -1237,7 +1259,9 @@ XMLNode::startElement(void *userData, const char *name, const char **atts) void XMLCALL XMLNode::characterData(void *userData, const XML_Char *s, int len) { - NOISY_PARSE(printf("CDATA: \"%s\"\n", String8(s, len).string())); + if (kIsDebugParse) { + printf("CDATA: \"%s\"\n", String8(s, len).string()); + } ParseState* st = (ParseState*)userData; sp<XMLNode> node = NULL; if (st->stack.size() == 0) { @@ -1264,7 +1288,9 @@ XMLNode::characterData(void *userData, const XML_Char *s, int len) void XMLCALL XMLNode::endElement(void *userData, const char *name) { - NOISY_PARSE(printf("End Element: %s\n", name)); + if (kIsDebugParse) { + printf("End Element: %s\n", name); + } ParseState* st = (ParseState*)userData; sp<XMLNode> node = st->stack.itemAt(st->stack.size()-1); node->setEndLineNumber(XML_GetCurrentLineNumber(st->parser)); @@ -1284,7 +1310,9 @@ void XMLCALL XMLNode::endNamespace(void *userData, const char *prefix) { const char* nonNullPrefix = prefix != NULL ? prefix : ""; - NOISY_PARSE(printf("End Namespace: %s\n", prefix)); + if (kIsDebugParse) { + printf("End Namespace: %s\n", prefix); + } ParseState* st = (ParseState*)userData; sp<XMLNode> node = st->stack.itemAt(st->stack.size()-1); node->setEndLineNumber(XML_GetCurrentLineNumber(st->parser)); @@ -1296,7 +1324,9 @@ XMLNode::endNamespace(void *userData, const char *prefix) void XMLCALL XMLNode::commentData(void *userData, const char *comment) { - NOISY_PARSE(printf("Comment: %s\n", comment)); + if (kIsDebugParse) { + printf("Comment: %s\n", comment); + } ParseState* st = (ParseState*)userData; if (st->pendingComment.size() > 0) { st->pendingComment.append(String16("\n")); @@ -1393,8 +1423,10 @@ status_t XMLNode::collect_attr_strings(StringPool* outPool, } if (idx < 0) { idx = outPool->add(attr.name); - NOISY(printf("Adding attr %s (resid 0x%08x) to pool: idx=%d\n", - String8(attr.name).string(), id, idx)); + if (kIsDebug) { + printf("Adding attr %s (resid 0x%08x) to pool: idx=%zd\n", + String8(attr.name).string(), id, SSIZE(idx)); + } if (id != 0) { while ((ssize_t)outResIds->size() <= idx) { outResIds->add(0); @@ -1403,8 +1435,9 @@ status_t XMLNode::collect_attr_strings(StringPool* outPool, } } attr.namePoolIdx = idx; - NOISY(printf("String %s offset=0x%08x\n", - String8(attr.name).string(), idx)); + if (kIsDebug) { + printf("String %s offset=0x%08zd\n", String8(attr.name).string(), SSIZE(idx)); + } } } diff --git a/tools/aapt/ZipEntry.cpp b/tools/aapt/ZipEntry.cpp index b575988f0277..54a8e9c9b247 100644 --- a/tools/aapt/ZipEntry.cpp +++ b/tools/aapt/ZipEntry.cpp @@ -141,33 +141,15 @@ void ZipEntry::initNew(const char* fileName, const char* comment) * * Initializes the CDE and the LFH. */ -status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, +status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */, const ZipEntry* pEntry) { - /* - * Copy everything in the CDE over, then fix up the hairy bits. - */ - memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); - - if (mCDE.mFileNameLength > 0) { - mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; - if (mCDE.mFileName == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); - } - if (mCDE.mFileCommentLength > 0) { - mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; - if (mCDE.mFileComment == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); - } - if (mCDE.mExtraFieldLength > 0) { - /* we null-terminate this, though it may not be a string */ - mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; - if (mCDE.mExtraField == NULL) - return NO_MEMORY; - memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, - mCDE.mExtraFieldLength+1); + mCDE = pEntry->mCDE; + // Check whether we got all the memory needed. + if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) || + (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) || + (mCDE.mExtraFieldLength > 0 && mCDE.mExtraField == NULL)) { + return NO_MEMORY; } /* construct the LFH from the CDE */ @@ -356,7 +338,7 @@ time_t ZipEntry::getModWhen(void) const */ void ZipEntry::setModWhen(time_t when) { -#ifdef HAVE_LOCALTIME_R +#if !defined(_WIN32) struct tm tmResult; #endif time_t even; @@ -368,7 +350,7 @@ void ZipEntry::setModWhen(time_t when) even = (time_t)(((unsigned long)(when) + 1) & (~1)); /* expand */ -#ifdef HAVE_LOCALTIME_R +#if !defined(_WIN32) ptm = localtime_r(&even, &tmResult); #else ptm = localtime(&even); @@ -694,3 +676,60 @@ void ZipEntry::CentralDirEntry::dump(void) const ALOGD(" comment: '%s'\n", mFileComment); } +/* + * Copy-assignment operator for CentralDirEntry. + */ +ZipEntry::CentralDirEntry& ZipEntry::CentralDirEntry::operator=(const ZipEntry::CentralDirEntry& src) { + if (this == &src) { + return *this; + } + + // Free up old data. + delete[] mFileName; + delete[] mExtraField; + delete[] mFileComment; + + // Copy scalars. + mVersionMadeBy = src.mVersionMadeBy; + mVersionToExtract = src.mVersionToExtract; + mGPBitFlag = src.mGPBitFlag; + mCompressionMethod = src.mCompressionMethod; + mLastModFileTime = src.mLastModFileTime; + mLastModFileDate = src.mLastModFileDate; + mCRC32 = src.mCRC32; + mCompressedSize = src.mCompressedSize; + mUncompressedSize = src.mUncompressedSize; + mFileNameLength = src.mFileNameLength; + mExtraFieldLength = src.mExtraFieldLength; + mFileCommentLength = src.mFileCommentLength; + mDiskNumberStart = src.mDiskNumberStart; + mInternalAttrs = src.mInternalAttrs; + mExternalAttrs = src.mExternalAttrs; + mLocalHeaderRelOffset = src.mLocalHeaderRelOffset; + + // Copy strings, if necessary. + if (mFileNameLength > 0) { + mFileName = new unsigned char[mFileNameLength + 1]; + if (mFileName != NULL) + strcpy((char*)mFileName, (char*)src.mFileName); + } else { + mFileName = NULL; + } + if (mFileCommentLength > 0) { + mFileComment = new unsigned char[mFileCommentLength + 1]; + if (mFileComment != NULL) + strcpy((char*)mFileComment, (char*)src.mFileComment); + } else { + mFileComment = NULL; + } + if (mExtraFieldLength > 0) { + /* we null-terminate this, though it may not be a string */ + mExtraField = new unsigned char[mExtraFieldLength + 1]; + if (mExtraField != NULL) + memcpy(mExtraField, src.mExtraField, mExtraFieldLength + 1); + } else { + mExtraField = NULL; + } + + return *this; +} diff --git a/tools/aapt/ZipEntry.h b/tools/aapt/ZipEntry.h index c2f3227cf230..287a540673a3 100644 --- a/tools/aapt/ZipEntry.h +++ b/tools/aapt/ZipEntry.h @@ -298,6 +298,8 @@ private: status_t read(FILE* fp); status_t write(FILE* fp); + CentralDirEntry& operator=(const CentralDirEntry& src); + // unsigned long mSignature; unsigned short mVersionMadeBy; unsigned short mVersionToExtract; diff --git a/tools/aapt/ZipFile.cpp b/tools/aapt/ZipFile.cpp index 8057068dd685..36f4e73b3ac2 100644 --- a/tools/aapt/ZipFile.cpp +++ b/tools/aapt/ZipFile.cpp @@ -676,8 +676,6 @@ status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) status_t ZipFile::copyDataToFp(FILE* dstFp, const void* data, size_t size, unsigned long* pCRC32) { - size_t count; - *pCRC32 = crc32(0L, Z_NULL, 0); if (size > 0) { *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); diff --git a/tools/aapt/qsort_r_compat.c b/tools/aapt/qsort_r_compat.c deleted file mode 100644 index 2a8dbe85f4c9..000000000000 --- a/tools/aapt/qsort_r_compat.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 deleted file mode 100644 index e14f999f736a..000000000000 --- a/tools/aapt/qsort_r_compat.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp index e795d81836bd..ef3860c72921 100644 --- a/tools/aapt/tests/AaptConfig_test.cpp +++ b/tools/aapt/tests/AaptConfig_test.cpp @@ -76,3 +76,9 @@ TEST(AaptConfigTest, ParseQualifierAddedInApi13) { EXPECT_TRUE(TestParse("sw600dp-v8", &config)); EXPECT_EQ(String8("sw600dp-v13"), config.toString()); } + +TEST(AaptConfigTest, TestParsingOfCarAttribute) { + ConfigDescription config; + EXPECT_TRUE(TestParse("car", &config)); + EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode); +} diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp index 45dd23b911e3..14c9f95a247b 100644 --- a/tools/aidl/aidl.cpp +++ b/tools/aidl/aidl.cpp @@ -228,7 +228,8 @@ check_filename(const char* filename, const char* package, buffer_type* name) } #endif -#ifdef OS_CASE_SENSITIVE + // aidl assumes case-insensitivity on Mac Os and Windows. +#if defined(__linux__) valid = (expected == p); #else valid = !strcasecmp(expected.c_str(), p); diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py index 393d2ec5d012..5c6d870f653d 100644 --- a/tools/apilint/apilint.py +++ b/tools/apilint/apilint.py @@ -48,8 +48,9 @@ def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): class Field(): - def __init__(self, clazz, raw, blame): + def __init__(self, clazz, line, raw, blame): self.clazz = clazz + self.line = line self.raw = raw.strip(" {;") self.blame = blame @@ -73,8 +74,9 @@ class Field(): class Method(): - def __init__(self, clazz, raw, blame): + def __init__(self, clazz, line, raw, blame): self.clazz = clazz + self.line = line self.raw = raw.strip(" {;") self.blame = blame @@ -110,8 +112,9 @@ class Method(): class Class(): - def __init__(self, pkg, raw, blame): + def __init__(self, pkg, line, raw, blame): self.pkg = pkg + self.line = line self.raw = raw.strip(" {;") self.blame = blame self.ctors = [] @@ -129,10 +132,14 @@ class Class(): if "extends" in raw: self.extends = raw[raw.index("extends")+1] + self.extends_path = self.extends.split(".") else: self.extends = None + self.extends_path = [] self.fullname = self.pkg.name + "." + self.fullname + self.fullname_path = self.fullname.split(".") + self.name = self.fullname[self.fullname.rindex(".")+1:] def __repr__(self): @@ -140,75 +147,110 @@ class Class(): class Package(): - def __init__(self, raw, blame): + def __init__(self, line, raw, blame): + self.line = line self.raw = raw.strip(" {;") self.blame = blame raw = raw.split() self.name = raw[raw.index("package")+1] + self.name_path = self.name.split(".") def __repr__(self): return self.raw -def parse_api(fn): +def _parse_stream(f, clazz_cb=None): + line = 0 api = {} pkg = None clazz = None blame = None re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$") - - with open(fn) as f: - for raw in f.readlines(): - raw = raw.rstrip() - match = re_blame.match(raw) - if match is not None: - blame = match.groups()[0:2] - raw = match.groups()[2] - else: - blame = None - - if raw.startswith("package"): - pkg = Package(raw, blame) - elif raw.startswith(" ") and raw.endswith("{"): - clazz = Class(pkg, raw, blame) + for raw in f: + line += 1 + raw = raw.rstrip() + match = re_blame.match(raw) + if match is not None: + blame = match.groups()[0:2] + raw = match.groups()[2] + else: + blame = None + + if raw.startswith("package"): + pkg = Package(line, raw, blame) + elif raw.startswith(" ") and raw.endswith("{"): + # When provided with class callback, we treat as incremental + # parse and don't build up entire API + if clazz and clazz_cb: + clazz_cb(clazz) + clazz = Class(pkg, line, raw, blame) + if not clazz_cb: api[clazz.fullname] = clazz - elif raw.startswith(" ctor"): - clazz.ctors.append(Method(clazz, raw, blame)) - elif raw.startswith(" method"): - clazz.methods.append(Method(clazz, raw, blame)) - elif raw.startswith(" field"): - clazz.fields.append(Field(clazz, raw, blame)) + elif raw.startswith(" ctor"): + clazz.ctors.append(Method(clazz, line, raw, blame)) + elif raw.startswith(" method"): + clazz.methods.append(Method(clazz, line, raw, blame)) + elif raw.startswith(" field"): + clazz.fields.append(Field(clazz, line, raw, blame)) + + # Handle last trailing class + if clazz and clazz_cb: + clazz_cb(clazz) return api +class Failure(): + def __init__(self, sig, clazz, detail, error, rule, msg): + self.sig = sig + self.error = error + self.rule = rule + self.msg = msg + + if error: + self.head = "Error %s" % (rule) if rule else "Error" + dump = "%s%s:%s %s" % (format(fg=RED, bg=BLACK, bold=True), self.head, format(reset=True), msg) + else: + self.head = "Warning %s" % (rule) if rule else "Warning" + dump = "%s%s:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), self.head, format(reset=True), msg) + + self.line = clazz.line + blame = clazz.blame + if detail is not None: + dump += "\n in " + repr(detail) + self.line = detail.line + blame = detail.blame + dump += "\n in " + repr(clazz) + dump += "\n in " + repr(clazz.pkg) + dump += "\n at line " + repr(self.line) + if blame is not None: + dump += "\n last modified by %s in %s" % (blame[1], blame[0]) + + self.dump = dump + + def __repr__(self): + return self.dump + + failures = {} -def _fail(clazz, detail, msg): +def _fail(clazz, detail, error, rule, msg): """Records an API failure to be processed later.""" global failures sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg) sig = sig.replace(" deprecated ", " ") - res = msg - blame = clazz.blame - if detail is not None: - res += "\n in " + repr(detail) - blame = detail.blame - res += "\n in " + repr(clazz) - res += "\n in " + repr(clazz.pkg) - if blame is not None: - res += "\n last modified by %s in %s" % (blame[1], blame[0]) - failures[sig] = res + failures[sig] = Failure(sig, clazz, detail, error, rule, msg) + -def warn(clazz, detail, msg): - _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), format(reset=True), msg)) +def warn(clazz, detail, rule, msg): + _fail(clazz, detail, False, rule, msg) -def error(clazz, detail, msg): - _fail(clazz, detail, "%sError:%s %s" % (format(fg=RED, bg=BLACK, bold=True), format(reset=True), msg)) +def error(clazz, detail, rule, msg): + _fail(clazz, detail, True, rule, msg) def verify_constants(clazz): @@ -218,13 +260,13 @@ def verify_constants(clazz): for f in clazz.fields: if "static" in f.split and "final" in f.split: if re.match("[A-Z0-9_]+", f.name) is None: - error(clazz, f, "Constant field names should be FOO_NAME") + error(clazz, f, "C2", "Constant field names must be FOO_NAME") def verify_enums(clazz): """Enums are bad, mmkay?""" if "extends java.lang.Enum" in clazz.raw: - error(clazz, None, "Enums are not allowed") + error(clazz, None, "F5", "Enums are not allowed") def verify_class_names(clazz): @@ -234,9 +276,9 @@ def verify_class_names(clazz): if re.match("android\.R\.[a-z]+", clazz.fullname): return if re.search("[A-Z]{2,}", clazz.name) is not None: - warn(clazz, None, "Class name style should be Mtp not MTP") + warn(clazz, None, "S1", "Class names with acronyms should be Mtp not MTP") if re.match("[^A-Z]", clazz.name): - error(clazz, None, "Class must start with uppercase char") + error(clazz, None, "S1", "Class must start with uppercase char") def verify_method_names(clazz): @@ -247,9 +289,9 @@ def verify_method_names(clazz): for m in clazz.methods: if re.search("[A-Z]{2,}", m.name) is not None: - warn(clazz, m, "Method name style should be getMtu() instead of getMTU()") + warn(clazz, m, "S1", "Method names with acronyms should be getMtu() instead of getMTU()") if re.match("[^a-z]", m.name): - error(clazz, m, "Method name must start with lowercase char") + error(clazz, m, "S1", "Method name must start with lowercase char") def verify_callbacks(clazz): @@ -259,17 +301,17 @@ def verify_callbacks(clazz): if clazz.fullname == "android.speech.tts.SynthesisCallback": return if clazz.name.endswith("Callbacks"): - error(clazz, None, "Class name must not be plural") + error(clazz, None, "L1", "Callback class names should be singular") if clazz.name.endswith("Observer"): - warn(clazz, None, "Class should be named FooCallback") + warn(clazz, None, "L1", "Class should be named FooCallback") if clazz.name.endswith("Callback"): if "interface" in clazz.split: - error(clazz, None, "Callback must be abstract class to enable extension in future API levels") + error(clazz, None, "CL3", "Callbacks must be abstract class to enable extension in future API levels") for m in clazz.methods: if not re.match("on[A-Z][a-z]*", m.name): - error(clazz, m, "Callback method names must be onFoo() style") + error(clazz, m, "L1", "Callback method names must be onFoo() style") def verify_listeners(clazz): @@ -281,16 +323,16 @@ def verify_listeners(clazz): if clazz.name.endswith("Listener"): if " abstract class " in clazz.raw: - error(clazz, None, "Listener should be an interface, otherwise renamed Callback") + error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback") for m in clazz.methods: if not re.match("on[A-Z][a-z]*", m.name): - error(clazz, m, "Listener method names must be onFoo() style") + error(clazz, m, "L1", "Listener method names must be onFoo() style") if len(clazz.methods) == 1 and clazz.name.startswith("On"): m = clazz.methods[0] if (m.name + "Listener").lower() != clazz.name.lower(): - error(clazz, m, "Single listener method name should match class name") + error(clazz, m, "L1", "Single listener method name must match class name") def verify_actions(clazz): @@ -308,7 +350,7 @@ def verify_actions(clazz): if "static" in f.split and "final" in f.split and f.typ == "java.lang.String": if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower(): if not f.name.startswith("ACTION_"): - error(clazz, f, "Intent action constant name must be ACTION_FOO") + error(clazz, f, "C3", "Intent action constant name must be ACTION_FOO") else: if clazz.fullname == "android.content.Intent": prefix = "android.intent.action" @@ -320,7 +362,7 @@ def verify_actions(clazz): prefix = clazz.pkg.name + ".action" expected = prefix + "." + f.name[7:] if f.value != expected: - error(clazz, f, "Inconsistent action value; expected %s" % (expected)) + error(clazz, f, "C4", "Inconsistent action value; expected %s" % (expected)) def verify_extras(clazz): @@ -340,7 +382,7 @@ def verify_extras(clazz): if "static" in f.split and "final" in f.split and f.typ == "java.lang.String": if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower(): if not f.name.startswith("EXTRA_"): - error(clazz, f, "Intent extra must be EXTRA_FOO") + error(clazz, f, "C3", "Intent extra must be EXTRA_FOO") else: if clazz.pkg.name == "android.content" and clazz.name == "Intent": prefix = "android.intent.extra" @@ -350,7 +392,7 @@ def verify_extras(clazz): prefix = clazz.pkg.name + ".extra" expected = prefix + "." + f.name[6:] if f.value != expected: - error(clazz, f, "Inconsistent extra value; expected %s" % (expected)) + error(clazz, f, "C4", "Inconsistent extra value; expected %s" % (expected)) def verify_equals(clazz): @@ -359,7 +401,7 @@ def verify_equals(clazz): eq = "equals" in methods hc = "hashCode" in methods if eq != hc: - error(clazz, None, "Must override both equals and hashCode; missing one") + error(clazz, None, "M8", "Must override both equals and hashCode; missing one") def verify_parcelable(clazz): @@ -370,17 +412,17 @@ def verify_parcelable(clazz): describe = [ i for i in clazz.methods if i.name == "describeContents" ] if len(creator) == 0 or len(write) == 0 or len(describe) == 0: - error(clazz, None, "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one") + error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one") def verify_protected(clazz): - """Verify that no protected methods are allowed.""" + """Verify that no protected methods or fields are allowed.""" for m in clazz.methods: if "protected" in m.split: - error(clazz, m, "No protected methods; must be public") + error(clazz, m, "M7", "Protected methods not allowed; must be public") for f in clazz.fields: if "protected" in f.split: - error(clazz, f, "No protected fields; must be public") + error(clazz, f, "M7", "Protected fields not allowed; must be public") def verify_fields(clazz): @@ -410,18 +452,18 @@ def verify_fields(clazz): elif clazz.fullname.startswith("android.util.Mutable"): pass else: - error(clazz, f, "Bare fields must be marked final; consider adding accessors") + error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable") if not "static" in f.split: if not re.match("[a-z]([a-zA-Z]+)?", f.name): - error(clazz, f, "Non-static fields must be named with myField style") + error(clazz, f, "S1", "Non-static fields must be named using myField style") if re.match("[ms][A-Z]", f.name): - error(clazz, f, "Don't expose your internal objects") + error(clazz, f, "F1", "Internal objects must not be exposed") if re.match("[A-Z_]+", f.name): if "static" not in f.split or "final" not in f.split: - error(clazz, f, "Constants must be marked static final") + error(clazz, f, "C2", "Constants must be marked static final") def verify_register(clazz): @@ -434,34 +476,34 @@ def verify_register(clazz): if m.name.startswith("register"): other = "unregister" + m.name[8:] if other not in methods: - error(clazz, m, "Missing unregister method") + error(clazz, m, "L2", "Missing unregister method") if m.name.startswith("unregister"): other = "register" + m.name[10:] if other not in methods: - error(clazz, m, "Missing register method") + error(clazz, m, "L2", "Missing register method") if m.name.startswith("add") or m.name.startswith("remove"): - error(clazz, m, "Callback methods should be named register/unregister") + error(clazz, m, "L3", "Callback methods should be named register/unregister") if "Listener" in m.raw: if m.name.startswith("add"): other = "remove" + m.name[3:] if other not in methods: - error(clazz, m, "Missing remove method") + error(clazz, m, "L2", "Missing remove method") if m.name.startswith("remove") and not m.name.startswith("removeAll"): other = "add" + m.name[6:] if other not in methods: - error(clazz, m, "Missing add method") + error(clazz, m, "L2", "Missing add method") if m.name.startswith("register") or m.name.startswith("unregister"): - error(clazz, m, "Listener methods should be named add/remove") + error(clazz, m, "L3", "Listener methods should be named add/remove") def verify_sync(clazz): """Verify synchronized methods aren't exposed.""" for m in clazz.methods: if "synchronized" in m.split: - error(clazz, m, "Internal lock exposed") + error(clazz, m, "M5", "Internal locks must not be exposed") def verify_intent_builder(clazz): @@ -473,7 +515,7 @@ def verify_intent_builder(clazz): if m.name.startswith("create") and m.name.endswith("Intent"): pass else: - error(clazz, m, "Methods creating an Intent should be named createFooIntent()") + error(clazz, m, "FW1", "Methods creating an Intent must be named createFooIntent()") def verify_helper_classes(clazz): @@ -483,57 +525,45 @@ def verify_helper_classes(clazz): if "extends android.app.Service" in clazz.raw: test_methods = True if not clazz.name.endswith("Service"): - error(clazz, None, "Inconsistent class name; should be FooService") + error(clazz, None, "CL4", "Inconsistent class name; should be FooService") found = False for f in clazz.fields: if f.name == "SERVICE_INTERFACE": found = True if f.value != clazz.fullname: - error(clazz, f, "Inconsistent interface constant; expected %s" % (clazz.fullname)) - - if not found: - warn(clazz, None, "Missing SERVICE_INTERFACE constant") - - if "abstract" in clazz.split and not clazz.fullname.startswith("android.service."): - warn(clazz, None, "Services extended by developers should be under android.service") + error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname)) if "extends android.content.ContentProvider" in clazz.raw: test_methods = True if not clazz.name.endswith("Provider"): - error(clazz, None, "Inconsistent class name; should be FooProvider") + error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider") found = False for f in clazz.fields: if f.name == "PROVIDER_INTERFACE": found = True if f.value != clazz.fullname: - error(clazz, f, "Inconsistent interface name; expected %s" % (clazz.fullname)) - - if not found: - warn(clazz, None, "Missing PROVIDER_INTERFACE constant") - - if "abstract" in clazz.split and not clazz.fullname.startswith("android.provider."): - warn(clazz, None, "Providers extended by developers should be under android.provider") + error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname)) if "extends android.content.BroadcastReceiver" in clazz.raw: test_methods = True if not clazz.name.endswith("Receiver"): - error(clazz, None, "Inconsistent class name; should be FooReceiver") + error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver") if "extends android.app.Activity" in clazz.raw: test_methods = True if not clazz.name.endswith("Activity"): - error(clazz, None, "Inconsistent class name; should be FooActivity") + error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity") if test_methods: for m in clazz.methods: if "final" in m.split: continue if not re.match("on[A-Z]", m.name): if "abstract" in m.split: - error(clazz, m, "Methods implemented by developers must be named onFoo()") + warn(clazz, m, None, "Methods implemented by developers should be named onFoo()") else: - warn(clazz, m, "If implemented by developer, should be named onFoo(); otherwise consider marking final") + warn(clazz, m, None, "If implemented by developer, should be named onFoo(); otherwise consider marking final") def verify_builder(clazz): @@ -543,7 +573,7 @@ def verify_builder(clazz): if not clazz.name.endswith("Builder"): return if clazz.name != "Builder": - warn(clazz, None, "Builder should be defined as inner class") + warn(clazz, None, None, "Builder should be defined as inner class") has_build = False for m in clazz.methods: @@ -555,26 +585,26 @@ def verify_builder(clazz): if m.name.startswith("clear"): continue if m.name.startswith("with"): - error(clazz, m, "Builder methods names must follow setFoo() style") + warn(clazz, m, None, "Builder methods names should use setFoo() style") if m.name.startswith("set"): if not m.typ.endswith(clazz.fullname): - warn(clazz, m, "Methods should return the builder") + warn(clazz, m, "M4", "Methods must return the builder object") if not has_build: - warn(clazz, None, "Missing build() method") + warn(clazz, None, None, "Missing build() method") def verify_aidl(clazz): """Catch people exposing raw AIDL.""" if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw: - error(clazz, None, "Exposing raw AIDL interface") + error(clazz, None, None, "Raw AIDL interfaces must not be exposed") def verify_internal(clazz): """Catch people exposing internal classes.""" if clazz.pkg.name.startswith("com.android"): - error(clazz, None, "Exposing internal class") + error(clazz, None, None, "Internal classes must not be exposed") def verify_layering(clazz): @@ -609,38 +639,56 @@ def verify_layering(clazz): for f in clazz.fields: ir = rank(f.typ) if ir and ir < cr: - warn(clazz, f, "Field type violates package layering") + warn(clazz, f, "FW6", "Field type violates package layering") for m in clazz.methods: ir = rank(m.typ) if ir and ir < cr: - warn(clazz, m, "Method return type violates package layering") + warn(clazz, m, "FW6", "Method return type violates package layering") for arg in m.args: ir = rank(arg) if ir and ir < cr: - warn(clazz, m, "Method argument type violates package layering") + warn(clazz, m, "FW6", "Method argument type violates package layering") -def verify_boolean(clazz, api): - """Catches people returning boolean from getFoo() style methods. - Ignores when matching setFoo() is present.""" +def verify_boolean(clazz): + """Verifies that boolean accessors are named correctly. + For example, hasFoo() and setHasFoo().""" - methods = [ m.name for m in clazz.methods ] + def is_get(m): return len(m.args) == 0 and m.typ == "boolean" + def is_set(m): return len(m.args) == 1 and m.args[0] == "boolean" + + gets = [ m for m in clazz.methods if is_get(m) ] + sets = [ m for m in clazz.methods if is_set(m) ] - builder = clazz.fullname + ".Builder" - builder_methods = [] - if builder in api: - builder_methods = [ m.name for m in api[builder].methods ] + def error_if_exists(methods, trigger, expected, actual): + for m in methods: + if m.name == actual: + error(clazz, m, "M6", "Symmetric method for %s must be named %s" % (trigger, expected)) for m in clazz.methods: - if m.typ == "boolean" and m.name.startswith("get") and m.name != "get" and len(m.args) == 0: - setter = "set" + m.name[3:] - if setter in methods: - pass - elif builder is not None and setter in builder_methods: - pass - else: - warn(clazz, m, "Methods returning boolean should be named isFoo, hasFoo, areFoo") + if is_get(m): + if re.match("is[A-Z]", m.name): + target = m.name[2:] + expected = "setIs" + target + error_if_exists(sets, m.name, expected, "setHas" + target) + elif re.match("has[A-Z]", m.name): + target = m.name[3:] + expected = "setHas" + target + error_if_exists(sets, m.name, expected, "setIs" + target) + error_if_exists(sets, m.name, expected, "set" + target) + elif re.match("get[A-Z]", m.name): + target = m.name[3:] + expected = "set" + target + error_if_exists(sets, m.name, expected, "setIs" + target) + error_if_exists(sets, m.name, expected, "setHas" + target) + + if is_set(m): + if re.match("set[A-Z]", m.name): + target = m.name[3:] + expected = "get" + target + error_if_exists(sets, m.name, expected, "is" + target) + error_if_exists(sets, m.name, expected, "has" + target) def verify_collections(clazz): @@ -651,10 +699,10 @@ def verify_collections(clazz): "java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"] for m in clazz.methods: if m.typ in bad: - error(clazz, m, "Return type is concrete collection; should be interface") + error(clazz, m, "CL2", "Return type is concrete collection; must be higher-level interface") for arg in m.args: if arg in bad: - error(clazz, m, "Argument is concrete collection; should be interface") + error(clazz, m, "CL2", "Argument is concrete collection; must be higher-level interface") def verify_flags(clazz): @@ -669,49 +717,291 @@ def verify_flags(clazz): scope = f.name[0:f.name.index("FLAG_")] if val & known[scope]: - warn(clazz, f, "Found overlapping flag constant value") + warn(clazz, f, "C1", "Found overlapping flag constant value") known[scope] |= val -def verify_style(api): - """Find all style issues in the given API level.""" +def verify_exception(clazz): + """Verifies that methods don't throw generic exceptions.""" + for m in clazz.methods: + if "throws java.lang.Exception" in m.raw or "throws java.lang.Throwable" in m.raw or "throws java.lang.Error" in m.raw: + error(clazz, m, "S1", "Methods must not throw generic exceptions") + + +def verify_google(clazz): + """Verifies that APIs never reference Google.""" + + if re.search("google", clazz.raw, re.IGNORECASE): + error(clazz, None, None, "Must never reference Google") + + test = [] + test.extend(clazz.ctors) + test.extend(clazz.fields) + test.extend(clazz.methods) + + for t in test: + if re.search("google", t.raw, re.IGNORECASE): + error(clazz, t, None, "Must never reference Google") + + +def verify_bitset(clazz): + """Verifies that we avoid using heavy BitSet.""" + + for f in clazz.fields: + if f.typ == "java.util.BitSet": + error(clazz, f, None, "Field type must not be heavy BitSet") + + for m in clazz.methods: + if m.typ == "java.util.BitSet": + error(clazz, m, None, "Return type must not be heavy BitSet") + for arg in m.args: + if arg == "java.util.BitSet": + error(clazz, m, None, "Argument type must not be heavy BitSet") + + +def verify_manager(clazz): + """Verifies that FooManager is only obtained from Context.""" + + if not clazz.name.endswith("Manager"): return + + for c in clazz.ctors: + error(clazz, c, None, "Managers must always be obtained from Context; no direct constructors") + + +def verify_boxed(clazz): + """Verifies that methods avoid boxed primitives.""" + + boxed = ["java.lang.Number","java.lang.Byte","java.lang.Double","java.lang.Float","java.lang.Integer","java.lang.Long","java.lang.Short"] + + for c in clazz.ctors: + for arg in c.args: + if arg in boxed: + error(clazz, c, "M11", "Must avoid boxed primitives") + + for f in clazz.fields: + if f.typ in boxed: + error(clazz, f, "M11", "Must avoid boxed primitives") + + for m in clazz.methods: + if m.typ in boxed: + error(clazz, m, "M11", "Must avoid boxed primitives") + for arg in m.args: + if arg in boxed: + error(clazz, m, "M11", "Must avoid boxed primitives") + + +def verify_static_utils(clazz): + """Verifies that helper classes can't be constructed.""" + if clazz.fullname.startswith("android.opengl"): return + if re.match("android\.R\.[a-z]+", clazz.fullname): return + + if len(clazz.fields) > 0: return + if len(clazz.methods) == 0: return + + for m in clazz.methods: + if "static" not in m.split: + return + + # At this point, we have no fields, and all methods are static + if len(clazz.ctors) > 0: + error(clazz, None, None, "Fully-static utility classes must not have constructor") + + +def verify_overload_args(clazz): + """Verifies that method overloads add new arguments at the end.""" + if clazz.fullname.startswith("android.opengl"): return + + overloads = collections.defaultdict(list) + for m in clazz.methods: + if "deprecated" in m.split: continue + overloads[m.name].append(m) + + for name, methods in overloads.items(): + if len(methods) <= 1: continue + + # Look for arguments common across all overloads + def cluster(args): + count = collections.defaultdict(int) + res = set() + for i in range(len(args)): + a = args[i] + res.add("%s#%d" % (a, count[a])) + count[a] += 1 + return res + + common_args = cluster(methods[0].args) + for m in methods: + common_args = common_args & cluster(m.args) + + if len(common_args) == 0: continue + + # Require that all common arguments are present at start of signature + locked_sig = None + for m in methods: + sig = m.args[0:len(common_args)] + if not common_args.issubset(cluster(sig)): + warn(clazz, m, "M2", "Expected common arguments [%s] at beginning of overloaded method" % (", ".join(common_args))) + elif not locked_sig: + locked_sig = sig + elif locked_sig != sig: + error(clazz, m, "M2", "Expected consistent argument ordering between overloads: %s..." % (", ".join(locked_sig))) + + +def verify_callback_handlers(clazz): + """Verifies that methods adding listener/callback have overload + for specifying delivery thread.""" + + # Ignore UI packages which assume main thread + skip = [ + "animation", + "view", + "graphics", + "transition", + "widget", + "webkit", + ] + for s in skip: + if s in clazz.pkg.name_path: return + if s in clazz.extends_path: return + + # Ignore UI classes which assume main thread + if "app" in clazz.pkg.name_path or "app" in clazz.extends_path: + for s in ["ActionBar","Dialog","Application","Activity","Fragment","Loader"]: + if s in clazz.fullname: return + if "content" in clazz.pkg.name_path or "content" in clazz.extends_path: + for s in ["Loader"]: + if s in clazz.fullname: return + + found = {} + by_name = collections.defaultdict(list) + for m in clazz.methods: + if m.name.startswith("unregister"): continue + if m.name.startswith("remove"): continue + if re.match("on[A-Z]+", m.name): continue + + by_name[m.name].append(m) + + for a in m.args: + if a.endswith("Listener") or a.endswith("Callback") or a.endswith("Callbacks"): + found[m.name] = m + + for f in found.values(): + takes_handler = False + for m in by_name[f.name]: + if "android.os.Handler" in m.args: + takes_handler = True + if not takes_handler: + warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Handler") + + +def verify_context_first(clazz): + """Verifies that methods accepting a Context keep it the first argument.""" + examine = clazz.ctors + clazz.methods + for m in examine: + if len(m.args) > 1 and m.args[0] != "android.content.Context": + if "android.content.Context" in m.args[1:]: + error(clazz, m, "M3", "Context is distinct, so it must be the first argument") + + +def verify_listener_last(clazz): + """Verifies that methods accepting a Listener or Callback keep them as last arguments.""" + examine = clazz.ctors + clazz.methods + for m in examine: + if "Listener" in m.name or "Callback" in m.name: continue + found = False + for a in m.args: + if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"): + found = True + elif found and a != "android.os.Handler": + warn(clazz, m, "M3", "Listeners should always be at end of argument list") + + +def verify_resource_names(clazz): + """Verifies that resource names have consistent case.""" + if not re.match("android\.R\.[a-z]+", clazz.fullname): return + + # Resources defined by files are foo_bar_baz + if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]: + for f in clazz.fields: + if re.match("[a-z1-9_]+$", f.name): continue + error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style") + + # Resources defined inside files are fooBarBaz + if clazz.name in ["array","attr","id","bool","fraction","integer"]: + for f in clazz.fields: + if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue + if re.match("layout_[a-z][a-zA-Z1-9]*$", f.name): continue + if re.match("state_[a-z_]*$", f.name): continue + + if re.match("[a-z][a-zA-Z1-9]*$", f.name): continue + error(clazz, f, "C7", "Expected resource name in this class to be fooBarBaz style") + + # Styles are FooBar_Baz + if clazz.name in ["style"]: + for f in clazz.fields: + if re.match("[A-Z][A-Za-z1-9]+(_[A-Z][A-Za-z1-9]+?)*$", f.name): continue + error(clazz, f, "C7", "Expected resource name in this class to be FooBar_Baz style") + + +def examine_clazz(clazz): + """Find all style issues in the given class.""" + if clazz.pkg.name.startswith("java"): return + if clazz.pkg.name.startswith("junit"): return + if clazz.pkg.name.startswith("org.apache"): return + if clazz.pkg.name.startswith("org.xml"): return + if clazz.pkg.name.startswith("org.json"): return + if clazz.pkg.name.startswith("org.w3c"): return + + verify_constants(clazz) + verify_enums(clazz) + verify_class_names(clazz) + verify_method_names(clazz) + verify_callbacks(clazz) + verify_listeners(clazz) + verify_actions(clazz) + verify_extras(clazz) + verify_equals(clazz) + verify_parcelable(clazz) + verify_protected(clazz) + verify_fields(clazz) + verify_register(clazz) + verify_sync(clazz) + verify_intent_builder(clazz) + verify_helper_classes(clazz) + verify_builder(clazz) + verify_aidl(clazz) + verify_internal(clazz) + verify_layering(clazz) + verify_boolean(clazz) + verify_collections(clazz) + verify_flags(clazz) + verify_exception(clazz) + verify_google(clazz) + verify_bitset(clazz) + verify_manager(clazz) + verify_boxed(clazz) + verify_static_utils(clazz) + verify_overload_args(clazz) + verify_callback_handlers(clazz) + verify_context_first(clazz) + verify_listener_last(clazz) + verify_resource_names(clazz) + + +def examine_stream(stream): + """Find all style issues in the given API stream.""" global failures + failures = {} + _parse_stream(stream, examine_clazz) + return failures + +def examine_api(api): + """Find all style issues in the given parsed API.""" + global failures failures = {} for key in sorted(api.keys()): - clazz = api[key] - - if clazz.pkg.name.startswith("java"): continue - if clazz.pkg.name.startswith("junit"): continue - if clazz.pkg.name.startswith("org.apache"): continue - if clazz.pkg.name.startswith("org.xml"): continue - if clazz.pkg.name.startswith("org.json"): continue - if clazz.pkg.name.startswith("org.w3c"): continue - - verify_constants(clazz) - verify_enums(clazz) - verify_class_names(clazz) - verify_method_names(clazz) - verify_callbacks(clazz) - verify_listeners(clazz) - verify_actions(clazz) - verify_extras(clazz) - verify_equals(clazz) - verify_parcelable(clazz) - verify_protected(clazz) - verify_fields(clazz) - verify_register(clazz) - verify_sync(clazz) - verify_intent_builder(clazz) - verify_helper_classes(clazz) - verify_builder(clazz) - verify_aidl(clazz) - verify_internal(clazz) - verify_layering(clazz) - verify_boolean(clazz, api) - verify_collections(clazz) - verify_flags(clazz) - + examine_clazz(api[key]) return failures @@ -749,49 +1039,52 @@ def verify_compat(cur, prev): prev_clazz = prev[key] if not class_exists(cur, prev_clazz): - error(prev_clazz, None, "Class removed or incompatible change") + error(prev_clazz, None, None, "Class removed or incompatible change") continue cur_clazz = cur[key] for test in prev_clazz.ctors: if not ctor_exists(cur, cur_clazz, test): - error(prev_clazz, prev_ctor, "Constructor removed or incompatible change") + error(prev_clazz, prev_ctor, None, "Constructor removed or incompatible change") methods = all_methods(prev, prev_clazz) for test in methods: if not method_exists(cur, cur_clazz, test): - error(prev_clazz, test, "Method removed or incompatible change") + error(prev_clazz, test, None, "Method removed or incompatible change") for test in prev_clazz.fields: if not field_exists(cur, cur_clazz, test): - error(prev_clazz, test, "Field removed or incompatible change") + error(prev_clazz, test, None, "Field removed or incompatible change") return failures -cur = parse_api(sys.argv[1]) -cur_fail = verify_style(cur) +if __name__ == "__main__": + with open(sys.argv[1]) as f: + cur_fail = examine_stream(f) -if len(sys.argv) > 2: - prev = parse_api(sys.argv[2]) - prev_fail = verify_style(prev) + if len(sys.argv) > 2: + with open(sys.argv[2]) as f: + prev_fail = examine_stream(f) - # ignore errors from previous API level - for p in prev_fail: - if p in cur_fail: - del cur_fail[p] + # ignore errors from previous API level + for p in prev_fail: + if p in cur_fail: + del cur_fail[p] - # look for compatibility issues - compat_fail = verify_compat(cur, prev) + """ + # NOTE: disabled because of memory pressure + # look for compatibility issues + compat_fail = verify_compat(cur, prev) - print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True))) - for f in sorted(compat_fail): - print compat_fail[f] - print + print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True))) + for f in sorted(compat_fail): + print compat_fail[f] + print + """ - -print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True))) -for f in sorted(cur_fail): - print cur_fail[f] - print + print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True))) + for f in sorted(cur_fail): + print cur_fail[f] + print diff --git a/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml b/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml index 595200245b43..a8736000a513 100644 --- a/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml +++ b/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml @@ -5,7 +5,7 @@ </CLASSES> <JAVADOC /> <SOURCES> - <root url="file://$ANDROID_SRC$/tools/base/layoutlib-api/src/main/java" /> + <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/layoutlib_api/layoutlib_api-sources.jar!/" /> </SOURCES> </library> </component>
\ No newline at end of file diff --git a/tools/layoutlib/.idea/misc.xml b/tools/layoutlib/.idea/misc.xml index fa48f70ac6a1..94bcd36b4b37 100644 --- a/tools/layoutlib/.idea/misc.xml +++ b/tools/layoutlib/.idea/misc.xml @@ -9,7 +9,33 @@ <component name="FrameworkDetectionExcludesConfiguration"> <type id="android" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK"> + <component name="NullableNotNullManager"> + <option name="myDefaultNullable" value="com.android.annotations.Nullable" /> + <option name="myDefaultNotNull" value="com.android.annotations.NonNull" /> + <option name="myNullables"> + <value> + <list size="5"> + <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> + <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> + <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" /> + <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" /> + <item index="4" class="java.lang.String" itemvalue="com.android.annotations.Nullable" /> + </list> + </value> + </option> + <option name="myNotNulls"> + <value> + <list size="5"> + <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> + <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> + <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> + <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" /> + <item index="4" class="java.lang.String" itemvalue="com.android.annotations.NonNull" /> + </list> + </value> + </option> + </component> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/out" /> </component> </project>
\ No newline at end of file diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index 8d24d38711c2..970b9d042f60 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -76,13 +76,6 @@ public final class Bitmap_Delegate { // ---- Public Helper methods ---- /** - * Returns the native delegate associated to a given {@link Bitmap_Delegate} object. - */ - public static Bitmap_Delegate getDelegate(Bitmap bitmap) { - return sManager.getDelegate(bitmap.mNativeBitmap); - } - - /** * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object. */ public static Bitmap_Delegate getDelegate(long native_bitmap) { @@ -187,19 +180,6 @@ public final class Bitmap_Delegate { return createBitmap(delegate, createFlags, density.getDpiValue()); } - /** - * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}. - */ - public static BufferedImage getImage(Bitmap bitmap) { - // get the delegate from the native int. - Bitmap_Delegate delegate = sManager.getDelegate(bitmap.mNativeBitmap); - if (delegate == null) { - return null; - } - - return delegate.mImage; - } - public static int getBufferedImageType(int nativeBitmapConfig) { switch (Config.nativeToConfig(nativeBitmapConfig)) { case ALPHA_8: diff --git a/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java index b9928fce6c93..e4fcf1be32bd 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java +++ b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java @@ -37,17 +37,17 @@ import java.awt.image.WritableRaster; */ public final class BlendComposite implements Composite { public enum BlendingMode { - MULTIPLY(Multiply), - SCREEN(Screen), - DARKEN(Darken), - LIGHTEN(Lighten), - OVERLAY(Overlay), - ADD(Add); + MULTIPLY(), + SCREEN(), + DARKEN(), + LIGHTEN(), + OVERLAY(), + ADD(); - private BlendComposite mComposite; + private final BlendComposite mComposite; - BlendingMode(BlendComposite composite) { - mComposite = composite; + BlendingMode() { + mComposite = new BlendComposite(this); } BlendComposite getBlendComposite() { @@ -55,13 +55,6 @@ public final class BlendComposite implements Composite { } } - public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY); - public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN); - public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN); - public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN); - public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY); - public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD); - private float alpha; private BlendingMode mode; diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index f42f48f04a47..1105c7b0fb19 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -661,9 +661,8 @@ public final class Matrix_Delegate { float[] src = new float[] { radius, 0.f, 0.f, radius }; d.mapVectors(src, 0, src, 0, 2); - float l1 = getPointLength(src, 0); - float l2 = getPointLength(src, 2); - + float l1 = (float) Math.hypot(src[0], src[1]); + float l2 = (float) Math.hypot(src[2], src[3]); return (float) Math.sqrt(l1 * l2); } @@ -918,10 +917,6 @@ public final class Matrix_Delegate { } } - private static float getPointLength(float[] src, int index) { - return (float) Math.sqrt(src[index] * src[index] + src[index + 1] * src[index + 1]); - } - /** * multiply two matrices and store them in a 3rd. * <p/>This in effect does dest = a*b diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 7b07404ca401..8ffd1d80a4e6 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -251,7 +251,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getFlags(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -264,7 +264,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setFlags(Paint thisPaint, int flags) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -280,7 +280,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getHinting(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return Paint.HINTING_ON; } @@ -291,7 +291,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setHinting(Paint thisPaint, int mode) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -337,7 +337,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getColor(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -348,7 +348,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setColor(Paint thisPaint, int color) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -359,7 +359,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getAlpha(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -370,7 +370,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setAlpha(Paint thisPaint, int a) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -381,7 +381,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getStrokeWidth(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -392,7 +392,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setStrokeWidth(Paint thisPaint, float width) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -403,7 +403,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getStrokeMiter(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -414,7 +414,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -441,14 +441,14 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static boolean isElegantTextHeight(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT; } @LayoutlibDelegate /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -459,7 +459,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getTextSize(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -470,7 +470,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setTextSize(Paint thisPaint, float textSize) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -482,7 +482,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getTextScaleX(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -493,7 +493,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -505,7 +505,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getTextSkewX(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -516,7 +516,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -528,7 +528,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float ascent(Paint thisPaint) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -545,7 +545,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float descent(Paint thisPaint) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -562,7 +562,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -573,7 +573,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -599,7 +599,7 @@ public class Paint_Delegate { /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index, int count, int bidiFlags) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -1232,7 +1232,7 @@ public class Paint_Delegate { private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java index 80179ee5703d..eb29835ded91 100644 --- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java @@ -184,7 +184,7 @@ public class RadialGradient_Delegate extends Gradient_Delegate { float _x = pt2[0]; float _y = pt2[1]; - float distance = (float) Math.sqrt(_x * _x + _y * _y); + float distance = (float) Math.hypot(_x, _y); data[index++] = getGradientColor(distance / mRadius); } diff --git a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java new file mode 100644 index 000000000000..24e4b54633e5 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2014 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.text; + +import com.android.annotations.NonNull; + +import android.text.Primitive.PrimitiveType; +import android.text.StaticLayout.LineBreaks; + +import java.util.ArrayList; +import java.util.List; + +import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY; + +// Based on the native implementation of GreedyLineBreaker in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class GreedyLineBreaker extends LineBreaker { + + public GreedyLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, + @NonNull TabStops tabStops) { + super(primitives, lineWidth, tabStops); + } + + @Override + public void computeBreaks(LineBreaks lineBreaks) { + BreakInfo breakInfo = new BreakInfo(); + int lineNum = 0; + float width = 0, printedWidth = 0; + boolean breakFound = false, goodBreakFound = false; + int breakIndex = 0, goodBreakIndex = 0; + float breakWidth = 0, goodBreakWidth = 0; + int firstTabIndex = Integer.MAX_VALUE; + + float maxWidth = mLineWidth.getLineWidth(lineNum); + + int numPrimitives = mPrimitives.size(); + // greedily fit as many characters as possible on each line + // loop over all primitives, and choose the best break point + // (if possible, a break point without splitting a word) + // after going over the maximum length + for (int i = 0; i < numPrimitives; i++) { + Primitive p = mPrimitives.get(i); + + // update the current line width + if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { + width += p.width; + if (p.type == PrimitiveType.BOX) { + printedWidth = width; + } + } else if (p.type == PrimitiveType.VARIABLE) { + width = mTabStops.width(width); + // keep track of first tab character in the region we are examining + // so we can determine whether or not a line contains a tab + firstTabIndex = Math.min(firstTabIndex, i); + } + + // find the best break point for the characters examined so far + if (printedWidth > maxWidth) { + //noinspection StatementWithEmptyBody + if (breakFound || goodBreakFound) { + if (goodBreakFound) { + // a true line break opportunity existed in the characters examined so far, + // so there is no need to split a word + i = goodBreakIndex; // no +1 because of i++ + lineNum++; + maxWidth = mLineWidth.getLineWidth(lineNum); + breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location); + breakInfo.mWidthsList.add(goodBreakWidth); + breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex); + firstTabIndex = Integer.MAX_VALUE; + } else { + // must split a word because there is no other option + i = breakIndex; // no +1 because of i++ + lineNum++; + maxWidth = mLineWidth.getLineWidth(lineNum); + breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location); + breakInfo.mWidthsList.add(breakWidth); + breakInfo.mFlagsList.add(firstTabIndex < breakIndex); + firstTabIndex = Integer.MAX_VALUE; + } + printedWidth = width = 0; + goodBreakFound = breakFound = false; + goodBreakWidth = breakWidth = 0; + continue; + } else { + // no choice, keep going... must make progress by putting at least one + // character on a line, even if part of that character is cut off -- + // there is no other option + } + } + + // update possible break points + if (p.type == PrimitiveType.PENALTY && + p.penalty < PENALTY_INFINITY) { + // this does not handle penalties with width + + // handle forced line break + if (p.penalty == -PENALTY_INFINITY) { + lineNum++; + maxWidth = mLineWidth.getLineWidth(lineNum); + breakInfo.mBreaksList.add(p.location); + breakInfo.mWidthsList.add(printedWidth); + breakInfo.mFlagsList.add(firstTabIndex < i); + firstTabIndex = Integer.MAX_VALUE; + printedWidth = width = 0; + goodBreakFound = breakFound = false; + goodBreakWidth = breakWidth = 0; + continue; + } + if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) { + breakFound = true; + breakIndex = i; + breakWidth = printedWidth; + } + if (i > goodBreakIndex && printedWidth <= maxWidth) { + goodBreakFound = true; + goodBreakIndex = i; + goodBreakWidth = printedWidth; + } + } else if (p.type == PrimitiveType.WORD_BREAK) { + // only do this if necessary -- we don't want to break words + // when possible, but sometimes it is unavoidable + if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) { + breakFound = true; + breakIndex = i; + breakWidth = printedWidth; + } + } + } + + if (breakFound || goodBreakFound) { + // output last break if there are more characters to output + if (goodBreakFound) { + breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location); + breakInfo.mWidthsList.add(goodBreakWidth); + breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex); + } else { + breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location); + breakInfo.mWidthsList.add(breakWidth); + breakInfo.mFlagsList.add(firstTabIndex < breakIndex); + } + } + breakInfo.copyTo(lineBreaks); + } + + private static class BreakInfo { + List<Integer> mBreaksList = new ArrayList<Integer>(); + List<Float> mWidthsList = new ArrayList<Float>(); + List<Boolean> mFlagsList = new ArrayList<Boolean>(); + + public void copyTo(LineBreaks lineBreaks) { + if (lineBreaks.breaks.length != mBreaksList.size()) { + lineBreaks.breaks = new int[mBreaksList.size()]; + lineBreaks.widths = new float[mWidthsList.size()]; + lineBreaks.flags = new boolean[mFlagsList.size()]; + } + + int i = 0; + for (int b : mBreaksList) { + lineBreaks.breaks[i] = b; + i++; + } + i = 0; + for (float b : mWidthsList) { + lineBreaks.widths[i] = b; + i++; + } + i = 0; + for (boolean b : mFlagsList) { + lineBreaks.flags[i] = b; + i++; + } + + mBreaksList = null; + mWidthsList = null; + mFlagsList = null; + } + } +} diff --git a/tools/layoutlib/bridge/src/android/text/LineBreaker.java b/tools/layoutlib/bridge/src/android/text/LineBreaker.java new file mode 100644 index 000000000000..8be36352dbab --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/LineBreaker.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 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.text; + +import android.text.StaticLayout.LineBreaks; +import com.android.annotations.NonNull; + +import java.util.Collections; +import java.util.List; + +// Based on the native implementation of LineBreaker in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public abstract class LineBreaker { + + protected final @NonNull List<Primitive> mPrimitives; + protected final @NonNull LineWidth mLineWidth; + protected final @NonNull TabStops mTabStops; + + public LineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, + @NonNull TabStops tabStops) { + mPrimitives = Collections.unmodifiableList(primitives); + mLineWidth = lineWidth; + mTabStops = tabStops; + } + + @NonNull + public abstract void computeBreaks(@NonNull LineBreaks breakInfo); +} diff --git a/tools/layoutlib/bridge/src/android/text/LineWidth.java b/tools/layoutlib/bridge/src/android/text/LineWidth.java new file mode 100644 index 000000000000..2ea886df88a4 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/LineWidth.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 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.text; + +// Based on the native implementation of LineWidth in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class LineWidth { + private final float mFirstWidth; + private final int mFirstWidthLineCount; + private float mRestWidth; + + public LineWidth(float firstWidth, int firstWidthLineCount, float restWidth) { + mFirstWidth = firstWidth; + mFirstWidthLineCount = firstWidthLineCount; + mRestWidth = restWidth; + } + + public float getLineWidth(int line) { + return (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth; + } +} diff --git a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java new file mode 100644 index 000000000000..d5d77983b56b --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2014 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.text; + +import com.android.annotations.NonNull; + +import android.text.Primitive.PrimitiveType; +import android.text.StaticLayout.LineBreaks; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY; + + +// Based on the native implementation of OptimizingLineBreaker in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +/** + * A more complex version of line breaking where we try to prevent the right edge from being too + * jagged. + */ +public class OptimizingLineBreaker extends LineBreaker { + + public OptimizingLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, + @NonNull TabStops tabStops) { + super(primitives, lineWidth, tabStops); + } + + @Override + public void computeBreaks(@NonNull LineBreaks breakInfo) { + int numBreaks = mPrimitives.size(); + assert numBreaks > 0; + if (numBreaks == 1) { + // This can be true only if it's an empty paragraph. + Primitive p = mPrimitives.get(0); + assert p.type == PrimitiveType.PENALTY; + breakInfo.breaks = new int[]{0}; + breakInfo.widths = new float[]{p.width}; + breakInfo.flags = new boolean[]{false}; + return; + } + Node[] opt = new Node[numBreaks]; + opt[0] = new Node(-1, 0, 0, 0, false); + opt[numBreaks - 1] = new Node(-1, 0, 0, 0, false); + + ArrayList<Integer> active = new ArrayList<Integer>(); + active.add(0); + int lastBreak = 0; + for (int i = 0; i < numBreaks; i++) { + Primitive p = mPrimitives.get(i); + if (p.type == PrimitiveType.PENALTY) { + boolean finalBreak = (i + 1 == numBreaks); + Node bestBreak = null; + + for (ListIterator<Integer> it = active.listIterator(); it.hasNext(); + /* incrementing done in loop */) { + int pos = it.next(); + int lines = opt[pos].mPrevCount; + float maxWidth = mLineWidth.getLineWidth(lines); + // we have to compute metrics every time -- + // we can't really pre-compute this stuff and just deal with breaks + // because of the way tab characters work, this makes it computationally + // harder, but this way, we can still optimize while treating tab characters + // correctly + LineMetrics lineMetrics = computeMetrics(pos, i); + if (lineMetrics.mPrintedWidth <= maxWidth) { + float demerits = computeDemerits(maxWidth, lineMetrics.mPrintedWidth, + finalBreak, p.penalty) + opt[pos].mDemerits; + if (bestBreak == null || demerits < bestBreak.mDemerits) { + if (bestBreak == null) { + bestBreak = new Node(pos, opt[pos].mPrevCount + 1, demerits, + lineMetrics.mPrintedWidth, lineMetrics.mHasTabs); + } else { + bestBreak.mPrev = pos; + bestBreak.mPrevCount = opt[pos].mPrevCount + 1; + bestBreak.mDemerits = demerits; + bestBreak.mWidth = lineMetrics.mPrintedWidth; + bestBreak.mHasTabs = lineMetrics.mHasTabs; + } + } + } else { + it.remove(); + } + } + if (p.penalty == -PENALTY_INFINITY) { + active.clear(); + } + if (bestBreak != null) { + opt[i] = bestBreak; + active.add(i); + lastBreak = i; + } + if (active.isEmpty()) { + // we can't give up! + LineMetrics lineMetrics = new LineMetrics(); + int lines = opt[lastBreak].mPrevCount; + float maxWidth = mLineWidth.getLineWidth(lines); + int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, lineMetrics); + opt[breakIndex] = new Node(lastBreak, lines + 1, 0 /*doesn't matter*/, + lineMetrics.mWidth, lineMetrics.mHasTabs); + active.add(breakIndex); + lastBreak = breakIndex; + i = breakIndex; // incremented by i++ + } + } + } + + int idx = numBreaks - 1; + int count = opt[idx].mPrevCount; + resize(breakInfo, count); + while (opt[idx].mPrev != -1) { + count--; + assert count >=0; + + breakInfo.breaks[count] = mPrimitives.get(idx).location; + breakInfo.widths[count] = opt[idx].mWidth; + breakInfo.flags [count] = opt[idx].mHasTabs; + idx = opt[idx].mPrev; + } + } + + private static void resize(LineBreaks lineBreaks, int size) { + if (lineBreaks.breaks.length == size) { + return; + } + int[] breaks = new int[size]; + float[] widths = new float[size]; + boolean[] flags = new boolean[size]; + + int toCopy = Math.min(size, lineBreaks.breaks.length); + System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy); + System.arraycopy(lineBreaks.widths, 0, widths, 0, toCopy); + System.arraycopy(lineBreaks.flags, 0, flags, 0, toCopy); + + lineBreaks.breaks = breaks; + lineBreaks.widths = widths; + lineBreaks.flags = flags; + } + + @NonNull + private LineMetrics computeMetrics(int start, int end) { + boolean f = false; + float w = 0, pw = 0; + for (int i = start; i < end; i++) { + Primitive p = mPrimitives.get(i); + if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { + w += p.width; + if (p.type == PrimitiveType.BOX) { + pw = w; + } + } else if (p.type == PrimitiveType.VARIABLE) { + w = mTabStops.width(w); + f = true; + } + } + return new LineMetrics(w, pw, f); + } + + private static float computeDemerits(float maxWidth, float width, boolean finalBreak, + float penalty) { + float deviation = finalBreak ? 0 : maxWidth - width; + return (deviation * deviation) + penalty; + } + + /** + * @return the last break position or -1 if failed. + */ + @SuppressWarnings("ConstantConditions") // method too complex to be analyzed. + private int desperateBreak(int start, int limit, float maxWidth, + @NonNull LineMetrics lineMetrics) { + float w = 0, pw = 0; + boolean breakFound = false; + int breakIndex = 0, firstTabIndex = Integer.MAX_VALUE; + for (int i = start; i < limit; i++) { + Primitive p = mPrimitives.get(i); + + if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { + w += p.width; + if (p.type == PrimitiveType.BOX) { + pw = w; + } + } else if (p.type == PrimitiveType.VARIABLE) { + w = mTabStops.width(w); + firstTabIndex = Math.min(firstTabIndex, i); + } + + if (pw > maxWidth && breakFound) { + break; + } + + // must make progress + if (i > start && + (p.type == PrimitiveType.PENALTY || p.type == PrimitiveType.WORD_BREAK)) { + breakFound = true; + breakIndex = i; + } + } + + if (breakFound) { + lineMetrics.mWidth = w; + lineMetrics.mPrintedWidth = pw; + lineMetrics.mHasTabs = (start <= firstTabIndex && firstTabIndex < breakIndex); + return breakIndex; + } else { + return -1; + } + } + + private static class LineMetrics { + /** Actual width of the line. */ + float mWidth; + /** Width of the line minus trailing whitespace. */ + float mPrintedWidth; + boolean mHasTabs; + + public LineMetrics() { + } + + public LineMetrics(float width, float printedWidth, boolean hasTabs) { + mWidth = width; + mPrintedWidth = printedWidth; + mHasTabs = hasTabs; + } + } + + /** + * A struct to store the info about a break. + */ + @SuppressWarnings("SpellCheckingInspection") // For the word struct. + private static class Node { + // -1 for the first node. + int mPrev; + // number of breaks so far. + int mPrevCount; + float mDemerits; + float mWidth; + boolean mHasTabs; + + public Node(int prev, int prevCount, float demerits, float width, boolean hasTabs) { + mPrev = prev; + mPrevCount = prevCount; + mDemerits = demerits; + mWidth = width; + mHasTabs = hasTabs; + } + } +} diff --git a/tools/layoutlib/bridge/src/android/text/Primitive.java b/tools/layoutlib/bridge/src/android/text/Primitive.java new file mode 100644 index 000000000000..ce77601582c9 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/Primitive.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2014 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.text; + +import com.android.annotations.NonNull; + +// Based on the native implementation of Primitive in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class Primitive { + public final @NonNull PrimitiveType type; + public final int location; + // The following fields don't make sense for all types. + // Box and Glue have width only. + // Penalty has both width and penalty. + // Word_break has penalty only. + public final float width; + public final float penalty; + + /** + * Use {@code PrimitiveType#getNewPrimitive()} + */ + private Primitive(@NonNull PrimitiveType type, int location, float width, float penalty) { + this.type = type; + this.location = location; + this.width = width; + this.penalty = penalty; + } + + public static enum PrimitiveType { + /** + * Something with a constant width that is to be typeset - like a character. + */ + BOX, + /** + * Blank space with fixed width. + */ + GLUE, + /** + * Aesthetic cost indicating how desirable breaking at this point will be. A penalty of + * {@link #PENALTY_INFINITY} means a forced non-break, whereas a penalty of negative + * {@code #PENALTY_INFINITY} means a forced break. + * <p/> + * Currently, it only stores penalty with values 0 or -infinity. + */ + PENALTY, + /** + * For tabs - variable width space. + */ + VARIABLE, + /** + * Possible breakpoints within a word. Think of this as a high cost {@link #PENALTY}. + */ + WORD_BREAK; + + public Primitive getNewPrimitive(int location) { + assert this == VARIABLE; + return new Primitive(this, location, 0f, 0f); + } + + public Primitive getNewPrimitive(int location, float value) { + assert this == BOX || this == GLUE || this == WORD_BREAK; + if (this == BOX || this == GLUE) { + return new Primitive(this, location, value, 0f); + } else { + return new Primitive(this, location, 0f, value); + } + } + + public Primitive getNewPrimitive(int location, float width, float penalty) { + assert this == PENALTY; + return new Primitive(this, location, width, penalty); + } + + // forced non-break, negative infinity is forced break. + public static final float PENALTY_INFINITY = 1e7f; + } +} + diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java index b0d79a8e8f22..e24b3d5ec7ef 100644 --- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java @@ -1,12 +1,15 @@ package android.text; +import com.android.annotations.NonNull; +import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import java.text.CharacterIterator; -import java.util.Arrays; -import java.util.Locale; +import android.text.StaticLayout.LineBreaks; +import android.text.Primitive.PrimitiveType; + +import java.util.ArrayList; +import java.util.List; -import com.ibm.icu.lang.UCharacter; import com.ibm.icu.text.BreakIterator; import com.ibm.icu.util.ULocale; import javax.swing.text.Segment; @@ -20,36 +23,112 @@ import javax.swing.text.Segment; */ public class StaticLayout_Delegate { - /** - * Fills the recycle array with positions that are suitable to break the text at. The array - * must be terminated by '-1'. - */ + private static final char CHAR_SPACE = 0x20; + private static final char CHAR_TAB = 0x09; + private static final char CHAR_NEWLINE = 0x0A; + private static final char CHAR_ZWSP = 0x200B; // Zero width space. + + // ---- Builder delegate manager ---- + private static final DelegateManager<Builder> sBuilderManager = + new DelegateManager<Builder>(Builder.class); + @LayoutlibDelegate - /*package*/ static int[] nLineBreakOpportunities(String locale, char[] text, int length, - int[] recycle) { - BreakIterator iterator = BreakIterator.getLineInstance(new ULocale(locale)); - Segment segment = new Segment(text, 0, length); - iterator.setText(segment); - if (recycle == null) { - // Because 42 is the answer to everything. - recycle = new int[42]; + /*package*/ static int nComputeLineBreaks(long nativeBuilder, char[] inputText, float[] widths, + int length, float firstWidth, int firstWidthLineCount, float restWidth, + int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle, + int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength) { + + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + // compute all possible breakpoints. + BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale)); + it.setText(new Segment(inputText, 0, length)); + // average word length in english is 5. So, initialize the possible breaks with a guess. + List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d)); + int loc; + it.first(); + while ((loc = it.next()) != BreakIterator.DONE) { + breaks.add(loc); + } + + LineWidth lineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth); + TabStops tabStopCalculator = new TabStops(variableTabStops, defaultTabStop); + List<Primitive> primitives = computePrimitives(inputText, widths, length, breaks); + LineBreaker lineBreaker; + if (optimize) { + lineBreaker = new OptimizingLineBreaker(primitives, lineWidth, tabStopCalculator); + } else { + lineBreaker = new GreedyLineBreaker(primitives, lineWidth, tabStopCalculator); } - int breakOpp = iterator.first(); - recycle[0] = breakOpp; - //noinspection ConstantConditions - assert BreakIterator.DONE == -1; - for (int i = 1; breakOpp != BreakIterator.DONE; ++i) { - if (i >= recycle.length) { - recycle = doubleSize(recycle); + lineBreaker.computeBreaks(recycle); + return recycle.breaks.length; + } + + /** + * Compute metadata each character - things which help in deciding if it's possible to break + * at a point or not. + */ + @NonNull + private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths, + int length, @NonNull List<Integer> breaks) { + // Initialize the list with a guess of the number of primitives: + // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars) + List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833))); + int breaksSize = breaks.size(); + int breakIndex = 0; + for (int i = 0; i < length; i++) { + char c = text[i]; + if (c == CHAR_SPACE || c == CHAR_ZWSP) { + primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i])); + } else if (c == CHAR_TAB) { + primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i)); + } else if (c != CHAR_NEWLINE) { + while (breakIndex < breaksSize && breaks.get(breakIndex) < i) { + breakIndex++; + } + Primitive p; + if (widths[i] != 0) { + if (breakIndex < breaksSize && breaks.get(breakIndex) == i) { + p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0); + } else { + p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0); + } + primitives.add(p); + } + + primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i])); } - assert (i < recycle.length); - breakOpp = iterator.next(); - recycle[i] = breakOpp; } - return recycle; + // final break at end of everything + primitives.add( + PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY)); + return primitives; } - private static int[] doubleSize(int[] array) { - return Arrays.copyOf(array, array.length * 2); + @LayoutlibDelegate + /*package*/ static long nNewBuilder() { + return sBuilderManager.addNewDelegate(new Builder()); + } + + @LayoutlibDelegate + /*package*/ static void nFinishBuilder(long nativeBuilder) { + } + + @LayoutlibDelegate + /*package*/ static void nFreeBuilder(long nativeBuilder) { + sBuilderManager.removeJavaReferenceFor(nativeBuilder); + } + + @LayoutlibDelegate + /*package*/ static void nBuilderSetLocale(long nativeBuilder, String locale) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + builder.mLocale = locale; + } + + /** + * Java representation of the native Builder class. It currently only stores the locale + * set by nBuilderSetLocale. + */ + static class Builder { + String mLocale; } } diff --git a/tools/layoutlib/bridge/src/android/text/TabStops.java b/tools/layoutlib/bridge/src/android/text/TabStops.java new file mode 100644 index 000000000000..cff6b93e8361 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/TabStops.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 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.text; + +import com.android.annotations.Nullable; + +// Based on the native implementation of TabStops in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class TabStops { + @Nullable + private int[] mStops; + private final int mTabWidth; + + public TabStops(@Nullable int[] stops, int defaultTabWidth) { + mTabWidth = defaultTabWidth; + mStops = stops; + } + + public float width(float widthSoFar) { + if (mStops != null) { + for (int i : mStops) { + if (i > widthSoFar) { + return i; + } + } + } + // find the next tabStop after widthSoFar. + return (int) ((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth; + } +} diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java deleted file mode 100644 index 8b4c60b5fead..000000000000 --- a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2007 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.util; - -import com.android.layoutlib.bridge.impl.DelegateManager; -import com.android.tools.layoutlib.annotations.LayoutlibDelegate; - -/** - * Delegate implementing the native methods of android.util.FloatMath - * - * Through the layoutlib_create tool, the original native methods of FloatMath have been replaced - * by calls to methods of the same name in this delegate class. - * - * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager} - * around to map int to instance of the delegate. - * - */ -/*package*/ final class FloatMath_Delegate { - - /** Prevents instantiation. */ - private FloatMath_Delegate() {} - - /** - * Returns the float conversion of the most positive (i.e. closest to - * positive infinity) integer value which is less than the argument. - * - * @param value to be converted - * @return the floor of value - */ - @LayoutlibDelegate - /*package*/ static float floor(float value) { - return (float)Math.floor(value); - } - - /** - * Returns the float conversion of the most negative (i.e. closest to - * negative infinity) integer value which is greater than the argument. - * - * @param value to be converted - * @return the ceiling of value - */ - @LayoutlibDelegate - /*package*/ static float ceil(float value) { - return (float)Math.ceil(value); - } - - /** - * Returns the closest float approximation of the sine of the argument. - * - * @param angle to compute the cosine of, in radians - * @return the sine of angle - */ - @LayoutlibDelegate - /*package*/ static float sin(float angle) { - return (float)Math.sin(angle); - } - - /** - * Returns the closest float approximation of the cosine of the argument. - * - * @param angle to compute the cosine of, in radians - * @return the cosine of angle - */ - @LayoutlibDelegate - /*package*/ static float cos(float angle) { - return (float)Math.cos(angle); - } - - /** - * Returns the closest float approximation of the square root of the - * argument. - * - * @param value to compute sqrt of - * @return the square root of value - */ - @LayoutlibDelegate - /*package*/ static float sqrt(float value) { - return (float)Math.sqrt(value); - } - - /** - * Returns the closest float approximation of the raising "e" to the power - * of the argument. - * - * @param value to compute the exponential of - * @return the exponential of value - */ - @LayoutlibDelegate - /*package*/ static float exp(float value) { - return (float)Math.exp(value); - } - - /** - * Returns the closest float approximation of the result of raising {@code - * x} to the power of {@code y}. - * - * @param x the base of the operation. - * @param y the exponent of the operation. - * @return {@code x} to the power of {@code y}. - */ - @LayoutlibDelegate - /*package*/ static float pow(float x, float y) { - return (float)Math.pow(x, y); - } - - /** - * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i> - * {@code y}</i><sup>{@code 2}</sup>{@code )}. - * - * @param x a float number - * @param y a float number - * @return the hypotenuse - */ - @LayoutlibDelegate - /*package*/ static float hypot(float x, float y) { - return (float)Math.sqrt(x*x + y*y); - } -} diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index 7e4ff6983315..8192b5fbbf82 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -22,9 +22,13 @@ import com.android.ide.common.rendering.api.MergeCookie; import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; +import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; +import com.android.layoutlib.bridge.android.support.RecyclerViewUtil.LayoutManagerType; import com.android.layoutlib.bridge.impl.ParserFactory; +import com.android.layoutlib.bridge.impl.RenderSessionImpl; import com.android.resources.ResourceType; import com.android.util.Pair; @@ -111,8 +115,7 @@ public final class BridgeInflater extends LayoutInflater { } catch (Exception e) { // Wrap the real exception in a ClassNotFoundException, so that the calling method // can deal with it. - ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e); - throw exception; + throw new ClassNotFoundException("onCreateView", e); } setupViewInContext(view, attrs); @@ -123,7 +126,7 @@ public final class BridgeInflater extends LayoutInflater { @Override public View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) { - View view = null; + View view; try { view = super.createViewFromTag(parent, name, attrs, inheritContext); } catch (InflateException e) { @@ -134,7 +137,7 @@ public final class BridgeInflater extends LayoutInflater { // Wrap the real exception in an InflateException so that the calling // method can deal with it. InflateException exception = new InflateException(); - if (e2.getClass().equals(ClassNotFoundException.class) == false) { + if (!e2.getClass().equals(ClassNotFoundException.class)) { exception.initCause(e2); } else { exception.initCause(e); @@ -184,7 +187,7 @@ public final class BridgeInflater extends LayoutInflater { return inflate(bridgeParser, root); } catch (Exception e) { Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - "Failed to parse file " + f.getAbsolutePath(), e, null /*data*/); + "Failed to parse file " + f.getAbsolutePath(), e, null); return null; } @@ -194,8 +197,7 @@ public final class BridgeInflater extends LayoutInflater { return null; } - private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException, - Exception{ + private View loadCustomView(String name, AttributeSet attrs) throws Exception { if (mProjectCallback != null) { // first get the classname in case it's not the node name if (name.equals("view")) { @@ -227,6 +229,22 @@ public final class BridgeInflater extends LayoutInflater { if (viewKey != null) { bc.addViewKey(view, viewKey); } + if (RenderSessionImpl.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) { + String type = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES, + BridgeConstants.ATTR_LAYOUT_MANAGER_TYPE); + if (type != null) { + LayoutManagerType layoutManagerType = LayoutManagerType.getByLogicalName(type); + if (layoutManagerType == null) { + layoutManagerType = LayoutManagerType.getByClassName(type); + } + if (layoutManagerType == null) { + // add the classname itself. + bc.addCookie(view, type); + } else { + bc.addCookie(view, layoutManagerType); + } + } + } } } diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 5176419a8b68..32305a090d76 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -215,6 +215,12 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void overridePendingAppTransitionClipReveal(int startX, int startY, + int startWidth, int startHeight) throws RemoteException { + // TODO Auto-generated method stub + } + + @Override public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, IRemoteCallback startedCallback, boolean scaleUp) throws RemoteException { // TODO Auto-generated method stub @@ -293,7 +299,7 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException { + public void setAppTask(IBinder arg0, int arg1) throws RemoteException { // TODO Auto-generated method stub } diff --git a/tools/layoutlib/bridge/src/android/view/ShadowPainter.java b/tools/layoutlib/bridge/src/android/view/ShadowPainter.java index 38846bdbb303..a0db7bfadb05 100644 --- a/tools/layoutlib/bridge/src/android/view/ShadowPainter.java +++ b/tools/layoutlib/bridge/src/android/view/ShadowPainter.java @@ -65,6 +65,9 @@ public class ShadowPainter { @SuppressWarnings({"SuspiciousNameCombination", "UnnecessaryLocalVariable"}) // Imported code public static BufferedImage createDropShadow(BufferedImage source, int shadowSize, float shadowOpacity, int shadowRgb) { + if (shadowSize == 0) { + return source; + } // This code is based on // http://www.jroller.com/gfx/entry/non_rectangular_shadow diff --git a/tools/layoutlib/bridge/src/android/view/WindowCallback.java b/tools/layoutlib/bridge/src/android/view/WindowCallback.java index 78242a8ab08e..823b2478458f 100644 --- a/tools/layoutlib/bridge/src/android/view/WindowCallback.java +++ b/tools/layoutlib/bridge/src/android/view/WindowCallback.java @@ -120,6 +120,11 @@ public class WindowCallback implements Window.Callback { } @Override + public ActionMode onWindowStartingActionMode(Callback callback, int type) { + return null; + } + + @Override public void onActionModeStarted(ActionMode mode) { } diff --git a/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java b/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java deleted file mode 100644 index 0100dc591039..000000000000 --- a/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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. - */ - -package com.android.internal.policy; - -import com.android.ide.common.rendering.api.LayoutLog; -import com.android.layoutlib.bridge.Bridge; -import com.android.layoutlib.bridge.impl.RenderAction; - -import android.content.Context; -import android.view.BridgeInflater; -import android.view.FallbackEventHandler; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.Window; -import android.view.WindowManagerPolicy; - -/** - * Custom implementation of PolicyManager that does nothing to run in LayoutLib. - * - */ -public class PolicyManager { - - public static Window makeNewWindow(Context context) { - // this will likely crash somewhere beyond so we log it. - Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "Call to PolicyManager.makeNewWindow is not supported", null); - return null; - } - - public static LayoutInflater makeNewLayoutInflater(Context context) { - return new BridgeInflater(context, RenderAction.getCurrentContext().getProjectCallback()); - } - - public static WindowManagerPolicy makeNewWindowManager() { - // this will likely crash somewhere beyond so we log it. - Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "Call to PolicyManager.makeNewWindowManager is not supported", null); - return null; - } - - public static FallbackEventHandler makeNewFallbackEventHandler(Context context) { - return new FallbackEventHandler() { - @Override - public void setView(View v) { - } - - @Override - public void preDispatchKeyEvent(KeyEvent event) { - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - return false; - } - }; - } -} 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 4d2c2fc20588..c6d60f8a76f6 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -181,7 +181,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { */ private static LayoutLog sCurrentLog = sDefaultLog; - private static final int LAST_SUPPORTED_FEATURE = Features.PREFERENCES_RENDERING; + private static final int LAST_SUPPORTED_FEATURE = Features.RENDER_ALL_DRAWABLE_STATES; @Override public int getApiLevel() { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java index eb9e7f1dafb3..1af69982236e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java @@ -48,4 +48,7 @@ public class BridgeConstants { public final static String MATCH_PARENT = "match_parent"; public final static String FILL_PARENT = "fill_parent"; public final static String WRAP_CONTENT = "wrap_content"; + + /** Attribute in the tools namespace used to specify layout manager for RecyclerView. */ + public static final String ATTR_LAYOUT_MANAGER_TYPE = "layoutManager"; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java index e0f87fd63b11..feb25905390c 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java @@ -86,11 +86,14 @@ public class BridgeRenderSession extends RenderSession { } @Override - public Result render(long timeout) { + public Result render(long timeout, boolean forceMeasure) { try { Bridge.prepareThread(); mLastResult = mSession.acquire(timeout); if (mLastResult.isSuccess()) { + if (forceMeasure) { + mSession.invalidateRenderingSize(); + } mLastResult = mSession.render(false /*freshRender*/); } } finally { 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 2f62b93ede6a..179a8e773482 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 @@ -16,7 +16,6 @@ package com.android.layoutlib.bridge.android; -import android.os.IBinder; import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.AssetRepository; import com.android.ide.common.rendering.api.ILayoutPullParser; @@ -64,6 +63,7 @@ import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.UserHandle; @@ -73,6 +73,7 @@ import android.util.TypedValue; import android.view.BridgeInflater; import android.view.Display; import android.view.DisplayAdjustments; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -94,6 +95,7 @@ import java.util.Map; /** * Custom implementation of Context/Activity to handle non compiled resources. */ +@SuppressWarnings("deprecation") // For use of Pair. public final class BridgeContext extends Context { /** The map adds cookies to each view so that IDE can link xml tags to views. */ @@ -495,6 +497,34 @@ public final class BridgeContext extends Context { throw new UnsupportedOperationException("Unsupported Service: " + service); } + @Override + public String getSystemServiceName(Class<?> serviceClass) { + if (serviceClass.equals(LayoutInflater.class)) { + return LAYOUT_INFLATER_SERVICE; + } + + if (serviceClass.equals(TextServicesManager.class)) { + return TEXT_SERVICES_MANAGER_SERVICE; + } + + if (serviceClass.equals(WindowManager.class)) { + return WINDOW_SERVICE; + } + + if (serviceClass.equals(PowerManager.class)) { + return POWER_SERVICE; + } + + if (serviceClass.equals(DisplayManager.class)) { + return DISPLAY_SERVICE; + } + + if (serviceClass.equals(AccessibilityManager.class)) { + return ACCESSIBILITY_SERVICE; + } + + throw new UnsupportedOperationException("Unsupported Service: " + serviceClass); + } @Override public final BridgeTypedArray obtainStyledAttributes(int[] attrs) { @@ -569,7 +599,7 @@ public final class BridgeContext extends Context { // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java if (set instanceof BridgeXmlBlockParser) { - BridgeXmlBlockParser parser = null; + BridgeXmlBlockParser parser; parser = (BridgeXmlBlockParser)set; isPlatformFile = parser.isPlatformFile(); @@ -590,7 +620,7 @@ public final class BridgeContext extends Context { } else if (set != null) { // null parser is ok // really this should not be happening since its instantiated in Bridge Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Parser is not a BridgeXmlBlockParser!", null /*data*/); + "Parser is not a BridgeXmlBlockParser!", null); return null; } @@ -602,7 +632,7 @@ public final class BridgeContext extends Context { // look for a custom style. String customStyle = null; if (set != null) { - customStyle = set.getAttributeValue(null /* namespace*/, "style"); + customStyle = set.getAttributeValue(null, "style"); } StyleResourceValue customStyleValues = null; @@ -625,31 +655,39 @@ public final class BridgeContext extends Context { // get the name from the int. Pair<String, Boolean> defStyleAttribute = searchAttr(defStyleAttr); - if (defaultPropMap != null) { - String defStyleName = defStyleAttribute.getFirst(); - if (defStyleAttribute.getSecond()) { - defStyleName = "android:" + defStyleName; + if (defStyleAttribute == null) { + // This should be rare. Happens trying to map R.style.foo to @style/foo fails. + // This will happen if the user explicitly used a non existing int value for + // defStyleAttr or there's something wrong with the project structure/build. + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE, + "Failed to find the style corresponding to the id " + defStyleAttr, null); + } else { + if (defaultPropMap != null) { + String defStyleName = defStyleAttribute.getFirst(); + if (defStyleAttribute.getSecond()) { + defStyleName = "android:" + defStyleName; + } + defaultPropMap.put("style", defStyleName); } - defaultPropMap.put("style", defStyleName); - } - // look for the style in the current theme, and its parent: - ResourceValue item = mRenderResources.findItemInTheme(defStyleAttribute.getFirst(), - defStyleAttribute.getSecond()); + // look for the style in the current theme, and its parent: + ResourceValue item = mRenderResources.findItemInTheme(defStyleAttribute.getFirst(), + defStyleAttribute.getSecond()); - if (item != null) { - // item is a reference to a style entry. Search for it. - item = mRenderResources.findResValue(item.getValue(), item.isFramework()); + if (item != null) { + // item is a reference to a style entry. Search for it. + item = mRenderResources.findResValue(item.getValue(), item.isFramework()); - if (item instanceof StyleResourceValue) { - defStyleValues = (StyleResourceValue)item; + if (item instanceof StyleResourceValue) { + defStyleValues = (StyleResourceValue) item; + } + } else { + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR, + String.format( + "Failed to find style '%s' in current theme", + defStyleAttribute.getFirst()), + null); } - } else { - Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR, - String.format( - "Failed to find style '%s' in current theme", - defStyleAttribute.getFirst()), - null /*data*/); } } else if (defStyleRes != 0) { boolean isFrameworkRes = true; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java index 0f51d00ca62d..4289689a37f5 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java @@ -191,12 +191,6 @@ public final class BridgeWindowSession implements IWindowSession { } @Override - public void setUniverseTransform(IBinder window, float alpha, float offx, float offy, - float dsdx, float dtdx, float dsdy, float dtdy) { - // pass for now. - } - - @Override public IBinder asBinder() { // pass for now. return null; @@ -212,4 +206,9 @@ public final class BridgeWindowSession implements IWindowSession { // pass for now. return null; } + + @Override + public void pokeDrawLock(IBinder window) { + // pass for now. + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java index e00ea6a207fa..2f45473bf53b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java @@ -16,20 +16,26 @@ package com.android.layoutlib.bridge.android; -import com.android.ide.common.rendering.api.SessionParams; +import com.android.ide.common.rendering.api.SessionParams.Key; /** - * This contains all known keys for the {@link SessionParams#getFlag(SessionParams.Key)}. + * This contains all known keys for the {@link RenderParams#getFlag(Key)}. * <p/> * The IDE has its own copy of this class which may be newer or older than this one. * <p/> * Constants should never be modified or removed from this class. */ -public final class SessionParamsFlags { +public final class RenderParamsFlags { - public static final SessionParams.Key<String> FLAG_KEY_ROOT_TAG = - new SessionParams.Key<String>("rootTag", String.class); + public static final Key<String> FLAG_KEY_ROOT_TAG = + new Key<String>("rootTag", String.class); + public static final Key<Boolean> FLAG_KEY_DISABLE_BITMAP_CACHING = + new Key<Boolean>("disableBitmapCaching", Boolean.class); + public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT = + new Key<Boolean>("recyclerViewSupport", Boolean.class); + public static final Key<Boolean> FLAG_KEY_RENDER_ALL_DRAWABLE_STATES = + new Key<Boolean>("renderAllDrawableStates", Boolean.class); // Disallow instances. - private SessionParamsFlags() {} + private RenderParamsFlags() {} } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java new file mode 100644 index 000000000000..b72329af2a9d --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2015 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.android.support; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.ide.common.rendering.api.IProjectCallback; +import com.android.ide.common.rendering.api.LayoutLog; +import com.android.ide.common.rendering.api.SessionParams; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.android.RenderParamsFlags; + +import android.content.Context; +import android.view.View; +import android.widget.LinearLayout; + +import java.lang.reflect.Method; +import java.util.HashMap; + +import static com.android.layoutlib.bridge.util.ReflectionUtils.*; + +/** + * Utility class for working with android.support.v7.widget.RecyclerView + */ +@SuppressWarnings("SpellCheckingInspection") // for "recycler". +public class RecyclerViewUtil { + + /** + * Used by {@link LayoutManagerType}. + * <p/> + * Not declared inside the enum, since it needs to be accessible in the constructor. + */ + private static final Object CONTEXT = new Object(); + + public static final String CN_RECYCLER_VIEW = "android.support.v7.widget.RecyclerView"; + private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager"; + private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter"; + + /** + * Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a + * LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView} + * that is passed. + * <p/> + * Any exceptions thrown during the process are logged in {@link Bridge#getLog()} + */ + public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context, + @NonNull SessionParams params) { + try { + setLayoutManager(recyclerView, context, params.getProjectCallback()); + Object adapter = createAdapter(params); + setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter"); + } catch (ReflectionException e) { + Bridge.getLog().error(LayoutLog.TAG_BROKEN, + "Error occured while trying to setup RecyclerView.", e, null); + } + } + + private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context, + @NonNull IProjectCallback callback) throws ReflectionException { + Object cookie = context.getCookie(recyclerView); + assert cookie == null || cookie instanceof LayoutManagerType || cookie instanceof String; + if (!(cookie instanceof LayoutManagerType)) { + if (cookie != null) { + // TODO: When layoutlib API is updated, try to load the class with a null + // constructor or a constructor taking one argument - the context. + Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, + "LayoutManager (" + cookie + ") not found, falling back to " + + "LinearLayoutManager", null); + } + cookie = LayoutManagerType.getDefault(); + } + Object layoutManager = createLayoutManager((LayoutManagerType) cookie, context, callback); + setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager"); + } + + @Nullable + private static Object createLayoutManager(@Nullable LayoutManagerType type, + @NonNull Context context, @NonNull IProjectCallback callback) + throws ReflectionException { + if (type == null) { + type = LayoutManagerType.getDefault(); + } + try { + return callback.loadView(type.getClassName(), type.getSignature(), type.getArgs(context)); + } catch (Exception e) { + throw new ReflectionException(e); + } + } + + @Nullable + private static Object createAdapter(@NonNull SessionParams params) throws ReflectionException { + Boolean ideSupport = params.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); + if (ideSupport != Boolean.TRUE) { + return null; + } + try { + return params.getProjectCallback().loadView(CN_ADAPTER, new Class[0], new Object[0]); + } catch (Exception e) { + throw new ReflectionException(e); + } + } + + private static void setProperty(@NonNull View recyclerView, @NonNull String propertyClassName, + @Nullable Object propertyValue, @NonNull String propertySetter) + throws ReflectionException { + if (propertyValue != null) { + Class<?> layoutManagerClass = getClassInstance(propertyValue, propertyClassName); + Method setLayoutManager = getMethod(recyclerView.getClass(), + propertySetter, layoutManagerClass); + if (setLayoutManager != null) { + invoke(setLayoutManager, recyclerView, propertyValue); + } + } + } + + /** + * Looks through the class hierarchy of {@code object} at runtime and returns the class matching + * the name {@code className}. + * <p/> + * This is used when we cannot use Class.forName() since the class we want was loaded from a + * different ClassLoader. + */ + @NonNull + private static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) { + Class<?> superClass = object.getClass(); + while (superClass != null) { + if (className.equals(superClass.getName())) { + return superClass; + } + superClass = superClass.getSuperclass(); + } + throw new RuntimeException("invalid object/classname combination."); + } + + /** Supported LayoutManagers. */ + public enum LayoutManagerType { + LINEAR_LAYOUT_MANGER("Linear", + "android.support.v7.widget.LinearLayoutManager", + new Class[]{Context.class}, new Object[]{CONTEXT}), + GRID_LAYOUT_MANAGER("Grid", + "android.support.v7.widget.GridLayoutManager", + new Class[]{Context.class, int.class}, new Object[]{CONTEXT, 2}), + STAGGERED_GRID_LAYOUT_MANAGER("StaggeredGrid", + "android.support.v7.widget.StaggeredGridLayoutManager", + new Class[]{int.class, int.class}, new Object[]{2, LinearLayout.VERTICAL}); + + private String mLogicalName; + private String mClassName; + private Class[] mSignature; + private Object[] mArgs; + + LayoutManagerType(String logicalName, String className, Class[] signature, Object[] args) { + mLogicalName = logicalName; + mClassName = className; + mSignature = signature; + mArgs = args; + } + + String getClassName() { + return mClassName; + } + + Class[] getSignature() { + return mSignature; + } + + @NonNull + Object[] getArgs(Context context) { + Object[] args = new Object[mArgs.length]; + System.arraycopy(mArgs, 0, args, 0, mArgs.length); + for (int i = 0; i < args.length; i++) { + if (args[i] == CONTEXT) { + args[i] = context; + } + } + return args; + } + + @NonNull + public static LayoutManagerType getDefault() { + return LINEAR_LAYOUT_MANGER; + } + + @Nullable + public static LayoutManagerType getByLogicalName(@NonNull String logicalName) { + for (LayoutManagerType type : values()) { + if (logicalName.equals(type.mLogicalName)) { + return type; + } + } + return null; + } + + @Nullable + public static LayoutManagerType getByClassName(@NonNull String className) { + for (LayoutManagerType type : values()) { + if (className.equals(type.mClassName)) { + return type; + } + } + return null; + } + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java index 3a0321a167ca..c34f9b51a349 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java @@ -35,6 +35,7 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; +import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; @@ -615,8 +616,22 @@ public class GcSnapshot { return; } - int width = layer.getImage().getWidth(); - int height = layer.getImage().getHeight(); + int width; + int height; + Rectangle clipBounds = originalGraphics.getClipBounds(); + if (clipBounds != null) { + if (clipBounds.width == 0 || clipBounds.height == 0) { + // Clip is 0 so no need to paint anything. + return; + } + // If we have clipBounds available, use them as they will always be + // smaller than the full layer size. + width = clipBounds.width; + height = clipBounds.height; + } else { + width = layer.getImage().getWidth(); + height = layer.getImage().getHeight(); + } // Create a temporary image to which the color filter will be applied. BufferedImage image = new BufferedImage(width, height, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java index 669e6b5d9865..3dee1e23dfe2 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java @@ -16,20 +16,20 @@ package com.android.layoutlib.bridge.impl; -import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; - import com.android.ide.common.rendering.api.DrawableParams; import com.android.ide.common.rendering.api.HardwareConfig; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.Result.Status; import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.android.RenderParamsFlags; import com.android.resources.ResourceType; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.graphics.drawable.StateListDrawable; import android.view.AttachInfo_Accessor; import android.view.View.MeasureSpec; import android.widget.FrameLayout; @@ -38,7 +38,9 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; -import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}. @@ -71,11 +73,37 @@ public class RenderDrawable extends RenderAction<DrawableParams> { return Status.ERROR_NOT_A_DRAWABLE.createResult(); } + Drawable d = ResourceHelper.getDrawable(drawableResource, context); + + final Boolean allStates = + params.getFlag(RenderParamsFlags.FLAG_KEY_RENDER_ALL_DRAWABLE_STATES); + if (allStates == Boolean.TRUE) { + final List<BufferedImage> result; + + if (d instanceof StateListDrawable) { + result = new ArrayList<BufferedImage>(); + final StateListDrawable stateList = (StateListDrawable) d; + for (int i = 0; i < stateList.getStateCount(); i++) { + final Drawable stateDrawable = stateList.getStateDrawable(i); + result.add(renderImage(hardwareConfig, stateDrawable, context)); + } + } else { + result = Collections.singletonList(renderImage(hardwareConfig, d, context)); + } + + return Status.SUCCESS.createResult(result); + } else { + BufferedImage image = renderImage(hardwareConfig, d, context); + return Status.SUCCESS.createResult(image); + } + } + + private BufferedImage renderImage(HardwareConfig hardwareConfig, Drawable d, + BridgeContext context) { // create a simple FrameLayout FrameLayout content = new FrameLayout(context); // get the actual Drawable object to draw - Drawable d = ResourceHelper.getDrawable(drawableResource, context); content.setBackground(d); // set the AttachInfo on the root view. @@ -83,8 +111,27 @@ public class RenderDrawable extends RenderAction<DrawableParams> { // measure - int w = hardwareConfig.getScreenWidth(); - int h = hardwareConfig.getScreenHeight(); + int w = d.getIntrinsicWidth(); + int h = d.getIntrinsicHeight(); + + final int screenWidth = hardwareConfig.getScreenWidth(); + final int screenHeight = hardwareConfig.getScreenHeight(); + + if (w == -1 || h == -1) { + // Use screen size when either intrinsic width or height isn't available + w = screenWidth; + h = screenHeight; + } else if (w > screenWidth || h > screenHeight) { + // If image wouldn't fit to the screen, resize it to avoid cropping. + + // We need to find scale such that scale * w <= screenWidth, scale * h <= screenHeight + double scale = Math.min((double) screenWidth / w, (double) screenHeight / h); + + // scale * w / scale * h = w / h, so, proportions are preserved. + w = (int) Math.floor(scale * w); + h = (int) Math.floor(scale * h); + } + int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY); int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY); content.measure(w_spec, h_spec); @@ -108,8 +155,7 @@ public class RenderDrawable extends RenderAction<DrawableParams> { // and draw content.draw(canvas); - - return Status.SUCCESS.createResult(image); + return image; } protected BufferedImage getImage(int w, int h) { 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 58acab9aa318..c9aa40010665 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 @@ -23,6 +23,8 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN; import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.HardwareConfig; import com.android.ide.common.rendering.api.IAnimationListener; @@ -50,7 +52,8 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; -import com.android.layoutlib.bridge.android.SessionParamsFlags; +import com.android.layoutlib.bridge.android.RenderParamsFlags; +import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; import com.android.layoutlib.bridge.bars.BridgeActionBar; import com.android.layoutlib.bridge.bars.AppCompatActionBar; import com.android.layoutlib.bridge.bars.Config; @@ -400,7 +403,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // it can instantiate the custom Fragment. Fragment_Delegate.setProjectCallback(params.getProjectCallback()); - String rootTag = params.getFlag(SessionParamsFlags.FLAG_KEY_ROOT_TAG); + String rootTag = params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG); boolean isPreference = "PreferenceScreen".equals(rootTag); View view; if (isPreference) { @@ -551,7 +554,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // draw the views // create the BufferedImage into which the layout will be rendered. boolean newImage = false; - if (newRenderSize || mCanvas == null) { + + // When disableBitmapCaching is true, we do not reuse mImage and + // we create a new one in every render. + // This is useful when mImage is just a wrapper of Graphics2D so + // it doesn't get cached. + boolean disableBitmapCaching = Boolean.TRUE.equals(params.getFlag( + RenderParamsFlags.FLAG_KEY_DISABLE_BITMAP_CACHING)); + if (newRenderSize || mCanvas == null || disableBitmapCaching) { if (params.getImageFactory() != null) { mImage = params.getImageFactory().getImage( mMeasuredScreenWidth, @@ -578,8 +588,12 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, true /*isMutable*/, hardwareConfig.getDensity()); - // create a Canvas around the Android bitmap - mCanvas = new Canvas(bitmap); + if (mCanvas == null) { + // create a Canvas around the Android bitmap + mCanvas = new Canvas(bitmap); + } else { + mCanvas.setBitmap(bitmap); + } mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue()); } @@ -1327,6 +1341,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } } + } else if (isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) { + RecyclerViewUtil.setAdapter(view, getContext(), getParams()); } else if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); @@ -1338,6 +1354,22 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } /** + * Check if the object is an instance of a class named {@code className}. This doesn't work + * for interfaces. + */ + public static boolean isInstanceOf(Object object, String className) { + Class superClass = object.getClass(); + while (superClass != null) { + String name = superClass.getName(); + if (name.equals(className)) { + return true; + } + superClass = superClass.getSuperclass(); + } + return false; + } + + /** * Sets up a {@link TabHost} object. * @param tabHost the TabHost to setup. * @param projectCallback The project callback object to access the project R class. @@ -1494,6 +1526,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { * @return an array of length two, with ViewInfo at index 0 is without offset and ViewInfo at * index 1 is with the offset. */ + @NonNull private ViewInfo[] visitContentRoot(View view, int offset, boolean setExtendedInfo) { ViewInfo[] result = new ViewInfo[2]; if (view == null) { @@ -1589,6 +1622,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { * The cookie for menu items are stored in menu item and not in the map from View stored in * BridgeContext. */ + @Nullable private Object getViewKey(View view) { BridgeContext context = getContext(); if (!(view instanceof MenuView.ItemView)) { @@ -1611,7 +1645,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { return null; } - private void invalidateRenderingSize() { + public void invalidateRenderingSize() { mMeasuredScreenWidth = mMeasuredScreenHeight = -1; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java new file mode 100644 index 000000000000..8e61edf90dcf --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 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.util; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Utility to convert checked Reflection exceptions to unchecked exceptions. + */ +public class ReflectionUtils { + + @Nullable + public static Method getMethod(@NonNull Class<?> clazz, @NonNull String name, + @Nullable Class<?>... params) throws ReflectionException { + try { + return clazz.getMethod(name, params); + } catch (NoSuchMethodException e) { + throw new ReflectionException(e); + } + } + + @Nullable + public static Object invoke(@NonNull Method method, @Nullable Object object, + @Nullable Object... args) throws ReflectionException { + Exception ex; + try { + return method.invoke(object, args); + } catch (IllegalAccessException e) { + ex = e; + } catch (InvocationTargetException e) { + ex = e; + } + throw new ReflectionException(ex); + } + + /** + * Wraps all reflection related exceptions. Created since ReflectiveOperationException was + * introduced in 1.7 and we are still on 1.6 + */ + public static class ReflectionException extends Exception { + public ReflectionException() { + super(); + } + + public ReflectionException(String message) { + super(message); + } + + public ReflectionException(String message, Throwable cause) { + super(message, cause); + } + + public ReflectionException(Throwable cause) { + super(cause); + } + } +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 98acd2f15657..f5e8292c2381 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -230,7 +230,6 @@ public final class CreateInfo implements ICreateInfo { "android.os.SystemProperties", "android.text.AndroidBidi", "android.text.StaticLayout", - "android.util.FloatMath", "android.view.Display", "libcore.icu.DateIntervalFormat", "libcore.icu.ICU", @@ -255,7 +254,6 @@ public final class CreateInfo implements ICreateInfo { "android.view.SurfaceView", "android.view._Original_SurfaceView", "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager", "android.webkit.WebView", "android.webkit._Original_WebView", - "com.android.internal.policy.PolicyManager", "com.android.internal.policy._Original_PolicyManager", }; /** diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk index 9ff56d634c21..1b1f63ec41c4 100644 --- a/tools/obbtool/Android.mk +++ b/tools/obbtool/Android.mk @@ -13,7 +13,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ Main.cpp -LOCAL_CFLAGS := -Wall -Werror +LOCAL_CFLAGS := -Wall -Werror -Wno-mismatched-tags #LOCAL_C_INCLUDES += @@ -36,10 +36,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := pbkdf2gen LOCAL_MODULE_TAGS := optional -LOCAL_CFLAGS := -Wall -Werror +LOCAL_CFLAGS := -Wall -Werror -Wno-mismatched-tags LOCAL_SRC_FILES := pbkdf2gen.cpp LOCAL_LDLIBS += -ldl -LOCAL_C_INCLUDES := external/openssl/include $(LOCAL_C_INCLUDES) LOCAL_STATIC_LIBRARIES := libcrypto_static include $(BUILD_HOST_EXECUTABLE) diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp index b2152e8f8468..64808c0a755f 100644 --- a/tools/obbtool/Main.cpp +++ b/tools/obbtool/Main.cpp @@ -89,7 +89,7 @@ void usage(void) " Prints the OBB signature information of a file.\n\n", gProgName); } -void doAdd(const char* filename, struct PackageInfo* info) { +void doAdd(const char* filename, PackageInfo* info) { ObbFile *obb = new ObbFile(); if (obb->readFrom(filename)) { fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename); @@ -182,7 +182,7 @@ int main(int argc, char* const argv[]) { int opt; int option_index = 0; - struct PackageInfo package_info; + PackageInfo package_info; int result = 1; // pessimistically assume an error. |