diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 000000000..405a2b306 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,10 @@ +name: "Validate Gradle Wrapper" +on: [push, pull_request] + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 diff --git a/.travis.yml b/.travis.yml index 1516d3727..d91b3d727 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,6 @@ android: components: - platform-tools - tools - - build-tools-23.0.2 - - android-23 + - build-tools-28.0.3 + - android-28 - extra diff --git a/README.md b/README.md index 62841c443..9de3571b9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ![Logo](https://github.com/nostra13/Android-Universal-Image-Loader/raw/master/sample/src/main/res/drawable-mdpi/ic_launcher.png) Universal Image Loader [![Build Status](https://travis-ci.org/nostra13/Android-Universal-Image-Loader.svg?branch=master)](https://travis-ci.org/nostra13/Android-Universal-Image-Loader) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.nostra13.universalimageloader/universal-image-loader/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.nostra13.universalimageloader/universal-image-loader) -Android library **[#1](https://www.gitrep.com/search?utf8=✓&omni_search=&public_tags%5B%5D=android&description=&search=true&sort=star_count&commit=Search)** on GitHub. +The great ancestor of modern image-loading libraries :) UIL aims to provide a powerful, flexible and highly customizable instrument for image loading, caching and displaying. It provides a lot of configuration options and good control over the image loading and caching process. ![Screenshot](https://github.com/nostra13/Android-Universal-Image-Loader/raw/master/UniversalImageLoader.png) @@ -11,17 +11,17 @@ UIL aims to provide a powerful, flexible and highly customizable instrument for * Thanks to all developers for your support :) ## Features - * Multithread image loading (async or sync) + * Multi-thread image loading (async or sync) * Wide customization of ImageLoader's configuration (thread executors, downloader, decoder, memory and disk cache, display image options, etc.) * Many customization options for every display image call (stub images, caching switch, decoding options, Bitmap processing and displaying, etc.) * Image caching in memory and/or on disk (device's file system or SD card) * Listening loading process (including downloading progress) -Android 2.0+ support +Android 4.1+ support ## Downloads * **[universal-image-loader-1.9.5.jar](https://github.com/nostra13/Android-Universal-Image-Loader/raw/master/downloads/universal-image-loader-1.9.5.jar)** - * [](https://play.google.com/store/apps/details?id=com.nostra13.universalimageloader.sample) [![QR Code](https://lh3.ggpht.com/csXEddxiLgQ6FxckefjQnP1PVugbaAYOdcuTa3vVtGV1PlWbFu2dYggoH8rI1w2RdEz1=w50)](http://chart.apis.google.com/chart?chs=300x300&cht=qr&chld=|1&chl=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.nostra13.universalimageloader.sample) [](https://github.com/nostra13/Android-Universal-Image-Loader/raw/master/downloads/universal-image-loader-sample-1.9.5.apk) + * [](https://play.google.com/store/apps/details?id=com.nostra13.universalimageloader.sample) [![QR Code](https://lh3.ggpht.com/csXEddxiLgQ6FxckefjQnP1PVugbaAYOdcuTa3vVtGV1PlWbFu2dYggoH8rI1w2RdEz1=w50)](http://chart.apis.google.com/chart?chs=300x300&cht=qr&chld=|1&chl=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.nostra13.universalimageloader.sample) [](https://github.com/nostra13/Android-Universal-Image-Loader/raw/master/downloads/universal-image-loader-sample-1.9.5.apk) ## [Documentation](https://github.com/nostra13/Android-Universal-Image-Loader/wiki) * **[Quick Setup](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Quick-Setup)** @@ -34,6 +34,12 @@ Android 2.0+ support ## Usage +### Dependency + +``` +implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' +``` + ### Acceptable URIs examples ``` java "http://site.com/image.png" // from Web @@ -123,31 +129,11 @@ Bitmap bmp = imageLoader.loadImageSync(imageUri, targetSize, options); ## Donation You can support the project and thank the author for his hard work :) -Click here to lend your support to: Universal Image Loader for Android and make a donation at pledgie.com ! Flattr this * **PayPal** - nostra.uil[at]gmail[dot]com ## Alternative libraries - * [AndroidQuery : ImageLoading](https://code.google.com/p/android-query/wiki/ImageLoading) - * [DroidParts : ImageFetcher](http://droidparts.org/image_fetcher.html) * [Fresco](https://github.com/facebook/fresco) * [Glide](https://github.com/bumptech/glide) * [Picasso](https://github.com/square/picasso) - * [UrlImageViewHelper](https://github.com/koush/UrlImageViewHelper) * [Volley : ImageLoader](https://android.googlesource.com/platform/frameworks/volley/) - -## License - - Copyright 2011-2015 Sergey Tarasevich - - 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. diff --git a/UniversalImageLoader.png b/UniversalImageLoader.png index 3b6d20246..6c679cbdf 100644 Binary files a/UniversalImageLoader.png and b/UniversalImageLoader.png differ diff --git a/build.gradle b/build.gradle index 66167eaae..338b1a897 100644 --- a/build.gradle +++ b/build.gradle @@ -2,16 +2,25 @@ buildscript { repositories { + google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:3.6.2' } } allprojects { repositories { + google() jcenter() } } +if (JavaVersion.current().isJava8Compatible()) { + allprojects { + tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0ff685080..01a199e8d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Jun 06 16:36:13 FET 2015 +#Tue Apr 23 15:38:19 MSK 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/library/build.gradle b/library/build.gradle index a0171adc6..ef58b3eff 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,26 +1,26 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 23 - buildToolsVersion "23.0.2" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { - minSdkVersion 5 - targetSdkVersion 22 + minSdkVersion 16 + targetSdkVersion 28 versionCode 40 versionName "1.9.5" } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_6 - targetCompatibility JavaVersion.VERSION_1_6 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { - testCompile 'junit:junit:4.12' - testCompile 'org.robolectric:robolectric:3.0-rc3' - testCompile 'com.squareup.assertj:assertj-android:1.0.0' + testImplementation 'junit:junit:4.12' + testImplementation 'org.robolectric:robolectric:3.0-rc3' + testImplementation 'com.squareup.assertj:assertj-android:1.0.0' } diff --git a/library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/BaseDiskCache.java b/library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/BaseDiskCache.java index c78d2e6bf..80069c675 100644 --- a/library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/BaseDiskCache.java +++ b/library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/BaseDiskCache.java @@ -36,11 +36,11 @@ * @since 1.0.0 */ public abstract class BaseDiskCache implements DiskCache { - /** {@value */ + /** {@value} */ public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb - /** {@value */ + /** {@value} */ public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG; - /** {@value */ + /** {@value} */ public static final int DEFAULT_COMPRESS_QUALITY = 100; private static final String ERROR_ARG_NULL = " argument must be not null"; diff --git a/library/src/main/java/com/nostra13/universalimageloader/core/ImageLoader.java b/library/src/main/java/com/nostra13/universalimageloader/core/ImageLoader.java index 30cc9ce6f..16a67d61f 100644 --- a/library/src/main/java/com/nostra13/universalimageloader/core/ImageLoader.java +++ b/library/src/main/java/com/nostra13/universalimageloader/core/ImageLoader.java @@ -749,12 +749,20 @@ public void handleSlowNetwork(boolean handleSlowNetwork) { * Already running tasks are not paused. */ public void pause() { - engine.pause(); + if (isInited()) { + engine.pause(); + } else { + L.w("Trying to pause not-initialized ImageLoader"); + } } /** Resumes waiting "load&display" tasks */ public void resume() { - engine.resume(); + if (isInited()) { + engine.resume(); + } else { + L.w("Trying to resume not-initialized ImageLoader"); + } } /** @@ -765,7 +773,11 @@ public void resume() { * ImageLoader still can be used after calling this method. */ public void stop() { - engine.stop(); + if (isInited()) { + engine.stop(); + } else { + L.w("Trying to stop not-initialized ImageLoader"); + } } /** @@ -774,11 +786,15 @@ public void stop() { * method. */ public void destroy() { - if (configuration != null) L.d(LOG_DESTROY); - stop(); - configuration.diskCache.close(); - engine = null; - configuration = null; + if (isInited()) { + L.d(LOG_DESTROY); + stop(); + configuration.diskCache.close(); + engine = null; + configuration = null; + } else { + L.w("Trying to destroy not-initialized ImageLoader"); + } } private static Handler defineHandler(DisplayImageOptions options) { diff --git a/library/src/main/java/com/nostra13/universalimageloader/core/ImageLoaderEngine.java b/library/src/main/java/com/nostra13/universalimageloader/core/ImageLoaderEngine.java index 378ce225f..57831af6c 100644 --- a/library/src/main/java/com/nostra13/universalimageloader/core/ImageLoaderEngine.java +++ b/library/src/main/java/com/nostra13/universalimageloader/core/ImageLoaderEngine.java @@ -31,6 +31,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; +import static com.nostra13.universalimageloader.core.download.ImageDownloader.*; + /** * {@link ImageLoader} engine which responsible for {@linkplain LoadAndDisplayImageTask display task} execution. * @@ -70,7 +72,8 @@ void submit(final LoadAndDisplayImageTask task) { @Override public void run() { File image = configuration.diskCache.get(task.getLoadingUri()); - boolean isImageCachedOnDisk = image != null && image.exists(); + boolean isImageCachedOnDisk = image != null && image.exists() + || isLocalUri(task.getLoadingUri()); initExecutorsIfNeed(); if (isImageCachedOnDisk) { taskExecutorForCachedImages.execute(task); @@ -87,6 +90,11 @@ void submit(ProcessAndDisplayImageTask task) { taskExecutorForCachedImages.execute(task); } + private boolean isLocalUri(String uri) { + Scheme scheme = Scheme.ofUri(uri); + return scheme == Scheme.ASSETS || scheme == Scheme.FILE || scheme == Scheme.DRAWABLE; + } + private void initExecutorsIfNeed() { if (!configuration.customExecutor && ((ExecutorService) taskExecutor).isShutdown()) { taskExecutor = createTaskExecutor(); diff --git a/library/src/main/java/com/nostra13/universalimageloader/core/display/CircleBitmapDisplayer.java b/library/src/main/java/com/nostra13/universalimageloader/core/display/CircleBitmapDisplayer.java index 5fba85f4b..96ae88078 100644 --- a/library/src/main/java/com/nostra13/universalimageloader/core/display/CircleBitmapDisplayer.java +++ b/library/src/main/java/com/nostra13/universalimageloader/core/display/CircleBitmapDisplayer.java @@ -82,10 +82,14 @@ public static class CircleDrawable extends Drawable { protected float strokeRadius; public CircleDrawable(Bitmap bitmap, Integer strokeColor, float strokeWidth) { - radius = Math.min(bitmap.getWidth(), bitmap.getHeight()) / 2; + int diameter = Math.min(bitmap.getWidth(), bitmap.getHeight()); + radius = diameter / 2f; bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); - mBitmapRect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()); + + float left = (bitmap.getWidth() - diameter) / 2f; + float top = (bitmap.getHeight() - diameter) / 2f; + mBitmapRect = new RectF(left, top, diameter, diameter); paint = new Paint(); paint.setAntiAlias(true); diff --git a/library/src/main/java/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java b/library/src/main/java/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java index f050eebc6..b94e4f133 100644 --- a/library/src/main/java/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java +++ b/library/src/main/java/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java @@ -286,7 +286,7 @@ private boolean isVideoContentUri(Uri uri) { } private boolean isVideoFileUri(String uri) { - String extension = MimeTypeMap.getFileExtensionFromUrl(uri); + String extension = MimeTypeMap.getFileExtensionFromUrl(Uri.encode(uri)); String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); return mimeType != null && mimeType.startsWith("video/"); } diff --git a/library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java b/library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java index 418f6b0a4..77a312a2b 100644 --- a/library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java +++ b/library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java @@ -76,7 +76,7 @@ public int getWidth() { if (width <= 0) { ImageView imageView = (ImageView) viewRef.get(); if (imageView != null) { - width = getImageViewFieldValue(imageView, "mMaxWidth"); // Check maxWidth parameter + width = imageView.getMaxWidth(); } } return width; @@ -93,7 +93,7 @@ public int getHeight() { if (height <= 0) { ImageView imageView = (ImageView) viewRef.get(); if (imageView != null) { - height = getImageViewFieldValue(imageView, "mMaxHeight"); // Check maxHeight parameter + height = imageView.getMaxHeight(); } } return height; @@ -125,19 +125,4 @@ protected void setImageDrawableInto(Drawable drawable, View view) { protected void setImageBitmapInto(Bitmap bitmap, View view) { ((ImageView) view).setImageBitmap(bitmap); } - - private static int getImageViewFieldValue(Object object, String fieldName) { - int value = 0; - try { - Field field = ImageView.class.getDeclaredField(fieldName); - field.setAccessible(true); - int fieldValue = (Integer) field.get(object); - if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) { - value = fieldValue; - } - } catch (Exception e) { - L.e(e); - } - return value; - } } diff --git a/library/src/main/java/com/nostra13/universalimageloader/utils/ImageSizeUtils.java b/library/src/main/java/com/nostra13/universalimageloader/utils/ImageSizeUtils.java index d899b8d83..7437587d7 100644 --- a/library/src/main/java/com/nostra13/universalimageloader/utils/ImageSizeUtils.java +++ b/library/src/main/java/com/nostra13/universalimageloader/utils/ImageSizeUtils.java @@ -17,6 +17,7 @@ import android.graphics.BitmapFactory; import android.opengl.GLES10; + import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.ViewScaleType; import com.nostra13.universalimageloader.core.imageaware.ImageAware; @@ -31,185 +32,193 @@ */ public final class ImageSizeUtils { - private static final int DEFAULT_MAX_BITMAP_DIMENSION = 2048; - - private static ImageSize maxBitmapSize; - - static { - int[] maxTextureSize = new int[1]; - GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0); - int maxBitmapDimension = Math.max(maxTextureSize[0], DEFAULT_MAX_BITMAP_DIMENSION); - maxBitmapSize = new ImageSize(maxBitmapDimension, maxBitmapDimension); - } - - private ImageSizeUtils() { - } - - /** - * Defines target size for image aware view. Size is defined by target - * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware view} parameters, configuration - * parameters or device display dimensions.
- */ - public static ImageSize defineTargetSizeForView(ImageAware imageAware, ImageSize maxImageSize) { - int width = imageAware.getWidth(); - if (width <= 0) width = maxImageSize.getWidth(); - - int height = imageAware.getHeight(); - if (height <= 0) height = maxImageSize.getHeight(); - - return new ImageSize(width, height); - } - - /** - * Computes sample size for downscaling image size (srcSize) to view size (targetSize). This sample - * size is used during - * {@linkplain BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options) - * decoding image} to bitmap.
- *
- * Examples:
- *

- *

-	 * srcSize(100x100), targetSize(10x10), powerOf2Scale = true -> sampleSize = 8
-	 * srcSize(100x100), targetSize(10x10), powerOf2Scale = false -> sampleSize = 10
-	 *
-	 * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> sampleSize = 5
-	 * srcSize(100x100), targetSize(20x40), viewScaleType = CROP       -> sampleSize = 2
-	 * 
- *

- *
- * The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded - * bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 - * the number of pixels. Any value <= 1 is treated the same as 1. - * - * @param srcSize Original (image) size - * @param targetSize Target (view) size - * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view - * @param powerOf2Scale true - if sample size be a power of 2 (1, 2, 4, 8, ...) - * @return Computed sample size - */ - public static int computeImageSampleSize(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType, - boolean powerOf2Scale) { - final int srcWidth = srcSize.getWidth(); - final int srcHeight = srcSize.getHeight(); - final int targetWidth = targetSize.getWidth(); - final int targetHeight = targetSize.getHeight(); - - int scale = 1; - - switch (viewScaleType) { - case FIT_INSIDE: - if (powerOf2Scale) { - final int halfWidth = srcWidth / 2; - final int halfHeight = srcHeight / 2; - while ((halfWidth / scale) > targetWidth || (halfHeight / scale) > targetHeight) { // || - scale *= 2; - } - } else { - scale = Math.max(srcWidth / targetWidth, srcHeight / targetHeight); // max - } - break; - case CROP: - if (powerOf2Scale) { - final int halfWidth = srcWidth / 2; - final int halfHeight = srcHeight / 2; - while ((halfWidth / scale) > targetWidth && (halfHeight / scale) > targetHeight) { // && - scale *= 2; - } - } else { - scale = Math.min(srcWidth / targetWidth, srcHeight / targetHeight); // min - } - break; - } - - if (scale < 1) { - scale = 1; - } - scale = considerMaxTextureSize(srcWidth, srcHeight, scale, powerOf2Scale); - - return scale; - } - - private static int considerMaxTextureSize(int srcWidth, int srcHeight, int scale, boolean powerOf2) { - final int maxWidth = maxBitmapSize.getWidth(); - final int maxHeight = maxBitmapSize.getHeight(); - while ((srcWidth / scale) > maxWidth || (srcHeight / scale) > maxHeight) { - if (powerOf2) { - scale *= 2; - } else { - scale++; - } - } - return scale; - } - - /** - * Computes minimal sample size for downscaling image so result image size won't exceed max acceptable OpenGL - * texture size.
- * We can't create Bitmap in memory with size exceed max texture size (usually this is 2048x2048) so this method - * calculate minimal sample size which should be applied to image to fit into these limits. - * - * @param srcSize Original image size - * @return Minimal sample size - */ - public static int computeMinImageSampleSize(ImageSize srcSize) { - final int srcWidth = srcSize.getWidth(); - final int srcHeight = srcSize.getHeight(); - final int targetWidth = maxBitmapSize.getWidth(); - final int targetHeight = maxBitmapSize.getHeight(); - - final int widthScale = (int) Math.ceil((float) srcWidth / targetWidth); - final int heightScale = (int) Math.ceil((float) srcHeight / targetHeight); - - return Math.max(widthScale, heightScale); // max - } - - /** - * Computes scale of target size (targetSize) to source size (srcSize).
- *
- * Examples:
- *

- *

-	 * srcSize(40x40), targetSize(10x10) -> scale = 0.25
-	 *
-	 * srcSize(10x10), targetSize(20x20), stretch = false -> scale = 1
-	 * srcSize(10x10), targetSize(20x20), stretch = true  -> scale = 2
-	 *
-	 * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> scale = 0.2
-	 * srcSize(100x100), targetSize(20x40), viewScaleType = CROP       -> scale = 0.4
-	 * 
- * - * @param srcSize Source (image) size - * @param targetSize Target (view) size - * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view - * @param stretch Whether source size should be stretched if target size is larger than source size. If false - * then result scale value can't be greater than 1. - * @return Computed scale - */ - public static float computeImageScale(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType, - boolean stretch) { - final int srcWidth = srcSize.getWidth(); - final int srcHeight = srcSize.getHeight(); - final int targetWidth = targetSize.getWidth(); - final int targetHeight = targetSize.getHeight(); - - final float widthScale = (float) srcWidth / targetWidth; - final float heightScale = (float) srcHeight / targetHeight; - - final int destWidth; - final int destHeight; - if ((viewScaleType == ViewScaleType.FIT_INSIDE && widthScale >= heightScale) || (viewScaleType == ViewScaleType.CROP && widthScale < heightScale)) { - destWidth = targetWidth; - destHeight = (int) (srcHeight / widthScale); - } else { - destWidth = (int) (srcWidth / heightScale); - destHeight = targetHeight; - } - - float scale = 1; - if ((!stretch && destWidth < srcWidth && destHeight < srcHeight) || (stretch && destWidth != srcWidth && destHeight != srcHeight)) { - scale = (float) destWidth / srcWidth; - } - - return scale; - } + private static final int DEFAULT_MAX_BITMAP_DIMENSION = 2048; + + private static ImageSize maxBitmapSize; + + static { + int[] maxTextureSize = new int[1]; + GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0); + int maxBitmapDimension = Math.max(maxTextureSize[0], DEFAULT_MAX_BITMAP_DIMENSION); + maxBitmapSize = new ImageSize(maxBitmapDimension, maxBitmapDimension); + } + + private ImageSizeUtils() { + } + + /** + * Defines target size for image aware view. Size is defined by target + * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware view} parameters, configuration + * parameters or device display dimensions.
+ */ + public static ImageSize defineTargetSizeForView(ImageAware imageAware, ImageSize maxImageSize) { + int width = imageAware.getWidth(); + if (width <= 0) { + width = maxImageSize.getWidth(); + } else { + width = Math.min(width, maxImageSize.getWidth()); + } + + int height = imageAware.getHeight(); + if (height <= 0) { + height = maxImageSize.getHeight(); + } else { + height = Math.min(height, maxImageSize.getHeight()); + } + + return new ImageSize(width, height); + } + + /** + * Computes sample size for downscaling image size (srcSize) to view size (targetSize). This sample + * size is used during + * {@linkplain BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options) + * decoding image} to bitmap.
+ *
+ * Examples:
+ *

+ *

+     * srcSize(100x100), targetSize(10x10), powerOf2Scale = true -> sampleSize = 8
+     * srcSize(100x100), targetSize(10x10), powerOf2Scale = false -> sampleSize = 10
+     *
+     * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> sampleSize = 5
+     * srcSize(100x100), targetSize(20x40), viewScaleType = CROP       -> sampleSize = 2
+     * 
+ *

+ *
+ * The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded + * bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 + * the number of pixels. Any value <= 1 is treated the same as 1. + * + * @param srcSize Original (image) size + * @param targetSize Target (view) size + * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view + * @param powerOf2Scale true - if sample size be a power of 2 (1, 2, 4, 8, ...) + * @return Computed sample size + */ + public static int computeImageSampleSize(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType, + boolean powerOf2Scale) { + final int srcWidth = srcSize.getWidth(); + final int srcHeight = srcSize.getHeight(); + final int targetWidth = targetSize.getWidth(); + final int targetHeight = targetSize.getHeight(); + + int scale = 1; + + switch (viewScaleType) { + case FIT_INSIDE: + if (powerOf2Scale) { + final int halfWidth = srcWidth / 2; + final int halfHeight = srcHeight / 2; + while ((halfWidth / scale) > targetWidth || (halfHeight / scale) > targetHeight) { // || + scale *= 2; + } + } else { + scale = Math.max(srcWidth / targetWidth, srcHeight / targetHeight); // max + } + break; + case CROP: + if (powerOf2Scale) { + final int halfWidth = srcWidth / 2; + final int halfHeight = srcHeight / 2; + while ((halfWidth / scale) > targetWidth && (halfHeight / scale) > targetHeight) { // && + scale *= 2; + } + } else { + scale = Math.min(srcWidth / targetWidth, srcHeight / targetHeight); // min + } + break; + } + + if (scale < 1) { + scale = 1; + } + scale = considerMaxTextureSize(srcWidth, srcHeight, scale, powerOf2Scale); + + return scale; + } + + private static int considerMaxTextureSize(int srcWidth, int srcHeight, int scale, boolean powerOf2) { + final int maxWidth = maxBitmapSize.getWidth(); + final int maxHeight = maxBitmapSize.getHeight(); + while ((srcWidth / scale) > maxWidth || (srcHeight / scale) > maxHeight) { + if (powerOf2) { + scale *= 2; + } else { + scale++; + } + } + return scale; + } + + /** + * Computes minimal sample size for downscaling image so result image size won't exceed max acceptable OpenGL + * texture size.
+ * We can't create Bitmap in memory with size exceed max texture size (usually this is 2048x2048) so this method + * calculate minimal sample size which should be applied to image to fit into these limits. + * + * @param srcSize Original image size + * @return Minimal sample size + */ + public static int computeMinImageSampleSize(ImageSize srcSize) { + final int srcWidth = srcSize.getWidth(); + final int srcHeight = srcSize.getHeight(); + final int targetWidth = maxBitmapSize.getWidth(); + final int targetHeight = maxBitmapSize.getHeight(); + + final int widthScale = (int) Math.ceil((float) srcWidth / targetWidth); + final int heightScale = (int) Math.ceil((float) srcHeight / targetHeight); + + return Math.max(widthScale, heightScale); // max + } + + /** + * Computes scale of target size (targetSize) to source size (srcSize).
+ *
+ * Examples:
+ *

+ *

+     * srcSize(40x40), targetSize(10x10) -> scale = 0.25
+     *
+     * srcSize(10x10), targetSize(20x20), stretch = false -> scale = 1
+     * srcSize(10x10), targetSize(20x20), stretch = true  -> scale = 2
+     *
+     * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> scale = 0.2
+     * srcSize(100x100), targetSize(20x40), viewScaleType = CROP       -> scale = 0.4
+     * 
+ * + * @param srcSize Source (image) size + * @param targetSize Target (view) size + * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view + * @param stretch Whether source size should be stretched if target size is larger than source size. If false + * then result scale value can't be greater than 1. + * @return Computed scale + */ + public static float computeImageScale(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType, + boolean stretch) { + final int srcWidth = srcSize.getWidth(); + final int srcHeight = srcSize.getHeight(); + final int targetWidth = targetSize.getWidth(); + final int targetHeight = targetSize.getHeight(); + + final float widthScale = (float) srcWidth / targetWidth; + final float heightScale = (float) srcHeight / targetHeight; + + final int destWidth; + final int destHeight; + if ((viewScaleType == ViewScaleType.FIT_INSIDE && widthScale >= heightScale) || (viewScaleType == ViewScaleType.CROP && widthScale < heightScale)) { + destWidth = targetWidth; + destHeight = (int) (srcHeight / widthScale); + } else { + destWidth = (int) (srcWidth / heightScale); + destHeight = targetHeight; + } + + float scale = 1; + if ((!stretch && destWidth < srcWidth && destHeight < srcHeight) || (stretch && destWidth != srcWidth && destHeight != srcHeight)) { + scale = (float) destWidth / srcWidth; + } + + return scale; + } } diff --git a/sample/build.gradle b/sample/build.gradle index 62bf27b77..7339f6c88 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 - buildToolsVersion "23.0.2" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { applicationId "com.nostra13.universalimageloader" - minSdkVersion 5 - targetSdkVersion 22 + minSdkVersion 16 + targetSdkVersion 28 versionCode 40 versionName "1.9.5" } @@ -19,9 +19,9 @@ android { } dependencies { - compile project(':library') - compile 'com.android.support:support-v4:23.1.1' - compile 'com.squareup.okhttp:okhttp:2.4.0' + implementation project(':library') + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.squareup.okhttp:okhttp:2.4.0' } diff --git a/sample/src/main/res/drawable-ldpi/ic_empty.png b/sample/src/main/res/drawable-ldpi/ic_empty.png deleted file mode 100644 index 01f614f3d..000000000 Binary files a/sample/src/main/res/drawable-ldpi/ic_empty.png and /dev/null differ diff --git a/sample/src/main/res/drawable-ldpi/ic_error.png b/sample/src/main/res/drawable-ldpi/ic_error.png deleted file mode 100644 index 4376473bb..000000000 Binary files a/sample/src/main/res/drawable-ldpi/ic_error.png and /dev/null differ diff --git a/sample/src/main/res/drawable-ldpi/ic_launcher.png b/sample/src/main/res/drawable-ldpi/ic_launcher.png deleted file mode 100644 index 657a6b43f..000000000 Binary files a/sample/src/main/res/drawable-ldpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/drawable-ldpi/ic_stub.png b/sample/src/main/res/drawable-ldpi/ic_stub.png deleted file mode 100644 index 65ec4074b..000000000 Binary files a/sample/src/main/res/drawable-ldpi/ic_stub.png and /dev/null differ