Repository: nostra13/Android-Universal-Image-Loader Branch: master Commit: ba33ec64d0da Files: 129 Total size: 569.5 KB Directory structure: gitextract_mm0i5r2z/ ├── .github/ │ └── workflows/ │ └── gradle-wrapper-validation.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle ├── downloads/ │ ├── universal-image-loader-1.9.5-javadoc.jar │ ├── universal-image-loader-1.9.5-sources.jar │ └── universal-image-loader-1.9.5.jar ├── gradle/ │ ├── maven_push.gradle │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── library/ │ ├── build.gradle │ ├── gradle.properties │ ├── project.properties │ └── src/ │ ├── main/ │ │ ├── AndroidManifest.xml │ │ └── java/ │ │ └── com/ │ │ └── nostra13/ │ │ └── universalimageloader/ │ │ ├── cache/ │ │ │ ├── disc/ │ │ │ │ ├── DiskCache.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── BaseDiskCache.java │ │ │ │ │ ├── LimitedAgeDiskCache.java │ │ │ │ │ ├── UnlimitedDiskCache.java │ │ │ │ │ └── ext/ │ │ │ │ │ ├── DiskLruCache.java │ │ │ │ │ ├── LruDiskCache.java │ │ │ │ │ ├── StrictLineReader.java │ │ │ │ │ └── Util.java │ │ │ │ └── naming/ │ │ │ │ ├── FileNameGenerator.java │ │ │ │ ├── HashCodeFileNameGenerator.java │ │ │ │ └── Md5FileNameGenerator.java │ │ │ └── memory/ │ │ │ ├── BaseMemoryCache.java │ │ │ ├── LimitedMemoryCache.java │ │ │ ├── MemoryCache.java │ │ │ └── impl/ │ │ │ ├── FIFOLimitedMemoryCache.java │ │ │ ├── FuzzyKeyMemoryCache.java │ │ │ ├── LRULimitedMemoryCache.java │ │ │ ├── LargestLimitedMemoryCache.java │ │ │ ├── LimitedAgeMemoryCache.java │ │ │ ├── LruMemoryCache.java │ │ │ ├── UsingFreqLimitedMemoryCache.java │ │ │ └── WeakMemoryCache.java │ │ ├── core/ │ │ │ ├── DefaultConfigurationFactory.java │ │ │ ├── DisplayBitmapTask.java │ │ │ ├── DisplayImageOptions.java │ │ │ ├── ImageLoader.java │ │ │ ├── ImageLoaderConfiguration.java │ │ │ ├── ImageLoaderEngine.java │ │ │ ├── ImageLoadingInfo.java │ │ │ ├── LoadAndDisplayImageTask.java │ │ │ ├── ProcessAndDisplayImageTask.java │ │ │ ├── assist/ │ │ │ │ ├── ContentLengthInputStream.java │ │ │ │ ├── FailReason.java │ │ │ │ ├── FlushedInputStream.java │ │ │ │ ├── ImageScaleType.java │ │ │ │ ├── ImageSize.java │ │ │ │ ├── LoadedFrom.java │ │ │ │ ├── QueueProcessingType.java │ │ │ │ ├── ViewScaleType.java │ │ │ │ └── deque/ │ │ │ │ ├── BlockingDeque.java │ │ │ │ ├── Deque.java │ │ │ │ ├── LIFOLinkedBlockingDeque.java │ │ │ │ └── LinkedBlockingDeque.java │ │ │ ├── decode/ │ │ │ │ ├── BaseImageDecoder.java │ │ │ │ ├── ImageDecoder.java │ │ │ │ └── ImageDecodingInfo.java │ │ │ ├── display/ │ │ │ │ ├── BitmapDisplayer.java │ │ │ │ ├── CircleBitmapDisplayer.java │ │ │ │ ├── FadeInBitmapDisplayer.java │ │ │ │ ├── RoundedBitmapDisplayer.java │ │ │ │ ├── RoundedVignetteBitmapDisplayer.java │ │ │ │ └── SimpleBitmapDisplayer.java │ │ │ ├── download/ │ │ │ │ ├── BaseImageDownloader.java │ │ │ │ └── ImageDownloader.java │ │ │ ├── imageaware/ │ │ │ │ ├── ImageAware.java │ │ │ │ ├── ImageViewAware.java │ │ │ │ ├── NonViewAware.java │ │ │ │ └── ViewAware.java │ │ │ ├── listener/ │ │ │ │ ├── ImageLoadingListener.java │ │ │ │ ├── ImageLoadingProgressListener.java │ │ │ │ ├── PauseOnScrollListener.java │ │ │ │ └── SimpleImageLoadingListener.java │ │ │ └── process/ │ │ │ └── BitmapProcessor.java │ │ └── utils/ │ │ ├── DiskCacheUtils.java │ │ ├── ImageSizeUtils.java │ │ ├── IoUtils.java │ │ ├── L.java │ │ ├── MemoryCacheUtils.java │ │ └── StorageUtils.java │ └── test/ │ └── java/ │ └── com/ │ └── nostra13/ │ └── universalimageloader/ │ └── core/ │ ├── assist/ │ │ └── ImageSizeTest.java │ └── download/ │ └── BaseImageDownloaderTest.java ├── sample/ │ ├── build.gradle │ ├── gradle.properties │ ├── project.properties │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── nostra13/ │ │ └── universalimageloader/ │ │ └── sample/ │ │ ├── Constants.java │ │ ├── UILApplication.java │ │ ├── activity/ │ │ │ ├── ComplexImageActivity.java │ │ │ ├── HomeActivity.java │ │ │ └── SimpleImageActivity.java │ │ ├── ext/ │ │ │ ├── Base64ImageDownloader.java │ │ │ ├── BrokenJpegImageDecoder.java │ │ │ ├── HttpClientImageDownloader.java │ │ │ ├── OkHttpImageDownloader.java │ │ │ └── OldRoundedBitmapDisplayer.java │ │ ├── fragment/ │ │ │ ├── AbsListViewBaseFragment.java │ │ │ ├── BaseFragment.java │ │ │ ├── ImageGalleryFragment.java │ │ │ ├── ImageGridFragment.java │ │ │ ├── ImageListFragment.java │ │ │ └── ImagePagerFragment.java │ │ └── widget/ │ │ └── UILWidgetProvider.java │ └── res/ │ ├── layout/ │ │ ├── ac_complex.xml │ │ ├── ac_home.xml │ │ ├── fr_image_gallery.xml │ │ ├── fr_image_grid.xml │ │ ├── fr_image_list.xml │ │ ├── fr_image_pager.xml │ │ ├── item_gallery_image.xml │ │ ├── item_grid_image.xml │ │ ├── item_list_image.xml │ │ ├── item_pager_image.xml │ │ └── widget.xml │ ├── menu/ │ │ └── main_menu.xml │ ├── values/ │ │ ├── strings.xml │ │ └── styles.xml │ ├── values-v11/ │ │ └── styles.xml │ └── xml/ │ └── widget_provider.xml └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/gradle-wrapper-validation.yml ================================================ 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 ================================================ FILE: .gitignore ================================================ # Android generated bin/ gen/ # Ant build.xml local.properties # Maven target/ pom.xml.* release.properties # Eclipse .classpath .project .externalToolBuilders/ # IntelliJ *.iml *.ipr *.iws .idea/ out/ # Mac .DS_Store # Ignore gradle files .gradle/ build/ # Private signing.properties ================================================ FILE: .travis.yml ================================================ sudo: false language: android android: components: - platform-tools - tools - build-tools-28.0.3 - android-28 - extra ================================================ FILE: CHANGELOG.md ================================================ Change Log === v1.9.5 *(27.11.2015)* --- * **New API:** * `ImageLoader.displayImage(..., ImageSize targetImageSize, ...)` * CircleBitmapDisplayer * Better rendering of scaled images. * Fixed bugs: * inPurgeable and inInputShareable causes file descriptor leak on KitKat ([#1020](https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020)) * markSupported() ([#1026](https://github.com/nostra13/Android-Universal-Image-Loader/issues/1026)) v1.9.4 *(29.05.2015)* --- * **New API:** * `ImageLoader.setDefaultLoadingListener(ImageLoadingListener)` * "Disc -> Disk" migration (deleted DiscCacheAware, MemoryCacheAware) * Video thumbnails support (`file://...`) * Fixed 0-length files problem v1.9.3 *(06.09.2014)* --- * Introduced `ImageScaleType.NONE_SAFE` * Video thumbnails support (`content://...`) * Animated drawables support (for `.showImageOnLoading()`, `.showImageOnFail()`, `.showImageForEmptyUri()`) * Fixed bugs: * `loadImageSync(...)` bug ([#636](https://github.com/nostra13/Android-Universal-Image-Loader/issues/636)) * NPE if no free space while init disk cache * "Bitmap too large ..." for all ImageScaleTypes * contacts photo considering v1.9.2 *(24.05.2014)* --- * New Disk cache API (preparing renaming `disc` -> `disk`) * ImageLoader can be called out of the Main thread. Callback will be delivered on separate thread. * Prevented broken image files (#511) * Interrupt non-actual tasks * `LruDiscCache` is default limited cache * Renaming: `ImageNonViewAware` -> `NonViewAware`. Extracted `ViewAware` from `ImageViewAware`. * Introduced `DiskCache` and `MemoryCache` interfaces instead of deprecated `DiscCacheAware` and `MemoryCacheAware`. * Removed `LimitedDiscCache`, `TotalSizeLimitedDiscCache`, `FileCountLimitedDiscCache`. Use `LruDisckCache` instead. v1.9.1 *(27.12.2013)* --- * **Changed API:** * `BitmapDisplayer.display(...) : Bitmap` -> `BitmapDisplayer.display(...) : void` * **New API:** * Added possibility to listen image loading progress by listener - `ImageLoadingProgressListener` * Non-actual downloads are interrupted (if loaded less than 75%) * Re-designed `RoundedBitmapDisplayer`. Added `RoundedVignetteBitmapDisplayer`. **NOTE:** New `RoundedBitmapDisplayer`'s behaviour can vary from old one. Also consider ["RoundedImageView" project](https://github.com/vinc3m1/RoundedImageView) for usage if new `RoundedBitmapDisplayer` doesn't work for you. * Maximum GL texture size is considered while decode images ([#281](https://github.com/nostra13/Android-Universal-Image-Loader/issues/281)) * `loadImage(...)` call cancels previous task for the same image URI ([#475](https://github.com/nostra13/Android-Universal-Image-Loader/issues/475)) * Fixed StrictMode warning `Explicit termination method 'close' not called` ([#482](https://github.com/nostra13/Android-Universal-Image-Loader/issues/482)) * `LruMemoryCache` is default memory cache for Android < 2.3 too. v1.9.0 *(27.11.2013)* --- * **Changed API:** * `BitmapDisplayer.display(..., ImageView, ...)` -> `BitmapDisplayer.display(..., ImageAware, ...)` * **New API:** * `ImageAware` * `ImageLoader.displayImage(..., ImageAware, ...)` * `ImageLoader.loadImageSync(...) : Bitmap` for synchronous loading of image * `DisplayImageOptions.considerExifParams(boolean)` * `DisplayImageOptions.showImageOnLoading(...)` (instead of `.showStubImage(...)`) * `ImageLoader` can process any view (or any other object) which implements `ImageAware` interface. E.g. `ImageViewAware` is adapter of `ImageView` for `ImageAware` * EXIF parameters of image are not considered by default anymore. Use new API to enable it. * Optimized image loading (prevented double-request on image loading, reuse existing stream) * Fixed bugs: * `loadImage(...)` bug (frequent `onLoadingCancelled()`) ([#356](https://github.com/nostra13/Android-Universal-Image-Loader/issues/356)) * Prevented NPE if `Context.getCacheDir()` returns `null` ([#392](https://github.com/nostra13/Android-Universal-Image-Loader/issues/392)) v1.8.6 *(25.07.2013)* --- * **Changed API:** `ImageLoaderConfiguration.enableLogging()` -> `ImageLoaderConfiguration.writeDebugLogs()` * **Fixed memory leak** ([#263](https://github.com/nostra13/Android-Universal-Image-Loader/issues/263)) * Added the bug of `loadImage(...)` method (`onLoadingCancelled()` is fired always) :) v1.8.5 *(30.06.2013)* --- * **Changed API:** `ImageLoaderConfiguration.discCacheExtraOptions(...)` -> `ImageLoaderConfiguration.discCacheExtraOptions(..., BitmapProcessor)` ([#314](https://github.com/nostra13/Android-Universal-Image-Loader/issues/314)) * **New API:** * `ImageLoaderConfiguration.memoryCacheSizePercentage(int)` ([#279](https://github.com/nostra13/Android-Universal-Image-Loader/issues/279)) * `DisplayImageOptions.cacheInMemory(boolean)`, `.cacheOnDisc(boolean)`, `.resetViewBeforeLoading(boolean)` ([#252](https://github.com/nostra13/Android-Universal-Image-Loader/issues/252)) * Added `LoadedFrom` flag to `BitmapDisplayer.display(..., LoadedFrom)` about image source ([#149](https://github.com/nostra13/Android-Universal-Image-Loader/issues/149), [#239](https://github.com/nostra13/Android-Universal-Image-Loader/issues/239)) * Added `L.disableLogging()` and `L.enableLogging()` to off/on logs completely ([#270](https://github.com/nostra13/Android-Universal-Image-Loader/issues/270)) * Prevent image decoding if image is reused ([#247](https://github.com/nostra13/Android-Universal-Image-Loader/issues/247)) * Not set cache dir on SD card if no appropriate permission ([#311](https://github.com/nostra13/Android-Universal-Image-Loader/issues/311)) * Increased buffer size for image downloads (8 KB -> 32 KB) ([#249](https://github.com/nostra13/Android-Universal-Image-Loader/issues/249)) * Fixed bugs: * Prevent recycling of cached in memory images ([#259](https://github.com/nostra13/Android-Universal-Image-Loader/issues/259)) * ConcurrentModificationException in `LruMemoryCache` ([#265](https://github.com/nostra13/Android-Universal-Image-Loader/issues/265)) * File counting if cached files disappeared `LimitedDiscCache` ([#316](https://github.com/nostra13/Android-Universal-Image-Loader/issues/316)) * NPE for ImageView without LayoutParams ([#272](https://github.com/nostra13/Android-Universal-Image-Loader/issues/272)) * NPE in `LoadAndDisplayImageTask` ([#271](https://github.com/nostra13/Android-Universal-Image-Loader/issues/271)) * NPE in ImageLoaderEngine ([#301](https://github.com/nostra13/Android-Universal-Image-Loader/issues/301)) * RoundedBitmapDisplayer doesn't display round corner correctly for CENTER_CROP ([#315](https://github.com/nostra13/Android-Universal-Image-Loader/issues/315)) v1.8.4 *(13.04.2013)* --- * Travis CI, added Unit tests ([#189](https://github.com/nostra13/Android-Universal-Image-Loader/issues/189)) * **New API:** `DisplayImageOptions.handler(Handler)` ([#231](https://github.com/nostra13/Android-Universal-Image-Loader/issues/231)) * Fixed bugs: * `ConcurrentModificationException` in `BaseMemoryCache` ([#229](https://github.com/nostra13/Android-Universal-Image-Loader/issues/229)) * `NullPointerException` in `LimitedDiscCache` ([#234](https://github.com/nostra13/Android-Universal-Image-Loader/issues/234)) * `NullPointerException` in `LruMemoryCache` ([#233](https://github.com/nostra13/Android-Universal-Image-Loader/issues/233)) * Improved work with Strings on UI thread ([#244](https://github.com/nostra13/Android-Universal-Image-Loader/issues/244)) v1.8.3 *(31.03.2013)* --- * Android 2.0+ support * Added EXIF orientation support ([#172](https://github.com/nostra13/Android-Universal-Image-Loader/issues/172)) * **New API:** * `ImageLoaderConfiguration.imageDecoder(ImageDecoder)` * `DisplayImageOptions.decodingOptions(BitmapFactory.Options)` * Handled disc cache non-availability * Use `LruMemoryCache` as default memory cache for API >= 9, `LRULimitedMemoryCache` - for API < 9. Default memory cache size - 1/8 of available app memory. * Improved `LimitedDiscCache` and `FuzzyKeyMemoryCache` performance * Fixed bugs: * `.denyCacheImageMultipleSizesInMemory` doesn't work if own memory cache is set * `java.lang.NoSuchMethodError` in sample app ([#206](https://github.com/nostra13/Android-Universal-Image-Loader/issues/206)) v1.8.2 *(13.03.2013)* --- * **Changed API:** * `ImageDownloader.getStream***(URI, ...)` -> `ImageDownloader.getStream***(String, ...)` * Made `FailReason` as a class instead of enum. Can be used in switches: `FailReason.getType()` * Removed `ImageLoader.offOutOfMemoryHandling()`. ImageLoader doesn't handle OutOfMemoryError by default anymore (but still catches it for callbacks). * **New API:** * `ImageLoader.taskExecutor(Executor)` and `ImageLoader.taskExecutorForCachedImages(Executor)` ([#187](https://github.com/nostra13/Android-Universal-Image-Loader/issues/187)) * `ImageLoader.destroy()` * Handled SD card unmount ([#170](https://github.com/nostra13/Android-Universal-Image-Loader/issues/170)) * Added `Scheme` class * Fixed bugs: * problem of loading of local files with encoded symbols in path ([#179](https://github.com/nostra13/Android-Universal-Image-Loader/issues/179)) * minor mistake in `getImageSizeScaleTo()` method ([#200](https://github.com/nostra13/Android-Universal-Image-Loader/issues/200)) * possible concurrency issue in memory caches ([#116](https://github.com/nostra13/Android-Universal-Image-Loader/issues/116)) * wrong visibility of methods `ImageLoader.denyNetworkDownloads(boolean)` and `ImageLoader.handleSlowNetworks(boolean)` v1.8.1 *(08.03.2013)* --- * **Changed API:** * `ImageLoader.denyNetworkDownloads()` -> `ImageLoader.denyNetworkDownloads(true)` * `ImageLoader.allowNetworkDownloads()` -> `ImageLoader.denyNetworkDownloads(false)` * **New API:** * `ImageLoader.denyNetworkDownloads(boolean)` * `ImageLoader.handleSlowNetwork(boolean)`. `FlushedInsputStream` isn't used for downloads by default. * Handled HTTP(S) redirects * Added `LruMemoryCache` (based on Android's LruCache), uses only strong references. * Fixed `DisplayImageOptions.cloneFrom(...)` ([#173](https://github.com/nostra13/Android-Universal-Image-Loader/issues/173)) * Fixed ConcurrentModification issue in `MemoryCacheUtil. findCacheKeysForImageUri(...)` ([#174](https://github.com/nostra13/Android-Universal-Image-Loader/issues/174)) * Fixed issue "Disc Cache can't find image by URI with special/local UTF-8 characters" * Improved calculation of target image size to scale (consider measured View width and height) v1.8.0 *(10.02.2013)* --- * **Changed API:** * Signatures: * `ImageLoader.loadImage(Context, ...)` -> `ImageLoader.loadImage(...)` * `ImageDownloader.getStream(URI)` -> `ImageDownloader.getStream(URI, Object)` ([#150](https://github.com/nostra13/Android-Universal-Image-Loader/issues/150)) * `ImageLoadingListener.onLoading***(...)` -> `ImageLoadingListener.onLoading***(String, View, ...)` ([#130](https://github.com/nostra13/Android-Universal-Image-Loader/issues/130)) * Constructor `PauseOnScrollListener(...)` -> `PauseOnScrollListener(ImageLoader, ...)` * `ImageDownloader` became interface, `URLConnectionImageDownloader` + `ExtendedImageDownloader` -> `BaseImageDownloader` * Renaming: `FileUtil` -> `IoUtil` * Removed deprecated `ImageScaleType.POWER_OF_2` and `ImageScaleType.EXACT` * Support of "content://", "assets://", "drawable://" URI schemes out of the box ([#162](https://github.com/nostra13/Android-Universal-Image-Loader/issues/162)) * **New API:** * `DisplayImageOptions.showImageOnFail(int)` * `DisplayImageOptions.preProcessor(BitmapProcessor)` and `DisplayImageOptions.postProcessor(BitmapProcessor)` ([#151](https://github.com/nostra13/Android-Universal-Image-Loader/issues/151)) * `DisplayImageOptions.extraForDownloader(Object)`, allows to pass auxiliary object which will be passed to `ImageDownloader.getStream(URI, Object)` ([#150](https://github.com/nostra13/Android-Universal-Image-Loader/issues/150)) * `ImageLoader.denyNetworkDownloads()` and `ImageLoader.allowNetworkDownloads()` ([#148](https://github.com/nostra13/Android-Universal-Image-Loader/issues/148)) * `FailReason.UNSUPPORTED_URI_SCHEME` and `FailReason.NETWORK_DENIED` * `ImageScaleType.NONE` * Added `DiscCacheUtil` * Prepared ImageLoader to be extandable for creation of multiple instances ([#158](https://github.com/nostra13/Android-Universal-Image-Loader/issues/158)) * Fixed bug "Images aren't loaded after "Clear Cache" in app info" ([#168](https://github.com/nostra13/Android-Universal-Image-Loader/issues/168)) * Switched to Apache 2.0 license v1.7.1 *(31.01.2013)* --- * Avoid I/O operations on the main thread, prevented ANR ([#129](https://github.com/nostra13/Android-Universal-Image-Loader/issues/129), [#154](https://github.com/nostra13/Android-Universal-Image-Loader/issues/154)) * Correctly handled every ImageView's scale type in `RoundedBitmapDisplayer` ([#70](https://github.com/nostra13/Android-Universal-Image-Loader/issues/70)) * Prevented slow precaching modified date in LimitedAgeDiscCache constructor (for large number of images in cache) ([#141](https://github.com/nostra13/Android-Universal-Image-Loader/issues/141)) * **New API:** `ImageLoader.isInited()` method. Throw IllegalStateException on `displayImage(...)`, `loadImage(...)`, `getMemoryCache()`, `clearMemoryCache()`, `getDiscCache()`, `clearDiscCache()` calls if ImageLoader isn't inited with config. * Closed OutputStream after Bitmap compressing ([#115](https://github.com/nostra13/Android-Universal-Image-Loader/issues/115)) * Sample: Refactored resources v1.7.0 *(27.11.2012)* --- * Maven support * **New API:** * `ImageLoader.pause()` and `ImageLoader.resume()` ([#106](https://github.com/nostra13/Android-Universal-Image-Loader/issues/106)) * `PauseOnScrollListener` (instead of `OnScrollSmartOptions`) for convenient pause/resume ImageLoader on scroll/fling in list views ([#106](https://github.com/nostra13/Android-Universal-Image-Loader/issues/106)) * Prevented consuming of lot of memory by cacheKeysForImageViews ([#108](https://github.com/nostra13/Android-Universal-Image-Loader/issues/108)) v1.6.4 *(20.11.2012)* --- * **New API:** * `DisplayImageOptions.bitmapConfig(Bitmap.Config)` ([#101](https://github.com/nostra13/Android-Universal-Image-Loader/issues/101)) * `DisplayImageOptions.delayBeforeLoading(int)` ([#103](https://github.com/nostra13/Android-Universal-Image-Loader/issues/103)) * `OnScrollSmartOptions` for convenient control of loading delay on fling in ListViews, GridViews * Added `FadeInBitmapDisplayer` * Prevented recycling of using Bitmap ([#101](https://github.com/nostra13/Android-Universal-Image-Loader/issues/101)) v1.6.3 *(03.11.2012)* --- * **New API:** `ImageLoaderConfiguration.tasksProcessingOrder(QueueProcessingType)` ([#89](https://github.com/nostra13/Android-Universal-Image-Loader/issues/89)) * Added `MemoryCacheUtil` for work with memory cache * Fixed calculation of size the original image is needed scale to ([#93](https://github.com/nostra13/Android-Universal-Image-Loader/issues/93)) * Allowed to create multiple `ImageLoader` instances ([#92](https://github.com/nostra13/Android-Universal-Image-Loader/issues/92)) v1.6.2 *(28.10.2012)* --- * Prevented showing wrong bitmap in reused view ([#85](https://github.com/nostra13/Android-Universal-Image-Loader/issues/85)) * Fixed bug "double displaying" if image is cached in memory * Prevented "MissingFormatWidthException" ([#88](https://github.com/nostra13/Android-Universal-Image-Loader/issues/88)) v1.6.1 *(02.10.2012)* --- * **Changed API:** Renaming: * `ImageScaleType.POWER_OF_2` -> `IN_SAMPLE_POWER_OF_2` * `ImageScaleType.EXACT` -> `IN_SAMPLE_INT` * **New API:** `ImageScaleType.EXACTLY` and `ImageScaleType.EXACTLY_STRETCHED` * Prevented `ImageLoadingListener` callbacks firing and displaying image in view after ImageLoader was stopped * Fixed bug of calculation of original image scale for decoding v1.6.0 *(29.09.2012)* --- * **New API:** `ImageLoader.loadImage(...)` which loads image, decodes it to Bitmap and returns Bitmap in callback ([#51](https://github.com/nostra13/Android-Universal-Image-Loader/issues/51)) * Avoided unnecessary image downloads ([#44](https://github.com/nostra13/Android-Universal-Image-Loader/issues/44)) v1.5.6 *(20.09.2012)* --- * **Changed API:** Changed `FileNameGenerator` to interface (instead of abstract class) * **New API:** `BitmapDisplayer` and `DisplayImageOptions.displayer(...)` * Multithread displaying of cached images (instead of single-thread) * Correctly handle UTF-8 symbols and spaces in image URL ([#31](https://github.com/nostra13/Android-Universal-Image-Loader/issues/31)) v1.5.5 *(18.08.2012)* --- * **Changed API:** * Removed `DisplayImageOptions.transform()` * Changed `FileNameGenerator` to abstract class (instead of interface) * Fire `ImageLoadingListener` callbacks if image URI is null ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: README.md ================================================ # ![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) 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) ## Project News * Really have no time for development... so I stop project maintaining since Nov 27 :( * UIL [27.11.2011 - 27.11.2015] * Thanks to all developers for your support :) ## Features * 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 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) ## [Documentation](https://github.com/nostra13/Android-Universal-Image-Loader/wiki) * **[Quick Setup](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Quick-Setup)** * **[Configuration](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Configuration)** * **[Display Options](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Display-Options)** * [Useful Info](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Useful-Info) - Read it before asking a question * [User Support](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/User-Support) - Read it before creating new issue * [Sample project](https://github.com/nostra13/Android-Universal-Image-Loader/tree/master/sample) - Learn it to understand the right way of library usage * [ChangeLog](https://github.com/nostra13/Android-Universal-Image-Loader/blob/master/CHANGELOG.md) - Info about API changes is here ## Usage ### Dependency ``` implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' ``` ### Acceptable URIs examples ``` java "http://site.com/image.png" // from Web "file:///mnt/sdcard/image.png" // from SD card "file:///mnt/sdcard/video.mp4" // from SD card (video thumbnail) "content://media/external/images/media/13" // from content provider "content://media/external/video/media/13" // from content provider (video thumbnail) "assets://image.png" // from assets "drawable://" + R.drawable.img // from drawables (non-9patch images) ``` **NOTE:** Use `drawable://` only if you really need it! Always **consider the native way** to load drawables - `ImageView.setImageResource(...)` instead of using of `ImageLoader`. ### Simple ``` java ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance ``` ``` java // Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view // which implements ImageAware interface) imageLoader.displayImage(imageUri, imageView); ``` ``` java // Load image, decode it to Bitmap and return Bitmap to callback imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { // Do whatever you want with Bitmap } }); ``` ``` java // Load image, decode it to Bitmap and return Bitmap synchronously Bitmap bmp = imageLoader.loadImageSync(imageUri); ``` ### Complete ``` java // Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view // which implements ImageAware interface) imageLoader.displayImage(imageUri, imageView, options, new ImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { ... } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { ... } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { ... } @Override public void onLoadingCancelled(String imageUri, View view) { ... } }, new ImageLoadingProgressListener() { @Override public void onProgressUpdate(String imageUri, View view, int current, int total) { ... } }); ``` ``` java // Load image, decode it to Bitmap and return Bitmap to callback ImageSize targetSize = new ImageSize(80, 50); // result Bitmap will be fit to this size imageLoader.loadImage(imageUri, targetSize, options, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { // Do whatever you want with Bitmap } }); ``` ``` java // Load image, decode it to Bitmap and return Bitmap synchronously ImageSize targetSize = new ImageSize(80, 50); // result Bitmap will be fit to this size Bitmap bmp = imageLoader.loadImageSync(imageUri, targetSize, options); ``` ## Load & Display Task Flow ![Task Flow](https://github.com/nostra13/Android-Universal-Image-Loader/raw/master/wiki/UIL_Flow.png) ## Applications using Universal Image Loader **[MediaHouse, UPnP/DLNA Browser](https://play.google.com/store/apps/details?id=com.dbapp.android.mediahouse)** | **[Prezzi Benzina (AndroidFuel)](https://play.google.com/store/apps/details?id=org.vernazza.androidfuel)** | **[ROM Toolbox Lite](https://play.google.com/store/apps/details?id=com.jrummy.liberty.toolbox)**, [Pro](https://play.google.com/store/apps/details?id=com.jrummy.liberty.toolboxpro) | [Stadium Astro](https://play.google.com/store/apps/details?id=com.astro.stadium.activities) | [Chef Astro](https://play.google.com/store/apps/details?id=com.sencha.test) | [Sporee - Live Soccer Scores](https://play.google.com/store/apps/details?id=com.sporee.android) | **[EyeEm - Photo Filter Camera](https://play.google.com/store/apps/details?id=com.baseapp.eyeem)** | **[Topface - meeting is easy](https://play.google.com/store/apps/details?id=com.topface.topface)** | **[reddit is fun](https://play.google.com/store/apps/details?id=com.andrewshu.android.reddit)** | **[Diaro - personal diary](https://play.google.com/store/apps/details?id=com.pixelcrater.Diaro)** | **[Meetup](https://play.google.com/store/apps/details?id=com.meetup)** | [Vingle - Magazines by Fans](https://play.google.com/store/apps/details?id=com.vingle.android) | [Anime Music Radio](https://play.google.com/store/apps/details?id=com.maxxt.animeradio) | [WidgetLocker Theme Viewer](https://play.google.com/store/apps/details?id=com.companionfree.WLThemeViewer) | [ShortBlogger for Tumblr](https://play.google.com/store/apps/details?id=com.luckydroid.tumblelog) | [SnapDish Food Camera](https://play.google.com/store/apps/details?id=com.vuzz.snapdish) | **[Twitch](https://play.google.com/store/apps/details?id=tv.twitch.android.viewer)** | [TVShow Time, TV show guide](https://play.google.com/store/apps/details?id=com.tozelabs.tvshowtime) | [Planning Center Services](https://play.google.com/store/apps/details?id=com.ministrycentered.PlanningCenter) | **[Lapse It](https://play.google.com/store/apps/details?id=com.ui.LapseIt)** | [My Cloud Player for SoundCloud](https://play.google.com/store/apps/details?id=com.mycloudplayers.mycloudplayer) | **[SoundTracking](https://play.google.com/store/apps/details?id=com.schematiclabs.soundtracking)** | [LoopLR Social Video](https://play.google.com/store/apps/details?id=com.looplr) | [Hír24](https://play.google.com/store/apps/details?id=hu.sanomamedia.hir24) | **[Immobilien Scout24](https://play.google.com/store/apps/details?id=de.is24.android)** | **[Lieferheld - Pizza Pasta Sushi](https://play.google.com/store/apps/details?id=de.lieferheld.android)** | [Loocator: free sex datings](https://play.google.com/store/apps/details?id=com.ivicode.loocator) | [벨팡-개편 이벤트,컬러링,벨소리,무료,최신가요,링투유](https://play.google.com/store/apps/details?id=com.mediahubs.www) | [Streambels AirPlay/DLNA Player](https://play.google.com/store/apps/details?id=com.tuxera.streambels) | [Ship Mate - All Cruise Lines](https://play.google.com/store/apps/details?id=shipmate.carnival) | [Disk & Storage Analyzer](https://play.google.com/store/apps/details?id=com.mobile_infographics_tools.mydrive) | [糗事百科](https://play.google.com/store/apps/details?id=qsbk.app) | [Balance BY](https://play.google.com/store/apps/details?id=com.vladyud.balance) | **[Anti Theft Alarm - Security](https://play.google.com/store/apps/details?id=br.com.verde.alarme)** | **[XiiaLive™ - Internet Radio](https://play.google.com/store/apps/details?id=com.android.DroidLiveLite)** | **[Bandsintown Concerts](https://play.google.com/store/apps/details?id=com.bandsintown)** | **[Save As Web Archive](https://play.google.com/store/apps/details?id=jp.fuukiemonster.webmemo)** | [MCPE STORE -Download MCPE file](https://play.google.com/store/apps/details?id=com.newidea.mcpestore) | **[All-In-One Toolbox (29 Tools)](http://aiotoolbox.com/)** | [Zaim](https://play.google.com/store/apps/details?id=net.zaim.android) | **[Calculator Plus Free](https://play.google.com/store/apps/details?id=com.digitalchemy.calculator.freedecimal)** | [Truedialer by Truecaller](https://play.google.com/store/apps/details?id=com.truecaller.phoneapp) | [DoggCatcher Podcast Player](https://play.google.com/store/apps/details?id=com.snoggdoggler.android.applications.doggcatcher.v1_0) | [PingTools Network Utilities](https://play.google.com/store/apps/details?id=ua.com.streamsoft.pingtools) | [The Traveler](https://play.google.com/store/apps/details?id=edu.bsu.android.apps.traveler) | [minube: travel photo album](https://play.google.com/store/apps/details?id=com.minube.app) | [Wear Store for Wear Apps](https://play.google.com/store/apps/details?id=goko.ws2) | [Cast Store for Chromecast Apps](https://play.google.com/store/apps/details?id=goko.gcs) | [WebMoney Keeper](https://play.google.com/store/apps/details?id=com.webmoney.my) ## Donation You can support the project and thank the author for his hard work :) * **PayPal** - nostra.uil[at]gmail[dot]com ## Alternative libraries * [Fresco](https://github.com/facebook/fresco) * [Glide](https://github.com/bumptech/glide) * [Picasso](https://github.com/square/picasso) * [Volley : ImageLoader](https://android.googlesource.com/platform/frameworks/volley/) ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() } dependencies { 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') } } } ================================================ FILE: gradle/maven_push.gradle ================================================ /* * Copyright 2013 Chris Banes * * 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. */ apply plugin: 'maven' apply plugin: 'signing' def isReleaseBuild() { return VERSION_NAME.contains("SNAPSHOT") == false } def getReleaseRepositoryUrl() { return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" } def getSnapshotRepositoryUrl() { return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL : "https://oss.sonatype.org/content/repositories/snapshots/" } def getRepositoryUsername() { return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" } def getRepositoryPassword() { return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" } afterEvaluate { project -> uploadArchives { repositories { mavenDeployer { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } pom.groupId = GROUP pom.artifactId = POM_ARTIFACT_ID pom.version = VERSION_NAME repository(url: getReleaseRepositoryUrl()) { authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) } snapshotRepository(url: getSnapshotRepositoryUrl()) { authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) } pom.project { name POM_NAME packaging POM_PACKAGING description POM_DESCRIPTION url POM_URL scm { url POM_SCM_URL connection POM_SCM_CONNECTION developerConnection POM_SCM_DEV_CONNECTION } licenses { license { name POM_LICENCE_NAME url POM_LICENCE_URL distribution POM_LICENCE_DIST } } developers { developer { id POM_DEVELOPER_ID name POM_DEVELOPER_NAME } } } } } } task installArchives(type: Upload) { description "Installs the artifacts to the local Maven repository." configuration = configurations['archives'] repositories { mavenDeployer { pom.groupId = GROUP pom.artifactId = POM_ARTIFACT_ID pom.version = VERSION_NAME repository url: "file://${System.properties['user.home']}/.m2/repository" } } } signing { required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } sign configurations.archives } task androidJavadocs(type: Javadoc) { source = android.sourceSets.main.java.srcDirs options { encoding = "UTF-8" } classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) } task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { classifier = 'javadoc' from androidJavadocs.destinationDir } task androidSourcesJar(type: Jar) { classifier = 'sources' from android.sourceSets.main.java.srcDirs } artifacts { archives androidSourcesJar archives androidJavadocsJar } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #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-5.6.4-all.zip ================================================ FILE: gradle.properties ================================================ org.gradle.daemon=true VERSION_NAME=1.9.5 VERSION_COE=40 GROUP=com.nostra13.universalimageloader POM_DESCRIPTION=Powerful and flexible instrument for asynchronous image loading, caching and displaying on Android POM_URL=https://github.com/nostra13/Android-Universal-Image-Loader POM_SCM_URL=https://github.com/nostra13/Android-Universal-Image-Loader POM_SCM_CONNECTION=scm:git@github.com:nostra13/Android-Universal-Image-Loader.git POM_SCM_DEV_CONNECTION=scm:git@github.com:nostra13/Android-Universal-Image-Loader.git POM_LICENCE_NAME=The Apache Software License, Version 2.0 POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt POM_LICENCE_DIST=repo POM_DEVELOPER_ID=nostra13 POM_DEVELOPER_NAME=Sergey Tarasevich ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: library/build.gradle ================================================ apply plugin: 'com.android.library' android { compileSdkVersion 28 buildToolsVersion "28.0.3" defaultConfig { minSdkVersion 16 targetSdkVersion 28 versionCode 40 versionName "1.9.5" } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { testImplementation 'junit:junit:4.12' testImplementation 'org.robolectric:robolectric:3.0-rc3' testImplementation 'com.squareup.assertj:assertj-android:1.0.0' } // build a jar with source files task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } artifacts { archives sourcesJar } // Build a jar file in addition to the default aar file android.libraryVariants.all { variant -> def name = variant.buildType.name def task = project.tasks.create "jar${name.capitalize()}", Jar task.dependsOn variant.javaCompile task.from variant.javaCompile.destinationDir artifacts.add('archives', task); } apply from: '../gradle/maven_push.gradle' ================================================ FILE: library/gradle.properties ================================================ POM_NAME=Universal Image Loader Library POM_ARTIFACT_ID=universal-image-loader POM_PACKAGING=jar ================================================ FILE: library/project.properties ================================================ # This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system use, # "ant.properties", and override values to adapt the script to your # project structure. # Project target. target=android-21 android.library=true ================================================ FILE: library/src/main/AndroidManifest.xml ================================================ ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/DiskCache.java ================================================ /******************************************************************************* * Copyright 2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.disc; import android.graphics.Bitmap; import com.nostra13.universalimageloader.utils.IoUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; /** * Interface for disk cache * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.2 */ public interface DiskCache { /** * Returns root directory of disk cache * * @return Root directory of disk cache */ File getDirectory(); /** * Returns file of cached image * * @param imageUri Original image URI * @return File of cached image or null if image wasn't cached */ File get(String imageUri); /** * Saves image stream in disk cache. * Incoming image stream shouldn't be closed in this method. * * @param imageUri Original image URI * @param imageStream Input stream of image (shouldn't be closed in this method) * @param listener Listener for saving progress, can be ignored if you don't use * {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener * progress listener} in ImageLoader calls * @return true - if image was saved successfully; false - if image wasn't saved in disk cache. * @throws java.io.IOException */ boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException; /** * Saves image bitmap in disk cache. * * @param imageUri Original image URI * @param bitmap Image bitmap * @return true - if bitmap was saved successfully; false - if bitmap wasn't saved in disk cache. * @throws IOException */ boolean save(String imageUri, Bitmap bitmap) throws IOException; /** * Removes image file associated with incoming URI * * @param imageUri Image URI * @return true - if image file is deleted successfully; false - if image file doesn't exist for * incoming URI or image file can't be deleted. */ boolean remove(String imageUri); /** Closes disk cache, releases resources. */ void close(); /** Clears disk cache. */ void clear(); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/BaseDiskCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.disc.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.disc.DiskCache; import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator; import com.nostra13.universalimageloader.core.DefaultConfigurationFactory; import com.nostra13.universalimageloader.utils.IoUtils; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Base disk cache. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see FileNameGenerator * @since 1.0.0 */ public abstract class BaseDiskCache implements DiskCache { /** {@value} */ public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb /** {@value} */ public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG; /** {@value} */ public static final int DEFAULT_COMPRESS_QUALITY = 100; private static final String ERROR_ARG_NULL = " argument must be not null"; private static final String TEMP_IMAGE_POSTFIX = ".tmp"; protected final File cacheDir; protected final File reserveCacheDir; protected final FileNameGenerator fileNameGenerator; protected int bufferSize = DEFAULT_BUFFER_SIZE; protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT; protected int compressQuality = DEFAULT_COMPRESS_QUALITY; /** @param cacheDir Directory for file caching */ public BaseDiskCache(File cacheDir) { this(cacheDir, null); } /** * @param cacheDir Directory for file caching * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available. */ public BaseDiskCache(File cacheDir, File reserveCacheDir) { this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator()); } /** * @param cacheDir Directory for file caching * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available. * @param fileNameGenerator {@linkplain com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator * Name generator} for cached files */ public BaseDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) { if (cacheDir == null) { throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL); } if (fileNameGenerator == null) { throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL); } this.cacheDir = cacheDir; this.reserveCacheDir = reserveCacheDir; this.fileNameGenerator = fileNameGenerator; } @Override public File getDirectory() { return cacheDir; } @Override public File get(String imageUri) { return getFile(imageUri); } @Override public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException { File imageFile = getFile(imageUri); File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX); boolean loaded = false; try { OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize); try { loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize); } finally { IoUtils.closeSilently(os); } } finally { if (loaded && !tmpFile.renameTo(imageFile)) { loaded = false; } if (!loaded) { tmpFile.delete(); } } return loaded; } @Override public boolean save(String imageUri, Bitmap bitmap) throws IOException { File imageFile = getFile(imageUri); File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX); OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize); boolean savedSuccessfully = false; try { savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os); } finally { IoUtils.closeSilently(os); if (savedSuccessfully && !tmpFile.renameTo(imageFile)) { savedSuccessfully = false; } if (!savedSuccessfully) { tmpFile.delete(); } } bitmap.recycle(); return savedSuccessfully; } @Override public boolean remove(String imageUri) { return getFile(imageUri).delete(); } @Override public void close() { // Nothing to do } @Override public void clear() { File[] files = cacheDir.listFiles(); if (files != null) { for (File f : files) { f.delete(); } } } /** Returns file object (not null) for incoming image URI. File object can reference to non-existing file. */ protected File getFile(String imageUri) { String fileName = fileNameGenerator.generate(imageUri); File dir = cacheDir; if (!cacheDir.exists() && !cacheDir.mkdirs()) { if (reserveCacheDir != null && (reserveCacheDir.exists() || reserveCacheDir.mkdirs())) { dir = reserveCacheDir; } } return new File(dir, fileName); } public void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; } public void setCompressFormat(Bitmap.CompressFormat compressFormat) { this.compressFormat = compressFormat; } public void setCompressQuality(int compressQuality) { this.compressQuality = compressQuality; } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/LimitedAgeDiskCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.disc.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator; import com.nostra13.universalimageloader.core.DefaultConfigurationFactory; import com.nostra13.universalimageloader.utils.IoUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Cache which deletes files which were loaded more than defined time. Cache size is unlimited. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.3.1 */ public class LimitedAgeDiskCache extends BaseDiskCache { private final long maxFileAge; private final Map loadingDates = Collections.synchronizedMap(new HashMap()); /** * @param cacheDir Directory for file caching * @param maxAge Max file age (in seconds). If file age will exceed this value then it'll be removed on next * treatment (and therefore be reloaded). */ public LimitedAgeDiskCache(File cacheDir, long maxAge) { this(cacheDir, null, DefaultConfigurationFactory.createFileNameGenerator(), maxAge); } /** * @param cacheDir Directory for file caching * @param maxAge Max file age (in seconds). If file age will exceed this value then it'll be removed on next * treatment (and therefore be reloaded). */ public LimitedAgeDiskCache(File cacheDir, File reserveCacheDir, long maxAge) { this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator(), maxAge); } /** * @param cacheDir Directory for file caching * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available. * @param fileNameGenerator Name generator for cached files * @param maxAge Max file age (in seconds). If file age will exceed this value then it'll be removed on next * treatment (and therefore be reloaded). */ public LimitedAgeDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long maxAge) { super(cacheDir, reserveCacheDir, fileNameGenerator); this.maxFileAge = maxAge * 1000; // to milliseconds } @Override public File get(String imageUri) { File file = super.get(imageUri); if (file != null && file.exists()) { boolean cached; Long loadingDate = loadingDates.get(file); if (loadingDate == null) { cached = false; loadingDate = file.lastModified(); } else { cached = true; } if (System.currentTimeMillis() - loadingDate > maxFileAge) { file.delete(); loadingDates.remove(file); } else if (!cached) { loadingDates.put(file, loadingDate); } } return file; } @Override public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException { boolean saved = super.save(imageUri, imageStream, listener); rememberUsage(imageUri); return saved; } @Override public boolean save(String imageUri, Bitmap bitmap) throws IOException { boolean saved = super.save(imageUri, bitmap); rememberUsage(imageUri); return saved; } @Override public boolean remove(String imageUri) { loadingDates.remove(getFile(imageUri)); return super.remove(imageUri); } @Override public void clear() { super.clear(); loadingDates.clear(); } private void rememberUsage(String imageUri) { File file = getFile(imageUri); long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime); loadingDates.put(file, currentTime); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/UnlimitedDiskCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.disc.impl; import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator; import java.io.File; /** * Default implementation of {@linkplain com.nostra13.universalimageloader.cache.disc.DiskCache disk cache}. * Cache size is unlimited. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public class UnlimitedDiskCache extends BaseDiskCache { /** @param cacheDir Directory for file caching */ public UnlimitedDiskCache(File cacheDir) { super(cacheDir); } /** * @param cacheDir Directory for file caching * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available. */ public UnlimitedDiskCache(File cacheDir, File reserveCacheDir) { super(cacheDir, reserveCacheDir); } /** * @param cacheDir Directory for file caching * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available. * @param fileNameGenerator {@linkplain com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator * Name generator} for cached files */ public UnlimitedDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) { super(cacheDir, reserveCacheDir, fileNameGenerator); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/ext/DiskLruCache.java ================================================ /* * Copyright (C) 2011 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.nostra13.universalimageloader.cache.disc.impl.ext; import java.io.BufferedWriter; import java.io.Closeable; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A cache that uses a bounded amount of space on a filesystem. Each cache * entry has a string key and a fixed number of values. Each key must match * the regex [a-z0-9_-]{1,64}. Values are byte sequences, * accessible as streams or files. Each value must be between {@code 0} and * {@code Integer.MAX_VALUE} bytes in length. * *

The cache stores its data in a directory on the filesystem. This * directory must be exclusive to the cache; the cache may delete or overwrite * files from its directory. It is an error for multiple processes to use the * same cache directory at the same time. * *

This cache limits the number of bytes that it will store on the * filesystem. When the number of stored bytes exceeds the limit, the cache will * remove entries in the background until the limit is satisfied. The limit is * not strict: the cache may temporarily exceed it while waiting for files to be * deleted. The limit does not include filesystem overhead or the cache * journal so space-sensitive applications should set a conservative limit. * *

Clients call {@link #edit} to create or update the values of an entry. An * entry may have only one editor at one time; if a value is not available to be * edited then {@link #edit} will return null. *

    *
  • When an entry is being created it is necessary to * supply a full set of values; the empty value should be used as a * placeholder if necessary. *
  • When an entry is being edited, it is not necessary * to supply data for every value; values default to their previous * value. *
* Every {@link #edit} call must be matched by a call to {@link Editor#commit} * or {@link Editor#abort}. Committing is atomic: a read observes the full set * of values as they were before or after the commit, but never a mix of values. * *

Clients call {@link #get} to read a snapshot of an entry. The read will * observe the value at the time that {@link #get} was called. Updates and * removals after the call do not impact ongoing reads. * *

This class is tolerant of some I/O errors. If files are missing from the * filesystem, the corresponding entries will be dropped from the cache. If * an error occurs while writing a cache value, the edit will fail silently. * Callers should handle other problems by catching {@code IOException} and * responding appropriately. */ final class DiskLruCache implements Closeable { static final String JOURNAL_FILE = "journal"; static final String JOURNAL_FILE_TEMP = "journal.tmp"; static final String JOURNAL_FILE_BACKUP = "journal.bkp"; static final String MAGIC = "libcore.io.DiskLruCache"; static final String VERSION_1 = "1"; static final long ANY_SEQUENCE_NUMBER = -1; static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,64}"); private static final String CLEAN = "CLEAN"; private static final String DIRTY = "DIRTY"; private static final String REMOVE = "REMOVE"; private static final String READ = "READ"; /* * This cache uses a journal file named "journal". A typical journal file * looks like this: * libcore.io.DiskLruCache * 1 * 100 * 2 * * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 * DIRTY 335c4c6028171cfddfbaae1a9c313c52 * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342 * REMOVE 335c4c6028171cfddfbaae1a9c313c52 * DIRTY 1ab96a171faeeee38496d8b330771a7a * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234 * READ 335c4c6028171cfddfbaae1a9c313c52 * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6 * * The first five lines of the journal form its header. They are the * constant string "libcore.io.DiskLruCache", the disk cache's version, * the application's version, the value count, and a blank line. * * Each of the subsequent lines in the file is a record of the state of a * cache entry. Each line contains space-separated values: a state, a key, * and optional state-specific values. * o DIRTY lines track that an entry is actively being created or updated. * Every successful DIRTY action should be followed by a CLEAN or REMOVE * action. DIRTY lines without a matching CLEAN or REMOVE indicate that * temporary files may need to be deleted. * o CLEAN lines track a cache entry that has been successfully published * and may be read. A publish line is followed by the lengths of each of * its values. * o READ lines track accesses for LRU. * o REMOVE lines track entries that have been deleted. * * The journal file is appended to as cache operations occur. The journal may * occasionally be compacted by dropping redundant lines. A temporary file named * "journal.tmp" will be used during compaction; that file should be deleted if * it exists when the cache is opened. */ private final File directory; private final File journalFile; private final File journalFileTmp; private final File journalFileBackup; private final int appVersion; private long maxSize; private int maxFileCount; private final int valueCount; private long size = 0; private int fileCount = 0; private Writer journalWriter; private final LinkedHashMap lruEntries = new LinkedHashMap(0, 0.75f, true); private int redundantOpCount; /** * To differentiate between old and current snapshots, each entry is given * a sequence number each time an edit is committed. A snapshot is stale if * its sequence number is not equal to its entry's sequence number. */ private long nextSequenceNumber = 0; /** This cache uses a single background thread to evict entries. */ final ThreadPoolExecutor executorService = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue()); private final Callable cleanupCallable = new Callable() { public Void call() throws Exception { synchronized (DiskLruCache.this) { if (journalWriter == null) { return null; // Closed. } trimToSize(); trimToFileCount(); if (journalRebuildRequired()) { rebuildJournal(); redundantOpCount = 0; } } return null; } }; private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount) { this.directory = directory; this.appVersion = appVersion; this.journalFile = new File(directory, JOURNAL_FILE); this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP); this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP); this.valueCount = valueCount; this.maxSize = maxSize; this.maxFileCount = maxFileCount; } /** * Opens the cache in {@code directory}, creating a cache if none exists * there. * * @param directory a writable directory * @param valueCount the number of values per cache entry. Must be positive. * @param maxSize the maximum number of bytes this cache should use to store * @param maxFileCount the maximum file count this cache should store * @throws IOException if reading or writing the cache directory fails */ public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount) throws IOException { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } if (maxFileCount <= 0) { throw new IllegalArgumentException("maxFileCount <= 0"); } if (valueCount <= 0) { throw new IllegalArgumentException("valueCount <= 0"); } // If a bkp file exists, use it instead. File backupFile = new File(directory, JOURNAL_FILE_BACKUP); if (backupFile.exists()) { File journalFile = new File(directory, JOURNAL_FILE); // If journal file also exists just delete backup file. if (journalFile.exists()) { backupFile.delete(); } else { renameTo(backupFile, journalFile, false); } } // Prefer to pick up where we left off. DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount); if (cache.journalFile.exists()) { try { cache.readJournal(); cache.processJournal(); cache.journalWriter = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(cache.journalFile, true), Util.US_ASCII)); return cache; } catch (IOException journalIsCorrupt) { System.out .println("DiskLruCache " + directory + " is corrupt: " + journalIsCorrupt.getMessage() + ", removing"); cache.delete(); } } // Create a new empty cache. directory.mkdirs(); cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount); cache.rebuildJournal(); return cache; } private void readJournal() throws IOException { StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII); try { String magic = reader.readLine(); String version = reader.readLine(); String appVersionString = reader.readLine(); String valueCountString = reader.readLine(); String blank = reader.readLine(); if (!MAGIC.equals(magic) || !VERSION_1.equals(version) || !Integer.toString(appVersion).equals(appVersionString) || !Integer.toString(valueCount).equals(valueCountString) || !"".equals(blank)) { throw new IOException("unexpected journal header: [" + magic + ", " + version + ", " + valueCountString + ", " + blank + "]"); } int lineCount = 0; while (true) { try { readJournalLine(reader.readLine()); lineCount++; } catch (EOFException endOfJournal) { break; } } redundantOpCount = lineCount - lruEntries.size(); } finally { Util.closeQuietly(reader); } } private void readJournalLine(String line) throws IOException { int firstSpace = line.indexOf(' '); if (firstSpace == -1) { throw new IOException("unexpected journal line: " + line); } int keyBegin = firstSpace + 1; int secondSpace = line.indexOf(' ', keyBegin); final String key; if (secondSpace == -1) { key = line.substring(keyBegin); if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) { lruEntries.remove(key); return; } } else { key = line.substring(keyBegin, secondSpace); } Entry entry = lruEntries.get(key); if (entry == null) { entry = new Entry(key); lruEntries.put(key, entry); } if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) { String[] parts = line.substring(secondSpace + 1).split(" "); entry.readable = true; entry.currentEditor = null; entry.setLengths(parts); } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) { entry.currentEditor = new Editor(entry); } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) { // This work was already done by calling lruEntries.get(). } else { throw new IOException("unexpected journal line: " + line); } } /** * Computes the initial size and collects garbage as a part of opening the * cache. Dirty entries are assumed to be inconsistent and will be deleted. */ private void processJournal() throws IOException { deleteIfExists(journalFileTmp); for (Iterator i = lruEntries.values().iterator(); i.hasNext(); ) { Entry entry = i.next(); if (entry.currentEditor == null) { for (int t = 0; t < valueCount; t++) { size += entry.lengths[t]; fileCount++; } } else { entry.currentEditor = null; for (int t = 0; t < valueCount; t++) { deleteIfExists(entry.getCleanFile(t)); deleteIfExists(entry.getDirtyFile(t)); } i.remove(); } } } /** * Creates a new journal that omits redundant information. This replaces the * current journal if it exists. */ private synchronized void rebuildJournal() throws IOException { if (journalWriter != null) { journalWriter.close(); } Writer writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII)); try { writer.write(MAGIC); writer.write("\n"); writer.write(VERSION_1); writer.write("\n"); writer.write(Integer.toString(appVersion)); writer.write("\n"); writer.write(Integer.toString(valueCount)); writer.write("\n"); writer.write("\n"); for (Entry entry : lruEntries.values()) { if (entry.currentEditor != null) { writer.write(DIRTY + ' ' + entry.key + '\n'); } else { writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); } } } finally { writer.close(); } if (journalFile.exists()) { renameTo(journalFile, journalFileBackup, true); } renameTo(journalFileTmp, journalFile, false); journalFileBackup.delete(); journalWriter = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII)); } private static void deleteIfExists(File file) throws IOException { if (file.exists() && !file.delete()) { throw new IOException(); } } private static void renameTo(File from, File to, boolean deleteDestination) throws IOException { if (deleteDestination) { deleteIfExists(to); } if (!from.renameTo(to)) { throw new IOException(); } } /** * Returns a snapshot of the entry named {@code key}, or null if it doesn't * exist is not currently readable. If a value is returned, it is moved to * the head of the LRU queue. */ public synchronized Snapshot get(String key) throws IOException { checkNotClosed(); validateKey(key); Entry entry = lruEntries.get(key); if (entry == null) { return null; } if (!entry.readable) { return null; } // Open all streams eagerly to guarantee that we see a single published // snapshot. If we opened streams lazily then the streams could come // from different edits. File[] files = new File[valueCount]; InputStream[] ins = new InputStream[valueCount]; try { File file; for (int i = 0; i < valueCount; i++) { file = entry.getCleanFile(i); files[i] = file; ins[i] = new FileInputStream(file); } } catch (FileNotFoundException e) { // A file must have been deleted manually! for (int i = 0; i < valueCount; i++) { if (ins[i] != null) { Util.closeQuietly(ins[i]); } else { break; } } return null; } redundantOpCount++; journalWriter.append(READ + ' ' + key + '\n'); if (journalRebuildRequired()) { executorService.submit(cleanupCallable); } return new Snapshot(key, entry.sequenceNumber, files, ins, entry.lengths); } /** * Returns an editor for the entry named {@code key}, or null if another * edit is in progress. */ public Editor edit(String key) throws IOException { return edit(key, ANY_SEQUENCE_NUMBER); } private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { checkNotClosed(); validateKey(key); Entry entry = lruEntries.get(key); if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) { return null; // Snapshot is stale. } if (entry == null) { entry = new Entry(key); lruEntries.put(key, entry); } else if (entry.currentEditor != null) { return null; // Another edit is in progress. } Editor editor = new Editor(entry); entry.currentEditor = editor; // Flush the journal before creating files to prevent file leaks. journalWriter.write(DIRTY + ' ' + key + '\n'); journalWriter.flush(); return editor; } /** Returns the directory where this cache stores its data. */ public File getDirectory() { return directory; } /** * Returns the maximum number of bytes that this cache should use to store * its data. */ public synchronized long getMaxSize() { return maxSize; } /** Returns the maximum number of files that this cache should store */ public synchronized int getMaxFileCount() { return maxFileCount; } /** * Changes the maximum number of bytes the cache can store and queues a job * to trim the existing store, if necessary. */ public synchronized void setMaxSize(long maxSize) { this.maxSize = maxSize; executorService.submit(cleanupCallable); } /** * Returns the number of bytes currently being used to store the values in * this cache. This may be greater than the max size if a background * deletion is pending. */ public synchronized long size() { return size; } /** * Returns the number of files currently being used to store the values in * this cache. This may be greater than the max file count if a background * deletion is pending. */ public synchronized long fileCount() { return fileCount; } private synchronized void completeEdit(Editor editor, boolean success) throws IOException { Entry entry = editor.entry; if (entry.currentEditor != editor) { throw new IllegalStateException(); } // If this edit is creating the entry for the first time, every index must have a value. if (success && !entry.readable) { for (int i = 0; i < valueCount; i++) { if (!editor.written[i]) { editor.abort(); throw new IllegalStateException("Newly created entry didn't create value for index " + i); } if (!entry.getDirtyFile(i).exists()) { editor.abort(); return; } } } for (int i = 0; i < valueCount; i++) { File dirty = entry.getDirtyFile(i); if (success) { if (dirty.exists()) { File clean = entry.getCleanFile(i); dirty.renameTo(clean); long oldLength = entry.lengths[i]; long newLength = clean.length(); entry.lengths[i] = newLength; size = size - oldLength + newLength; fileCount++; } } else { deleteIfExists(dirty); } } redundantOpCount++; entry.currentEditor = null; if (entry.readable | success) { entry.readable = true; journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); if (success) { entry.sequenceNumber = nextSequenceNumber++; } } else { lruEntries.remove(entry.key); journalWriter.write(REMOVE + ' ' + entry.key + '\n'); } journalWriter.flush(); if (size > maxSize || fileCount > maxFileCount || journalRebuildRequired()) { executorService.submit(cleanupCallable); } } /** * We only rebuild the journal when it will halve the size of the journal * and eliminate at least 2000 ops. */ private boolean journalRebuildRequired() { final int redundantOpCompactThreshold = 2000; return redundantOpCount >= redundantOpCompactThreshold // && redundantOpCount >= lruEntries.size(); } /** * Drops the entry for {@code key} if it exists and can be removed. Entries * actively being edited cannot be removed. * * @return true if an entry was removed. */ public synchronized boolean remove(String key) throws IOException { checkNotClosed(); validateKey(key); Entry entry = lruEntries.get(key); if (entry == null || entry.currentEditor != null) { return false; } for (int i = 0; i < valueCount; i++) { File file = entry.getCleanFile(i); if (file.exists() && !file.delete()) { throw new IOException("failed to delete " + file); } size -= entry.lengths[i]; fileCount--; entry.lengths[i] = 0; } redundantOpCount++; journalWriter.append(REMOVE + ' ' + key + '\n'); lruEntries.remove(key); if (journalRebuildRequired()) { executorService.submit(cleanupCallable); } return true; } /** Returns true if this cache has been closed. */ public synchronized boolean isClosed() { return journalWriter == null; } private void checkNotClosed() { if (journalWriter == null) { throw new IllegalStateException("cache is closed"); } } /** Force buffered operations to the filesystem. */ public synchronized void flush() throws IOException { checkNotClosed(); trimToSize(); trimToFileCount(); journalWriter.flush(); } /** Closes this cache. Stored values will remain on the filesystem. */ public synchronized void close() throws IOException { if (journalWriter == null) { return; // Already closed. } for (Entry entry : new ArrayList(lruEntries.values())) { if (entry.currentEditor != null) { entry.currentEditor.abort(); } } trimToSize(); trimToFileCount(); journalWriter.close(); journalWriter = null; } private void trimToSize() throws IOException { while (size > maxSize) { Map.Entry toEvict = lruEntries.entrySet().iterator().next(); remove(toEvict.getKey()); } } private void trimToFileCount() throws IOException { while (fileCount > maxFileCount) { Map.Entry toEvict = lruEntries.entrySet().iterator().next(); remove(toEvict.getKey()); } } /** * Closes the cache and deletes all of its stored values. This will delete * all files in the cache directory including files that weren't created by * the cache. */ public void delete() throws IOException { close(); Util.deleteContents(directory); } private void validateKey(String key) { Matcher matcher = LEGAL_KEY_PATTERN.matcher(key); if (!matcher.matches()) { throw new IllegalArgumentException("keys must match regex [a-z0-9_-]{1,64}: \"" + key + "\""); } } private static String inputStreamToString(InputStream in) throws IOException { return Util.readFully(new InputStreamReader(in, Util.UTF_8)); } /** A snapshot of the values for an entry. */ public final class Snapshot implements Closeable { private final String key; private final long sequenceNumber; private File[] files; private final InputStream[] ins; private final long[] lengths; private Snapshot(String key, long sequenceNumber, File[] files, InputStream[] ins, long[] lengths) { this.key = key; this.sequenceNumber = sequenceNumber; this.files = files; this.ins = ins; this.lengths = lengths; } /** * Returns an editor for this snapshot's entry, or null if either the * entry has changed since this snapshot was created or if another edit * is in progress. */ public Editor edit() throws IOException { return DiskLruCache.this.edit(key, sequenceNumber); } /** Returns file with the value for {@code index}. */ public File getFile(int index) { return files[index]; } /** Returns the unbuffered stream with the value for {@code index}. */ public InputStream getInputStream(int index) { return ins[index]; } /** Returns the string value for {@code index}. */ public String getString(int index) throws IOException { return inputStreamToString(getInputStream(index)); } /** Returns the byte length of the value for {@code index}. */ public long getLength(int index) { return lengths[index]; } public void close() { for (InputStream in : ins) { Util.closeQuietly(in); } } } private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() { @Override public void write(int b) throws IOException { // Eat all writes silently. Nom nom. } }; /** Edits the values for an entry. */ public final class Editor { private final Entry entry; private final boolean[] written; private boolean hasErrors; private boolean committed; private Editor(Entry entry) { this.entry = entry; this.written = (entry.readable) ? null : new boolean[valueCount]; } /** * Returns an unbuffered input stream to read the last committed value, * or null if no value has been committed. */ public InputStream newInputStream(int index) throws IOException { synchronized (DiskLruCache.this) { if (entry.currentEditor != this) { throw new IllegalStateException(); } if (!entry.readable) { return null; } try { return new FileInputStream(entry.getCleanFile(index)); } catch (FileNotFoundException e) { return null; } } } /** * Returns the last committed value as a string, or null if no value * has been committed. */ public String getString(int index) throws IOException { InputStream in = newInputStream(index); return in != null ? inputStreamToString(in) : null; } /** * Returns a new unbuffered output stream to write the value at * {@code index}. If the underlying output stream encounters errors * when writing to the filesystem, this edit will be aborted when * {@link #commit} is called. The returned output stream does not throw * IOExceptions. */ public OutputStream newOutputStream(int index) throws IOException { synchronized (DiskLruCache.this) { if (entry.currentEditor != this) { throw new IllegalStateException(); } if (!entry.readable) { written[index] = true; } File dirtyFile = entry.getDirtyFile(index); FileOutputStream outputStream; try { outputStream = new FileOutputStream(dirtyFile); } catch (FileNotFoundException e) { // Attempt to recreate the cache directory. directory.mkdirs(); try { outputStream = new FileOutputStream(dirtyFile); } catch (FileNotFoundException e2) { // We are unable to recover. Silently eat the writes. return NULL_OUTPUT_STREAM; } } return new FaultHidingOutputStream(outputStream); } } /** Sets the value at {@code index} to {@code value}. */ public void set(int index, String value) throws IOException { Writer writer = null; try { writer = new OutputStreamWriter(newOutputStream(index), Util.UTF_8); writer.write(value); } finally { Util.closeQuietly(writer); } } /** * Commits this edit so it is visible to readers. This releases the * edit lock so another edit may be started on the same key. */ public void commit() throws IOException { if (hasErrors) { completeEdit(this, false); remove(entry.key); // The previous entry is stale. } else { completeEdit(this, true); } committed = true; } /** * Aborts this edit. This releases the edit lock so another edit may be * started on the same key. */ public void abort() throws IOException { completeEdit(this, false); } public void abortUnlessCommitted() { if (!committed) { try { abort(); } catch (IOException ignored) { } } } private class FaultHidingOutputStream extends FilterOutputStream { private FaultHidingOutputStream(OutputStream out) { super(out); } @Override public void write(int oneByte) { try { out.write(oneByte); } catch (IOException e) { hasErrors = true; } } @Override public void write(byte[] buffer, int offset, int length) { try { out.write(buffer, offset, length); } catch (IOException e) { hasErrors = true; } } @Override public void close() { try { out.close(); } catch (IOException e) { hasErrors = true; } } @Override public void flush() { try { out.flush(); } catch (IOException e) { hasErrors = true; } } } } private final class Entry { private final String key; /** Lengths of this entry's files. */ private final long[] lengths; /** True if this entry has ever been published. */ private boolean readable; /** The ongoing edit or null if this entry is not being edited. */ private Editor currentEditor; /** The sequence number of the most recently committed edit to this entry. */ private long sequenceNumber; private Entry(String key) { this.key = key; this.lengths = new long[valueCount]; } public String getLengths() throws IOException { StringBuilder result = new StringBuilder(); for (long size : lengths) { result.append(' ').append(size); } return result.toString(); } /** Set lengths using decimal numbers like "10123". */ private void setLengths(String[] strings) throws IOException { if (strings.length != valueCount) { throw invalidLengths(strings); } try { for (int i = 0; i < strings.length; i++) { lengths[i] = Long.parseLong(strings[i]); } } catch (NumberFormatException e) { throw invalidLengths(strings); } } private IOException invalidLengths(String[] strings) throws IOException { throw new IOException("unexpected journal line: " + java.util.Arrays.toString(strings)); } public File getCleanFile(int i) { return new File(directory, key + "." + i); } public File getDirtyFile(int i) { return new File(directory, key + "." + i + ".tmp"); } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/ext/LruDiskCache.java ================================================ /******************************************************************************* * Copyright 2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.disc.impl.ext; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.disc.DiskCache; import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator; import com.nostra13.universalimageloader.utils.IoUtils; import com.nostra13.universalimageloader.utils.L; import java.io.BufferedOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Disk cache based on "Least-Recently Used" principle. Adapter pattern, adapts * {@link com.nostra13.universalimageloader.cache.disc.impl.ext.DiskLruCache DiskLruCache} to * {@link com.nostra13.universalimageloader.cache.disc.DiskCache DiskCache} * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see FileNameGenerator * @since 1.9.2 */ public class LruDiskCache implements DiskCache { /** {@value */ public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb /** {@value */ public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG; /** {@value */ public static final int DEFAULT_COMPRESS_QUALITY = 100; private static final String ERROR_ARG_NULL = " argument must be not null"; private static final String ERROR_ARG_NEGATIVE = " argument must be positive number"; protected DiskLruCache cache; private File reserveCacheDir; protected final FileNameGenerator fileNameGenerator; protected int bufferSize = DEFAULT_BUFFER_SIZE; protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT; protected int compressQuality = DEFAULT_COMPRESS_QUALITY; /** * @param cacheDir Directory for file caching * @param fileNameGenerator {@linkplain com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator * Name generator} for cached files. Generated names must match the regex * [a-z0-9_-]{1,64} * @param cacheMaxSize Max cache size in bytes. 0 means cache size is unlimited. * @throws IOException if cache can't be initialized (e.g. "No space left on device") */ public LruDiskCache(File cacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize) throws IOException { this(cacheDir, null, fileNameGenerator, cacheMaxSize, 0); } /** * @param cacheDir Directory for file caching * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available. * @param fileNameGenerator {@linkplain com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator * Name generator} for cached files. Generated names must match the regex * [a-z0-9_-]{1,64} * @param cacheMaxSize Max cache size in bytes. 0 means cache size is unlimited. * @param cacheMaxFileCount Max file count in cache. 0 means file count is unlimited. * @throws IOException if cache can't be initialized (e.g. "No space left on device") */ public LruDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize, int cacheMaxFileCount) throws IOException { if (cacheDir == null) { throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL); } if (cacheMaxSize < 0) { throw new IllegalArgumentException("cacheMaxSize" + ERROR_ARG_NEGATIVE); } if (cacheMaxFileCount < 0) { throw new IllegalArgumentException("cacheMaxFileCount" + ERROR_ARG_NEGATIVE); } if (fileNameGenerator == null) { throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL); } if (cacheMaxSize == 0) { cacheMaxSize = Long.MAX_VALUE; } if (cacheMaxFileCount == 0) { cacheMaxFileCount = Integer.MAX_VALUE; } this.reserveCacheDir = reserveCacheDir; this.fileNameGenerator = fileNameGenerator; initCache(cacheDir, reserveCacheDir, cacheMaxSize, cacheMaxFileCount); } private void initCache(File cacheDir, File reserveCacheDir, long cacheMaxSize, int cacheMaxFileCount) throws IOException { try { cache = DiskLruCache.open(cacheDir, 1, 1, cacheMaxSize, cacheMaxFileCount); } catch (IOException e) { L.e(e); if (reserveCacheDir != null) { initCache(reserveCacheDir, null, cacheMaxSize, cacheMaxFileCount); } if (cache == null) { throw e; //new RuntimeException("Can't initialize disk cache", e); } } } @Override public File getDirectory() { return cache.getDirectory(); } @Override public File get(String imageUri) { DiskLruCache.Snapshot snapshot = null; try { snapshot = cache.get(getKey(imageUri)); return snapshot == null ? null : snapshot.getFile(0); } catch (IOException e) { L.e(e); return null; } finally { if (snapshot != null) { snapshot.close(); } } } @Override public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException { DiskLruCache.Editor editor = cache.edit(getKey(imageUri)); if (editor == null) { return false; } OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize); boolean copied = false; try { copied = IoUtils.copyStream(imageStream, os, listener, bufferSize); } finally { IoUtils.closeSilently(os); if (copied) { editor.commit(); } else { editor.abort(); } } return copied; } @Override public boolean save(String imageUri, Bitmap bitmap) throws IOException { DiskLruCache.Editor editor = cache.edit(getKey(imageUri)); if (editor == null) { return false; } OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize); boolean savedSuccessfully = false; try { savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os); } finally { IoUtils.closeSilently(os); } if (savedSuccessfully) { editor.commit(); } else { editor.abort(); } return savedSuccessfully; } @Override public boolean remove(String imageUri) { try { return cache.remove(getKey(imageUri)); } catch (IOException e) { L.e(e); return false; } } @Override public void close() { try { cache.close(); } catch (IOException e) { L.e(e); } cache = null; } @Override public void clear() { try { cache.delete(); } catch (IOException e) { L.e(e); } try { initCache(cache.getDirectory(), reserveCacheDir, cache.getMaxSize(), cache.getMaxFileCount()); } catch (IOException e) { L.e(e); } } private String getKey(String imageUri) { return fileNameGenerator.generate(imageUri); } public void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; } public void setCompressFormat(Bitmap.CompressFormat compressFormat) { this.compressFormat = compressFormat; } public void setCompressQuality(int compressQuality) { this.compressQuality = compressQuality; } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/ext/StrictLineReader.java ================================================ /* * 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.nostra13.universalimageloader.cache.disc.impl.ext; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; /** * Buffers input from an {@link InputStream} for reading lines. * *

This class is used for buffered reading of lines. For purposes of this class, a line ends * with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated * line at end of input is invalid and will be ignored, the caller may use {@code * hasUnterminatedLine()} to detect it after catching the {@code EOFException}. * *

This class is intended for reading input that strictly consists of lines, such as line-based * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different * end-of-input reporting and a more restrictive definition of a line. * *

This class supports only charsets that encode '\r' and '\n' as a single byte with value 13 * and 10, respectively, and the representation of no other character contains these values. * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1. * The default charset is US_ASCII. */ class StrictLineReader implements Closeable { private static final byte CR = (byte) '\r'; private static final byte LF = (byte) '\n'; private final InputStream in; private final Charset charset; /* * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end * and the data in the range [pos, end) is buffered for reading. At end of input, if there is * an unterminated line, we set end == -1, otherwise end == pos. If the underlying * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1. */ private byte[] buf; private int pos; private int end; /** * Constructs a new {@code LineReader} with the specified charset and the default capacity. * * @param in the {@code InputStream} to read data from. * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are * supported. * @throws NullPointerException if {@code in} or {@code charset} is null. * @throws IllegalArgumentException if the specified charset is not supported. */ public StrictLineReader(InputStream in, Charset charset) { this(in, 8192, charset); } /** * Constructs a new {@code LineReader} with the specified capacity and charset. * * @param in the {@code InputStream} to read data from. * @param capacity the capacity of the buffer. * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are * supported. * @throws NullPointerException if {@code in} or {@code charset} is null. * @throws IllegalArgumentException if {@code capacity} is negative or zero * or the specified charset is not supported. */ public StrictLineReader(InputStream in, int capacity, Charset charset) { if (in == null || charset == null) { throw new NullPointerException(); } if (capacity < 0) { throw new IllegalArgumentException("capacity <= 0"); } if (!(charset.equals(Util.US_ASCII))) { throw new IllegalArgumentException("Unsupported encoding"); } this.in = in; this.charset = charset; buf = new byte[capacity]; } /** * Closes the reader by closing the underlying {@code InputStream} and * marking this reader as closed. * * @throws IOException for errors when closing the underlying {@code InputStream}. */ public void close() throws IOException { synchronized (in) { if (buf != null) { buf = null; in.close(); } } } /** * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"}, * this end of line marker is not included in the result. * * @return the next line from the input. * @throws IOException for underlying {@code InputStream} errors. * @throws EOFException for the end of source stream. */ public String readLine() throws IOException { synchronized (in) { if (buf == null) { throw new IOException("LineReader is closed"); } // Read more data if we are at the end of the buffered data. // Though it's an error to read after an exception, we will let {@code fillBuf()} // throw again if that happens; thus we need to handle end == -1 as well as end == pos. if (pos >= end) { fillBuf(); } // Try to find LF in the buffered data and return the line if successful. for (int i = pos; i != end; ++i) { if (buf[i] == LF) { int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i; String res = new String(buf, pos, lineEnd - pos, charset.name()); pos = i + 1; return res; } } // Let's anticipate up to 80 characters on top of those already read. ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) { @Override public String toString() { int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count; try { return new String(buf, 0, length, charset.name()); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); // Since we control the charset this will never happen. } } }; while (true) { out.write(buf, pos, end - pos); // Mark unterminated line in case fillBuf throws EOFException or IOException. end = -1; fillBuf(); // Try to find LF in the buffered data and return the line if successful. for (int i = pos; i != end; ++i) { if (buf[i] == LF) { if (i != pos) { out.write(buf, pos, i - pos); } pos = i + 1; return out.toString(); } } } } } /** * Reads new input data into the buffer. Call only with pos == end or end == -1, * depending on the desired outcome if the function throws. */ private void fillBuf() throws IOException { int result = in.read(buf, 0, buf.length); if (result == -1) { throw new EOFException(); } pos = 0; end = result; } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/ext/Util.java ================================================ /* * Copyright (C) 2010 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.nostra13.universalimageloader.cache.disc.impl.ext; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.Reader; import java.io.StringWriter; import java.nio.charset.Charset; /** Junk drawer of utility methods. */ final class Util { static final Charset US_ASCII = Charset.forName("US-ASCII"); static final Charset UTF_8 = Charset.forName("UTF-8"); private Util() { } static String readFully(Reader reader) throws IOException { try { StringWriter writer = new StringWriter(); char[] buffer = new char[1024]; int count; while ((count = reader.read(buffer)) != -1) { writer.write(buffer, 0, count); } return writer.toString(); } finally { reader.close(); } } /** * Deletes the contents of {@code dir}. Throws an IOException if any file * could not be deleted, or if {@code dir} is not a readable directory. */ static void deleteContents(File dir) throws IOException { File[] files = dir.listFiles(); if (files == null) { throw new IOException("not a readable directory: " + dir); } for (File file : files) { if (file.isDirectory()) { deleteContents(file); } if (!file.delete()) { throw new IOException("failed to delete file: " + file); } } } static void closeQuietly(/*Auto*/Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { } } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/naming/FileNameGenerator.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.disc.naming; /** * Generates names for files at disk cache * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.3.1 */ public interface FileNameGenerator { /** Generates unique file name for image defined by URI */ String generate(String imageUri); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/naming/HashCodeFileNameGenerator.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.disc.naming; /** * Names image file as image URI {@linkplain String#hashCode() hashcode} * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.3.1 */ public class HashCodeFileNameGenerator implements FileNameGenerator { @Override public String generate(String imageUri) { return String.valueOf(imageUri.hashCode()); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/naming/Md5FileNameGenerator.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.disc.naming; import com.nostra13.universalimageloader.utils.L; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * Names image file as MD5 hash of image URI * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.4.0 */ public class Md5FileNameGenerator implements FileNameGenerator { private static final String HASH_ALGORITHM = "MD5"; private static final int RADIX = 10 + 26; // 10 digits + 26 letters @Override public String generate(String imageUri) { byte[] md5 = getMD5(imageUri.getBytes()); BigInteger bi = new BigInteger(md5).abs(); return bi.toString(RADIX); } private byte[] getMD5(byte[] data) { byte[] hash = null; try { MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM); digest.update(data); hash = digest.digest(); } catch (NoSuchAlgorithmException e) { L.e(e); } return hash; } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/BaseMemoryCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory; import android.graphics.Bitmap; import java.lang.ref.Reference; import java.util.*; /** * Base memory cache. Implements common functionality for memory cache. Provides object references ( * {@linkplain Reference not strong}) storing. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public abstract class BaseMemoryCache implements MemoryCache { /** Stores not strong references to objects */ private final Map> softMap = Collections.synchronizedMap(new HashMap>()); @Override public Bitmap get(String key) { Bitmap result = null; Reference reference = softMap.get(key); if (reference != null) { result = reference.get(); } return result; } @Override public boolean put(String key, Bitmap value) { softMap.put(key, createReference(value)); return true; } @Override public Bitmap remove(String key) { Reference bmpRef = softMap.remove(key); return bmpRef == null ? null : bmpRef.get(); } @Override public Collection keys() { synchronized (softMap) { return new HashSet(softMap.keySet()); } } @Override public void clear() { softMap.clear(); } /** Creates {@linkplain Reference not strong} reference of value */ protected abstract Reference createReference(Bitmap value); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/LimitedMemoryCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory; import android.graphics.Bitmap; import com.nostra13.universalimageloader.utils.L; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Limited cache. Provides object storing. Size of all stored bitmaps will not to exceed size limit ( * {@link #getSizeLimit()}).
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see BaseMemoryCache * @since 1.0.0 */ public abstract class LimitedMemoryCache extends BaseMemoryCache { private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16; private static final int MAX_NORMAL_CACHE_SIZE = MAX_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024; private final int sizeLimit; private final AtomicInteger cacheSize; /** * Contains strong references to stored objects. Each next object is added last. If hard cache size will exceed * limit then first object is deleted (but it continue exist at {@link #softMap} and can be collected by GC at any * time) */ private final List hardCache = Collections.synchronizedList(new LinkedList()); /** @param sizeLimit Maximum size for cache (in bytes) */ public LimitedMemoryCache(int sizeLimit) { this.sizeLimit = sizeLimit; cacheSize = new AtomicInteger(); if (sizeLimit > MAX_NORMAL_CACHE_SIZE) { L.w("You set too large memory cache size (more than %1$d Mb)", MAX_NORMAL_CACHE_SIZE_IN_MB); } } @Override public boolean put(String key, Bitmap value) { boolean putSuccessfully = false; // Try to add value to hard cache int valueSize = getSize(value); int sizeLimit = getSizeLimit(); int curCacheSize = cacheSize.get(); if (valueSize < sizeLimit) { while (curCacheSize + valueSize > sizeLimit) { Bitmap removedValue = removeNext(); if (hardCache.remove(removedValue)) { curCacheSize = cacheSize.addAndGet(-getSize(removedValue)); } } hardCache.add(value); cacheSize.addAndGet(valueSize); putSuccessfully = true; } // Add value to soft cache super.put(key, value); return putSuccessfully; } @Override public Bitmap remove(String key) { Bitmap value = super.get(key); if (value != null) { if (hardCache.remove(value)) { cacheSize.addAndGet(-getSize(value)); } } return super.remove(key); } @Override public void clear() { hardCache.clear(); cacheSize.set(0); super.clear(); } protected int getSizeLimit() { return sizeLimit; } protected abstract int getSize(Bitmap value); protected abstract Bitmap removeNext(); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/MemoryCache.java ================================================ /******************************************************************************* * Copyright 2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory; import android.graphics.Bitmap; import java.util.Collection; /** * Interface for memory cache * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.2 */ public interface MemoryCache { /** * Puts value into cache by key * * @return true - if value was put into cache successfully, false - if value was not put into * cache */ boolean put(String key, Bitmap value); /** Returns value by key. If there is no value for key then null will be returned. */ Bitmap get(String key); /** Removes item by key */ Bitmap remove(String key); /** Returns all keys of cache */ Collection keys(); /** Remove all items from cache */ void clear(); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/impl/FIFOLimitedMemoryCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to * exceed size limit. When cache reaches limit size then cache clearing is processed by FIFO principle.
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public class FIFOLimitedMemoryCache extends LimitedMemoryCache { private final List queue = Collections.synchronizedList(new LinkedList()); public FIFOLimitedMemoryCache(int sizeLimit) { super(sizeLimit); } @Override public boolean put(String key, Bitmap value) { if (super.put(key, value)) { queue.add(value); return true; } else { return false; } } @Override public Bitmap remove(String key) { Bitmap value = super.get(key); if (value != null) { queue.remove(value); } return super.remove(key); } @Override public void clear() { queue.clear(); super.clear(); } @Override protected int getSize(Bitmap value) { return value.getRowBytes() * value.getHeight(); } @Override protected Bitmap removeNext() { return queue.remove(0); } @Override protected Reference createReference(Bitmap value) { return new WeakReference(value); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/impl/FuzzyKeyMemoryCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.memory.MemoryCache; import java.util.Collection; import java.util.Comparator; /** * Decorator for {@link MemoryCache}. Provides special feature for cache: some different keys are considered as * equals (using {@link Comparator comparator}). And when you try to put some value into cache by key so entries with * "equals" keys will be removed from cache before.
* NOTE: Used for internal needs. Normally you don't need to use this class. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public class FuzzyKeyMemoryCache implements MemoryCache { private final MemoryCache cache; private final Comparator keyComparator; public FuzzyKeyMemoryCache(MemoryCache cache, Comparator keyComparator) { this.cache = cache; this.keyComparator = keyComparator; } @Override public boolean put(String key, Bitmap value) { // Search equal key and remove this entry synchronized (cache) { String keyToRemove = null; for (String cacheKey : cache.keys()) { if (keyComparator.compare(key, cacheKey) == 0) { keyToRemove = cacheKey; break; } } if (keyToRemove != null) { cache.remove(keyToRemove); } } return cache.put(key, value); } @Override public Bitmap get(String key) { return cache.get(key); } @Override public Bitmap remove(String key) { return cache.remove(key); } @Override public void clear() { cache.clear(); } @Override public Collection keys() { return cache.keys(); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/impl/LRULimitedMemoryCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; /** * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to * exceed size limit. When cache reaches limit size then the least recently used bitmap is deleted from cache.
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.3.0 */ public class LRULimitedMemoryCache extends LimitedMemoryCache { private static final int INITIAL_CAPACITY = 10; private static final float LOAD_FACTOR = 1.1f; /** Cache providing Least-Recently-Used logic */ private final Map lruCache = Collections.synchronizedMap(new LinkedHashMap(INITIAL_CAPACITY, LOAD_FACTOR, true)); /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */ public LRULimitedMemoryCache(int maxSize) { super(maxSize); } @Override public boolean put(String key, Bitmap value) { if (super.put(key, value)) { lruCache.put(key, value); return true; } else { return false; } } @Override public Bitmap get(String key) { lruCache.get(key); // call "get" for LRU logic return super.get(key); } @Override public Bitmap remove(String key) { lruCache.remove(key); return super.remove(key); } @Override public void clear() { lruCache.clear(); super.clear(); } @Override protected int getSize(Bitmap value) { return value.getRowBytes() * value.getHeight(); } @Override protected Bitmap removeNext() { Bitmap mostLongUsedValue = null; synchronized (lruCache) { Iterator> it = lruCache.entrySet().iterator(); if (it.hasNext()) { Entry entry = it.next(); mostLongUsedValue = entry.getValue(); it.remove(); } } return mostLongUsedValue; } @Override protected Reference createReference(Bitmap value) { return new WeakReference(value); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/impl/LargestLimitedMemoryCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to * exceed size limit. When cache reaches limit size then the bitmap which has the largest size is deleted from * cache.
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public class LargestLimitedMemoryCache extends LimitedMemoryCache { /** * Contains strong references to stored objects (keys) and sizes of the objects. If hard cache * size will exceed limit then object with the largest size is deleted (but it continue exist at * {@link #softMap} and can be collected by GC at any time) */ private final Map valueSizes = Collections.synchronizedMap(new HashMap()); public LargestLimitedMemoryCache(int sizeLimit) { super(sizeLimit); } @Override public boolean put(String key, Bitmap value) { if (super.put(key, value)) { valueSizes.put(value, getSize(value)); return true; } else { return false; } } @Override public Bitmap remove(String key) { Bitmap value = super.get(key); if (value != null) { valueSizes.remove(value); } return super.remove(key); } @Override public void clear() { valueSizes.clear(); super.clear(); } @Override protected int getSize(Bitmap value) { return value.getRowBytes() * value.getHeight(); } @Override protected Bitmap removeNext() { Integer maxSize = null; Bitmap largestValue = null; Set> entries = valueSizes.entrySet(); synchronized (valueSizes) { for (Entry entry : entries) { if (largestValue == null) { largestValue = entry.getKey(); maxSize = entry.getValue(); } else { Integer size = entry.getValue(); if (size > maxSize) { maxSize = size; largestValue = entry.getKey(); } } } } valueSizes.remove(largestValue); return largestValue; } @Override protected Reference createReference(Bitmap value) { return new WeakReference(value); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/impl/LimitedAgeMemoryCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.memory.MemoryCache; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Decorator for {@link MemoryCache}. Provides special feature for cache: if some cached object age exceeds defined * value then this object will be removed from cache. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see MemoryCache * @since 1.3.1 */ public class LimitedAgeMemoryCache implements MemoryCache { private final MemoryCache cache; private final long maxAge; private final Map loadingDates = Collections.synchronizedMap(new HashMap()); /** * @param cache Wrapped memory cache * @param maxAge Max object age (in seconds). If object age will exceed this value then it'll be removed from * cache on next treatment (and therefore be reloaded). */ public LimitedAgeMemoryCache(MemoryCache cache, long maxAge) { this.cache = cache; this.maxAge = maxAge * 1000; // to milliseconds } @Override public boolean put(String key, Bitmap value) { boolean putSuccesfully = cache.put(key, value); if (putSuccesfully) { loadingDates.put(key, System.currentTimeMillis()); } return putSuccesfully; } @Override public Bitmap get(String key) { Long loadingDate = loadingDates.get(key); if (loadingDate != null && System.currentTimeMillis() - loadingDate > maxAge) { cache.remove(key); loadingDates.remove(key); } return cache.get(key); } @Override public Bitmap remove(String key) { loadingDates.remove(key); return cache.remove(key); } @Override public Collection keys() { return cache.keys(); } @Override public void clear() { cache.clear(); loadingDates.clear(); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/impl/LruMemoryCache.java ================================================ package com.nostra13.universalimageloader.cache.memory.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.memory.MemoryCache; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; /** * A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to * the head of a queue. When a Bitmap is added to a full cache, the Bitmap at the end of that queue is evicted and may * become eligible for garbage collection.
*
* NOTE: This cache uses only strong references for stored Bitmaps. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.1 */ public class LruMemoryCache implements MemoryCache { private final LinkedHashMap map; private final int maxSize; /** Size of this cache in bytes */ private int size; /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */ public LruMemoryCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } this.maxSize = maxSize; this.map = new LinkedHashMap(0, 0.75f, true); } /** * Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head * of the queue. This returns null if a Bitmap is not cached. */ @Override public final Bitmap get(String key) { if (key == null) { throw new NullPointerException("key == null"); } synchronized (this) { return map.get(key); } } /** Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue. */ @Override public final boolean put(String key, Bitmap value) { if (key == null || value == null) { throw new NullPointerException("key == null || value == null"); } synchronized (this) { size += sizeOf(key, value); Bitmap previous = map.put(key, value); if (previous != null) { size -= sizeOf(key, previous); } } trimToSize(maxSize); return true; } /** * Remove the eldest entries until the total of remaining entries is at or below the requested size. * * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements. */ private void trimToSize(int maxSize) { while (true) { String key; Bitmap value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if (size <= maxSize || map.isEmpty()) { break; } Map.Entry toEvict = map.entrySet().iterator().next(); if (toEvict == null) { break; } key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= sizeOf(key, value); } } } /** Removes the entry for {@code key} if it exists. */ @Override public final Bitmap remove(String key) { if (key == null) { throw new NullPointerException("key == null"); } synchronized (this) { Bitmap previous = map.remove(key); if (previous != null) { size -= sizeOf(key, previous); } return previous; } } @Override public Collection keys() { synchronized (this) { return new HashSet(map.keySet()); } } @Override public void clear() { trimToSize(-1); // -1 will evict 0-sized elements } /** * Returns the size {@code Bitmap} in bytes. *

* An entry's size must not change while it is in the cache. */ private int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } @Override public synchronized final String toString() { return String.format("LruCache[maxSize=%d]", maxSize); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/impl/UsingFreqLimitedMemoryCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to * exceed size limit. When cache reaches limit size then the bitmap which used the least frequently is deleted from * cache.
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache { /** * Contains strong references to stored objects (keys) and last object usage date (in milliseconds). If hard cache * size will exceed limit then object with the least frequently usage is deleted (but it continue exist at * {@link #softMap} and can be collected by GC at any time) */ private final Map usingCounts = Collections.synchronizedMap(new HashMap()); public UsingFreqLimitedMemoryCache(int sizeLimit) { super(sizeLimit); } @Override public boolean put(String key, Bitmap value) { if (super.put(key, value)) { usingCounts.put(value, 0); return true; } else { return false; } } @Override public Bitmap get(String key) { Bitmap value = super.get(key); // Increment usage count for value if value is contained in hardCahe if (value != null) { Integer usageCount = usingCounts.get(value); if (usageCount != null) { usingCounts.put(value, usageCount + 1); } } return value; } @Override public Bitmap remove(String key) { Bitmap value = super.get(key); if (value != null) { usingCounts.remove(value); } return super.remove(key); } @Override public void clear() { usingCounts.clear(); super.clear(); } @Override protected int getSize(Bitmap value) { return value.getRowBytes() * value.getHeight(); } @Override protected Bitmap removeNext() { Integer minUsageCount = null; Bitmap leastUsedValue = null; Set> entries = usingCounts.entrySet(); synchronized (usingCounts) { for (Entry entry : entries) { if (leastUsedValue == null) { leastUsedValue = entry.getKey(); minUsageCount = entry.getValue(); } else { Integer lastValueUsage = entry.getValue(); if (lastValueUsage < minUsageCount) { minUsageCount = lastValueUsage; leastUsedValue = entry.getKey(); } } } } usingCounts.remove(leastUsedValue); return leastUsedValue; } @Override protected Reference createReference(Bitmap value) { return new WeakReference(value); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/impl/WeakMemoryCache.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.cache.memory.impl; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.memory.BaseMemoryCache; import java.lang.ref.Reference; import java.lang.ref.WeakReference; /** * Memory cache with {@linkplain WeakReference weak references} to {@linkplain android.graphics.Bitmap bitmaps}
*
* NOTE: This cache uses only weak references for stored Bitmaps. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.5.3 */ public class WeakMemoryCache extends BaseMemoryCache { @Override protected Reference createReference(Bitmap value) { return new WeakReference(value); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/DefaultConfigurationFactory.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core; import android.annotation.TargetApi; import android.app.ActivityManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Build; import com.nostra13.universalimageloader.cache.disc.DiskCache; import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache; import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator; import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator; import com.nostra13.universalimageloader.cache.memory.MemoryCache; import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import com.nostra13.universalimageloader.core.assist.deque.LIFOLinkedBlockingDeque; import com.nostra13.universalimageloader.core.decode.BaseImageDecoder; import com.nostra13.universalimageloader.core.decode.ImageDecoder; import com.nostra13.universalimageloader.core.display.BitmapDisplayer; import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import com.nostra13.universalimageloader.core.download.ImageDownloader; import com.nostra13.universalimageloader.utils.L; import com.nostra13.universalimageloader.utils.StorageUtils; import java.io.File; import java.io.IOException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Factory for providing of default options for {@linkplain ImageLoaderConfiguration configuration} * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.5.6 */ public class DefaultConfigurationFactory { /** Creates default implementation of task executor */ public static Executor createExecutor(int threadPoolSize, int threadPriority, QueueProcessingType tasksProcessingType) { boolean lifo = tasksProcessingType == QueueProcessingType.LIFO; BlockingQueue taskQueue = lifo ? new LIFOLinkedBlockingDeque() : new LinkedBlockingQueue(); return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue, createThreadFactory(threadPriority, "uil-pool-")); } /** Creates default implementation of task distributor */ public static Executor createTaskDistributor() { return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-")); } /** Creates {@linkplain HashCodeFileNameGenerator default implementation} of FileNameGenerator */ public static FileNameGenerator createFileNameGenerator() { return new HashCodeFileNameGenerator(); } /** * Creates default implementation of {@link DiskCache} depends on incoming parameters */ public static DiskCache createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator, long diskCacheSize, int diskCacheFileCount) { File reserveCacheDir = createReserveDiskCacheDir(context); if (diskCacheSize > 0 || diskCacheFileCount > 0) { File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context); try { return new LruDiskCache(individualCacheDir, reserveCacheDir, diskCacheFileNameGenerator, diskCacheSize, diskCacheFileCount); } catch (IOException e) { L.e(e); // continue and create unlimited cache } } File cacheDir = StorageUtils.getCacheDirectory(context); return new UnlimitedDiskCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator); } /** Creates reserve disk cache folder which will be used if primary disk cache folder becomes unavailable */ private static File createReserveDiskCacheDir(Context context) { File cacheDir = StorageUtils.getCacheDirectory(context, false); File individualDir = new File(cacheDir, "uil-images"); if (individualDir.exists() || individualDir.mkdir()) { cacheDir = individualDir; } return cacheDir; } /** * Creates default implementation of {@link MemoryCache} - {@link LruMemoryCache}
* Default cache size = 1/8 of available app memory. */ public static MemoryCache createMemoryCache(Context context, int memoryCacheSize) { if (memoryCacheSize == 0) { ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); int memoryClass = am.getMemoryClass(); if (hasHoneycomb() && isLargeHeap(context)) { memoryClass = getLargeMemoryClass(am); } memoryCacheSize = 1024 * 1024 * memoryClass / 8; } return new LruMemoryCache(memoryCacheSize); } private static boolean hasHoneycomb() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private static boolean isLargeHeap(Context context) { return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0; } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private static int getLargeMemoryClass(ActivityManager am) { return am.getLargeMemoryClass(); } /** Creates default implementation of {@link ImageDownloader} - {@link BaseImageDownloader} */ public static ImageDownloader createImageDownloader(Context context) { return new BaseImageDownloader(context); } /** Creates default implementation of {@link ImageDecoder} - {@link BaseImageDecoder} */ public static ImageDecoder createImageDecoder(boolean loggingEnabled) { return new BaseImageDecoder(loggingEnabled); } /** Creates default implementation of {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer} */ public static BitmapDisplayer createBitmapDisplayer() { return new SimpleBitmapDisplayer(); } /** Creates default implementation of {@linkplain ThreadFactory thread factory} for task executor */ private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) { return new DefaultThreadFactory(threadPriority, threadNamePrefix); } private static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; private final int threadPriority; DefaultThreadFactory(int threadPriority, String threadNamePrefix) { this.threadPriority = threadPriority; group = Thread.currentThread().getThreadGroup(); namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-"; } @Override public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); t.setPriority(threadPriority); return t; } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/DisplayBitmapTask.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core; import android.graphics.Bitmap; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.display.BitmapDisplayer; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.utils.L; /** * Displays bitmap in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}. Must be called on UI thread. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see ImageLoadingListener * @see BitmapDisplayer * @since 1.3.1 */ final class DisplayBitmapTask implements Runnable { private static final String LOG_DISPLAY_IMAGE_IN_IMAGEAWARE = "Display image in ImageAware (loaded from %1$s) [%2$s]"; private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]"; private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]"; private final Bitmap bitmap; private final String imageUri; private final ImageAware imageAware; private final String memoryCacheKey; private final BitmapDisplayer displayer; private final ImageLoadingListener listener; private final ImageLoaderEngine engine; private final LoadedFrom loadedFrom; public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, ImageLoaderEngine engine, LoadedFrom loadedFrom) { this.bitmap = bitmap; imageUri = imageLoadingInfo.uri; imageAware = imageLoadingInfo.imageAware; memoryCacheKey = imageLoadingInfo.memoryCacheKey; displayer = imageLoadingInfo.options.getDisplayer(); listener = imageLoadingInfo.listener; this.engine = engine; this.loadedFrom = loadedFrom; } @Override public void run() { if (imageAware.isCollected()) { L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey); listener.onLoadingCancelled(imageUri, imageAware.getWrappedView()); } else if (isViewWasReused()) { L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey); listener.onLoadingCancelled(imageUri, imageAware.getWrappedView()); } else { L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey); displayer.display(bitmap, imageAware, loadedFrom); engine.cancelDisplayTaskFor(imageAware); listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap); } } /** Checks whether memory cache key (image URI) for current ImageAware is actual */ private boolean isViewWasReused() { String currentCacheKey = engine.getLoadingUriForView(imageAware); return !memoryCacheKey.equals(currentCacheKey); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/DisplayImageOptions.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory.Options; import android.graphics.drawable.Drawable; import android.os.Handler; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.display.BitmapDisplayer; import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; import com.nostra13.universalimageloader.core.download.ImageDownloader; import com.nostra13.universalimageloader.core.process.BitmapProcessor; /** * Contains options for image display. Defines: *

    *
  • whether stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} during image loading
  • *
  • whether stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} if empty URI is passed
  • *
  • whether stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} if image loading fails
  • *
  • whether {@link com.nostra13.universalimageloader.core.imageaware.ImageAware image aware view} should be reset * before image loading start
  • *
  • whether loaded image will be cached in memory
  • *
  • whether loaded image will be cached on disk
  • *
  • image scale type
  • *
  • decoding options (including bitmap decoding configuration)
  • *
  • delay before loading of image
  • *
  • whether consider EXIF parameters of image
  • *
  • auxiliary object which will be passed to {@link ImageDownloader#getStream(String, Object) ImageDownloader}
  • *
  • pre-processor for image Bitmap (before caching in memory)
  • *
  • post-processor for image Bitmap (after caching in memory, before displaying)
  • *
  • how decoded {@link Bitmap} will be displayed
  • *
*

* You can create instance: *

    *
  • with {@link Builder}:
    * i.e. : * new {@link DisplayImageOptions}.Builder().{@link Builder#cacheInMemory() cacheInMemory()}. * {@link Builder#showImageOnLoading(int) showImageOnLoading()}.{@link Builder#build() build()}
    *
  • *
  • or by static method: {@link #createSimple()}

  • * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public final class DisplayImageOptions { private final int imageResOnLoading; private final int imageResForEmptyUri; private final int imageResOnFail; private final Drawable imageOnLoading; private final Drawable imageForEmptyUri; private final Drawable imageOnFail; private final boolean resetViewBeforeLoading; private final boolean cacheInMemory; private final boolean cacheOnDisk; private final ImageScaleType imageScaleType; private final Options decodingOptions; private final int delayBeforeLoading; private final boolean considerExifParams; private final Object extraForDownloader; private final BitmapProcessor preProcessor; private final BitmapProcessor postProcessor; private final BitmapDisplayer displayer; private final Handler handler; private final boolean isSyncLoading; private DisplayImageOptions(Builder builder) { imageResOnLoading = builder.imageResOnLoading; imageResForEmptyUri = builder.imageResForEmptyUri; imageResOnFail = builder.imageResOnFail; imageOnLoading = builder.imageOnLoading; imageForEmptyUri = builder.imageForEmptyUri; imageOnFail = builder.imageOnFail; resetViewBeforeLoading = builder.resetViewBeforeLoading; cacheInMemory = builder.cacheInMemory; cacheOnDisk = builder.cacheOnDisk; imageScaleType = builder.imageScaleType; decodingOptions = builder.decodingOptions; delayBeforeLoading = builder.delayBeforeLoading; considerExifParams = builder.considerExifParams; extraForDownloader = builder.extraForDownloader; preProcessor = builder.preProcessor; postProcessor = builder.postProcessor; displayer = builder.displayer; handler = builder.handler; isSyncLoading = builder.isSyncLoading; } public boolean shouldShowImageOnLoading() { return imageOnLoading != null || imageResOnLoading != 0; } public boolean shouldShowImageForEmptyUri() { return imageForEmptyUri != null || imageResForEmptyUri != 0; } public boolean shouldShowImageOnFail() { return imageOnFail != null || imageResOnFail != 0; } public boolean shouldPreProcess() { return preProcessor != null; } public boolean shouldPostProcess() { return postProcessor != null; } public boolean shouldDelayBeforeLoading() { return delayBeforeLoading > 0; } public Drawable getImageOnLoading(Resources res) { return imageResOnLoading != 0 ? res.getDrawable(imageResOnLoading) : imageOnLoading; } public Drawable getImageForEmptyUri(Resources res) { return imageResForEmptyUri != 0 ? res.getDrawable(imageResForEmptyUri) : imageForEmptyUri; } public Drawable getImageOnFail(Resources res) { return imageResOnFail != 0 ? res.getDrawable(imageResOnFail) : imageOnFail; } public boolean isResetViewBeforeLoading() { return resetViewBeforeLoading; } public boolean isCacheInMemory() { return cacheInMemory; } public boolean isCacheOnDisk() { return cacheOnDisk; } public ImageScaleType getImageScaleType() { return imageScaleType; } public Options getDecodingOptions() { return decodingOptions; } public int getDelayBeforeLoading() { return delayBeforeLoading; } public boolean isConsiderExifParams() { return considerExifParams; } public Object getExtraForDownloader() { return extraForDownloader; } public BitmapProcessor getPreProcessor() { return preProcessor; } public BitmapProcessor getPostProcessor() { return postProcessor; } public BitmapDisplayer getDisplayer() { return displayer; } public Handler getHandler() { return handler; } boolean isSyncLoading() { return isSyncLoading; } /** * Builder for {@link DisplayImageOptions} * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public static class Builder { private int imageResOnLoading = 0; private int imageResForEmptyUri = 0; private int imageResOnFail = 0; private Drawable imageOnLoading = null; private Drawable imageForEmptyUri = null; private Drawable imageOnFail = null; private boolean resetViewBeforeLoading = false; private boolean cacheInMemory = false; private boolean cacheOnDisk = false; private ImageScaleType imageScaleType = ImageScaleType.IN_SAMPLE_POWER_OF_2; private Options decodingOptions = new Options(); private int delayBeforeLoading = 0; private boolean considerExifParams = false; private Object extraForDownloader = null; private BitmapProcessor preProcessor = null; private BitmapProcessor postProcessor = null; private BitmapDisplayer displayer = DefaultConfigurationFactory.createBitmapDisplayer(); private Handler handler = null; private boolean isSyncLoading = false; /** * Stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} during image loading * * @param imageRes Stub image resource * @deprecated Use {@link #showImageOnLoading(int)} instead */ @Deprecated public Builder showStubImage(int imageRes) { imageResOnLoading = imageRes; return this; } /** * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} during image loading * * @param imageRes Image resource */ public Builder showImageOnLoading(int imageRes) { imageResOnLoading = imageRes; return this; } /** * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} during image loading. * This option will be ignored if {@link DisplayImageOptions.Builder#showImageOnLoading(int)} is set. */ public Builder showImageOnLoading(Drawable drawable) { imageOnLoading = drawable; return this; } /** * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} if empty URI (null or empty * string) will be passed to ImageLoader.displayImage(...) method. * * @param imageRes Image resource */ public Builder showImageForEmptyUri(int imageRes) { imageResForEmptyUri = imageRes; return this; } /** * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} if empty URI (null or empty * string) will be passed to ImageLoader.displayImage(...) method. * This option will be ignored if {@link DisplayImageOptions.Builder#showImageForEmptyUri(int)} is set. */ public Builder showImageForEmptyUri(Drawable drawable) { imageForEmptyUri = drawable; return this; } /** * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} if some error occurs during * requested image loading/decoding. * * @param imageRes Image resource */ public Builder showImageOnFail(int imageRes) { imageResOnFail = imageRes; return this; } /** * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} if some error occurs during * requested image loading/decoding. * This option will be ignored if {@link DisplayImageOptions.Builder#showImageOnFail(int)} is set. */ public Builder showImageOnFail(Drawable drawable) { imageOnFail = drawable; return this; } /** * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} will be reset (set null) before image loading start * * @deprecated Use {@link #resetViewBeforeLoading(boolean) resetViewBeforeLoading(true)} instead */ public Builder resetViewBeforeLoading() { resetViewBeforeLoading = true; return this; } /** * Sets whether {@link com.nostra13.universalimageloader.core.imageaware.ImageAware * image aware view} will be reset (set null) before image loading start */ public Builder resetViewBeforeLoading(boolean resetViewBeforeLoading) { this.resetViewBeforeLoading = resetViewBeforeLoading; return this; } /** * Loaded image will be cached in memory * * @deprecated Use {@link #cacheInMemory(boolean) cacheInMemory(true)} instead */ @Deprecated public Builder cacheInMemory() { cacheInMemory = true; return this; } /** Sets whether loaded image will be cached in memory */ public Builder cacheInMemory(boolean cacheInMemory) { this.cacheInMemory = cacheInMemory; return this; } /** * Loaded image will be cached on disk * * @deprecated Use {@link #cacheOnDisk(boolean) cacheOnDisk(true)} instead */ @Deprecated public Builder cacheOnDisc() { return cacheOnDisk(true); } /** * Sets whether loaded image will be cached on disk * * @deprecated Use {@link #cacheOnDisk(boolean)} instead */ @Deprecated public Builder cacheOnDisc(boolean cacheOnDisk) { return cacheOnDisk(cacheOnDisk); } /** Sets whether loaded image will be cached on disk */ public Builder cacheOnDisk(boolean cacheOnDisk) { this.cacheOnDisk = cacheOnDisk; return this; } /** * Sets {@linkplain ImageScaleType scale type} for decoding image. This parameter is used while define scale * size for decoding image to Bitmap. Default value - {@link ImageScaleType#IN_SAMPLE_POWER_OF_2} */ public Builder imageScaleType(ImageScaleType imageScaleType) { this.imageScaleType = imageScaleType; return this; } /** Sets {@link Bitmap.Config bitmap config} for image decoding. Default value - {@link Bitmap.Config#ARGB_8888} */ public Builder bitmapConfig(Bitmap.Config bitmapConfig) { if (bitmapConfig == null) throw new IllegalArgumentException("bitmapConfig can't be null"); decodingOptions.inPreferredConfig = bitmapConfig; return this; } /** * Sets options for image decoding.
    * NOTE: {@link Options#inSampleSize} of incoming options will NOT be considered. Library * calculate the most appropriate sample size itself according yo {@link #imageScaleType(ImageScaleType)} * options.
    * NOTE: This option overlaps {@link #bitmapConfig(android.graphics.Bitmap.Config) bitmapConfig()} * option. */ public Builder decodingOptions(Options decodingOptions) { if (decodingOptions == null) throw new IllegalArgumentException("decodingOptions can't be null"); this.decodingOptions = decodingOptions; return this; } /** Sets delay time before starting loading task. Default - no delay. */ public Builder delayBeforeLoading(int delayInMillis) { this.delayBeforeLoading = delayInMillis; return this; } /** Sets auxiliary object which will be passed to {@link ImageDownloader#getStream(String, Object)} */ public Builder extraForDownloader(Object extra) { this.extraForDownloader = extra; return this; } /** Sets whether ImageLoader will consider EXIF parameters of JPEG image (rotate, flip) */ public Builder considerExifParams(boolean considerExifParams) { this.considerExifParams = considerExifParams; return this; } /** * Sets bitmap processor which will be process bitmaps before they will be cached in memory. So memory cache * will contain bitmap processed by incoming preProcessor.
    * Image will be pre-processed even if caching in memory is disabled. */ public Builder preProcessor(BitmapProcessor preProcessor) { this.preProcessor = preProcessor; return this; } /** * Sets bitmap processor which will be process bitmaps before they will be displayed in * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware image aware view} but * after they'll have been saved in memory cache. */ public Builder postProcessor(BitmapProcessor postProcessor) { this.postProcessor = postProcessor; return this; } /** * Sets custom {@link BitmapDisplayer displayer} for image loading task. Default value - * {@link DefaultConfigurationFactory#createBitmapDisplayer()} */ public Builder displayer(BitmapDisplayer displayer) { if (displayer == null) throw new IllegalArgumentException("displayer can't be null"); this.displayer = displayer; return this; } Builder syncLoading(boolean isSyncLoading) { this.isSyncLoading = isSyncLoading; return this; } /** * Sets custom {@linkplain Handler handler} for displaying images and firing {@linkplain ImageLoadingListener * listener} events. */ public Builder handler(Handler handler) { this.handler = handler; return this; } /** Sets all options equal to incoming options */ public Builder cloneFrom(DisplayImageOptions options) { imageResOnLoading = options.imageResOnLoading; imageResForEmptyUri = options.imageResForEmptyUri; imageResOnFail = options.imageResOnFail; imageOnLoading = options.imageOnLoading; imageForEmptyUri = options.imageForEmptyUri; imageOnFail = options.imageOnFail; resetViewBeforeLoading = options.resetViewBeforeLoading; cacheInMemory = options.cacheInMemory; cacheOnDisk = options.cacheOnDisk; imageScaleType = options.imageScaleType; decodingOptions = options.decodingOptions; delayBeforeLoading = options.delayBeforeLoading; considerExifParams = options.considerExifParams; extraForDownloader = options.extraForDownloader; preProcessor = options.preProcessor; postProcessor = options.postProcessor; displayer = options.displayer; handler = options.handler; isSyncLoading = options.isSyncLoading; return this; } /** Builds configured {@link DisplayImageOptions} object */ public DisplayImageOptions build() { return new DisplayImageOptions(this); } } /** * Creates options appropriate for single displaying: *
      *
    • View will not be reset before loading
    • *
    • Loaded image will not be cached in memory
    • *
    • Loaded image will not be cached on disk
    • *
    • {@link ImageScaleType#IN_SAMPLE_POWER_OF_2} decoding type will be used
    • *
    • {@link Bitmap.Config#ARGB_8888} bitmap config will be used for image decoding
    • *
    • {@link SimpleBitmapDisplayer} will be used for image displaying
    • *
    *

    * These option are appropriate for simple single-use image (from drawables or from Internet) displaying. */ public static DisplayImageOptions createSimple() { return new Builder().build(); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/ImageLoader.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core; import android.graphics.Bitmap; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; import android.view.View; import android.widget.ImageView; import com.nostra13.universalimageloader.cache.disc.DiskCache; import com.nostra13.universalimageloader.cache.memory.MemoryCache; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.FlushedInputStream; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.assist.ViewScaleType; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import com.nostra13.universalimageloader.core.imageaware.ImageViewAware; import com.nostra13.universalimageloader.core.imageaware.NonViewAware; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import com.nostra13.universalimageloader.utils.ImageSizeUtils; import com.nostra13.universalimageloader.utils.L; import com.nostra13.universalimageloader.utils.MemoryCacheUtils; /** * Singletone for image loading and displaying at {@link ImageView ImageViews}
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before any other method. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public class ImageLoader { public static final String TAG = ImageLoader.class.getSimpleName(); static final String LOG_INIT_CONFIG = "Initialize ImageLoader with configuration"; static final String LOG_DESTROY = "Destroy ImageLoader"; static final String LOG_LOAD_IMAGE_FROM_MEMORY_CACHE = "Load image from memory cache [%s]"; private static final String WARNING_RE_INIT_CONFIG = "Try to initialize ImageLoader which had already been initialized before. " + "To re-init ImageLoader with new configuration call ImageLoader.destroy() at first."; private static final String ERROR_WRONG_ARGUMENTS = "Wrong arguments were passed to displayImage() method (ImageView reference must not be null)"; private static final String ERROR_NOT_INIT = "ImageLoader must be init with configuration before using"; private static final String ERROR_INIT_CONFIG_WITH_NULL = "ImageLoader configuration can not be initialized with null"; private ImageLoaderConfiguration configuration; private ImageLoaderEngine engine; private ImageLoadingListener defaultListener = new SimpleImageLoadingListener(); private volatile static ImageLoader instance; /** Returns singleton class instance */ public static ImageLoader getInstance() { if (instance == null) { synchronized (ImageLoader.class) { if (instance == null) { instance = new ImageLoader(); } } } return instance; } protected ImageLoader() { } /** * Initializes ImageLoader instance with configuration.
    * If configurations was set before ( {@link #isInited()} == true) then this method does nothing.
    * To force initialization with new configuration you should {@linkplain #destroy() destroy ImageLoader} at first. * * @param configuration {@linkplain ImageLoaderConfiguration ImageLoader configuration} * @throws IllegalArgumentException if configuration parameter is null */ public synchronized void init(ImageLoaderConfiguration configuration) { if (configuration == null) { throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL); } if (this.configuration == null) { L.d(LOG_INIT_CONFIG); engine = new ImageLoaderEngine(configuration); this.configuration = configuration; } else { L.w(WARNING_RE_INIT_CONFIG); } } /** * Returns true - if ImageLoader {@linkplain #init(ImageLoaderConfiguration) is initialized with * configuration}; false - otherwise */ public boolean isInited() { return configuration != null; } /** * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
    * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration * configuration} will be used.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} * which should display image * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageAware is null */ public void displayImage(String uri, ImageAware imageAware) { displayImage(uri, imageAware, null, null, null); } /** * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
    * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration * configuration} will be used.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} * which should display image * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on * UI thread if this method is called on UI thread. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageAware is null */ public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener) { displayImage(uri, imageAware, null, listener, null); } /** * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} * which should display image * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageAware is null */ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options) { displayImage(uri, imageAware, options, null, null); } /** * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} * which should display image * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used. * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on * UI thread if this method is called on UI thread. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageAware is null */ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener) { displayImage(uri, imageAware, options, listener, null); } /** * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} * which should display image * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used. * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires * events on UI thread if this method is called on UI thread. * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener * Listener} for image loading progress. Listener fires events on UI thread if this method * is called on UI thread. Caching on disk should be enabled in * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make * this listener work. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageAware is null */ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { displayImage(uri, imageAware, options, null, listener, progressListener); } /** * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} * which should display image * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used. * @param targetSize {@linkplain ImageSize} Image target size. If null - size will depend on the view * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires * events on UI thread if this method is called on UI thread. * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener * Listener} for image loading progress. Listener fires events on UI thread if this method * is called on UI thread. Caching on disk should be enabled in * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make * this listener work. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageAware is null */ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { checkConfiguration(); if (imageAware == null) { throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); } if (listener == null) { listener = defaultListener; } if (options == null) { options = configuration.defaultDisplayImageOptions; } if (TextUtils.isEmpty(uri)) { engine.cancelDisplayTaskFor(imageAware); listener.onLoadingStarted(uri, imageAware.getWrappedView()); if (options.shouldShowImageForEmptyUri()) { imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources)); } else { imageAware.setImageDrawable(null); } listener.onLoadingComplete(uri, imageAware.getWrappedView(), null); return; } if (targetSize == null) { targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize()); } String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize); engine.prepareDisplayTaskFor(imageAware, memoryCacheKey); listener.onLoadingStarted(uri, imageAware.getWrappedView()); Bitmap bmp = configuration.memoryCache.get(memoryCacheKey); if (bmp != null && !bmp.isRecycled()) { L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey); if (options.shouldPostProcess()) { ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, engine.getLockForUri(uri)); ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo, defineHandler(options)); if (options.isSyncLoading()) { displayTask.run(); } else { engine.submit(displayTask); } } else { options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE); listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp); } } else { if (options.shouldShowImageOnLoading()) { imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources)); } else if (options.isResetViewBeforeLoading()) { imageAware.setImageDrawable(null); } ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, engine.getLockForUri(uri)); LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo, defineHandler(options)); if (options.isSyncLoading()) { displayTask.run(); } else { engine.submit(displayTask); } } } /** * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
    * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration * configuration} will be used.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageView {@link ImageView} which should display image * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageView is null */ public void displayImage(String uri, ImageView imageView) { displayImage(uri, new ImageViewAware(imageView), null, null, null); } /** * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
    * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration * configuration} will be used.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageView {@link ImageView} which should display image * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageView is null */ public void displayImage(String uri, ImageView imageView, ImageSize targetImageSize) { displayImage(uri, new ImageViewAware(imageView), null, targetImageSize, null, null); } /** * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageView {@link ImageView} which should display image * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageView is null */ public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) { displayImage(uri, new ImageViewAware(imageView), options, null, null); } /** * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
    * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration * configuration} will be used.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageView {@link ImageView} which should display image * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on * UI thread if this method is called on UI thread. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageView is null */ public void displayImage(String uri, ImageView imageView, ImageLoadingListener listener) { displayImage(uri, new ImageViewAware(imageView), null, listener, null); } /** * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageView {@link ImageView} which should display image * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used. * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on * UI thread if this method is called on UI thread. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageView is null */ public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) { displayImage(uri, imageView, options, listener, null); } /** * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param imageView {@link ImageView} which should display image * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used. * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires * events on UI thread if this method is called on UI thread. * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener * Listener} for image loading progress. Listener fires events on UI thread if this method * is called on UI thread. Caching on disk should be enabled in * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make * this listener work. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageView is null */ public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { displayImage(uri, new ImageViewAware(imageView), options, listener, progressListener); } /** * Adds load image task to execution pool. Image will be returned with * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}. *
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI * thread if this method is called on UI thread. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public void loadImage(String uri, ImageLoadingListener listener) { loadImage(uri, null, null, listener, null); } /** * Adds load image task to execution pool. Image will be returned with * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}. *
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in * {@linkplain ImageLoadingListener#onLoadingComplete(String, android.view.View, * android.graphics.Bitmap)} callback}. Downloaded image will be decoded * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit * larger) than incoming targetImageSize. * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires * events on UI thread if this method is called on UI thread. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public void loadImage(String uri, ImageSize targetImageSize, ImageLoadingListener listener) { loadImage(uri, targetImageSize, null, listener, null); } /** * Adds load image task to execution pool. Image will be returned with * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}. *
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from * configuration} will be used.
    * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI * thread if this method is called on UI thread. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public void loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener) { loadImage(uri, null, options, listener, null); } /** * Adds load image task to execution pool. Image will be returned with * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}. *
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in * {@linkplain ImageLoadingListener#onLoadingComplete(String, android.view.View, * android.graphics.Bitmap)} callback}. Downloaded image will be decoded * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit * larger) than incoming targetImageSize. * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used.
    * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires * events on UI thread if this method is called on UI thread. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options, ImageLoadingListener listener) { loadImage(uri, targetImageSize, options, listener, null); } /** * Adds load image task to execution pool. Image will be returned with * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}. *
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in * {@linkplain ImageLoadingListener#onLoadingComplete(String, android.view.View, * android.graphics.Bitmap)} callback}. Downloaded image will be decoded * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit * larger) than incoming targetImageSize. * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and displaying. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used.
    * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires * events on UI thread if this method is called on UI thread. * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener * Listener} for image loading progress. Listener fires events on UI thread if this method * is called on UI thread. Caching on disk should be enabled in * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make * this listener work. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { checkConfiguration(); if (targetImageSize == null) { targetImageSize = configuration.getMaxImageSize(); } if (options == null) { options = configuration.defaultDisplayImageOptions; } NonViewAware imageAware = new NonViewAware(uri, targetImageSize, ViewScaleType.CROP); displayImage(uri, imageAware, options, listener, progressListener); } /** * Loads and decodes image synchronously.
    * Default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from * configuration} will be used.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public Bitmap loadImageSync(String uri) { return loadImageSync(uri, null, null); } /** * Loads and decodes image synchronously.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and scaling. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from * configuration} will be used. * @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public Bitmap loadImageSync(String uri, DisplayImageOptions options) { return loadImageSync(uri, null, options); } /** * Loads and decodes image synchronously.
    * Default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from * configuration} will be used.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit * larger) than incoming targetImageSize. * @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public Bitmap loadImageSync(String uri, ImageSize targetImageSize) { return loadImageSync(uri, targetImageSize, null); } /** * Loads and decodes image synchronously.
    * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call * * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png") * @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit * larger) than incoming targetImageSize. * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image * decoding and scaling. If null - default display image options * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) * from configuration} will be used. * @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled. * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public Bitmap loadImageSync(String uri, ImageSize targetImageSize, DisplayImageOptions options) { if (options == null) { options = configuration.defaultDisplayImageOptions; } options = new DisplayImageOptions.Builder().cloneFrom(options).syncLoading(true).build(); SyncImageLoadingListener listener = new SyncImageLoadingListener(); loadImage(uri, targetImageSize, options, listener); return listener.getLoadedBitmap(); } /** * Checks if ImageLoader's configuration was initialized * * @throws IllegalStateException if configuration wasn't initialized */ private void checkConfiguration() { if (configuration == null) { throw new IllegalStateException(ERROR_NOT_INIT); } } /** Sets a default loading listener for all display and loading tasks. */ public void setDefaultLoadingListener(ImageLoadingListener listener) { defaultListener = listener == null ? new SimpleImageLoadingListener() : listener; } /** * Returns memory cache * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public MemoryCache getMemoryCache() { checkConfiguration(); return configuration.memoryCache; } /** * Clears memory cache * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public void clearMemoryCache() { checkConfiguration(); configuration.memoryCache.clear(); } /** * Returns disk cache * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @deprecated Use {@link #getDiskCache()} instead */ @Deprecated public DiskCache getDiscCache() { return getDiskCache(); } /** * Returns disk cache * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public DiskCache getDiskCache() { checkConfiguration(); return configuration.diskCache; } /** * Clears disk cache. * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @deprecated Use {@link #clearDiskCache()} instead */ @Deprecated public void clearDiscCache() { clearDiskCache(); } /** * Clears disk cache. * * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before */ public void clearDiskCache() { checkConfiguration(); configuration.diskCache.clear(); } /** * Returns URI of image which is loading at this moment into passed * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware} */ public String getLoadingUriForView(ImageAware imageAware) { return engine.getLoadingUriForView(imageAware); } /** * Returns URI of image which is loading at this moment into passed * {@link android.widget.ImageView ImageView} */ public String getLoadingUriForView(ImageView imageView) { return engine.getLoadingUriForView(new ImageViewAware(imageView)); } /** * Cancel the task of loading and displaying image for passed * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware}. * * @param imageAware {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware} for * which display task will be cancelled */ public void cancelDisplayTask(ImageAware imageAware) { engine.cancelDisplayTaskFor(imageAware); } /** * Cancel the task of loading and displaying image for passed * {@link android.widget.ImageView ImageView}. * * @param imageView {@link android.widget.ImageView ImageView} for which display task will be cancelled */ public void cancelDisplayTask(ImageView imageView) { engine.cancelDisplayTaskFor(new ImageViewAware(imageView)); } /** * Denies or allows ImageLoader to download images from the network.
    *
    * If downloads are denied and if image isn't cached then * {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired with * {@link FailReason.FailType#NETWORK_DENIED} * * @param denyNetworkDownloads pass true - to deny engine to download images from the network; false - * to allow engine to download images from network. */ public void denyNetworkDownloads(boolean denyNetworkDownloads) { engine.denyNetworkDownloads(denyNetworkDownloads); } /** * Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle this known problem or not. * * @param handleSlowNetwork pass true - to use {@link FlushedInputStream} for network downloads; false * - otherwise. */ public void handleSlowNetwork(boolean handleSlowNetwork) { engine.handleSlowNetwork(handleSlowNetwork); } /** * Pause ImageLoader. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}. *
    * Already running tasks are not paused. */ public void pause() { if (isInited()) { engine.pause(); } else { L.w("Trying to pause not-initialized ImageLoader"); } } /** Resumes waiting "load&display" tasks */ public void resume() { if (isInited()) { engine.resume(); } else { L.w("Trying to resume not-initialized ImageLoader"); } } /** * Cancels all running and scheduled display image tasks.
    * NOTE: This method doesn't shutdown * {@linkplain com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor) * custom task executors} if you set them.
    * ImageLoader still can be used after calling this method. */ public void stop() { if (isInited()) { engine.stop(); } else { L.w("Trying to stop not-initialized ImageLoader"); } } /** * {@linkplain #stop() Stops ImageLoader} and clears current configuration.
    * You can {@linkplain #init(ImageLoaderConfiguration) init} ImageLoader with new configuration after calling this * method. */ public void destroy() { 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) { Handler handler = options.getHandler(); if (options.isSyncLoading()) { handler = null; } else if (handler == null && Looper.myLooper() == Looper.getMainLooper()) { handler = new Handler(); } return handler; } /** * Listener which is designed for synchronous image loading. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.0 */ private static class SyncImageLoadingListener extends SimpleImageLoadingListener { private Bitmap loadedImage; @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { this.loadedImage = loadedImage; } public Bitmap getLoadedBitmap() { return loadedImage; } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core; import android.content.Context; import android.content.res.Resources; import android.util.DisplayMetrics; import com.nostra13.universalimageloader.cache.disc.DiskCache; import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator; import com.nostra13.universalimageloader.cache.memory.MemoryCache; import com.nostra13.universalimageloader.cache.memory.impl.FuzzyKeyMemoryCache; import com.nostra13.universalimageloader.core.assist.FlushedInputStream; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import com.nostra13.universalimageloader.core.decode.ImageDecoder; import com.nostra13.universalimageloader.core.download.ImageDownloader; import com.nostra13.universalimageloader.core.process.BitmapProcessor; import com.nostra13.universalimageloader.utils.L; import com.nostra13.universalimageloader.utils.MemoryCacheUtils; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.Executor; /** * Presents configuration for {@link ImageLoader} * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see ImageLoader * @see MemoryCache * @see DiskCache * @see DisplayImageOptions * @see ImageDownloader * @see FileNameGenerator * @since 1.0.0 */ public final class ImageLoaderConfiguration { final Resources resources; final int maxImageWidthForMemoryCache; final int maxImageHeightForMemoryCache; final int maxImageWidthForDiskCache; final int maxImageHeightForDiskCache; final BitmapProcessor processorForDiskCache; final Executor taskExecutor; final Executor taskExecutorForCachedImages; final boolean customExecutor; final boolean customExecutorForCachedImages; final int threadPoolSize; final int threadPriority; final QueueProcessingType tasksProcessingType; final MemoryCache memoryCache; final DiskCache diskCache; final ImageDownloader downloader; final ImageDecoder decoder; final DisplayImageOptions defaultDisplayImageOptions; final ImageDownloader networkDeniedDownloader; final ImageDownloader slowNetworkDownloader; private ImageLoaderConfiguration(final Builder builder) { resources = builder.context.getResources(); maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache; maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache; maxImageWidthForDiskCache = builder.maxImageWidthForDiskCache; maxImageHeightForDiskCache = builder.maxImageHeightForDiskCache; processorForDiskCache = builder.processorForDiskCache; taskExecutor = builder.taskExecutor; taskExecutorForCachedImages = builder.taskExecutorForCachedImages; threadPoolSize = builder.threadPoolSize; threadPriority = builder.threadPriority; tasksProcessingType = builder.tasksProcessingType; diskCache = builder.diskCache; memoryCache = builder.memoryCache; defaultDisplayImageOptions = builder.defaultDisplayImageOptions; downloader = builder.downloader; decoder = builder.decoder; customExecutor = builder.customExecutor; customExecutorForCachedImages = builder.customExecutorForCachedImages; networkDeniedDownloader = new NetworkDeniedImageDownloader(downloader); slowNetworkDownloader = new SlowNetworkImageDownloader(downloader); L.writeDebugLogs(builder.writeLogs); } /** * Creates default configuration for {@link ImageLoader}
    * Default values: *

      *
    • maxImageWidthForMemoryCache = device's screen width
    • *
    • maxImageHeightForMemoryCache = device's screen height
    • *
    • maxImageWidthForDikcCache = unlimited
    • *
    • maxImageHeightForDiskCache = unlimited
    • *
    • threadPoolSize = {@link Builder#DEFAULT_THREAD_POOL_SIZE this}
    • *
    • threadPriority = {@link Builder#DEFAULT_THREAD_PRIORITY this}
    • *
    • allow to cache different sizes of image in memory
    • *
    • memoryCache = {@link DefaultConfigurationFactory#createMemoryCache(android.content.Context, int)}
    • *
    • diskCache = {@link com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache}
    • *
    • imageDownloader = {@link DefaultConfigurationFactory#createImageDownloader(Context)}
    • *
    • imageDecoder = {@link DefaultConfigurationFactory#createImageDecoder(boolean)}
    • *
    • diskCacheFileNameGenerator = {@link DefaultConfigurationFactory#createFileNameGenerator()}
    • *
    • defaultDisplayImageOptions = {@link DisplayImageOptions#createSimple() Simple options}
    • *
    • tasksProcessingOrder = {@link QueueProcessingType#FIFO}
    • *
    • detailed logging disabled
    • *
    */ public static ImageLoaderConfiguration createDefault(Context context) { return new Builder(context).build(); } ImageSize getMaxImageSize() { DisplayMetrics displayMetrics = resources.getDisplayMetrics(); int width = maxImageWidthForMemoryCache; if (width <= 0) { width = displayMetrics.widthPixels; } int height = maxImageHeightForMemoryCache; if (height <= 0) { height = displayMetrics.heightPixels; } return new ImageSize(width, height); } /** * Builder for {@link ImageLoaderConfiguration} * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public static class Builder { private static final String WARNING_OVERLAP_DISK_CACHE_PARAMS = "diskCache(), diskCacheSize() and diskCacheFileCount calls overlap each other"; private static final String WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR = "diskCache() and diskCacheFileNameGenerator() calls overlap each other"; private static final String WARNING_OVERLAP_MEMORY_CACHE = "memoryCache() and memoryCacheSize() calls overlap each other"; private static final String WARNING_OVERLAP_EXECUTOR = "threadPoolSize(), threadPriority() and tasksProcessingOrder() calls " + "can overlap taskExecutor() and taskExecutorForCachedImages() calls."; /** {@value} */ public static final int DEFAULT_THREAD_POOL_SIZE = 3; /** {@value} */ public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 2; /** {@value} */ public static final QueueProcessingType DEFAULT_TASK_PROCESSING_TYPE = QueueProcessingType.FIFO; private Context context; private int maxImageWidthForMemoryCache = 0; private int maxImageHeightForMemoryCache = 0; private int maxImageWidthForDiskCache = 0; private int maxImageHeightForDiskCache = 0; private BitmapProcessor processorForDiskCache = null; private Executor taskExecutor = null; private Executor taskExecutorForCachedImages = null; private boolean customExecutor = false; private boolean customExecutorForCachedImages = false; private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE; private int threadPriority = DEFAULT_THREAD_PRIORITY; private boolean denyCacheImageMultipleSizesInMemory = false; private QueueProcessingType tasksProcessingType = DEFAULT_TASK_PROCESSING_TYPE; private int memoryCacheSize = 0; private long diskCacheSize = 0; private int diskCacheFileCount = 0; private MemoryCache memoryCache = null; private DiskCache diskCache = null; private FileNameGenerator diskCacheFileNameGenerator = null; private ImageDownloader downloader = null; private ImageDecoder decoder; private DisplayImageOptions defaultDisplayImageOptions = null; private boolean writeLogs = false; public Builder(Context context) { this.context = context.getApplicationContext(); } /** * Sets options for memory cache * * @param maxImageWidthForMemoryCache Maximum image width which will be used for memory saving during decoding * an image to {@link android.graphics.Bitmap Bitmap}. Default value - device's screen width * @param maxImageHeightForMemoryCache Maximum image height which will be used for memory saving during decoding * an image to {@link android.graphics.Bitmap Bitmap}. Default value - device's screen height */ public Builder memoryCacheExtraOptions(int maxImageWidthForMemoryCache, int maxImageHeightForMemoryCache) { this.maxImageWidthForMemoryCache = maxImageWidthForMemoryCache; this.maxImageHeightForMemoryCache = maxImageHeightForMemoryCache; return this; } /** * @deprecated Use * {@link #diskCacheExtraOptions(int, int, com.nostra13.universalimageloader.core.process.BitmapProcessor)} * instead */ @Deprecated public Builder discCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache, BitmapProcessor processorForDiskCache) { return diskCacheExtraOptions(maxImageWidthForDiskCache, maxImageHeightForDiskCache, processorForDiskCache); } /** * Sets options for resizing/compressing of downloaded images before saving to disk cache.
    * NOTE: Use this option only when you have appropriate needs. It can make ImageLoader slower. * * @param maxImageWidthForDiskCache Maximum width of downloaded images for saving at disk cache * @param maxImageHeightForDiskCache Maximum height of downloaded images for saving at disk cache * @param processorForDiskCache null-ok; {@linkplain BitmapProcessor Bitmap processor} which process images before saving them in disc cache */ public Builder diskCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache, BitmapProcessor processorForDiskCache) { this.maxImageWidthForDiskCache = maxImageWidthForDiskCache; this.maxImageHeightForDiskCache = maxImageHeightForDiskCache; this.processorForDiskCache = processorForDiskCache; return this; } /** * Sets custom {@linkplain Executor executor} for tasks of loading and displaying images.
    *
    * NOTE: If you set custom executor then following configuration options will not be considered for this * executor: *
      *
    • {@link #threadPoolSize(int)}
    • *
    • {@link #threadPriority(int)}
    • *
    • {@link #tasksProcessingOrder(QueueProcessingType)}
    • *
    * * @see #taskExecutorForCachedImages(Executor) */ public Builder taskExecutor(Executor executor) { if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) { L.w(WARNING_OVERLAP_EXECUTOR); } this.taskExecutor = executor; return this; } /** * Sets custom {@linkplain Executor executor} for tasks of displaying cached on disk images (these tasks * are executed quickly so UIL prefer to use separate executor for them).
    *
    * If you set the same executor for {@linkplain #taskExecutor(Executor) general tasks} and * tasks about cached images (this method) then these tasks will be in the * same thread pool. So short-lived tasks can wait a long time for their turn.
    *
    * NOTE: If you set custom executor then following configuration options will not be considered for this * executor: *
      *
    • {@link #threadPoolSize(int)}
    • *
    • {@link #threadPriority(int)}
    • *
    • {@link #tasksProcessingOrder(QueueProcessingType)}
    • *
    * * @see #taskExecutor(Executor) */ public Builder taskExecutorForCachedImages(Executor executorForCachedImages) { if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) { L.w(WARNING_OVERLAP_EXECUTOR); } this.taskExecutorForCachedImages = executorForCachedImages; return this; } /** * Sets thread pool size for image display tasks.
    * Default value - {@link #DEFAULT_THREAD_POOL_SIZE this} */ public Builder threadPoolSize(int threadPoolSize) { if (taskExecutor != null || taskExecutorForCachedImages != null) { L.w(WARNING_OVERLAP_EXECUTOR); } this.threadPoolSize = threadPoolSize; return this; } /** * Sets the priority for image loading threads. Should be NOT greater than {@link Thread#MAX_PRIORITY} or * less than {@link Thread#MIN_PRIORITY}
    * Default value - {@link #DEFAULT_THREAD_PRIORITY this} */ public Builder threadPriority(int threadPriority) { if (taskExecutor != null || taskExecutorForCachedImages != null) { L.w(WARNING_OVERLAP_EXECUTOR); } if (threadPriority < Thread.MIN_PRIORITY) { this.threadPriority = Thread.MIN_PRIORITY; } else { if (threadPriority > Thread.MAX_PRIORITY) { this.threadPriority = Thread.MAX_PRIORITY; } else { this.threadPriority = threadPriority; } } return this; } /** * When you display an image in a small {@link android.widget.ImageView ImageView} and later you try to display * this image (from identical URI) in a larger {@link android.widget.ImageView ImageView} so decoded image of * bigger size will be cached in memory as a previous decoded image of smaller size.
    * So the default behavior is to allow to cache multiple sizes of one image in memory. You can * deny it by calling this method: so when some image will be cached in memory then previous * cached size of this image (if it exists) will be removed from memory cache before. */ public Builder denyCacheImageMultipleSizesInMemory() { this.denyCacheImageMultipleSizesInMemory = true; return this; } /** * Sets type of queue processing for tasks for loading and displaying images.
    * Default value - {@link QueueProcessingType#FIFO} */ public Builder tasksProcessingOrder(QueueProcessingType tasksProcessingType) { if (taskExecutor != null || taskExecutorForCachedImages != null) { L.w(WARNING_OVERLAP_EXECUTOR); } this.tasksProcessingType = tasksProcessingType; return this; } /** * Sets maximum memory cache size for {@link android.graphics.Bitmap bitmaps} (in bytes).
    * Default value - 1/8 of available app memory.
    * NOTE: If you use this method then * {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache} will be used as * memory cache. You can use {@link #memoryCache(MemoryCache)} method to set your own implementation of * {@link MemoryCache}. */ public Builder memoryCacheSize(int memoryCacheSize) { if (memoryCacheSize <= 0) throw new IllegalArgumentException("memoryCacheSize must be a positive number"); if (memoryCache != null) { L.w(WARNING_OVERLAP_MEMORY_CACHE); } this.memoryCacheSize = memoryCacheSize; return this; } /** * Sets maximum memory cache size (in percent of available app memory) for {@link android.graphics.Bitmap * bitmaps}.
    * Default value - 1/8 of available app memory.
    * NOTE: If you use this method then * {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache} will be used as * memory cache. You can use {@link #memoryCache(MemoryCache)} method to set your own implementation of * {@link MemoryCache}. */ public Builder memoryCacheSizePercentage(int availableMemoryPercent) { if (availableMemoryPercent <= 0 || availableMemoryPercent >= 100) { throw new IllegalArgumentException("availableMemoryPercent must be in range (0 < % < 100)"); } if (memoryCache != null) { L.w(WARNING_OVERLAP_MEMORY_CACHE); } long availableMemory = Runtime.getRuntime().maxMemory(); memoryCacheSize = (int) (availableMemory * (availableMemoryPercent / 100f)); return this; } /** * Sets memory cache for {@link android.graphics.Bitmap bitmaps}.
    * Default value - {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache} * with limited memory cache size (size = 1/8 of available app memory)
    *
    * NOTE: If you set custom memory cache then following configuration option will not be considered: *
      *
    • {@link #memoryCacheSize(int)}
    • *
    */ public Builder memoryCache(MemoryCache memoryCache) { if (memoryCacheSize != 0) { L.w(WARNING_OVERLAP_MEMORY_CACHE); } this.memoryCache = memoryCache; return this; } /** @deprecated Use {@link #diskCacheSize(int)} instead */ @Deprecated public Builder discCacheSize(int maxCacheSize) { return diskCacheSize(maxCacheSize); } /** * Sets maximum disk cache size for images (in bytes).
    * By default: disk cache is unlimited.
    * NOTE: If you use this method then * {@link com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache LruDiskCache} * will be used as disk cache. You can use {@link #diskCache(DiskCache)} method for introduction your own * implementation of {@link DiskCache} */ public Builder diskCacheSize(int maxCacheSize) { if (maxCacheSize <= 0) throw new IllegalArgumentException("maxCacheSize must be a positive number"); if (diskCache != null) { L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS); } this.diskCacheSize = maxCacheSize; return this; } /** @deprecated Use {@link #diskCacheFileCount(int)} instead */ @Deprecated public Builder discCacheFileCount(int maxFileCount) { return diskCacheFileCount(maxFileCount); } /** * Sets maximum file count in disk cache directory.
    * By default: disk cache is unlimited.
    * NOTE: If you use this method then * {@link com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache LruDiskCache} * will be used as disk cache. You can use {@link #diskCache(DiskCache)} method for introduction your own * implementation of {@link DiskCache} */ public Builder diskCacheFileCount(int maxFileCount) { if (maxFileCount <= 0) throw new IllegalArgumentException("maxFileCount must be a positive number"); if (diskCache != null) { L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS); } this.diskCacheFileCount = maxFileCount; return this; } /** @deprecated Use {@link #diskCacheFileNameGenerator(com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator)} */ @Deprecated public Builder discCacheFileNameGenerator(FileNameGenerator fileNameGenerator) { return diskCacheFileNameGenerator(fileNameGenerator); } /** * Sets name generator for files cached in disk cache.
    * Default value - * {@link com.nostra13.universalimageloader.core.DefaultConfigurationFactory#createFileNameGenerator() * DefaultConfigurationFactory.createFileNameGenerator()} */ public Builder diskCacheFileNameGenerator(FileNameGenerator fileNameGenerator) { if (diskCache != null) { L.w(WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR); } this.diskCacheFileNameGenerator = fileNameGenerator; return this; } /** @deprecated Use {@link #diskCache(com.nostra13.universalimageloader.cache.disc.DiskCache)} */ @Deprecated public Builder discCache(DiskCache diskCache) { return diskCache(diskCache); } /** * Sets disk cache for images.
    * Default value - {@link com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache * UnlimitedDiskCache}. Cache directory is defined by * {@link com.nostra13.universalimageloader.utils.StorageUtils#getCacheDirectory(Context) * StorageUtils.getCacheDirectory(Context)}.
    *
    * NOTE: If you set custom disk cache then following configuration option will not be considered: *
      *
    • {@link #diskCacheSize(int)}
    • *
    • {@link #diskCacheFileCount(int)}
    • *
    • {@link #diskCacheFileNameGenerator(FileNameGenerator)}
    • *
    */ public Builder diskCache(DiskCache diskCache) { if (diskCacheSize > 0 || diskCacheFileCount > 0) { L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS); } if (diskCacheFileNameGenerator != null) { L.w(WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR); } this.diskCache = diskCache; return this; } /** * Sets utility which will be responsible for downloading of image.
    * Default value - * {@link com.nostra13.universalimageloader.core.DefaultConfigurationFactory#createImageDownloader(Context) * DefaultConfigurationFactory.createImageDownloader()} */ public Builder imageDownloader(ImageDownloader imageDownloader) { this.downloader = imageDownloader; return this; } /** * Sets utility which will be responsible for decoding of image stream.
    * Default value - * {@link com.nostra13.universalimageloader.core.DefaultConfigurationFactory#createImageDecoder(boolean) * DefaultConfigurationFactory.createImageDecoder()} */ public Builder imageDecoder(ImageDecoder imageDecoder) { this.decoder = imageDecoder; return this; } /** * Sets default {@linkplain DisplayImageOptions display image options} for image displaying. These options will * be used for every {@linkplain ImageLoader#displayImage(String, android.widget.ImageView) image display call} * without passing custom {@linkplain DisplayImageOptions options}
    * Default value - {@link DisplayImageOptions#createSimple() Simple options} */ public Builder defaultDisplayImageOptions(DisplayImageOptions defaultDisplayImageOptions) { this.defaultDisplayImageOptions = defaultDisplayImageOptions; return this; } /** * Enables detail logging of {@link ImageLoader} work. To prevent detail logs don't call this method. * Consider {@link com.nostra13.universalimageloader.utils.L#disableLogging()} to disable * ImageLoader logging completely (even error logs) */ public Builder writeDebugLogs() { this.writeLogs = true; return this; } /** Builds configured {@link ImageLoaderConfiguration} object */ public ImageLoaderConfiguration build() { initEmptyFieldsWithDefaultValues(); return new ImageLoaderConfiguration(this); } private void initEmptyFieldsWithDefaultValues() { if (taskExecutor == null) { taskExecutor = DefaultConfigurationFactory .createExecutor(threadPoolSize, threadPriority, tasksProcessingType); } else { customExecutor = true; } if (taskExecutorForCachedImages == null) { taskExecutorForCachedImages = DefaultConfigurationFactory .createExecutor(threadPoolSize, threadPriority, tasksProcessingType); } else { customExecutorForCachedImages = true; } if (diskCache == null) { if (diskCacheFileNameGenerator == null) { diskCacheFileNameGenerator = DefaultConfigurationFactory.createFileNameGenerator(); } diskCache = DefaultConfigurationFactory .createDiskCache(context, diskCacheFileNameGenerator, diskCacheSize, diskCacheFileCount); } if (memoryCache == null) { memoryCache = DefaultConfigurationFactory.createMemoryCache(context, memoryCacheSize); } if (denyCacheImageMultipleSizesInMemory) { memoryCache = new FuzzyKeyMemoryCache(memoryCache, MemoryCacheUtils.createFuzzyKeyComparator()); } if (downloader == null) { downloader = DefaultConfigurationFactory.createImageDownloader(context); } if (decoder == null) { decoder = DefaultConfigurationFactory.createImageDecoder(writeLogs); } if (defaultDisplayImageOptions == null) { defaultDisplayImageOptions = DisplayImageOptions.createSimple(); } } } /** * Decorator. Prevents downloads from network (throws {@link IllegalStateException exception}).
    * In most cases this downloader shouldn't be used directly. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.0 */ private static class NetworkDeniedImageDownloader implements ImageDownloader { private final ImageDownloader wrappedDownloader; public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) { this.wrappedDownloader = wrappedDownloader; } @Override public InputStream getStream(String imageUri, Object extra) throws IOException { switch (Scheme.ofUri(imageUri)) { case HTTP: case HTTPS: throw new IllegalStateException(); default: return wrappedDownloader.getStream(imageUri, extra); } } } /** * Decorator. Handles this problem on slow networks * using {@link com.nostra13.universalimageloader.core.assist.FlushedInputStream}. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.1 */ private static class SlowNetworkImageDownloader implements ImageDownloader { private final ImageDownloader wrappedDownloader; public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) { this.wrappedDownloader = wrappedDownloader; } @Override public InputStream getStream(String imageUri, Object extra) throws IOException { InputStream imageStream = wrappedDownloader.getStream(imageUri, extra); switch (Scheme.ofUri(imageUri)) { case HTTP: case HTTPS: return new FlushedInputStream(imageStream); default: return imageStream; } } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/ImageLoaderEngine.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core; import android.view.View; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.FlushedInputStream; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import java.io.File; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; 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. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.7.1 */ class ImageLoaderEngine { final ImageLoaderConfiguration configuration; private Executor taskExecutor; private Executor taskExecutorForCachedImages; private Executor taskDistributor; private final Map cacheKeysForImageAwares = Collections .synchronizedMap(new HashMap()); private final Map uriLocks = new WeakHashMap(); private final AtomicBoolean paused = new AtomicBoolean(false); private final AtomicBoolean networkDenied = new AtomicBoolean(false); private final AtomicBoolean slowNetwork = new AtomicBoolean(false); private final Object pauseLock = new Object(); ImageLoaderEngine(ImageLoaderConfiguration configuration) { this.configuration = configuration; taskExecutor = configuration.taskExecutor; taskExecutorForCachedImages = configuration.taskExecutorForCachedImages; taskDistributor = DefaultConfigurationFactory.createTaskDistributor(); } /** Submits task to execution pool */ void submit(final LoadAndDisplayImageTask task) { taskDistributor.execute(new Runnable() { @Override public void run() { File image = configuration.diskCache.get(task.getLoadingUri()); boolean isImageCachedOnDisk = image != null && image.exists() || isLocalUri(task.getLoadingUri()); initExecutorsIfNeed(); if (isImageCachedOnDisk) { taskExecutorForCachedImages.execute(task); } else { taskExecutor.execute(task); } } }); } /** Submits task to execution pool */ void submit(ProcessAndDisplayImageTask task) { initExecutorsIfNeed(); 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(); } if (!configuration.customExecutorForCachedImages && ((ExecutorService) taskExecutorForCachedImages) .isShutdown()) { taskExecutorForCachedImages = createTaskExecutor(); } } private Executor createTaskExecutor() { return DefaultConfigurationFactory .createExecutor(configuration.threadPoolSize, configuration.threadPriority, configuration.tasksProcessingType); } /** * Returns URI of image which is loading at this moment into passed {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} */ String getLoadingUriForView(ImageAware imageAware) { return cacheKeysForImageAwares.get(imageAware.getId()); } /** * Associates memoryCacheKey with imageAware. Then it helps to define image URI is loaded into View at * exact moment. */ void prepareDisplayTaskFor(ImageAware imageAware, String memoryCacheKey) { cacheKeysForImageAwares.put(imageAware.getId(), memoryCacheKey); } /** * Cancels the task of loading and displaying image for incoming imageAware. * * @param imageAware {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} for which display task * will be cancelled */ void cancelDisplayTaskFor(ImageAware imageAware) { cacheKeysForImageAwares.remove(imageAware.getId()); } /** * Denies or allows engine to download images from the network.

    If downloads are denied and if image * isn't cached then {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired * with {@link FailReason.FailType#NETWORK_DENIED} * * @param denyNetworkDownloads pass true - to deny engine to download images from the network; false - * to allow engine to download images from network. */ void denyNetworkDownloads(boolean denyNetworkDownloads) { networkDenied.set(denyNetworkDownloads); } /** * Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle this known problem or not. * * @param handleSlowNetwork pass true - to use {@link FlushedInputStream} for network downloads; false * - otherwise. */ void handleSlowNetwork(boolean handleSlowNetwork) { slowNetwork.set(handleSlowNetwork); } /** * Pauses engine. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.
    Already running tasks are not paused. */ void pause() { paused.set(true); } /** Resumes engine work. Paused "load&display" tasks will continue its work. */ void resume() { paused.set(false); synchronized (pauseLock) { pauseLock.notifyAll(); } } /** * Stops engine, cancels all running and scheduled display image tasks. Clears internal data. *
    * NOTE: This method doesn't shutdown * {@linkplain com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor) * custom task executors} if you set them. */ void stop() { if (!configuration.customExecutor) { ((ExecutorService) taskExecutor).shutdownNow(); } if (!configuration.customExecutorForCachedImages) { ((ExecutorService) taskExecutorForCachedImages).shutdownNow(); } cacheKeysForImageAwares.clear(); uriLocks.clear(); } void fireCallback(Runnable r) { taskDistributor.execute(r); } ReentrantLock getLockForUri(String uri) { ReentrantLock lock = uriLocks.get(uri); if (lock == null) { lock = new ReentrantLock(); uriLocks.put(uri, lock); } return lock; } AtomicBoolean getPause() { return paused; } Object getPauseLock() { return pauseLock; } boolean isNetworkDenied() { return networkDenied.get(); } boolean isSlowNetwork() { return slowNetwork.get(); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/ImageLoadingInfo.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import java.util.concurrent.locks.ReentrantLock; /** * Information for load'n'display image task * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see com.nostra13.universalimageloader.utils.MemoryCacheUtils * @see DisplayImageOptions * @see ImageLoadingListener * @see com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener * @since 1.3.1 */ final class ImageLoadingInfo { final String uri; final String memoryCacheKey; final ImageAware imageAware; final ImageSize targetSize; final DisplayImageOptions options; final ImageLoadingListener listener; final ImageLoadingProgressListener progressListener; final ReentrantLock loadFromUriLock; public ImageLoadingInfo(String uri, ImageAware imageAware, ImageSize targetSize, String memoryCacheKey, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener, ReentrantLock loadFromUriLock) { this.uri = uri; this.imageAware = imageAware; this.targetSize = targetSize; this.options = options; this.listener = listener; this.progressListener = progressListener; this.loadFromUriLock = loadFromUriLock; this.memoryCacheKey = memoryCacheKey; } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core; import android.graphics.Bitmap; import android.os.Handler; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.FailReason.FailType; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.assist.ViewScaleType; import com.nostra13.universalimageloader.core.decode.ImageDecoder; import com.nostra13.universalimageloader.core.decode.ImageDecodingInfo; import com.nostra13.universalimageloader.core.download.ImageDownloader; import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener; import com.nostra13.universalimageloader.utils.IoUtils; import com.nostra13.universalimageloader.utils.L; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; /** * Presents load'n'display image task. Used to load image from Internet or file system, decode it to {@link Bitmap}, and * display it in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} using {@link DisplayBitmapTask}. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see ImageLoaderConfiguration * @see ImageLoadingInfo * @since 1.3.1 */ final class LoadAndDisplayImageTask implements Runnable, IoUtils.CopyListener { private static final String LOG_WAITING_FOR_RESUME = "ImageLoader is paused. Waiting... [%s]"; private static final String LOG_RESUME_AFTER_PAUSE = ".. Resume loading [%s]"; private static final String LOG_DELAY_BEFORE_LOADING = "Delay %d ms before loading... [%s]"; private static final String LOG_START_DISPLAY_IMAGE_TASK = "Start display image task [%s]"; private static final String LOG_WAITING_FOR_IMAGE_LOADED = "Image already is loading. Waiting... [%s]"; private static final String LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING = "...Get cached bitmap from memory after waiting. [%s]"; private static final String LOG_LOAD_IMAGE_FROM_NETWORK = "Load image from network [%s]"; private static final String LOG_LOAD_IMAGE_FROM_DISK_CACHE = "Load image from disk cache [%s]"; private static final String LOG_RESIZE_CACHED_IMAGE_FILE = "Resize image in disk cache [%s]"; private static final String LOG_PREPROCESS_IMAGE = "PreProcess image before caching in memory [%s]"; private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]"; private static final String LOG_CACHE_IMAGE_IN_MEMORY = "Cache image in memory [%s]"; private static final String LOG_CACHE_IMAGE_ON_DISK = "Cache image on disk [%s]"; private static final String LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK = "Process image before cache on disk [%s]"; private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]"; private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]"; private static final String LOG_TASK_INTERRUPTED = "Task was interrupted [%s]"; private static final String ERROR_NO_IMAGE_STREAM = "No stream for image [%s]"; private static final String ERROR_PRE_PROCESSOR_NULL = "Pre-processor returned null [%s]"; private static final String ERROR_POST_PROCESSOR_NULL = "Post-processor returned null [%s]"; private static final String ERROR_PROCESSOR_FOR_DISK_CACHE_NULL = "Bitmap processor for disk cache returned null [%s]"; private final ImageLoaderEngine engine; private final ImageLoadingInfo imageLoadingInfo; private final Handler handler; // Helper references private final ImageLoaderConfiguration configuration; private final ImageDownloader downloader; private final ImageDownloader networkDeniedDownloader; private final ImageDownloader slowNetworkDownloader; private final ImageDecoder decoder; final String uri; private final String memoryCacheKey; final ImageAware imageAware; private final ImageSize targetSize; final DisplayImageOptions options; final ImageLoadingListener listener; final ImageLoadingProgressListener progressListener; private final boolean syncLoading; // State vars private LoadedFrom loadedFrom = LoadedFrom.NETWORK; public LoadAndDisplayImageTask(ImageLoaderEngine engine, ImageLoadingInfo imageLoadingInfo, Handler handler) { this.engine = engine; this.imageLoadingInfo = imageLoadingInfo; this.handler = handler; configuration = engine.configuration; downloader = configuration.downloader; networkDeniedDownloader = configuration.networkDeniedDownloader; slowNetworkDownloader = configuration.slowNetworkDownloader; decoder = configuration.decoder; uri = imageLoadingInfo.uri; memoryCacheKey = imageLoadingInfo.memoryCacheKey; imageAware = imageLoadingInfo.imageAware; targetSize = imageLoadingInfo.targetSize; options = imageLoadingInfo.options; listener = imageLoadingInfo.listener; progressListener = imageLoadingInfo.progressListener; syncLoading = options.isSyncLoading(); } @Override public void run() { if (waitIfPaused()) return; if (delayIfNeed()) return; ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock; L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey); if (loadFromUriLock.isLocked()) { L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey); } loadFromUriLock.lock(); Bitmap bmp; try { checkTaskNotActual(); bmp = configuration.memoryCache.get(memoryCacheKey); if (bmp == null || bmp.isRecycled()) { bmp = tryLoadBitmap(); if (bmp == null) return; // listener callback already was fired checkTaskNotActual(); checkTaskInterrupted(); if (options.shouldPreProcess()) { L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey); bmp = options.getPreProcessor().process(bmp); if (bmp == null) { L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey); } } if (bmp != null && options.isCacheInMemory()) { L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey); configuration.memoryCache.put(memoryCacheKey, bmp); } } else { loadedFrom = LoadedFrom.MEMORY_CACHE; L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey); } if (bmp != null && options.shouldPostProcess()) { L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey); bmp = options.getPostProcessor().process(bmp); if (bmp == null) { L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey); } } checkTaskNotActual(); checkTaskInterrupted(); } catch (TaskCancelledException e) { fireCancelEvent(); return; } finally { loadFromUriLock.unlock(); } DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom); runTask(displayBitmapTask, syncLoading, handler, engine); } /** @return true - if task should be interrupted; false - otherwise */ private boolean waitIfPaused() { AtomicBoolean pause = engine.getPause(); if (pause.get()) { synchronized (engine.getPauseLock()) { if (pause.get()) { L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey); try { engine.getPauseLock().wait(); } catch (InterruptedException e) { L.e(LOG_TASK_INTERRUPTED, memoryCacheKey); return true; } L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey); } } } return isTaskNotActual(); } /** @return true - if task should be interrupted; false - otherwise */ private boolean delayIfNeed() { if (options.shouldDelayBeforeLoading()) { L.d(LOG_DELAY_BEFORE_LOADING, options.getDelayBeforeLoading(), memoryCacheKey); try { Thread.sleep(options.getDelayBeforeLoading()); } catch (InterruptedException e) { L.e(LOG_TASK_INTERRUPTED, memoryCacheKey); return true; } return isTaskNotActual(); } return false; } private Bitmap tryLoadBitmap() throws TaskCancelledException { Bitmap bitmap = null; try { File imageFile = configuration.diskCache.get(uri); if (imageFile != null && imageFile.exists() && imageFile.length() > 0) { L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey); loadedFrom = LoadedFrom.DISC_CACHE; checkTaskNotActual(); bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath())); } if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) { L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey); loadedFrom = LoadedFrom.NETWORK; String imageUriForDecoding = uri; if (options.isCacheOnDisk() && tryCacheImageOnDisk()) { imageFile = configuration.diskCache.get(uri); if (imageFile != null) { imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath()); } } checkTaskNotActual(); bitmap = decodeImage(imageUriForDecoding); if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) { fireFailEvent(FailType.DECODING_ERROR, null); } } } catch (IllegalStateException e) { fireFailEvent(FailType.NETWORK_DENIED, null); } catch (TaskCancelledException e) { throw e; } catch (IOException e) { L.e(e); fireFailEvent(FailType.IO_ERROR, e); } catch (OutOfMemoryError e) { L.e(e); fireFailEvent(FailType.OUT_OF_MEMORY, e); } catch (Throwable e) { L.e(e); fireFailEvent(FailType.UNKNOWN, e); } return bitmap; } private Bitmap decodeImage(String imageUri) throws IOException { ViewScaleType viewScaleType = imageAware.getScaleType(); ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType, getDownloader(), options); return decoder.decode(decodingInfo); } /** @return true - if image was downloaded successfully; false - otherwise */ private boolean tryCacheImageOnDisk() throws TaskCancelledException { L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey); boolean loaded; try { loaded = downloadImage(); if (loaded) { int width = configuration.maxImageWidthForDiskCache; int height = configuration.maxImageHeightForDiskCache; if (width > 0 || height > 0) { L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey); resizeAndSaveImage(width, height); // TODO : process boolean result } } } catch (IOException e) { L.e(e); loaded = false; } return loaded; } private boolean downloadImage() throws IOException { InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader()); if (is == null) { L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey); return false; } else { try { return configuration.diskCache.save(uri, is, this); } finally { IoUtils.closeSilently(is); } } } /** Decodes image file into Bitmap, resize it and save it back */ private boolean resizeAndSaveImage(int maxWidth, int maxHeight) throws IOException { // Decode image file, compress and re-save it boolean saved = false; File targetFile = configuration.diskCache.get(uri); if (targetFile != null && targetFile.exists()) { ImageSize targetImageSize = new ImageSize(maxWidth, maxHeight); DisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options) .imageScaleType(ImageScaleType.IN_SAMPLE_INT).build(); ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, Scheme.FILE.wrap(targetFile.getAbsolutePath()), uri, targetImageSize, ViewScaleType.FIT_INSIDE, getDownloader(), specialOptions); Bitmap bmp = decoder.decode(decodingInfo); if (bmp != null && configuration.processorForDiskCache != null) { L.d(LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK, memoryCacheKey); bmp = configuration.processorForDiskCache.process(bmp); if (bmp == null) { L.e(ERROR_PROCESSOR_FOR_DISK_CACHE_NULL, memoryCacheKey); } } if (bmp != null) { saved = configuration.diskCache.save(uri, bmp); bmp.recycle(); } } return saved; } @Override public boolean onBytesCopied(int current, int total) { return syncLoading || fireProgressEvent(current, total); } /** @return true - if loading should be continued; false - if loading should be interrupted */ private boolean fireProgressEvent(final int current, final int total) { if (isTaskInterrupted() || isTaskNotActual()) return false; if (progressListener != null) { Runnable r = new Runnable() { @Override public void run() { progressListener.onProgressUpdate(uri, imageAware.getWrappedView(), current, total); } }; runTask(r, false, handler, engine); } return true; } private void fireFailEvent(final FailType failType, final Throwable failCause) { if (syncLoading || isTaskInterrupted() || isTaskNotActual()) return; Runnable r = new Runnable() { @Override public void run() { if (options.shouldShowImageOnFail()) { imageAware.setImageDrawable(options.getImageOnFail(configuration.resources)); } listener.onLoadingFailed(uri, imageAware.getWrappedView(), new FailReason(failType, failCause)); } }; runTask(r, false, handler, engine); } private void fireCancelEvent() { if (syncLoading || isTaskInterrupted()) return; Runnable r = new Runnable() { @Override public void run() { listener.onLoadingCancelled(uri, imageAware.getWrappedView()); } }; runTask(r, false, handler, engine); } private ImageDownloader getDownloader() { ImageDownloader d; if (engine.isNetworkDenied()) { d = networkDeniedDownloader; } else if (engine.isSlowNetwork()) { d = slowNetworkDownloader; } else { d = downloader; } return d; } /** * @throws TaskCancelledException if task is not actual (target ImageAware is collected by GC or the image URI of * this task doesn't match to image URI which is actual for current ImageAware at * this moment) */ private void checkTaskNotActual() throws TaskCancelledException { checkViewCollected(); checkViewReused(); } /** * @return true - if task is not actual (target ImageAware is collected by GC or the image URI of this task * doesn't match to image URI which is actual for current ImageAware at this moment)); false - otherwise */ private boolean isTaskNotActual() { return isViewCollected() || isViewReused(); } /** @throws TaskCancelledException if target ImageAware is collected */ private void checkViewCollected() throws TaskCancelledException { if (isViewCollected()) { throw new TaskCancelledException(); } } /** @return true - if target ImageAware is collected by GC; false - otherwise */ private boolean isViewCollected() { if (imageAware.isCollected()) { L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey); return true; } return false; } /** @throws TaskCancelledException if target ImageAware is collected by GC */ private void checkViewReused() throws TaskCancelledException { if (isViewReused()) { throw new TaskCancelledException(); } } /** @return true - if current ImageAware is reused for displaying another image; false - otherwise */ private boolean isViewReused() { String currentCacheKey = engine.getLoadingUriForView(imageAware); // Check whether memory cache key (image URI) for current ImageAware is actual. // If ImageAware is reused for another task then current task should be cancelled. boolean imageAwareWasReused = !memoryCacheKey.equals(currentCacheKey); if (imageAwareWasReused) { L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey); return true; } return false; } /** @throws TaskCancelledException if current task was interrupted */ private void checkTaskInterrupted() throws TaskCancelledException { if (isTaskInterrupted()) { throw new TaskCancelledException(); } } /** @return true - if current task was interrupted; false - otherwise */ private boolean isTaskInterrupted() { if (Thread.interrupted()) { L.d(LOG_TASK_INTERRUPTED, memoryCacheKey); return true; } return false; } String getLoadingUri() { return uri; } static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) { if (sync) { r.run(); } else if (handler == null) { engine.fireCallback(r); } else { handler.post(r); } } /** * Exceptions for case when task is cancelled (thread is interrupted, image view is reused for another task, view is * collected by GC). * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.1 */ class TaskCancelledException extends Exception { } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/ProcessAndDisplayImageTask.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core; import android.graphics.Bitmap; import android.os.Handler; import android.widget.ImageView; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.process.BitmapProcessor; import com.nostra13.universalimageloader.utils.L; /** * Presents process'n'display image task. Processes image {@linkplain Bitmap} and display it in {@link ImageView} using * {@link DisplayBitmapTask}. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.0 */ final class ProcessAndDisplayImageTask implements Runnable { private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]"; private final ImageLoaderEngine engine; private final Bitmap bitmap; private final ImageLoadingInfo imageLoadingInfo; private final Handler handler; public ProcessAndDisplayImageTask(ImageLoaderEngine engine, Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, Handler handler) { this.engine = engine; this.bitmap = bitmap; this.imageLoadingInfo = imageLoadingInfo; this.handler = handler; } @Override public void run() { L.d(LOG_POSTPROCESS_IMAGE, imageLoadingInfo.memoryCacheKey); BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor(); Bitmap processedBitmap = processor.process(bitmap); DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine, LoadedFrom.MEMORY_CACHE); LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/ContentLengthInputStream.java ================================================ /******************************************************************************* * Copyright 2013-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.assist; import java.io.IOException; import java.io.InputStream; /** * Decorator for {@link java.io.InputStream InputStream}. Provides possibility to return defined stream length by * {@link #available()} method. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com), Mariotaku * @since 1.9.1 */ public class ContentLengthInputStream extends InputStream { private final InputStream stream; private final int length; public ContentLengthInputStream(InputStream stream, int length) { this.stream = stream; this.length = length; } @Override public int available() { return length; } @Override public void close() throws IOException { stream.close(); } @Override public void mark(int readLimit) { stream.mark(readLimit); } @Override public int read() throws IOException { return stream.read(); } @Override public int read(byte[] buffer) throws IOException { return stream.read(buffer); } @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { return stream.read(buffer, byteOffset, byteCount); } @Override public void reset() throws IOException { stream.reset(); } @Override public long skip(long byteCount) throws IOException { return stream.skip(byteCount); } @Override public boolean markSupported() { return stream.markSupported(); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/FailReason.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.assist; /** * Presents the reason why image loading and displaying was failed * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public class FailReason { private final FailType type; private final Throwable cause; public FailReason(FailType type, Throwable cause) { this.type = type; this.cause = cause; } /** @return {@linkplain FailType Fail type} */ public FailType getType() { return type; } /** @return Thrown exception/error, can be null */ public Throwable getCause() { return cause; } /** Presents type of fail while image loading */ public static enum FailType { /** Input/output error. Can be caused by network communication fail or error while caching image on file system. */ IO_ERROR, /** * Error while * {@linkplain android.graphics.BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options) * decode image to Bitmap} */ DECODING_ERROR, /** * {@linkplain com.nostra13.universalimageloader.core.ImageLoader#denyNetworkDownloads(boolean) Network * downloads are denied} and requested image wasn't cached in disk cache before. */ NETWORK_DENIED, /** Not enough memory to create needed Bitmap for image */ OUT_OF_MEMORY, /** Unknown error was occurred while loading image */ UNKNOWN } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java ================================================ package com.nostra13.universalimageloader.core.assist; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; /** * Many streams obtained over slow connection show this * problem. */ public class FlushedInputStream extends FilterInputStream { public FlushedInputStream(InputStream inputStream) { super(inputStream); } @Override public long skip(long n) throws IOException { long totalBytesSkipped = 0L; while (totalBytesSkipped < n) { long bytesSkipped = in.skip(n - totalBytesSkipped); if (bytesSkipped == 0L) { int by_te = read(); if (by_te < 0) { break; // we reached EOF } else { bytesSkipped = 1; // we read one byte } } totalBytesSkipped += bytesSkipped; } return totalBytesSkipped; } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/ImageScaleType.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.assist; /** * Type of image scaling during decoding. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.5.0 */ public enum ImageScaleType { /** Image won't be scaled */ NONE, /** * Image will be scaled down only if image size is greater than * {@linkplain javax.microedition.khronos.opengles.GL10#GL_MAX_TEXTURE_SIZE maximum acceptable texture size}. * Usually it's 2048x2048.
    * If Bitmap is expected to display than it must not exceed this size (otherwise you'll get the exception * "OpenGLRenderer: Bitmap too large to be uploaded into a texture".
    * Image will be subsampled in an integer number of times (1, 2, 3, ...) to maximum texture size of device. */ NONE_SAFE, /** * Image will be reduces 2-fold until next reduce step make image smaller target size.
    * It's fast type and it's preferable for usage in lists/grids/galleries (and other * {@linkplain android.widget.AdapterView adapter-views}) .
    * Relates to {@link android.graphics.BitmapFactory.Options#inSampleSize}
    * Note: If original image size is smaller than target size then original image won't be scaled. */ IN_SAMPLE_POWER_OF_2, /** * Image will be subsampled in an integer number of times (1, 2, 3, ...). Use it if memory economy is quite * important.
    * Relates to {@link android.graphics.BitmapFactory.Options#inSampleSize}
    * Note: If original image size is smaller than target size then original image won't be scaled. */ IN_SAMPLE_INT, /** * Image will scaled-down exactly to target size (scaled width or height or both will be equal to target size; * depends on {@linkplain android.widget.ImageView.ScaleType ImageView's scale type}). Use it if memory economy is * critically important.
    * Note: If original image size is smaller than target size then original image won't be scaled.
    *
    * NOTE: For creating result Bitmap (of exact size) additional Bitmap will be created with * {@link android.graphics.Bitmap#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean) * Bitmap.createBitmap(...)}.
    * Cons: Saves memory by keeping smaller Bitmap in memory cache (comparing with IN_SAMPLE... scale types)
    * Pros: Requires more memory in one time for creation of result Bitmap. */ EXACTLY, /** * Image will scaled exactly to target size (scaled width or height or both will be equal to target size; depends on * {@linkplain android.widget.ImageView.ScaleType ImageView's scale type}). Use it if memory economy is critically * important.
    * Note: If original image size is smaller than target size then original image will be stretched to * target size.
    *
    * NOTE: For creating result Bitmap (of exact size) additional Bitmap will be created with * {@link android.graphics.Bitmap#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean) * Bitmap.createBitmap(...)}.
    * Cons: Saves memory by keeping smaller Bitmap in memory cache (comparing with IN_SAMPLE... scale types)
    * Pros: Requires more memory in one time for creation of result Bitmap. */ EXACTLY_STRETCHED } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/ImageSize.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.assist; /** * Present width and height values * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public class ImageSize { private static final int TO_STRING_MAX_LENGHT = 9; // "9999x9999".length() private static final String SEPARATOR = "x"; private final int width; private final int height; public ImageSize(int width, int height) { this.width = width; this.height = height; } public ImageSize(int width, int height, int rotation) { if (rotation % 180 == 0) { this.width = width; this.height = height; } else { this.width = height; this.height = width; } } public int getWidth() { return width; } public int getHeight() { return height; } /** Scales down dimensions in sampleSize times. Returns new object. */ public ImageSize scaleDown(int sampleSize) { return new ImageSize(width / sampleSize, height / sampleSize); } /** Scales dimensions according to incoming scale. Returns new object. */ public ImageSize scale(float scale) { return new ImageSize((int) (width * scale), (int) (height * scale)); } @Override public String toString() { return new StringBuilder(TO_STRING_MAX_LENGHT).append(width).append(SEPARATOR).append(height).toString(); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/LoadedFrom.java ================================================ package com.nostra13.universalimageloader.core.assist; /** * Source image loaded from. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public enum LoadedFrom { NETWORK, DISC_CACHE, MEMORY_CACHE } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/QueueProcessingType.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.assist; /** * Queue processing type which will be used for display task processing * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.6.3 */ public enum QueueProcessingType { FIFO, LIFO } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/ViewScaleType.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.assist; import android.widget.ImageView; import android.widget.ImageView.ScaleType; /** * Simplify {@linkplain ScaleType ImageView's scale type} to 2 types: {@link #FIT_INSIDE} and {@link #CROP} * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.6.1 */ public enum ViewScaleType { /** * Scale the image uniformly (maintain the image's aspect ratio) so that at least one dimension (width or height) of * the image will be equal to or less the corresponding dimension of the view. */ FIT_INSIDE, /** * Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the * image will be equal to or larger than the corresponding dimension of the view. */ CROP; /** * Defines scale type of ImageView. * * @param imageView {@link ImageView} * @return {@link #FIT_INSIDE} for *
      *
    • {@link ScaleType#FIT_CENTER}
    • *
    • {@link ScaleType#FIT_XY}
    • *
    • {@link ScaleType#FIT_START}
    • *
    • {@link ScaleType#FIT_END}
    • *
    • {@link ScaleType#CENTER_INSIDE}
    • *
    * {@link #CROP} for *
      *
    • {@link ScaleType#CENTER}
    • *
    • {@link ScaleType#CENTER_CROP}
    • *
    • {@link ScaleType#MATRIX}
    • *
    */ public static ViewScaleType fromImageView(ImageView imageView) { switch (imageView.getScaleType()) { case FIT_CENTER: case FIT_XY: case FIT_START: case FIT_END: case CENTER_INSIDE: return FIT_INSIDE; case MATRIX: case CENTER: case CENTER_CROP: default: return CROP; } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/deque/BlockingDeque.java ================================================ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/licenses/publicdomain */ package com.nostra13.universalimageloader.core.assist.deque; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; /** * A {@link Deque} that additionally supports blocking operations that wait * for the deque to become non-empty when retrieving an element, and wait for * space to become available in the deque when storing an element. *

    *

    BlockingDeque methods come in four forms, with different ways * of handling operations that cannot be satisfied immediately, but may be * satisfied at some point in the future: * one throws an exception, the second returns a special value (either * null or false, depending on the operation), the third * blocks the current thread indefinitely until the operation can succeed, * and the fourth blocks for only a given maximum time limit before giving * up. These methods are summarized in the following table: *

    *

    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    First Element (Head)
    Throws exceptionSpecial valueBlocksTimes out
    Insert{@link #addFirst addFirst(e)}{@link #offerFirst offerFirst(e)}{@link #putFirst putFirst(e)}{@link #offerFirst offerFirst(e, time, unit)}
    Remove{@link #removeFirst removeFirst()}{@link #pollFirst pollFirst()}{@link #takeFirst takeFirst()}{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}
    Examine{@link #getFirst getFirst()}{@link #peekFirst peekFirst()}not applicablenot applicable
    Last Element (Tail)
    Throws exceptionSpecial valueBlocksTimes out
    Insert{@link #addLast addLast(e)}{@link #offerLast offerLast(e)}{@link #putLast putLast(e)}{@link #offerLast offerLast(e, time, unit)}
    Remove{@link #removeLast() removeLast()}{@link #pollLast() pollLast()}{@link #takeLast takeLast()}{@link #pollLast(long, TimeUnit) pollLast(time, unit)}
    Examine{@link #getLast getLast()}{@link #peekLast peekLast()}not applicablenot applicable
    *

    *

    Like any {@link BlockingQueue}, a BlockingDeque is thread safe, * does not permit null elements, and may (or may not) be * capacity-constrained. *

    *

    A BlockingDeque implementation may be used directly as a FIFO * BlockingQueue. The methods inherited from the * BlockingQueue interface are precisely equivalent to * BlockingDeque methods as indicated in the following table: *

    *

    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    BlockingQueue Method Equivalent BlockingDeque Method
    Insert
    {@link #add add(e)}{@link #addLast addLast(e)}
    {@link #offer offer(e)}{@link #offerLast offerLast(e)}
    {@link #put put(e)}{@link #putLast putLast(e)}
    {@link #offer offer(e, time, unit)}{@link #offerLast offerLast(e, time, unit)}
    Remove
    {@link #remove() remove()}{@link #removeFirst() removeFirst()}
    {@link #poll() poll()}{@link #pollFirst() pollFirst()}
    {@link #take() take()}{@link #takeFirst() takeFirst()}
    {@link #poll(long, TimeUnit) poll(time, unit)}{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}
    Examine
    {@link #element() element()}{@link #getFirst() getFirst()}
    {@link #peek() peek()}{@link #peekFirst() peekFirst()}
    *

    *

    Memory consistency effects: As with other concurrent * collections, actions in a thread prior to placing an object into a * {@code BlockingDeque} * happen-before * actions subsequent to the access or removal of that element from * the {@code BlockingDeque} in another thread. *

    *

    This interface is a member of the * * Java Collections Framework. * * @param the type of elements held in this collection * @author Doug Lea * @since 1.6 */ public interface BlockingDeque extends BlockingQueue, Deque { /* * We have "diamond" multiple interface inheritance here, and that * introduces ambiguities. Methods might end up with different * specs depending on the branch chosen by javadoc. Thus a lot of * methods specs here are copied from superinterfaces. */ /** * Inserts the specified element at the front of this deque if it is * possible to do so immediately without violating capacity restrictions, * throwing an IllegalStateException if no space is currently * available. When using a capacity-restricted deque, it is generally * preferable to use {@link #offerFirst offerFirst}. * * @param e the element to add * @throws IllegalStateException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException {@inheritDoc} */ void addFirst(E e); /** * Inserts the specified element at the end of this deque if it is * possible to do so immediately without violating capacity restrictions, * throwing an IllegalStateException if no space is currently * available. When using a capacity-restricted deque, it is generally * preferable to use {@link #offerLast offerLast}. * * @param e the element to add * @throws IllegalStateException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException {@inheritDoc} */ void addLast(E e); /** * Inserts the specified element at the front of this deque if it is * possible to do so immediately without violating capacity restrictions, * returning true upon success and false if no space is * currently available. * When using a capacity-restricted deque, this method is generally * preferable to the {@link #addFirst addFirst} method, which can * fail to insert an element only by throwing an exception. * * @param e the element to add * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException {@inheritDoc} */ boolean offerFirst(E e); /** * Inserts the specified element at the end of this deque if it is * possible to do so immediately without violating capacity restrictions, * returning true upon success and false if no space is * currently available. * When using a capacity-restricted deque, this method is generally * preferable to the {@link #addLast addLast} method, which can * fail to insert an element only by throwing an exception. * * @param e the element to add * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException {@inheritDoc} */ boolean offerLast(E e); /** * Inserts the specified element at the front of this deque, * waiting if necessary for space to become available. * * @param e the element to add * @throws InterruptedException if interrupted while waiting * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ void putFirst(E e) throws InterruptedException; /** * Inserts the specified element at the end of this deque, * waiting if necessary for space to become available. * * @param e the element to add * @throws InterruptedException if interrupted while waiting * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ void putLast(E e) throws InterruptedException; /** * Inserts the specified element at the front of this deque, * waiting up to the specified wait time if necessary for space to * become available. * * @param e the element to add * @param timeout how long to wait before giving up, in units of * unit * @param unit a TimeUnit determining how to interpret the * timeout parameter * @return true if successful, or false if * the specified waiting time elapses before space is available * @throws InterruptedException if interrupted while waiting * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException; /** * Inserts the specified element at the end of this deque, * waiting up to the specified wait time if necessary for space to * become available. * * @param e the element to add * @param timeout how long to wait before giving up, in units of * unit * @param unit a TimeUnit determining how to interpret the * timeout parameter * @return true if successful, or false if * the specified waiting time elapses before space is available * @throws InterruptedException if interrupted while waiting * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException; /** * Retrieves and removes the first element of this deque, waiting * if necessary until an element becomes available. * * @return the head of this deque * @throws InterruptedException if interrupted while waiting */ E takeFirst() throws InterruptedException; /** * Retrieves and removes the last element of this deque, waiting * if necessary until an element becomes available. * * @return the tail of this deque * @throws InterruptedException if interrupted while waiting */ E takeLast() throws InterruptedException; /** * Retrieves and removes the first element of this deque, waiting * up to the specified wait time if necessary for an element to * become available. * * @param timeout how long to wait before giving up, in units of * unit * @param unit a TimeUnit determining how to interpret the * timeout parameter * @return the head of this deque, or null if the specified * waiting time elapses before an element is available * @throws InterruptedException if interrupted while waiting */ E pollFirst(long timeout, TimeUnit unit) throws InterruptedException; /** * Retrieves and removes the last element of this deque, waiting * up to the specified wait time if necessary for an element to * become available. * * @param timeout how long to wait before giving up, in units of * unit * @param unit a TimeUnit determining how to interpret the * timeout parameter * @return the tail of this deque, or null if the specified * waiting time elapses before an element is available * @throws InterruptedException if interrupted while waiting */ E pollLast(long timeout, TimeUnit unit) throws InterruptedException; /** * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the first element e such that * o.equals(e) (if such an element exists). * Returns true if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque (optional) * @throws NullPointerException if the specified element is null (optional) */ boolean removeFirstOccurrence(Object o); /** * Removes the last occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the last element e such that * o.equals(e) (if such an element exists). * Returns true if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque (optional) * @throws NullPointerException if the specified element is null (optional) */ boolean removeLastOccurrence(Object o); // *** BlockingQueue methods *** /** * Inserts the specified element into the queue represented by this deque * (in other words, at the tail of this deque) if it is possible to do so * immediately without violating capacity restrictions, returning * true upon success and throwing an * IllegalStateException if no space is currently available. * When using a capacity-restricted deque, it is generally preferable to * use {@link #offer offer}. *

    *

    This method is equivalent to {@link #addLast addLast}. * * @param e the element to add * @throws IllegalStateException {@inheritDoc} * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ boolean add(E e); /** * Inserts the specified element into the queue represented by this deque * (in other words, at the tail of this deque) if it is possible to do so * immediately without violating capacity restrictions, returning * true upon success and false if no space is currently * available. When using a capacity-restricted deque, this method is * generally preferable to the {@link #add} method, which can fail to * insert an element only by throwing an exception. *

    *

    This method is equivalent to {@link #offerLast offerLast}. * * @param e the element to add * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ boolean offer(E e); /** * Inserts the specified element into the queue represented by this deque * (in other words, at the tail of this deque), waiting if necessary for * space to become available. *

    *

    This method is equivalent to {@link #putLast putLast}. * * @param e the element to add * @throws InterruptedException {@inheritDoc} * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ void put(E e) throws InterruptedException; /** * Inserts the specified element into the queue represented by this deque * (in other words, at the tail of this deque), waiting up to the * specified wait time if necessary for space to become available. *

    *

    This method is equivalent to * {@link #offerLast offerLast}. * * @param e the element to add * @return true if the element was added to this deque, else * false * @throws InterruptedException {@inheritDoc} * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException; /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque). * This method differs from {@link #poll poll} only in that it * throws an exception if this deque is empty. *

    *

    This method is equivalent to {@link #removeFirst() removeFirst}. * * @return the head of the queue represented by this deque * @throws NoSuchElementException if this deque is empty */ E remove(); /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque), or returns * null if this deque is empty. *

    *

    This method is equivalent to {@link #pollFirst()}. * * @return the head of this deque, or null if this deque is empty */ E poll(); /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque), waiting if * necessary until an element becomes available. *

    *

    This method is equivalent to {@link #takeFirst() takeFirst}. * * @return the head of this deque * @throws InterruptedException if interrupted while waiting */ E take() throws InterruptedException; /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque), waiting up to the * specified wait time if necessary for an element to become available. *

    *

    This method is equivalent to * {@link #pollFirst(long, TimeUnit) pollFirst}. * * @return the head of this deque, or null if the * specified waiting time elapses before an element is available * @throws InterruptedException if interrupted while waiting */ E poll(long timeout, TimeUnit unit) throws InterruptedException; /** * Retrieves, but does not remove, the head of the queue represented by * this deque (in other words, the first element of this deque). * This method differs from {@link #peek peek} only in that it throws an * exception if this deque is empty. *

    *

    This method is equivalent to {@link #getFirst() getFirst}. * * @return the head of this deque * @throws NoSuchElementException if this deque is empty */ E element(); /** * Retrieves, but does not remove, the head of the queue represented by * this deque (in other words, the first element of this deque), or * returns null if this deque is empty. *

    *

    This method is equivalent to {@link #peekFirst() peekFirst}. * * @return the head of this deque, or null if this deque is empty */ E peek(); /** * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the first element e such that * o.equals(e) (if such an element exists). * Returns true if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). *

    *

    This method is equivalent to * {@link #removeFirstOccurrence removeFirstOccurrence}. * * @param o element to be removed from this deque, if present * @return true if this deque changed as a result of the call * @throws ClassCastException if the class of the specified element * is incompatible with this deque (optional) * @throws NullPointerException if the specified element is null (optional) */ boolean remove(Object o); /** * Returns true if this deque contains the specified element. * More formally, returns true if and only if this deque contains * at least one element e such that o.equals(e). * * @param o object to be checked for containment in this deque * @return true if this deque contains the specified element * @throws ClassCastException if the class of the specified element * is incompatible with this deque (optional) * @throws NullPointerException if the specified element is null (optional) */ public boolean contains(Object o); /** * Returns the number of elements in this deque. * * @return the number of elements in this deque */ public int size(); /** * Returns an iterator over the elements in this deque in proper sequence. * The elements will be returned in order from first (head) to last (tail). * * @return an iterator over the elements in this deque in proper sequence */ Iterator iterator(); // *** Stack methods *** /** * Pushes an element onto the stack represented by this deque. In other * words, inserts the element at the front of this deque unless it would * violate capacity restrictions. *

    *

    This method is equivalent to {@link #addFirst addFirst}. * * @throws IllegalStateException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified element is null * @throws IllegalArgumentException {@inheritDoc} */ void push(E e); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/deque/Deque.java ================================================ /* * Written by Doug Lea and Josh Bloch with assistance from members of * JCP JSR-166 Expert Group and released to the public domain, as explained * at http://creativecommons.org/licenses/publicdomain */ package com.nostra13.universalimageloader.core.assist.deque; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Stack; /** * A linear collection that supports element insertion and removal at * both ends. The name deque is short for "double ended queue" * and is usually pronounced "deck". Most Deque * implementations place no fixed limits on the number of elements * they may contain, but this interface supports capacity-restricted * deques as well as those with no fixed size limit. *

    *

    This interface defines methods to access the elements at both * ends of the deque. Methods are provided to insert, remove, and * examine the element. Each of these methods exists in two forms: * one throws an exception if the operation fails, the other returns a * special value (either null or false, depending on * the operation). The latter form of the insert operation is * designed specifically for use with capacity-restricted * Deque implementations; in most implementations, insert * operations cannot fail. *

    *

    The twelve methods described above are summarized in the * following table: *

    *

    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    First Element (Head) Last Element (Tail)
    Throws exceptionSpecial valueThrows exceptionSpecial value
    Insert{@link #addFirst addFirst(e)}{@link #offerFirst offerFirst(e)}{@link #addLast addLast(e)}{@link #offerLast offerLast(e)}
    Remove{@link #removeFirst removeFirst()}{@link #pollFirst pollFirst()}{@link #removeLast removeLast()}{@link #pollLast pollLast()}
    Examine{@link #getFirst getFirst()}{@link #peekFirst peekFirst()}{@link #getLast getLast()}{@link #peekLast peekLast()}
    *

    *

    This interface extends the {@link Queue} interface. When a deque is * used as a queue, FIFO (First-In-First-Out) behavior results. Elements are * added at the end of the deque and removed from the beginning. The methods * inherited from the Queue interface are precisely equivalent to * Deque methods as indicated in the following table: *

    *

    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Queue Method Equivalent Deque Method
    {@link java.util.Queue#add add(e)}{@link #addLast addLast(e)}
    {@link java.util.Queue#offer offer(e)}{@link #offerLast offerLast(e)}
    {@link java.util.Queue#remove remove()}{@link #removeFirst removeFirst()}
    {@link java.util.Queue#poll poll()}{@link #pollFirst pollFirst()}
    {@link java.util.Queue#element element()}{@link #getFirst getFirst()}
    {@link java.util.Queue#peek peek()}{@link #peek peekFirst()}
    *

    *

    Deques can also be used as LIFO (Last-In-First-Out) stacks. This * interface should be used in preference to the legacy {@link Stack} class. * When a deque is used as a stack, elements are pushed and popped from the * beginning of the deque. Stack methods are precisely equivalent to * Deque methods as indicated in the table below: *

    *

    * * * * * * * * * * * * * * * * * *
    Stack Method Equivalent Deque Method
    {@link #push push(e)}{@link #addFirst addFirst(e)}
    {@link #pop pop()}{@link #removeFirst removeFirst()}
    {@link #peek peek()}{@link #peekFirst peekFirst()}
    *

    *

    Note that the {@link #peek peek} method works equally well when * a deque is used as a queue or a stack; in either case, elements are * drawn from the beginning of the deque. *

    *

    This interface provides two methods to remove interior * elements, {@link #removeFirstOccurrence removeFirstOccurrence} and * {@link #removeLastOccurrence removeLastOccurrence}. *

    *

    Unlike the {@link List} interface, this interface does not * provide support for indexed access to elements. *

    *

    While Deque implementations are not strictly required * to prohibit the insertion of null elements, they are strongly * encouraged to do so. Users of any Deque implementations * that do allow null elements are strongly encouraged not to * take advantage of the ability to insert nulls. This is so because * null is used as a special return value by various methods * to indicated that the deque is empty. *

    *

    Deque implementations generally do not define * element-based versions of the equals and hashCode * methods, but instead inherit the identity-based versions from class * Object. * * @param the type of elements held in this collection * @author Doug Lea * @author Josh Bloch * @since 1.6 */ public interface Deque extends Queue { /** * Inserts the specified element at the front of this deque if it is * possible to do so immediately without violating capacity restrictions. * When using a capacity-restricted deque, it is generally preferable to * use method {@link #offerFirst}. * * @param e the element to add * @throws IllegalStateException if the element cannot be added at this * time due to capacity restrictions * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null and this * deque does not permit null elements * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ void addFirst(E e); /** * Inserts the specified element at the end of this deque if it is * possible to do so immediately without violating capacity restrictions. * When using a capacity-restricted deque, it is generally preferable to * use method {@link #offerLast}. *

    *

    This method is equivalent to {@link #add}. * * @param e the element to add * @throws IllegalStateException if the element cannot be added at this * time due to capacity restrictions * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null and this * deque does not permit null elements * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ void addLast(E e); /** * Inserts the specified element at the front of this deque unless it would * violate capacity restrictions. When using a capacity-restricted deque, * this method is generally preferable to the {@link #addFirst} method, * which can fail to insert an element only by throwing an exception. * * @param e the element to add * @return true if the element was added to this deque, else * false * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null and this * deque does not permit null elements * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ boolean offerFirst(E e); /** * Inserts the specified element at the end of this deque unless it would * violate capacity restrictions. When using a capacity-restricted deque, * this method is generally preferable to the {@link #addLast} method, * which can fail to insert an element only by throwing an exception. * * @param e the element to add * @return true if the element was added to this deque, else * false * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null and this * deque does not permit null elements * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ boolean offerLast(E e); /** * Retrieves and removes the first element of this deque. This method * differs from {@link #pollFirst pollFirst} only in that it throws an * exception if this deque is empty. * * @return the head of this deque * @throws NoSuchElementException if this deque is empty */ E removeFirst(); /** * Retrieves and removes the last element of this deque. This method * differs from {@link #pollLast pollLast} only in that it throws an * exception if this deque is empty. * * @return the tail of this deque * @throws NoSuchElementException if this deque is empty */ E removeLast(); /** * Retrieves and removes the first element of this deque, * or returns null if this deque is empty. * * @return the head of this deque, or null if this deque is empty */ E pollFirst(); /** * Retrieves and removes the last element of this deque, * or returns null if this deque is empty. * * @return the tail of this deque, or null if this deque is empty */ E pollLast(); /** * Retrieves, but does not remove, the first element of this deque. *

    * This method differs from {@link #peekFirst peekFirst} only in that it * throws an exception if this deque is empty. * * @return the head of this deque * @throws NoSuchElementException if this deque is empty */ E getFirst(); /** * Retrieves, but does not remove, the last element of this deque. * This method differs from {@link #peekLast peekLast} only in that it * throws an exception if this deque is empty. * * @return the tail of this deque * @throws NoSuchElementException if this deque is empty */ E getLast(); /** * Retrieves, but does not remove, the first element of this deque, * or returns null if this deque is empty. * * @return the head of this deque, or null if this deque is empty */ E peekFirst(); /** * Retrieves, but does not remove, the last element of this deque, * or returns null if this deque is empty. * * @return the tail of this deque, or null if this deque is empty */ E peekLast(); /** * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the first element e such that * (o==null ? e==null : o.equals(e)) * (if such an element exists). * Returns true if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque (optional) * @throws NullPointerException if the specified element is null and this * deque does not permit null elements (optional) */ boolean removeFirstOccurrence(Object o); /** * Removes the last occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the last element e such that * (o==null ? e==null : o.equals(e)) * (if such an element exists). * Returns true if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque (optional) * @throws NullPointerException if the specified element is null and this * deque does not permit null elements (optional) */ boolean removeLastOccurrence(Object o); // *** Queue methods *** /** * Inserts the specified element into the queue represented by this deque * (in other words, at the tail of this deque) if it is possible to do so * immediately without violating capacity restrictions, returning * true upon success and throwing an * IllegalStateException if no space is currently available. * When using a capacity-restricted deque, it is generally preferable to * use {@link #offer offer}. *

    *

    This method is equivalent to {@link #addLast}. * * @param e the element to add * @return true (as specified by {@link Collection#add}) * @throws IllegalStateException if the element cannot be added at this * time due to capacity restrictions * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null and this * deque does not permit null elements * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ boolean add(E e); /** * Inserts the specified element into the queue represented by this deque * (in other words, at the tail of this deque) if it is possible to do so * immediately without violating capacity restrictions, returning * true upon success and false if no space is currently * available. When using a capacity-restricted deque, this method is * generally preferable to the {@link #add} method, which can fail to * insert an element only by throwing an exception. *

    *

    This method is equivalent to {@link #offerLast}. * * @param e the element to add * @return true if the element was added to this deque, else * false * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null and this * deque does not permit null elements * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ boolean offer(E e); /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque). * This method differs from {@link #poll poll} only in that it throws an * exception if this deque is empty. *

    *

    This method is equivalent to {@link #removeFirst()}. * * @return the head of the queue represented by this deque * @throws NoSuchElementException if this deque is empty */ E remove(); /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque), or returns * null if this deque is empty. *

    *

    This method is equivalent to {@link #pollFirst()}. * * @return the first element of this deque, or null if * this deque is empty */ E poll(); /** * Retrieves, but does not remove, the head of the queue represented by * this deque (in other words, the first element of this deque). * This method differs from {@link #peek peek} only in that it throws an * exception if this deque is empty. *

    *

    This method is equivalent to {@link #getFirst()}. * * @return the head of the queue represented by this deque * @throws NoSuchElementException if this deque is empty */ E element(); /** * Retrieves, but does not remove, the head of the queue represented by * this deque (in other words, the first element of this deque), or * returns null if this deque is empty. *

    *

    This method is equivalent to {@link #peekFirst()}. * * @return the head of the queue represented by this deque, or * null if this deque is empty */ E peek(); // *** Stack methods *** /** * Pushes an element onto the stack represented by this deque (in other * words, at the head of this deque) if it is possible to do so * immediately without violating capacity restrictions, returning * true upon success and throwing an * IllegalStateException if no space is currently available. *

    *

    This method is equivalent to {@link #addFirst}. * * @param e the element to push * @throws IllegalStateException if the element cannot be added at this * time due to capacity restrictions * @throws ClassCastException if the class of the specified element * prevents it from being added to this deque * @throws NullPointerException if the specified element is null and this * deque does not permit null elements * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ void push(E e); /** * Pops an element from the stack represented by this deque. In other * words, removes and returns the first element of this deque. *

    *

    This method is equivalent to {@link #removeFirst()}. * * @return the element at the front of this deque (which is the top * of the stack represented by this deque) * @throws NoSuchElementException if this deque is empty */ E pop(); // *** Collection methods *** /** * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the first element e such that * (o==null ? e==null : o.equals(e)) * (if such an element exists). * Returns true if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). *

    *

    This method is equivalent to {@link #removeFirstOccurrence}. * * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque (optional) * @throws NullPointerException if the specified element is null and this * deque does not permit null elements (optional) */ boolean remove(Object o); /** * Returns true if this deque contains the specified element. * More formally, returns true if and only if this deque contains * at least one element e such that * (o==null ? e==null : o.equals(e)). * * @param o element whose presence in this deque is to be tested * @return true if this deque contains the specified element * @throws ClassCastException if the type of the specified element * is incompatible with this deque (optional) * @throws NullPointerException if the specified element is null and this * deque does not permit null elements (optional) */ boolean contains(Object o); /** * Returns the number of elements in this deque. * * @return the number of elements in this deque */ public int size(); /** * Returns an iterator over the elements in this deque in proper sequence. * The elements will be returned in order from first (head) to last (tail). * * @return an iterator over the elements in this deque in proper sequence */ Iterator iterator(); /** * Returns an iterator over the elements in this deque in reverse * sequential order. The elements will be returned in order from * last (tail) to first (head). * * @return an iterator over the elements in this deque in reverse * sequence */ Iterator descendingIterator(); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/deque/LIFOLinkedBlockingDeque.java ================================================ package com.nostra13.universalimageloader.core.assist.deque; import java.util.NoSuchElementException; /** * {@link LinkedBlockingDeque} using LIFO algorithm * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.6.3 */ public class LIFOLinkedBlockingDeque extends LinkedBlockingDeque { private static final long serialVersionUID = -4114786347960826192L; /** * Inserts the specified element at the front of this deque if it is possible to do so immediately without violating * capacity restrictions, returning true upon success and false if no space is currently * available. When using a capacity-restricted deque, this method is generally preferable to the {@link #addFirst * addFirst} method, which can fail to insert an element only by throwing an exception. * * @param e * the element to add * @throws ClassCastException * {@inheritDoc} * @throws NullPointerException * if the specified element is null * @throws IllegalArgumentException * {@inheritDoc} */ @Override public boolean offer(T e) { return super.offerFirst(e); } /** * Retrieves and removes the first element of this deque. This method differs from {@link #pollFirst pollFirst} only * in that it throws an exception if this deque is empty. * * @return the head of this deque * @throws NoSuchElementException * if this deque is empty */ @Override public T remove() { return super.removeFirst(); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/deque/LinkedBlockingDeque.java ================================================ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/licenses/publicdomain */ package com.nostra13.universalimageloader.core.assist.deque; import java.util.AbstractQueue; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * An optionally-bounded {@linkplain BlockingDeque blocking deque} based on * linked nodes. *

    *

    The optional capacity bound constructor argument serves as a * way to prevent excessive expansion. The capacity, if unspecified, * is equal to {@link Integer#MAX_VALUE}. Linked nodes are * dynamically created upon each insertion unless this would bring the * deque above capacity. *

    *

    Most operations run in constant time (ignoring time spent * blocking). Exceptions include {@link #remove(Object) remove}, * {@link #removeFirstOccurrence removeFirstOccurrence}, {@link * #removeLastOccurrence removeLastOccurrence}, {@link #contains * contains}, {@link #iterator iterator.remove()}, and the bulk * operations, all of which run in linear time. *

    *

    This class and its iterator implement all of the * optional methods of the {@link Collection} and {@link * Iterator} interfaces. *

    *

    This class is a member of the * * Java Collections Framework. * * @param the type of elements held in this collection * @author Doug Lea * @since 1.6 */ public class LinkedBlockingDeque extends AbstractQueue implements BlockingDeque, java.io.Serializable { /* * Implemented as a simple doubly-linked list protected by a * single lock and using conditions to manage blocking. * * To implement weakly consistent iterators, it appears we need to * keep all Nodes GC-reachable from a predecessor dequeued Node. * That would cause two problems: * - allow a rogue Iterator to cause unbounded memory retention * - cause cross-generational linking of old Nodes to new Nodes if * a Node was tenured while live, which generational GCs have a * hard time dealing with, causing repeated major collections. * However, only non-deleted Nodes need to be reachable from * dequeued Nodes, and reachability does not necessarily have to * be of the kind understood by the GC. We use the trick of * linking a Node that has just been dequeued to itself. Such a * self-link implicitly means to jump to "first" (for next links) * or "last" (for prev links). */ /* * We have "diamond" multiple interface/abstract class inheritance * here, and that introduces ambiguities. Often we want the * BlockingDeque javadoc combined with the AbstractQueue * implementation, so a lot of method specs are duplicated here. */ private static final long serialVersionUID = -387911632671998426L; /** * Doubly-linked list node class */ static final class Node { /** * The item, or null if this node has been removed. */ E item; /** * One of: * - the real predecessor Node * - this Node, meaning the predecessor is tail * - null, meaning there is no predecessor */ Node prev; /** * One of: * - the real successor Node * - this Node, meaning the successor is head * - null, meaning there is no successor */ Node next; Node(E x) { item = x; } } /** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ transient Node first; /** * Pointer to last node. * Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */ transient Node last; /** * Number of items in the deque */ private transient int count; /** * Maximum number of items in the deque */ private final int capacity; /** * Main lock guarding all access */ final ReentrantLock lock = new ReentrantLock(); /** * Condition for waiting takes */ private final Condition notEmpty = lock.newCondition(); /** * Condition for waiting puts */ private final Condition notFull = lock.newCondition(); /** * Creates a {@code LinkedBlockingDeque} with a capacity of * {@link Integer#MAX_VALUE}. */ public LinkedBlockingDeque() { this(Integer.MAX_VALUE); } /** * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity. * * @param capacity the capacity of this deque * @throws IllegalArgumentException if {@code capacity} is less than 1 */ public LinkedBlockingDeque(int capacity) { if (capacity <= 0) throw new IllegalArgumentException(); this.capacity = capacity; } /** * Creates a {@code LinkedBlockingDeque} with a capacity of * {@link Integer#MAX_VALUE}, initially containing the elements of * the given collection, added in traversal order of the * collection's iterator. * * @param c the collection of elements to initially contain * @throws NullPointerException if the specified collection or any * of its elements are null */ public LinkedBlockingDeque(Collection c) { this(Integer.MAX_VALUE); final ReentrantLock lock = this.lock; lock.lock(); // Never contended, but necessary for visibility try { for (E e : c) { if (e == null) throw new NullPointerException(); if (!linkLast(new Node(e))) throw new IllegalStateException("Deque full"); } } finally { lock.unlock(); } } // Basic linking and unlinking operations, called only while holding lock /** * Links node as first element, or returns false if full. */ private boolean linkFirst(Node node) { // assert lock.isHeldByCurrentThread(); if (count >= capacity) return false; Node f = first; node.next = f; first = node; if (last == null) last = node; else f.prev = node; ++count; notEmpty.signal(); return true; } /** * Links node as last element, or returns false if full. */ private boolean linkLast(Node node) { // assert lock.isHeldByCurrentThread(); if (count >= capacity) return false; Node l = last; node.prev = l; last = node; if (first == null) first = node; else l.next = node; ++count; notEmpty.signal(); return true; } /** * Removes and returns first element, or null if empty. */ private E unlinkFirst() { // assert lock.isHeldByCurrentThread(); Node f = first; if (f == null) return null; Node n = f.next; E item = f.item; f.item = null; f.next = f; // help GC first = n; if (n == null) last = null; else n.prev = null; --count; notFull.signal(); return item; } /** * Removes and returns last element, or null if empty. */ private E unlinkLast() { // assert lock.isHeldByCurrentThread(); Node l = last; if (l == null) return null; Node p = l.prev; E item = l.item; l.item = null; l.prev = l; // help GC last = p; if (p == null) first = null; else p.next = null; --count; notFull.signal(); return item; } /** * Unlinks x. */ void unlink(Node x) { // assert lock.isHeldByCurrentThread(); Node p = x.prev; Node n = x.next; if (p == null) { unlinkFirst(); } else if (n == null) { unlinkLast(); } else { p.next = n; n.prev = p; x.item = null; // Don't mess with x's links. They may still be in use by // an iterator. --count; notFull.signal(); } } // BlockingDeque methods /** * @throws IllegalStateException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public void addFirst(E e) { if (!offerFirst(e)) throw new IllegalStateException("Deque full"); } /** * @throws IllegalStateException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public void addLast(E e) { if (!offerLast(e)) throw new IllegalStateException("Deque full"); } /** * @throws NullPointerException {@inheritDoc} */ public boolean offerFirst(E e) { if (e == null) throw new NullPointerException(); Node node = new Node(e); final ReentrantLock lock = this.lock; lock.lock(); try { return linkFirst(node); } finally { lock.unlock(); } } /** * @throws NullPointerException {@inheritDoc} */ public boolean offerLast(E e) { if (e == null) throw new NullPointerException(); Node node = new Node(e); final ReentrantLock lock = this.lock; lock.lock(); try { return linkLast(node); } finally { lock.unlock(); } } /** * @throws NullPointerException {@inheritDoc} * @throws InterruptedException {@inheritDoc} */ public void putFirst(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); Node node = new Node(e); final ReentrantLock lock = this.lock; lock.lock(); try { while (!linkFirst(node)) notFull.await(); } finally { lock.unlock(); } } /** * @throws NullPointerException {@inheritDoc} * @throws InterruptedException {@inheritDoc} */ public void putLast(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); Node node = new Node(e); final ReentrantLock lock = this.lock; lock.lock(); try { while (!linkLast(node)) notFull.await(); } finally { lock.unlock(); } } /** * @throws NullPointerException {@inheritDoc} * @throws InterruptedException {@inheritDoc} */ public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) throw new NullPointerException(); Node node = new Node(e); long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (!linkFirst(node)) { if (nanos <= 0) return false; nanos = notFull.awaitNanos(nanos); } return true; } finally { lock.unlock(); } } /** * @throws NullPointerException {@inheritDoc} * @throws InterruptedException {@inheritDoc} */ public boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) throw new NullPointerException(); Node node = new Node(e); long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (!linkLast(node)) { if (nanos <= 0) return false; nanos = notFull.awaitNanos(nanos); } return true; } finally { lock.unlock(); } } /** * @throws NoSuchElementException {@inheritDoc} */ public E removeFirst() { E x = pollFirst(); if (x == null) throw new NoSuchElementException(); return x; } /** * @throws NoSuchElementException {@inheritDoc} */ public E removeLast() { E x = pollLast(); if (x == null) throw new NoSuchElementException(); return x; } public E pollFirst() { final ReentrantLock lock = this.lock; lock.lock(); try { return unlinkFirst(); } finally { lock.unlock(); } } public E pollLast() { final ReentrantLock lock = this.lock; lock.lock(); try { return unlinkLast(); } finally { lock.unlock(); } } public E takeFirst() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lock(); try { E x; while ((x = unlinkFirst()) == null) notEmpty.await(); return x; } finally { lock.unlock(); } } public E takeLast() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lock(); try { E x; while ((x = unlinkLast()) == null) notEmpty.await(); return x; } finally { lock.unlock(); } } public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { E x; while ((x = unlinkFirst()) == null) { if (nanos <= 0) return null; nanos = notEmpty.awaitNanos(nanos); } return x; } finally { lock.unlock(); } } public E pollLast(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { E x; while ((x = unlinkLast()) == null) { if (nanos <= 0) return null; nanos = notEmpty.awaitNanos(nanos); } return x; } finally { lock.unlock(); } } /** * @throws NoSuchElementException {@inheritDoc} */ public E getFirst() { E x = peekFirst(); if (x == null) throw new NoSuchElementException(); return x; } /** * @throws NoSuchElementException {@inheritDoc} */ public E getLast() { E x = peekLast(); if (x == null) throw new NoSuchElementException(); return x; } public E peekFirst() { final ReentrantLock lock = this.lock; lock.lock(); try { return (first == null) ? null : first.item; } finally { lock.unlock(); } } public E peekLast() { final ReentrantLock lock = this.lock; lock.lock(); try { return (last == null) ? null : last.item; } finally { lock.unlock(); } } public boolean removeFirstOccurrence(Object o) { if (o == null) return false; final ReentrantLock lock = this.lock; lock.lock(); try { for (Node p = first; p != null; p = p.next) { if (o.equals(p.item)) { unlink(p); return true; } } return false; } finally { lock.unlock(); } } public boolean removeLastOccurrence(Object o) { if (o == null) return false; final ReentrantLock lock = this.lock; lock.lock(); try { for (Node p = last; p != null; p = p.prev) { if (o.equals(p.item)) { unlink(p); return true; } } return false; } finally { lock.unlock(); } } // BlockingQueue methods /** * Inserts the specified element at the end of this deque unless it would * violate capacity restrictions. When using a capacity-restricted deque, * it is generally preferable to use method {@link #offer offer}. *

    *

    This method is equivalent to {@link #addLast}. * * @throws IllegalStateException if the element cannot be added at this * time due to capacity restrictions * @throws NullPointerException if the specified element is null */ public boolean add(E e) { addLast(e); return true; } /** * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { return offerLast(e); } /** * @throws NullPointerException {@inheritDoc} * @throws InterruptedException {@inheritDoc} */ public void put(E e) throws InterruptedException { putLast(e); } /** * @throws NullPointerException {@inheritDoc} * @throws InterruptedException {@inheritDoc} */ public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { return offerLast(e, timeout, unit); } /** * Retrieves and removes the head of the queue represented by this deque. * This method differs from {@link #poll poll} only in that it throws an * exception if this deque is empty. *

    *

    This method is equivalent to {@link #removeFirst() removeFirst}. * * @return the head of the queue represented by this deque * @throws NoSuchElementException if this deque is empty */ public E remove() { return removeFirst(); } public E poll() { return pollFirst(); } public E take() throws InterruptedException { return takeFirst(); } public E poll(long timeout, TimeUnit unit) throws InterruptedException { return pollFirst(timeout, unit); } /** * Retrieves, but does not remove, the head of the queue represented by * this deque. This method differs from {@link #peek peek} only in that * it throws an exception if this deque is empty. *

    *

    This method is equivalent to {@link #getFirst() getFirst}. * * @return the head of the queue represented by this deque * @throws NoSuchElementException if this deque is empty */ public E element() { return getFirst(); } public E peek() { return peekFirst(); } /** * Returns the number of additional elements that this deque can ideally * (in the absence of memory or resource constraints) accept without * blocking. This is always equal to the initial capacity of this deque * less the current {@code size} of this deque. *

    *

    Note that you cannot always tell if an attempt to insert * an element will succeed by inspecting {@code remainingCapacity} * because it may be the case that another thread is about to * insert or remove an element. */ public int remainingCapacity() { final ReentrantLock lock = this.lock; lock.lock(); try { return capacity - count; } finally { lock.unlock(); } } /** * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection c) { return drainTo(c, Integer.MAX_VALUE); } /** * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection c, int maxElements) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); final ReentrantLock lock = this.lock; lock.lock(); try { int n = Math.min(maxElements, count); for (int i = 0; i < n; i++) { c.add(first.item); // In this order, in case add() throws. unlinkFirst(); } return n; } finally { lock.unlock(); } } // Stack methods /** * @throws IllegalStateException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public void push(E e) { addFirst(e); } /** * @throws NoSuchElementException {@inheritDoc} */ public E pop() { return removeFirst(); } // Collection methods /** * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the first element {@code e} such that * {@code o.equals(e)} (if such an element exists). * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). *

    *

    This method is equivalent to * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}. * * @param o element to be removed from this deque, if present * @return {@code true} if this deque changed as a result of the call */ public boolean remove(Object o) { return removeFirstOccurrence(o); } /** * Returns the number of elements in this deque. * * @return the number of elements in this deque */ public int size() { final ReentrantLock lock = this.lock; lock.lock(); try { return count; } finally { lock.unlock(); } } /** * Returns {@code true} if this deque contains the specified element. * More formally, returns {@code true} if and only if this deque contains * at least one element {@code e} such that {@code o.equals(e)}. * * @param o object to be checked for containment in this deque * @return {@code true} if this deque contains the specified element */ public boolean contains(Object o) { if (o == null) return false; final ReentrantLock lock = this.lock; lock.lock(); try { for (Node p = first; p != null; p = p.next) if (o.equals(p.item)) return true; return false; } finally { lock.unlock(); } } /* * TODO: Add support for more efficient bulk operations. * * We don't want to acquire the lock for every iteration, but we * also want other threads a chance to interact with the * collection, especially when count is close to capacity. */ // /** // * Adds all of the elements in the specified collection to this // * queue. Attempts to addAll of a queue to itself result in // * {@code IllegalArgumentException}. Further, the behavior of // * this operation is undefined if the specified collection is // * modified while the operation is in progress. // * // * @param c collection containing elements to be added to this queue // * @return {@code true} if this queue changed as a result of the call // * @throws ClassCastException {@inheritDoc} // * @throws NullPointerException {@inheritDoc} // * @throws IllegalArgumentException {@inheritDoc} // * @throws IllegalStateException {@inheritDoc} // * @see #add(Object) // */ // public boolean addAll(Collection c) { // if (c == null) // throw new NullPointerException(); // if (c == this) // throw new IllegalArgumentException(); // final ReentrantLock lock = this.lock; // lock.lock(); // try { // boolean modified = false; // for (E e : c) // if (linkLast(e)) // modified = true; // return modified; // } finally { // lock.unlock(); // } // } /** * Returns an array containing all of the elements in this deque, in * proper sequence (from first to last element). *

    *

    The returned array will be "safe" in that no references to it are * maintained by this deque. (In other words, this method must allocate * a new array). The caller is thus free to modify the returned array. *

    *

    This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this deque */ public Object[] toArray() { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] a = new Object[count]; int k = 0; for (Node p = first; p != null; p = p.next) a[k++] = p.item; return a; } finally { lock.unlock(); } } /** * Returns an array containing all of the elements in this deque, in * proper sequence; the runtime type of the returned array is that of * the specified array. If the deque fits in the specified array, it * is returned therein. Otherwise, a new array is allocated with the * runtime type of the specified array and the size of this deque. *

    *

    If this deque fits in the specified array with room to spare * (i.e., the array has more elements than this deque), the element in * the array immediately following the end of the deque is set to * {@code null}. *

    *

    Like the {@link #toArray()} method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. *

    *

    Suppose {@code x} is a deque known to contain only strings. * The following code can be used to dump the deque into a newly * allocated array of {@code String}: *

    *

         *     String[] y = x.toArray(new String[0]);
    * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. * * @param a the array into which the elements of the deque are to * be stored, if it is big enough; otherwise, a new array of the * same runtime type is allocated for this purpose * @return an array containing all of the elements in this deque * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in * this deque * @throws NullPointerException if the specified array is null */ @SuppressWarnings("unchecked") public T[] toArray(T[] a) { final ReentrantLock lock = this.lock; lock.lock(); try { if (a.length < count) a = (T[]) java.lang.reflect.Array.newInstance (a.getClass().getComponentType(), count); int k = 0; for (Node p = first; p != null; p = p.next) a[k++] = (T) p.item; if (a.length > k) a[k] = null; return a; } finally { lock.unlock(); } } public String toString() { final ReentrantLock lock = this.lock; lock.lock(); try { Node p = first; if (p == null) return "[]"; StringBuilder sb = new StringBuilder(); sb.append('['); for (; ; ) { E e = p.item; sb.append(e == this ? "(this Collection)" : e); p = p.next; if (p == null) return sb.append(']').toString(); sb.append(',').append(' '); } } finally { lock.unlock(); } } /** * Atomically removes all of the elements from this deque. * The deque will be empty after this call returns. */ public void clear() { final ReentrantLock lock = this.lock; lock.lock(); try { for (Node f = first; f != null; ) { f.item = null; Node n = f.next; f.prev = null; f.next = null; f = n; } first = last = null; count = 0; notFull.signalAll(); } finally { lock.unlock(); } } /** * Returns an iterator over the elements in this deque in proper sequence. * The elements will be returned in order from first (head) to last (tail). *

    *

    The returned iterator is a "weakly consistent" iterator that * will never throw {@link java.util.ConcurrentModificationException * ConcurrentModificationException}, and guarantees to traverse * elements as they existed upon construction of the iterator, and * may (but is not guaranteed to) reflect any modifications * subsequent to construction. * * @return an iterator over the elements in this deque in proper sequence */ public Iterator iterator() { return new Itr(); } /** * Returns an iterator over the elements in this deque in reverse * sequential order. The elements will be returned in order from * last (tail) to first (head). *

    *

    The returned iterator is a "weakly consistent" iterator that * will never throw {@link java.util.ConcurrentModificationException * ConcurrentModificationException}, and guarantees to traverse * elements as they existed upon construction of the iterator, and * may (but is not guaranteed to) reflect any modifications * subsequent to construction. * * @return an iterator over the elements in this deque in reverse order */ public Iterator descendingIterator() { return new DescendingItr(); } /** * Base class for Iterators for LinkedBlockingDeque */ private abstract class AbstractItr implements Iterator { /** * The next node to return in next() */ Node next; /** * nextItem holds on to item fields because once we claim that * an element exists in hasNext(), we must return item read * under lock (in advance()) even if it was in the process of * being removed when hasNext() was called. */ E nextItem; /** * Node returned by most recent call to next. Needed by remove. * Reset to null if this element is deleted by a call to remove. */ private Node lastRet; abstract Node firstNode(); abstract Node nextNode(Node n); AbstractItr() { // set to initial position final ReentrantLock lock = LinkedBlockingDeque.this.lock; lock.lock(); try { next = firstNode(); nextItem = (next == null) ? null : next.item; } finally { lock.unlock(); } } /** * Returns the successor node of the given non-null, but * possibly previously deleted, node. */ private Node succ(Node n) { // Chains of deleted nodes ending in null or self-links // are possible if multiple interior nodes are removed. for (; ; ) { Node s = nextNode(n); if (s == null) return null; else if (s.item != null) return s; else if (s == n) return firstNode(); else n = s; } } /** * Advances next. */ void advance() { final ReentrantLock lock = LinkedBlockingDeque.this.lock; lock.lock(); try { // assert next != null; next = succ(next); nextItem = (next == null) ? null : next.item; } finally { lock.unlock(); } } public boolean hasNext() { return next != null; } public E next() { if (next == null) throw new NoSuchElementException(); lastRet = next; E x = nextItem; advance(); return x; } public void remove() { Node n = lastRet; if (n == null) throw new IllegalStateException(); lastRet = null; final ReentrantLock lock = LinkedBlockingDeque.this.lock; lock.lock(); try { if (n.item != null) unlink(n); } finally { lock.unlock(); } } } /** * Forward iterator */ private class Itr extends AbstractItr { Node firstNode() { return first; } Node nextNode(Node n) { return n.next; } } /** * Descending iterator */ private class DescendingItr extends AbstractItr { Node firstNode() { return last; } Node nextNode(Node n) { return n.prev; } } /** * Save the state of this deque to a stream (that is, serialize it). * * @param s the stream * @serialData The capacity (int), followed by elements (each an * {@code Object}) in the proper order, followed by a null */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { final ReentrantLock lock = this.lock; lock.lock(); try { // Write out capacity and any hidden stuff s.defaultWriteObject(); // Write out all elements in the proper order. for (Node p = first; p != null; p = p.next) s.writeObject(p.item); // Use trailing null as sentinel s.writeObject(null); } finally { lock.unlock(); } } /** * Reconstitute this deque from a stream (that is, * deserialize it). * * @param s the stream */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); count = 0; first = null; last = null; // Read in all elements and place in queue for (; ; ) { @SuppressWarnings("unchecked") E item = (E) s.readObject(); if (item == null) break; add(item); } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/decode/BaseImageDecoder.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.decode; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.Matrix; import android.media.ExifInterface; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme; import com.nostra13.universalimageloader.utils.ImageSizeUtils; import com.nostra13.universalimageloader.utils.IoUtils; import com.nostra13.universalimageloader.utils.L; import java.io.IOException; import java.io.InputStream; /** * Decodes images to {@link Bitmap}, scales them to needed size * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see ImageDecodingInfo * @since 1.8.3 */ public class BaseImageDecoder implements ImageDecoder { protected static final String LOG_SUBSAMPLE_IMAGE = "Subsample original image (%1$s) to %2$s (scale = %3$d) [%4$s]"; protected static final String LOG_SCALE_IMAGE = "Scale subsampled image (%1$s) to %2$s (scale = %3$.5f) [%4$s]"; protected static final String LOG_ROTATE_IMAGE = "Rotate image on %1$d\u00B0 [%2$s]"; protected static final String LOG_FLIP_IMAGE = "Flip image horizontally [%s]"; protected static final String ERROR_NO_IMAGE_STREAM = "No stream for image [%s]"; protected static final String ERROR_CANT_DECODE_IMAGE = "Image can't be decoded [%s]"; protected final boolean loggingEnabled; /** * @param loggingEnabled Whether debug logs will be written to LogCat. Usually should match {@link * com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#writeDebugLogs() * ImageLoaderConfiguration.writeDebugLogs()} */ public BaseImageDecoder(boolean loggingEnabled) { this.loggingEnabled = loggingEnabled; } /** * Decodes image from URI into {@link Bitmap}. Image is scaled close to incoming {@linkplain ImageSize target size} * during decoding (depend on incoming parameters). * * @param decodingInfo Needed data for decoding image * @return Decoded bitmap * @throws IOException if some I/O exception occurs during image reading * @throws UnsupportedOperationException if image URI has unsupported scheme(protocol) */ @Override public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException { Bitmap decodedBitmap; ImageFileInfo imageInfo; InputStream imageStream = getImageStream(decodingInfo); if (imageStream == null) { L.e(ERROR_NO_IMAGE_STREAM, decodingInfo.getImageKey()); return null; } try { imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo); imageStream = resetStream(imageStream, decodingInfo); Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo); decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions); } finally { IoUtils.closeSilently(imageStream); } if (decodedBitmap == null) { L.e(ERROR_CANT_DECODE_IMAGE, decodingInfo.getImageKey()); } else { decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation, imageInfo.exif.flipHorizontal); } return decodedBitmap; } protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException { return decodingInfo.getDownloader().getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader()); } protected ImageFileInfo defineImageSizeAndRotation(InputStream imageStream, ImageDecodingInfo decodingInfo) throws IOException { Options options = new Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(imageStream, null, options); ExifInfo exif; String imageUri = decodingInfo.getImageUri(); if (decodingInfo.shouldConsiderExifParams() && canDefineExifParams(imageUri, options.outMimeType)) { exif = defineExifOrientation(imageUri); } else { exif = new ExifInfo(); } return new ImageFileInfo(new ImageSize(options.outWidth, options.outHeight, exif.rotation), exif); } private boolean canDefineExifParams(String imageUri, String mimeType) { return "image/jpeg".equalsIgnoreCase(mimeType) && (Scheme.ofUri(imageUri) == Scheme.FILE); } protected ExifInfo defineExifOrientation(String imageUri) { int rotation = 0; boolean flip = false; try { ExifInterface exif = new ExifInterface(Scheme.FILE.crop(imageUri)); int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (exifOrientation) { case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: flip = true; case ExifInterface.ORIENTATION_NORMAL: rotation = 0; break; case ExifInterface.ORIENTATION_TRANSVERSE: flip = true; case ExifInterface.ORIENTATION_ROTATE_90: rotation = 90; break; case ExifInterface.ORIENTATION_FLIP_VERTICAL: flip = true; case ExifInterface.ORIENTATION_ROTATE_180: rotation = 180; break; case ExifInterface.ORIENTATION_TRANSPOSE: flip = true; case ExifInterface.ORIENTATION_ROTATE_270: rotation = 270; break; } } catch (IOException e) { L.w("Can't read EXIF tags from file [%s]", imageUri); } return new ExifInfo(rotation, flip); } protected Options prepareDecodingOptions(ImageSize imageSize, ImageDecodingInfo decodingInfo) { ImageScaleType scaleType = decodingInfo.getImageScaleType(); int scale; if (scaleType == ImageScaleType.NONE) { scale = 1; } else if (scaleType == ImageScaleType.NONE_SAFE) { scale = ImageSizeUtils.computeMinImageSampleSize(imageSize); } else { ImageSize targetSize = decodingInfo.getTargetSize(); boolean powerOf2 = scaleType == ImageScaleType.IN_SAMPLE_POWER_OF_2; scale = ImageSizeUtils.computeImageSampleSize(imageSize, targetSize, decodingInfo.getViewScaleType(), powerOf2); } if (scale > 1 && loggingEnabled) { L.d(LOG_SUBSAMPLE_IMAGE, imageSize, imageSize.scaleDown(scale), scale, decodingInfo.getImageKey()); } Options decodingOptions = decodingInfo.getDecodingOptions(); decodingOptions.inSampleSize = scale; return decodingOptions; } protected InputStream resetStream(InputStream imageStream, ImageDecodingInfo decodingInfo) throws IOException { if (imageStream.markSupported()) { try { imageStream.reset(); return imageStream; } catch (IOException ignored) { } } IoUtils.closeSilently(imageStream); return getImageStream(decodingInfo); } protected Bitmap considerExactScaleAndOrientatiton(Bitmap subsampledBitmap, ImageDecodingInfo decodingInfo, int rotation, boolean flipHorizontal) { Matrix m = new Matrix(); // Scale to exact size if need ImageScaleType scaleType = decodingInfo.getImageScaleType(); if (scaleType == ImageScaleType.EXACTLY || scaleType == ImageScaleType.EXACTLY_STRETCHED) { ImageSize srcSize = new ImageSize(subsampledBitmap.getWidth(), subsampledBitmap.getHeight(), rotation); float scale = ImageSizeUtils.computeImageScale(srcSize, decodingInfo.getTargetSize(), decodingInfo .getViewScaleType(), scaleType == ImageScaleType.EXACTLY_STRETCHED); if (Float.compare(scale, 1f) != 0) { m.setScale(scale, scale); if (loggingEnabled) { L.d(LOG_SCALE_IMAGE, srcSize, srcSize.scale(scale), scale, decodingInfo.getImageKey()); } } } // Flip bitmap if need if (flipHorizontal) { m.postScale(-1, 1); if (loggingEnabled) L.d(LOG_FLIP_IMAGE, decodingInfo.getImageKey()); } // Rotate bitmap if need if (rotation != 0) { m.postRotate(rotation); if (loggingEnabled) L.d(LOG_ROTATE_IMAGE, rotation, decodingInfo.getImageKey()); } Bitmap finalBitmap = Bitmap.createBitmap(subsampledBitmap, 0, 0, subsampledBitmap.getWidth(), subsampledBitmap .getHeight(), m, true); if (finalBitmap != subsampledBitmap) { subsampledBitmap.recycle(); } return finalBitmap; } protected static class ExifInfo { public final int rotation; public final boolean flipHorizontal; protected ExifInfo() { this.rotation = 0; this.flipHorizontal = false; } protected ExifInfo(int rotation, boolean flipHorizontal) { this.rotation = rotation; this.flipHorizontal = flipHorizontal; } } protected static class ImageFileInfo { public final ImageSize imageSize; public final ExifInfo exif; protected ImageFileInfo(ImageSize imageSize, ExifInfo exif) { this.imageSize = imageSize; this.exif = exif; } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/decode/ImageDecoder.java ================================================ /******************************************************************************* * Copyright 2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.decode; import android.graphics.Bitmap; import java.io.IOException; /** * Provide decoding image to result {@link Bitmap}. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see ImageDecodingInfo * @since 1.8.3 */ public interface ImageDecoder { /** * Decodes image to {@link Bitmap} according target size and other parameters. * * @param imageDecodingInfo * @return * @throws IOException */ Bitmap decode(ImageDecodingInfo imageDecodingInfo) throws IOException; } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/decode/ImageDecodingInfo.java ================================================ /******************************************************************************* * Copyright 2013-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.decode; import android.annotation.TargetApi; import android.graphics.BitmapFactory.Options; import android.os.Build; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.ViewScaleType; import com.nostra13.universalimageloader.core.download.ImageDownloader; /** * Contains needed information for decoding image to Bitmap * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.3 */ public class ImageDecodingInfo { private final String imageKey; private final String imageUri; private final String originalImageUri; private final ImageSize targetSize; private final ImageScaleType imageScaleType; private final ViewScaleType viewScaleType; private final ImageDownloader downloader; private final Object extraForDownloader; private final boolean considerExifParams; private final Options decodingOptions; public ImageDecodingInfo(String imageKey, String imageUri, String originalImageUri, ImageSize targetSize, ViewScaleType viewScaleType, ImageDownloader downloader, DisplayImageOptions displayOptions) { this.imageKey = imageKey; this.imageUri = imageUri; this.originalImageUri = originalImageUri; this.targetSize = targetSize; this.imageScaleType = displayOptions.getImageScaleType(); this.viewScaleType = viewScaleType; this.downloader = downloader; this.extraForDownloader = displayOptions.getExtraForDownloader(); considerExifParams = displayOptions.isConsiderExifParams(); decodingOptions = new Options(); copyOptions(displayOptions.getDecodingOptions(), decodingOptions); } private void copyOptions(Options srcOptions, Options destOptions) { destOptions.inDensity = srcOptions.inDensity; destOptions.inDither = srcOptions.inDither; destOptions.inInputShareable = srcOptions.inInputShareable; destOptions.inJustDecodeBounds = srcOptions.inJustDecodeBounds; destOptions.inPreferredConfig = srcOptions.inPreferredConfig; destOptions.inPurgeable = srcOptions.inPurgeable; destOptions.inSampleSize = srcOptions.inSampleSize; destOptions.inScaled = srcOptions.inScaled; destOptions.inScreenDensity = srcOptions.inScreenDensity; destOptions.inTargetDensity = srcOptions.inTargetDensity; destOptions.inTempStorage = srcOptions.inTempStorage; if (Build.VERSION.SDK_INT >= 10) copyOptions10(srcOptions, destOptions); if (Build.VERSION.SDK_INT >= 11) copyOptions11(srcOptions, destOptions); } @TargetApi(10) private void copyOptions10(Options srcOptions, Options destOptions) { destOptions.inPreferQualityOverSpeed = srcOptions.inPreferQualityOverSpeed; } @TargetApi(11) private void copyOptions11(Options srcOptions, Options destOptions) { destOptions.inBitmap = srcOptions.inBitmap; destOptions.inMutable = srcOptions.inMutable; } /** @return Original {@linkplain com.nostra13.universalimageloader.utils.MemoryCacheUtils#generateKey(String, ImageSize) image key} (used in memory cache). */ public String getImageKey() { return imageKey; } /** @return Image URI for decoding (usually image from disk cache) */ public String getImageUri() { return imageUri; } /** @return The original image URI which was passed to ImageLoader */ public String getOriginalImageUri() { return originalImageUri; } /** * @return Target size for image. Decoded bitmap should close to this size according to {@linkplain ImageScaleType * image scale type} and {@linkplain ViewScaleType view scale type}. */ public ImageSize getTargetSize() { return targetSize; } /** * @return {@linkplain ImageScaleType Scale type for image sampling and scaling}. This parameter affects result size * of decoded bitmap. */ public ImageScaleType getImageScaleType() { return imageScaleType; } /** @return {@linkplain ViewScaleType View scale type}. This parameter affects result size of decoded bitmap. */ public ViewScaleType getViewScaleType() { return viewScaleType; } /** @return Downloader for image loading */ public ImageDownloader getDownloader() { return downloader; } /** @return Auxiliary object for downloader */ public Object getExtraForDownloader() { return extraForDownloader; } /** @return true - if EXIF params of image should be considered; false - otherwise */ public boolean shouldConsiderExifParams() { return considerExifParams; } /** @return Decoding options */ public Options getDecodingOptions() { return decodingOptions; } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/BitmapDisplayer.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.display; import android.graphics.Bitmap; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.imageaware.ImageAware; /** * Displays {@link Bitmap} in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}. Implementations can * apply some changes to Bitmap or any animation for displaying Bitmap.
    * Implementations have to be thread-safe. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see com.nostra13.universalimageloader.core.imageaware.ImageAware * @see com.nostra13.universalimageloader.core.assist.LoadedFrom * @since 1.5.6 */ public interface BitmapDisplayer { /** * Displays bitmap in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}. * NOTE: This method is called on UI thread so it's strongly recommended not to do any heavy work in it. * * @param bitmap Source bitmap * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} to * display Bitmap * @param loadedFrom Source of loaded image */ void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/CircleBitmapDisplayer.java ================================================ /******************************************************************************* * Copyright 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.display; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.Drawable; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import com.nostra13.universalimageloader.core.imageaware.ImageViewAware; /** * Can display bitmap cropped by a circle. This implementation works only with ImageViews wrapped * in ImageViewAware. *
    * If this implementation doesn't meet your needs then consider * RoundedImageView or * CircularImageView projects for usage. * * @author Qualtagh, Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.5 */ public class CircleBitmapDisplayer implements BitmapDisplayer { protected final Integer strokeColor; protected final float strokeWidth; public CircleBitmapDisplayer() { this(null); } public CircleBitmapDisplayer(Integer strokeColor) { this(strokeColor, 0); } public CircleBitmapDisplayer(Integer strokeColor, float strokeWidth) { this.strokeColor = strokeColor; this.strokeWidth = strokeWidth; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { if (!(imageAware instanceof ImageViewAware)) { throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected."); } imageAware.setImageDrawable(new CircleDrawable(bitmap, strokeColor, strokeWidth)); } public static class CircleDrawable extends Drawable { protected float radius; protected final RectF mRect = new RectF(); protected final RectF mBitmapRect; protected final BitmapShader bitmapShader; protected final Paint paint; protected final Paint strokePaint; protected final float strokeWidth; protected float strokeRadius; public CircleDrawable(Bitmap bitmap, Integer strokeColor, float strokeWidth) { int diameter = Math.min(bitmap.getWidth(), bitmap.getHeight()); radius = diameter / 2f; bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 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); paint.setShader(bitmapShader); paint.setFilterBitmap(true); paint.setDither(true); if (strokeColor == null) { strokePaint = null; } else { strokePaint = new Paint(); strokePaint.setStyle(Paint.Style.STROKE); strokePaint.setColor(strokeColor); strokePaint.setStrokeWidth(strokeWidth); strokePaint.setAntiAlias(true); } this.strokeWidth = strokeWidth; strokeRadius = radius - strokeWidth / 2; } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); mRect.set(0, 0, bounds.width(), bounds.height()); radius = Math.min(bounds.width(), bounds.height()) / 2; strokeRadius = radius - strokeWidth / 2; // Resize the original bitmap to fit the new bound Matrix shaderMatrix = new Matrix(); shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL); bitmapShader.setLocalMatrix(shaderMatrix); } @Override public void draw(Canvas canvas) { canvas.drawCircle(radius, radius, radius, paint); if (strokePaint != null) { canvas.drawCircle(radius, radius, strokeRadius, strokePaint); } } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public void setAlpha(int alpha) { paint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter cf) { paint.setColorFilter(cf); } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/FadeInBitmapDisplayer.java ================================================ /******************************************************************************* * Copyright 2011-2014 Sergey Tarasevich, Daniel Martí * * 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.nostra13.universalimageloader.core.display; import android.graphics.Bitmap; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.imageaware.ImageAware; /** * Displays image with "fade in" animation * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com), Daniel Martí * @since 1.6.4 */ public class FadeInBitmapDisplayer implements BitmapDisplayer { private final int durationMillis; private final boolean animateFromNetwork; private final boolean animateFromDisk; private final boolean animateFromMemory; /** * @param durationMillis Duration of "fade-in" animation (in milliseconds) */ public FadeInBitmapDisplayer(int durationMillis) { this(durationMillis, true, true, true); } /** * @param durationMillis Duration of "fade-in" animation (in milliseconds) * @param animateFromNetwork Whether animation should be played if image is loaded from network * @param animateFromDisk Whether animation should be played if image is loaded from disk cache * @param animateFromMemory Whether animation should be played if image is loaded from memory cache */ public FadeInBitmapDisplayer(int durationMillis, boolean animateFromNetwork, boolean animateFromDisk, boolean animateFromMemory) { this.durationMillis = durationMillis; this.animateFromNetwork = animateFromNetwork; this.animateFromDisk = animateFromDisk; this.animateFromMemory = animateFromMemory; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { imageAware.setImageBitmap(bitmap); if ((animateFromNetwork && loadedFrom == LoadedFrom.NETWORK) || (animateFromDisk && loadedFrom == LoadedFrom.DISC_CACHE) || (animateFromMemory && loadedFrom == LoadedFrom.MEMORY_CACHE)) { animate(imageAware.getWrappedView(), durationMillis); } } /** * Animates {@link ImageView} with "fade-in" effect * * @param imageView {@link ImageView} which display image in * @param durationMillis The length of the animation in milliseconds */ public static void animate(View imageView, int durationMillis) { if (imageView != null) { AlphaAnimation fadeImage = new AlphaAnimation(0, 1); fadeImage.setDuration(durationMillis); fadeImage.setInterpolator(new DecelerateInterpolator()); imageView.startAnimation(fadeImage); } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/RoundedBitmapDisplayer.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.display; import android.graphics.*; import android.graphics.drawable.Drawable; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import com.nostra13.universalimageloader.core.imageaware.ImageViewAware; /** * Can display bitmap with rounded corners. This implementation works only with ImageViews wrapped * in ImageViewAware. *
    * This implementation is inspired by * * Romain Guy's article. It rounds images using custom drawable drawing. Original bitmap isn't changed. *
    *
    * If this implementation doesn't meet your needs then consider * RoundedImageView or * CircularImageView projects for usage. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.5.6 */ public class RoundedBitmapDisplayer implements BitmapDisplayer { protected final int cornerRadius; protected final int margin; public RoundedBitmapDisplayer(int cornerRadiusPixels) { this(cornerRadiusPixels, 0); } public RoundedBitmapDisplayer(int cornerRadiusPixels, int marginPixels) { this.cornerRadius = cornerRadiusPixels; this.margin = marginPixels; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { if (!(imageAware instanceof ImageViewAware)) { throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected."); } imageAware.setImageDrawable(new RoundedDrawable(bitmap, cornerRadius, margin)); } public static class RoundedDrawable extends Drawable { protected final float cornerRadius; protected final int margin; protected final RectF mRect = new RectF(), mBitmapRect; protected final BitmapShader bitmapShader; protected final Paint paint; public RoundedDrawable(Bitmap bitmap, int cornerRadius, int margin) { this.cornerRadius = cornerRadius; this.margin = margin; bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mBitmapRect = new RectF (margin, margin, bitmap.getWidth() - margin, bitmap.getHeight() - margin); paint = new Paint(); paint.setAntiAlias(true); paint.setShader(bitmapShader); paint.setFilterBitmap(true); paint.setDither(true); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); mRect.set(margin, margin, bounds.width() - margin, bounds.height() - margin); // Resize the original bitmap to fit the new bound Matrix shaderMatrix = new Matrix(); shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL); bitmapShader.setLocalMatrix(shaderMatrix); } @Override public void draw(Canvas canvas) { canvas.drawRoundRect(mRect, cornerRadius, cornerRadius, paint); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public void setAlpha(int alpha) { paint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter cf) { paint.setColorFilter(cf); } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/RoundedVignetteBitmapDisplayer.java ================================================ /******************************************************************************* * Copyright 2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.display; import android.graphics.*; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import com.nostra13.universalimageloader.core.imageaware.ImageViewAware; /** * Can display bitmap with rounded corners and vignette effect. This implementation works only with ImageViews wrapped * in ImageViewAware. *
    * This implementation is inspired by * * Romain Guy's article. It rounds images using custom drawable drawing. Original bitmap isn't changed. *
    *
    * If this implementation doesn't meet your needs then consider * this project for usage. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.1 */ public class RoundedVignetteBitmapDisplayer extends RoundedBitmapDisplayer { public RoundedVignetteBitmapDisplayer(int cornerRadiusPixels, int marginPixels) { super(cornerRadiusPixels, marginPixels); } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { if (!(imageAware instanceof ImageViewAware)) { throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected."); } imageAware.setImageDrawable(new RoundedVignetteDrawable(bitmap, cornerRadius, margin)); } protected static class RoundedVignetteDrawable extends RoundedDrawable { RoundedVignetteDrawable(Bitmap bitmap, int cornerRadius, int margin) { super(bitmap, cornerRadius, margin); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); RadialGradient vignette = new RadialGradient( mRect.centerX(), mRect.centerY() * 1.0f / 0.7f, mRect.centerX() * 1.3f, new int[]{0, 0, 0x7f000000}, new float[]{0.0f, 0.7f, 1.0f}, Shader.TileMode.CLAMP); Matrix oval = new Matrix(); oval.setScale(1.0f, 0.7f); vignette.setLocalMatrix(oval); paint.setShader(new ComposeShader(bitmapShader, vignette, PorterDuff.Mode.SRC_OVER)); } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/SimpleBitmapDisplayer.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.display; import android.graphics.Bitmap; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.imageaware.ImageAware; /** * Just displays {@link Bitmap} in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.5.6 */ public final class SimpleBitmapDisplayer implements BitmapDisplayer { @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { imageAware.setImageBitmap(bitmap); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.download; import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.Build; import android.provider.ContactsContract; import android.provider.MediaStore; import android.webkit.MimeTypeMap; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.assist.ContentLengthInputStream; import com.nostra13.universalimageloader.utils.IoUtils; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; /** * Provides retrieving of {@link InputStream} of image by URI from network or file system or app resources.
    * {@link URLConnection} is used to retrieve image stream from network. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.0 */ public class BaseImageDownloader implements ImageDownloader { /** {@value} */ public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = 5 * 1000; // milliseconds /** {@value} */ public static final int DEFAULT_HTTP_READ_TIMEOUT = 20 * 1000; // milliseconds /** {@value} */ protected static final int BUFFER_SIZE = 32 * 1024; // 32 Kb /** {@value} */ protected static final String ALLOWED_URI_CHARS = "@#&=*+-_.,:!?()/~'%"; protected static final int MAX_REDIRECT_COUNT = 5; protected static final String CONTENT_CONTACTS_URI_PREFIX = "content://com.android.contacts/"; private static final String ERROR_UNSUPPORTED_SCHEME = "UIL doesn't support scheme(protocol) by default [%s]. " + "You should implement this support yourself (BaseImageDownloader.getStreamFromOtherSource(...))"; protected final Context context; protected final int connectTimeout; protected final int readTimeout; public BaseImageDownloader(Context context) { this(context, DEFAULT_HTTP_CONNECT_TIMEOUT, DEFAULT_HTTP_READ_TIMEOUT); } public BaseImageDownloader(Context context, int connectTimeout, int readTimeout) { this.context = context.getApplicationContext(); this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; } @Override public InputStream getStream(String imageUri, Object extra) throws IOException { switch (Scheme.ofUri(imageUri)) { case HTTP: case HTTPS: return getStreamFromNetwork(imageUri, extra); case FILE: return getStreamFromFile(imageUri, extra); case CONTENT: return getStreamFromContent(imageUri, extra); case ASSETS: return getStreamFromAssets(imageUri, extra); case DRAWABLE: return getStreamFromDrawable(imageUri, extra); case UNKNOWN: default: return getStreamFromOtherSource(imageUri, extra); } } /** * Retrieves {@link InputStream} of image by URI (image is located in the network). * * @param imageUri Image URI * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object) * DisplayImageOptions.extraForDownloader(Object)}; can be null * @return {@link InputStream} of image * @throws IOException if some I/O error occurs during network request or if no InputStream could be created for * URL. */ protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException { HttpURLConnection conn = createConnection(imageUri, extra); int redirectCount = 0; while (conn.getResponseCode() / 100 == 3 && redirectCount < MAX_REDIRECT_COUNT) { conn = createConnection(conn.getHeaderField("Location"), extra); redirectCount++; } InputStream imageStream; try { imageStream = conn.getInputStream(); } catch (IOException e) { // Read all data to allow reuse connection (http://bit.ly/1ad35PY) IoUtils.readAndCloseStream(conn.getErrorStream()); throw e; } if (!shouldBeProcessed(conn)) { IoUtils.closeSilently(imageStream); throw new IOException("Image request failed with response code " + conn.getResponseCode()); } return new ContentLengthInputStream(new BufferedInputStream(imageStream, BUFFER_SIZE), conn.getContentLength()); } /** * @param conn Opened request connection (response code is available) * @return true - if data from connection is correct and should be read and processed; * false - if response contains irrelevant data and shouldn't be processed * @throws IOException */ protected boolean shouldBeProcessed(HttpURLConnection conn) throws IOException { return conn.getResponseCode() == 200; } /** * Create {@linkplain HttpURLConnection HTTP connection} for incoming URL * * @param url URL to connect to * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object) * DisplayImageOptions.extraForDownloader(Object)}; can be null * @return {@linkplain HttpURLConnection Connection} for incoming URL. Connection isn't established so it still configurable. * @throws IOException if some I/O error occurs during network request or if no InputStream could be created for * URL. */ protected HttpURLConnection createConnection(String url, Object extra) throws IOException { String encodedUrl = Uri.encode(url, ALLOWED_URI_CHARS); HttpURLConnection conn = (HttpURLConnection) new URL(encodedUrl).openConnection(); conn.setConnectTimeout(connectTimeout); conn.setReadTimeout(readTimeout); return conn; } /** * Retrieves {@link InputStream} of image by URI (image is located on the local file system or SD card). * * @param imageUri Image URI * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object) * DisplayImageOptions.extraForDownloader(Object)}; can be null * @return {@link InputStream} of image * @throws IOException if some I/O error occurs reading from file system */ protected InputStream getStreamFromFile(String imageUri, Object extra) throws IOException { String filePath = Scheme.FILE.crop(imageUri); if (isVideoFileUri(imageUri)) { return getVideoThumbnailStream(filePath); } else { BufferedInputStream imageStream = new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE); return new ContentLengthInputStream(imageStream, (int) new File(filePath).length()); } } @TargetApi(Build.VERSION_CODES.FROYO) private InputStream getVideoThumbnailStream(String filePath) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { Bitmap bitmap = ThumbnailUtils .createVideoThumbnail(filePath, MediaStore.Images.Thumbnails.FULL_SCREEN_KIND); if (bitmap != null) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(CompressFormat.PNG, 0, bos); return new ByteArrayInputStream(bos.toByteArray()); } } return null; } /** * Retrieves {@link InputStream} of image by URI (image is accessed using {@link ContentResolver}). * * @param imageUri Image URI * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object) * DisplayImageOptions.extraForDownloader(Object)}; can be null * @return {@link InputStream} of image * @throws FileNotFoundException if the provided URI could not be opened */ protected InputStream getStreamFromContent(String imageUri, Object extra) throws FileNotFoundException { ContentResolver res = context.getContentResolver(); Uri uri = Uri.parse(imageUri); if (isVideoContentUri(uri)) { // video thumbnail Long origId = Long.valueOf(uri.getLastPathSegment()); Bitmap bitmap = MediaStore.Video.Thumbnails .getThumbnail(res, origId, MediaStore.Images.Thumbnails.MINI_KIND, null); if (bitmap != null) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(CompressFormat.PNG, 0, bos); return new ByteArrayInputStream(bos.toByteArray()); } } else if (imageUri.startsWith(CONTENT_CONTACTS_URI_PREFIX)) { // contacts photo return getContactPhotoStream(uri); } return res.openInputStream(uri); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) protected InputStream getContactPhotoStream(Uri uri) { ContentResolver res = context.getContentResolver(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { return ContactsContract.Contacts.openContactPhotoInputStream(res, uri, true); } else { return ContactsContract.Contacts.openContactPhotoInputStream(res, uri); } } /** * Retrieves {@link InputStream} of image by URI (image is located in assets of application). * * @param imageUri Image URI * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object) * DisplayImageOptions.extraForDownloader(Object)}; can be null * @return {@link InputStream} of image * @throws IOException if some I/O error occurs file reading */ protected InputStream getStreamFromAssets(String imageUri, Object extra) throws IOException { String filePath = Scheme.ASSETS.crop(imageUri); return context.getAssets().open(filePath); } /** * Retrieves {@link InputStream} of image by URI (image is located in drawable resources of application). * * @param imageUri Image URI * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object) * DisplayImageOptions.extraForDownloader(Object)}; can be null * @return {@link InputStream} of image */ protected InputStream getStreamFromDrawable(String imageUri, Object extra) { String drawableIdString = Scheme.DRAWABLE.crop(imageUri); int drawableId = Integer.parseInt(drawableIdString); return context.getResources().openRawResource(drawableId); } /** * Retrieves {@link InputStream} of image by URI from other source with unsupported scheme. Should be overriden by * successors to implement image downloading from special sources.
    * This method is called only if image URI has unsupported scheme. Throws {@link UnsupportedOperationException} by * default. * * @param imageUri Image URI * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object) * DisplayImageOptions.extraForDownloader(Object)}; can be null * @return {@link InputStream} of image * @throws IOException if some I/O error occurs * @throws UnsupportedOperationException if image URI has unsupported scheme(protocol) */ protected InputStream getStreamFromOtherSource(String imageUri, Object extra) throws IOException { throw new UnsupportedOperationException(String.format(ERROR_UNSUPPORTED_SCHEME, imageUri)); } private boolean isVideoContentUri(Uri uri) { String mimeType = context.getContentResolver().getType(uri); return mimeType != null && mimeType.startsWith("video/"); } private boolean isVideoFileUri(String uri) { String extension = MimeTypeMap.getFileExtensionFromUrl(Uri.encode(uri)); String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); return mimeType != null && mimeType.startsWith("video/"); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/download/ImageDownloader.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.download; import com.nostra13.universalimageloader.core.DisplayImageOptions; import java.io.IOException; import java.io.InputStream; import java.util.Locale; /** * Provides retrieving of {@link InputStream} of image by URI.
    * Implementations have to be thread-safe. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.4.0 */ public interface ImageDownloader { /** * Retrieves {@link InputStream} of image by URI. * * @param imageUri Image URI * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object) * DisplayImageOptions.extraForDownloader(Object)}; can be null * @return {@link InputStream} of image * @throws IOException if some I/O error occurs during getting image stream * @throws UnsupportedOperationException if image URI has unsupported scheme(protocol) */ InputStream getStream(String imageUri, Object extra) throws IOException; /** Represents supported schemes(protocols) of URI. Provides convenient methods for work with schemes and URIs. */ public enum Scheme { HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"), ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN(""); private String scheme; private String uriPrefix; Scheme(String scheme) { this.scheme = scheme; uriPrefix = scheme + "://"; } /** * Defines scheme of incoming URI * * @param uri URI for scheme detection * @return Scheme of incoming URI */ public static Scheme ofUri(String uri) { if (uri != null) { for (Scheme s : values()) { if (s.belongsTo(uri)) { return s; } } } return UNKNOWN; } private boolean belongsTo(String uri) { return uri.toLowerCase(Locale.US).startsWith(uriPrefix); } /** Appends scheme to incoming path */ public String wrap(String path) { return uriPrefix + path; } /** Removed scheme part ("scheme://") from incoming URI */ public String crop(String uri) { if (!belongsTo(uri)) { throw new IllegalArgumentException(String.format("URI [%1$s] doesn't have expected scheme [%2$s]", uri, scheme)); } return uri.substring(uriPrefix.length()); } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ImageAware.java ================================================ /******************************************************************************* * Copyright 2013-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.imageaware; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.view.View; import com.nostra13.universalimageloader.core.assist.ViewScaleType; /** * Represents image aware view which provides all needed properties and behavior for image processing and displaying * through {@link com.nostra13.universalimageloader.core.ImageLoader ImageLoader}. * It can wrap any Android {@link android.view.View View} which can be accessed by {@link #getWrappedView()}. Wrapped * view is returned in {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener ImageLoadingListener}'s * callbacks. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see ViewAware * @see ImageViewAware * @see NonViewAware * @since 1.9.0 */ public interface ImageAware { /** * Returns width of image aware view. This value is used to define scale size for original image. * Can return 0 if width is undefined.
    * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread. */ int getWidth(); /** * Returns height of image aware view. This value is used to define scale size for original image. * Can return 0 if height is undefined.
    * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread. */ int getHeight(); /** * Returns {@linkplain com.nostra13.universalimageloader.core.assist.ViewScaleType scale type} which is used for * scaling image for this image aware view. Must NOT return null. */ ViewScaleType getScaleType(); /** * Returns wrapped Android {@link android.view.View View}. Can return null if no view is wrapped or view was * collected by GC.
    * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread. */ View getWrappedView(); /** * Returns a flag whether image aware view is collected by GC or whatsoever. If so then ImageLoader stop processing * of task for this image aware view and fires * {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener#onLoadingCancelled(String, * android.view.View) ImageLoadingListener#onLoadingCancelled(String, View)} callback.
    * Mey be called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread. * * @return true - if view is collected by GC and ImageLoader should stop processing this image aware view; * false - otherwise */ boolean isCollected(); /** * Returns ID of image aware view. Point of ID is similar to Object's hashCode. This ID should be unique for every * image view instance and should be the same for same instances. This ID identifies processing task in ImageLoader * so ImageLoader won't process two image aware views with the same ID in one time. When ImageLoader get new task * it cancels old task with this ID (if any) and starts new task. *

    * It's reasonable to return hash code of wrapped view (if any) to prevent displaying non-actual images in view * because of view re-using. */ int getId(); /** * Sets image drawable into this image aware view.
    * Displays drawable in this image aware view * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageForEmptyUri( *android.graphics.drawable.Drawable) for empty Uri}, * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageOnLoading( *android.graphics.drawable.Drawable) on loading} or * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageOnFail( *android.graphics.drawable.Drawable) on loading fail}. These drawables can be specified in * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions display options}.
    * Also can be called in {@link com.nostra13.universalimageloader.core.display.BitmapDisplayer BitmapDisplayer}.< br /> * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread. * * @return true if drawable was set successfully; false - otherwise */ boolean setImageDrawable(Drawable drawable); /** * Sets image bitmap into this image aware view.
    * Displays loaded and decoded image {@link android.graphics.Bitmap} in this image view aware. * Actually it's used only in * {@link com.nostra13.universalimageloader.core.display.BitmapDisplayer BitmapDisplayer}.< br /> * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread. * * @return true if bitmap was set successfully; false - otherwise */ boolean setImageBitmap(Bitmap bitmap); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java ================================================ /******************************************************************************* * Copyright 2013-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.imageaware; import android.graphics.Bitmap; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.view.View; import android.widget.ImageView; import com.nostra13.universalimageloader.core.assist.ViewScaleType; import com.nostra13.universalimageloader.utils.L; import java.lang.reflect.Field; /** * Wrapper for Android {@link android.widget.ImageView ImageView}. Keeps weak reference of ImageView to prevent memory * leaks. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.0 */ public class ImageViewAware extends ViewAware { /** * Constructor.
    * References {@link #ImageViewAware(android.widget.ImageView, boolean) ImageViewAware(imageView, true)}. * * @param imageView {@link android.widget.ImageView ImageView} to work with */ public ImageViewAware(ImageView imageView) { super(imageView); } /** * Constructor * * @param imageView {@link android.widget.ImageView ImageView} to work with * @param checkActualViewSize true - then {@link #getWidth()} and {@link #getHeight()} will check actual * size of ImageView. It can cause known issues like * this. * But it helps to save memory because memory cache keeps bitmaps of actual (less in * general) size. *

    * false - then {@link #getWidth()} and {@link #getHeight()} will NOT * consider actual size of ImageView, just layout parameters.
    If you set 'false' * it's recommended 'android:layout_width' and 'android:layout_height' (or * 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to * save memory. *

    */ public ImageViewAware(ImageView imageView, boolean checkActualViewSize) { super(imageView, checkActualViewSize); } /** * {@inheritDoc} *
    * 3) Get maxWidth. */ @Override public int getWidth() { int width = super.getWidth(); if (width <= 0) { ImageView imageView = (ImageView) viewRef.get(); if (imageView != null) { width = imageView.getMaxWidth(); } } return width; } /** * {@inheritDoc} *
    * 3) Get maxHeight */ @Override public int getHeight() { int height = super.getHeight(); if (height <= 0) { ImageView imageView = (ImageView) viewRef.get(); if (imageView != null) { height = imageView.getMaxHeight(); } } return height; } @Override public ViewScaleType getScaleType() { ImageView imageView = (ImageView) viewRef.get(); if (imageView != null) { return ViewScaleType.fromImageView(imageView); } return super.getScaleType(); } @Override public ImageView getWrappedView() { return (ImageView) super.getWrappedView(); } @Override protected void setImageDrawableInto(Drawable drawable, View view) { ((ImageView) view).setImageDrawable(drawable); if (drawable instanceof AnimationDrawable) { ((AnimationDrawable)drawable).start(); } } @Override protected void setImageBitmapInto(Bitmap bitmap, View view) { ((ImageView) view).setImageBitmap(bitmap); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/imageaware/NonViewAware.java ================================================ /******************************************************************************* * Copyright 2013-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.imageaware; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.view.View; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.ViewScaleType; /** * ImageAware which provides needed info for processing of original image but do nothing for displaying image. It's * used when user need just load and decode image and get it in {@linkplain * com.nostra13.universalimageloader.core.listener.ImageLoadingListener#onLoadingComplete(String, android.view.View, * android.graphics.Bitmap) callback}. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.0 */ public class NonViewAware implements ImageAware { protected final String imageUri; protected final ImageSize imageSize; protected final ViewScaleType scaleType; public NonViewAware(ImageSize imageSize, ViewScaleType scaleType) { this(null, imageSize, scaleType); } public NonViewAware(String imageUri, ImageSize imageSize, ViewScaleType scaleType) { if (imageSize == null) throw new IllegalArgumentException("imageSize must not be null"); if (scaleType == null) throw new IllegalArgumentException("scaleType must not be null"); this.imageUri = imageUri; this.imageSize = imageSize; this.scaleType = scaleType; } @Override public int getWidth() { return imageSize.getWidth(); } @Override public int getHeight() { return imageSize.getHeight(); } @Override public ViewScaleType getScaleType() { return scaleType; } @Override public View getWrappedView() { return null; } @Override public boolean isCollected() { return false; } @Override public int getId() { return TextUtils.isEmpty(imageUri) ? super.hashCode() : imageUri.hashCode(); } @Override public boolean setImageDrawable(Drawable drawable) { // Do nothing return true; } @Override public boolean setImageBitmap(Bitmap bitmap) { // Do nothing return true; } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ViewAware.java ================================================ /******************************************************************************* * Copyright 2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.imageaware; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.os.Looper; import android.view.View; import android.view.ViewGroup; import com.nostra13.universalimageloader.core.assist.ViewScaleType; import com.nostra13.universalimageloader.utils.L; import java.lang.ref.Reference; import java.lang.ref.WeakReference; /** * Wrapper for Android {@link android.view.View View}. Keeps weak reference of View to prevent memory leaks. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.2 */ public abstract class ViewAware implements ImageAware { public static final String WARN_CANT_SET_DRAWABLE = "Can't set a drawable into view. You should call ImageLoader on UI thread for it."; public static final String WARN_CANT_SET_BITMAP = "Can't set a bitmap into view. You should call ImageLoader on UI thread for it."; protected Reference viewRef; protected boolean checkActualViewSize; /** * Constructor.
    * References {@link #ViewAware(android.view.View, boolean) ImageViewAware(imageView, true)}. * * @param view {@link android.view.View View} to work with */ public ViewAware(View view) { this(view, true); } /** * Constructor * * @param view {@link android.view.View View} to work with * @param checkActualViewSize true - then {@link #getWidth()} and {@link #getHeight()} will check actual * size of View. It can cause known issues like * this. * But it helps to save memory because memory cache keeps bitmaps of actual (less in * general) size. *

    * false - then {@link #getWidth()} and {@link #getHeight()} will NOT * consider actual size of View, just layout parameters.
    If you set 'false' * it's recommended 'android:layout_width' and 'android:layout_height' (or * 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to * save memory. */ public ViewAware(View view, boolean checkActualViewSize) { if (view == null) throw new IllegalArgumentException("view must not be null"); this.viewRef = new WeakReference(view); this.checkActualViewSize = checkActualViewSize; } /** * {@inheritDoc} *

    * Width is defined by target {@link android.view.View view} parameters, configuration * parameters or device display dimensions.
    * Size computing algorithm (go by steps until get non-zero value):
    * 1) Get the actual drawn getWidth() of the View
    * 2) Get layout_width */ @Override public int getWidth() { View view = viewRef.get(); if (view != null) { final ViewGroup.LayoutParams params = view.getLayoutParams(); int width = 0; if (checkActualViewSize && params != null && params.width != ViewGroup.LayoutParams.WRAP_CONTENT) { width = view.getWidth(); // Get actual image width } if (width <= 0 && params != null) width = params.width; // Get layout width parameter return width; } return 0; } /** * {@inheritDoc} *

    * Height is defined by target {@link android.view.View view} parameters, configuration * parameters or device display dimensions.
    * Size computing algorithm (go by steps until get non-zero value):
    * 1) Get the actual drawn getHeight() of the View
    * 2) Get layout_height */ @Override public int getHeight() { View view = viewRef.get(); if (view != null) { final ViewGroup.LayoutParams params = view.getLayoutParams(); int height = 0; if (checkActualViewSize && params != null && params.height != ViewGroup.LayoutParams.WRAP_CONTENT) { height = view.getHeight(); // Get actual image height } if (height <= 0 && params != null) height = params.height; // Get layout height parameter return height; } return 0; } @Override public ViewScaleType getScaleType() { return ViewScaleType.CROP; } @Override public View getWrappedView() { return viewRef.get(); } @Override public boolean isCollected() { return viewRef.get() == null; } @Override public int getId() { View view = viewRef.get(); return view == null ? super.hashCode() : view.hashCode(); } @Override public boolean setImageDrawable(Drawable drawable) { if (Looper.myLooper() == Looper.getMainLooper()) { View view = viewRef.get(); if (view != null) { setImageDrawableInto(drawable, view); return true; } } else { L.w(WARN_CANT_SET_DRAWABLE); } return false; } @Override public boolean setImageBitmap(Bitmap bitmap) { if (Looper.myLooper() == Looper.getMainLooper()) { View view = viewRef.get(); if (view != null) { setImageBitmapInto(bitmap, view); return true; } } else { L.w(WARN_CANT_SET_BITMAP); } return false; } /** * Should set drawable into incoming view. Incoming view is guaranteed not null.
    * This method is called on UI thread. */ protected abstract void setImageDrawableInto(Drawable drawable, View view); /** * Should set Bitmap into incoming view. Incoming view is guaranteed not null.< br /> * This method is called on UI thread. */ protected abstract void setImageBitmapInto(Bitmap bitmap, View view); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/listener/ImageLoadingListener.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.listener; import android.graphics.Bitmap; import android.view.View; import com.nostra13.universalimageloader.core.assist.FailReason; /** * Listener for image loading process.
    * You can use {@link SimpleImageLoadingListener} for implementing only needed methods. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @see SimpleImageLoadingListener * @see com.nostra13.universalimageloader.core.assist.FailReason * @since 1.0.0 */ public interface ImageLoadingListener { /** * Is called when image loading task was started * * @param imageUri Loading image URI * @param view View for image */ void onLoadingStarted(String imageUri, View view); /** * Is called when an error was occurred during image loading * * @param imageUri Loading image URI * @param view View for image. Can be null. * @param failReason {@linkplain com.nostra13.universalimageloader.core.assist.FailReason The reason} why image * loading was failed */ void onLoadingFailed(String imageUri, View view, FailReason failReason); /** * Is called when image is loaded successfully (and displayed in View if one was specified) * * @param imageUri Loaded image URI * @param view View for image. Can be null. * @param loadedImage Bitmap of loaded and decoded image */ void onLoadingComplete(String imageUri, View view, Bitmap loadedImage); /** * Is called when image loading task was cancelled because View for image was reused in newer task * * @param imageUri Loading image URI * @param view View for image. Can be null. */ void onLoadingCancelled(String imageUri, View view); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener.java ================================================ /******************************************************************************* * Copyright 2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.listener; import android.view.View; /** * Listener for image loading progress. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.9.1 */ public interface ImageLoadingProgressListener { /** * Is called when image loading progress changed. * * @param imageUri Image URI * @param view View for image. Can be null. * @param current Downloaded size in bytes * @param total Total size in bytes */ void onProgressUpdate(String imageUri, View view, int current, int total); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/listener/PauseOnScrollListener.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.listener; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.GridView; import android.widget.ListView; import com.nostra13.universalimageloader.core.ImageLoader; /** * Listener-helper for {@linkplain AbsListView list views} ({@link ListView}, {@link GridView}) which can * {@linkplain ImageLoader#pause() pause ImageLoader's tasks} while list view is scrolling (touch scrolling and/or * fling). It prevents redundant loadings.
    * Set it to your list view's {@link AbsListView#setOnScrollListener(OnScrollListener) setOnScrollListener(...)}.
    * This listener can wrap your custom {@linkplain OnScrollListener listener}. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.7.0 */ public class PauseOnScrollListener implements OnScrollListener { private ImageLoader imageLoader; private final boolean pauseOnScroll; private final boolean pauseOnFling; private final OnScrollListener externalListener; /** * Constructor * * @param imageLoader {@linkplain ImageLoader} instance for controlling * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling */ public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling) { this(imageLoader, pauseOnScroll, pauseOnFling, null); } /** * Constructor * * @param imageLoader {@linkplain ImageLoader} instance for controlling * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling * @param customListener Your custom {@link OnScrollListener} for {@linkplain AbsListView list view} which also * will be get scroll events */ public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling, OnScrollListener customListener) { this.imageLoader = imageLoader; this.pauseOnScroll = pauseOnScroll; this.pauseOnFling = pauseOnFling; externalListener = customListener; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case OnScrollListener.SCROLL_STATE_IDLE: imageLoader.resume(); break; case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: if (pauseOnScroll) { imageLoader.pause(); } break; case OnScrollListener.SCROLL_STATE_FLING: if (pauseOnFling) { imageLoader.pause(); } break; } if (externalListener != null) { externalListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (externalListener != null) { externalListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/listener/SimpleImageLoadingListener.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.listener; import android.graphics.Bitmap; import android.view.View; import com.nostra13.universalimageloader.core.assist.FailReason; /** * A convenient class to extend when you only want to listen for a subset of all the image loading events. This * implements all methods in the {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener} but does * nothing. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.4.0 */ public class SimpleImageLoadingListener implements ImageLoadingListener { @Override public void onLoadingStarted(String imageUri, View view) { // Empty implementation } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { // Empty implementation } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { // Empty implementation } @Override public void onLoadingCancelled(String imageUri, View view) { // Empty implementation } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/core/process/BitmapProcessor.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.core.process; import android.graphics.Bitmap; import com.nostra13.universalimageloader.core.DisplayImageOptions; /** * Makes some processing on {@link Bitmap}. Implementations can apply any changes to original {@link Bitmap}.
    * Implementations have to be thread-safe. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.0 */ public interface BitmapProcessor { /** * Makes some processing of incoming bitmap.
    * This method is executing on additional thread (not on UI thread).
    * Note: If this processor is used as {@linkplain DisplayImageOptions.Builder#preProcessor(BitmapProcessor) * pre-processor} then don't forget {@linkplain Bitmap#recycle() to recycle} incoming bitmap if you return a new * created one. * * @param bitmap Original {@linkplain Bitmap bitmap} * @return Processed {@linkplain Bitmap bitmap} */ Bitmap process(Bitmap bitmap); } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/utils/DiskCacheUtils.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.utils; import com.nostra13.universalimageloader.cache.disc.DiskCache; import java.io.File; /** * Utility for convenient work with disk cache.
    * NOTE: This utility works with file system so avoid using it on application main thread. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.0 */ public final class DiskCacheUtils { private DiskCacheUtils() { } /** Returns {@link File} of cached image or null if image was not cached in disk cache */ public static File findInCache(String imageUri, DiskCache diskCache) { File image = diskCache.get(imageUri); return image != null && image.exists() ? image : null; } /** * Removed cached image file from disk cache (if image was cached in disk cache before) * * @return true - if cached image file existed and was deleted; false - otherwise. */ public static boolean removeFromCache(String imageUri, DiskCache diskCache) { File image = diskCache.get(imageUri); return image != null && image.exists() && image.delete(); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/utils/ImageSizeUtils.java ================================================ /******************************************************************************* * Copyright 2013-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.utils; 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; import javax.microedition.khronos.opengles.GL10; /** * Provides calculations with image sizes, scales * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.8.3 */ 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(); } 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; } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/utils/IoUtils.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.utils; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Provides I/O operations * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public final class IoUtils { /** {@value} */ public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 KB /** {@value} */ public static final int DEFAULT_IMAGE_TOTAL_SIZE = 500 * 1024; // 500 Kb /** {@value} */ public static final int CONTINUE_LOADING_PERCENTAGE = 75; private IoUtils() { } /** * Copies stream, fires progress events by listener, can be interrupted by listener. Uses buffer size = * {@value #DEFAULT_BUFFER_SIZE} bytes. * * @param is Input stream * @param os Output stream * @param listener null-ok; Listener of copying progress and controller of copying interrupting * @return true - if stream copied successfully; false - if copying was interrupted by listener * @throws IOException */ public static boolean copyStream(InputStream is, OutputStream os, CopyListener listener) throws IOException { return copyStream(is, os, listener, DEFAULT_BUFFER_SIZE); } /** * Copies stream, fires progress events by listener, can be interrupted by listener. * * @param is Input stream * @param os Output stream * @param listener null-ok; Listener of copying progress and controller of copying interrupting * @param bufferSize Buffer size for copying, also represents a step for firing progress listener callback, i.e. * progress event will be fired after every copied bufferSize bytes * @return true - if stream copied successfully; false - if copying was interrupted by listener * @throws IOException */ public static boolean copyStream(InputStream is, OutputStream os, CopyListener listener, int bufferSize) throws IOException { int current = 0; int total = is.available(); if (total <= 0) { total = DEFAULT_IMAGE_TOTAL_SIZE; } final byte[] bytes = new byte[bufferSize]; int count; if (shouldStopLoading(listener, current, total)) return false; while ((count = is.read(bytes, 0, bufferSize)) != -1) { os.write(bytes, 0, count); current += count; if (shouldStopLoading(listener, current, total)) return false; } os.flush(); return true; } private static boolean shouldStopLoading(CopyListener listener, int current, int total) { if (listener != null) { boolean shouldContinue = listener.onBytesCopied(current, total); if (!shouldContinue) { if (100 * current / total < CONTINUE_LOADING_PERCENTAGE) { return true; // if loaded more than 75% then continue loading anyway } } } return false; } /** * Reads all data from stream and close it silently * * @param is Input stream */ public static void readAndCloseStream(InputStream is) { final byte[] bytes = new byte[DEFAULT_BUFFER_SIZE]; try { while (is.read(bytes, 0, DEFAULT_BUFFER_SIZE) != -1); } catch (IOException ignored) { } finally { closeSilently(is); } } public static void closeSilently(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (Exception ignored) { } } } /** Listener and controller for copy process */ public static interface CopyListener { /** * @param current Loaded bytes * @param total Total bytes for loading * @return true - if copying should be continued; false - if copying should be interrupted */ boolean onBytesCopied(int current, int total); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/utils/L.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.utils; import android.util.Log; import com.nostra13.universalimageloader.core.ImageLoader; /** * "Less-word" analog of Android {@link android.util.Log logger} * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.6.4 */ public final class L { private static final String LOG_FORMAT = "%1$s\n%2$s"; private static volatile boolean writeDebugLogs = false; private static volatile boolean writeLogs = true; private L() { } /** * Enables logger (if {@link #disableLogging()} was called before) * * @deprecated Use {@link #writeLogs(boolean) writeLogs(true)} instead */ @Deprecated public static void enableLogging() { writeLogs(true); } /** * Disables logger, no logs will be passed to LogCat, all log methods will do nothing * * @deprecated Use {@link #writeLogs(boolean) writeLogs(false)} instead */ @Deprecated public static void disableLogging() { writeLogs(false); } /** * Enables/disables detail logging of {@link ImageLoader} work. * Consider {@link com.nostra13.universalimageloader.utils.L#disableLogging()} to disable * ImageLoader logging completely (even error logs)
    * Debug logs are disabled by default. */ public static void writeDebugLogs(boolean writeDebugLogs) { L.writeDebugLogs = writeDebugLogs; } /** Enables/disables logging of {@link ImageLoader} completely (even error logs). */ public static void writeLogs(boolean writeLogs) { L.writeLogs = writeLogs; } public static void d(String message, Object... args) { if (writeDebugLogs) { log(Log.DEBUG, null, message, args); } } public static void i(String message, Object... args) { log(Log.INFO, null, message, args); } public static void w(String message, Object... args) { log(Log.WARN, null, message, args); } public static void e(Throwable ex) { log(Log.ERROR, ex, null); } public static void e(String message, Object... args) { log(Log.ERROR, null, message, args); } public static void e(Throwable ex, String message, Object... args) { log(Log.ERROR, ex, message, args); } private static void log(int priority, Throwable ex, String message, Object... args) { if (!writeLogs) return; if (args.length > 0) { message = String.format(message, args); } String log; if (ex == null) { log = message; } else { String logMessage = message == null ? ex.getMessage() : message; String logBody = Log.getStackTraceString(ex); log = String.format(LOG_FORMAT, logMessage, logBody); } Log.println(priority, ImageLoader.TAG, log); } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/utils/MemoryCacheUtils.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.utils; import android.graphics.Bitmap; import com.nostra13.universalimageloader.cache.memory.MemoryCache; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.ImageSize; import java.util.ArrayList; import java.util.Comparator; import java.util.List; /** * Utility for generating of keys for memory cache, key comparing and other work with memory cache * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.6.3 */ public final class MemoryCacheUtils { private static final String URI_AND_SIZE_SEPARATOR = "_"; private static final String WIDTH_AND_HEIGHT_SEPARATOR = "x"; private MemoryCacheUtils() { } /** * Generates key for memory cache for incoming image (URI + size).
    * Pattern for cache key - [imageUri]_[width]x[height]. */ public static String generateKey(String imageUri, ImageSize targetSize) { return new StringBuilder(imageUri).append(URI_AND_SIZE_SEPARATOR).append(targetSize.getWidth()).append(WIDTH_AND_HEIGHT_SEPARATOR).append(targetSize.getHeight()).toString(); } public static Comparator createFuzzyKeyComparator() { return new Comparator() { @Override public int compare(String key1, String key2) { String imageUri1 = key1.substring(0, key1.lastIndexOf(URI_AND_SIZE_SEPARATOR)); String imageUri2 = key2.substring(0, key2.lastIndexOf(URI_AND_SIZE_SEPARATOR)); return imageUri1.compareTo(imageUri2); } }; } /** * Searches all bitmaps in memory cache which are corresponded to incoming URI.
    * Note: Memory cache can contain multiple sizes of the same image if only you didn't set * {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory() * denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration} */ public static List findCachedBitmapsForImageUri(String imageUri, MemoryCache memoryCache) { List values = new ArrayList(); for (String key : memoryCache.keys()) { if (key.startsWith(imageUri)) { values.add(memoryCache.get(key)); } } return values; } /** * Searches all keys in memory cache which are corresponded to incoming URI.
    * Note: Memory cache can contain multiple sizes of the same image if only you didn't set * {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory() * denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration} */ public static List findCacheKeysForImageUri(String imageUri, MemoryCache memoryCache) { List values = new ArrayList(); for (String key : memoryCache.keys()) { if (key.startsWith(imageUri)) { values.add(key); } } return values; } /** * Removes from memory cache all images for incoming URI.
    * Note: Memory cache can contain multiple sizes of the same image if only you didn't set * {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory() * denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration} */ public static void removeFromCache(String imageUri, MemoryCache memoryCache) { List keysToRemove = new ArrayList(); for (String key : memoryCache.keys()) { if (key.startsWith(imageUri)) { keysToRemove.add(key); } } for (String keyToRemove : keysToRemove) { memoryCache.remove(keyToRemove); } } } ================================================ FILE: library/src/main/java/com/nostra13/universalimageloader/utils/StorageUtils.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.utils; import android.content.Context; import android.content.pm.PackageManager; import android.os.Environment; import java.io.File; import java.io.IOException; import static android.os.Environment.MEDIA_MOUNTED; /** * Provides application storage paths * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */ public final class StorageUtils { private static final String EXTERNAL_STORAGE_PERMISSION = "android.permission.WRITE_EXTERNAL_STORAGE"; private static final String INDIVIDUAL_DIR_NAME = "uil-images"; private StorageUtils() { } /** * Returns application cache directory. Cache directory will be created on SD card * ("/Android/data/[app_package_name]/cache") if card is mounted and app has appropriate permission. Else - * Android defines cache directory on device's file system. * * @param context Application context * @return Cache {@link File directory}.
    * NOTE: Can be null in some unpredictable cases (if SD card is unmounted and * {@link android.content.Context#getCacheDir() Context.getCacheDir()} returns null). */ public static File getCacheDirectory(Context context) { return getCacheDirectory(context, true); } /** * Returns application cache directory. Cache directory will be created on SD card * ("/Android/data/[app_package_name]/cache") (if card is mounted and app has appropriate permission) or * on device's file system depending incoming parameters. * * @param context Application context * @param preferExternal Whether prefer external location for cache * @return Cache {@link File directory}.
    * NOTE: Can be null in some unpredictable cases (if SD card is unmounted and * {@link android.content.Context#getCacheDir() Context.getCacheDir()} returns null). */ public static File getCacheDirectory(Context context, boolean preferExternal) { File appCacheDir = null; String externalStorageState; try { externalStorageState = Environment.getExternalStorageState(); } catch (NullPointerException e) { // (sh)it happens (Issue #660) externalStorageState = ""; } catch (IncompatibleClassChangeError e) { // (sh)it happens too (Issue #989) externalStorageState = ""; } if (preferExternal && MEDIA_MOUNTED.equals(externalStorageState) && hasExternalStoragePermission(context)) { appCacheDir = getExternalCacheDir(context); } if (appCacheDir == null) { appCacheDir = context.getCacheDir(); } if (appCacheDir == null) { String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache/"; L.w("Can't define system cache directory! '%s' will be used.", cacheDirPath); appCacheDir = new File(cacheDirPath); } return appCacheDir; } /** * Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be * created on SD card ("/Android/data/[app_package_name]/cache/uil-images") if card is mounted and app has * appropriate permission. Else - Android defines cache directory on device's file system. * * @param context Application context * @return Cache {@link File directory} */ public static File getIndividualCacheDirectory(Context context) { return getIndividualCacheDirectory(context, INDIVIDUAL_DIR_NAME); } /** * Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be * created on SD card ("/Android/data/[app_package_name]/cache/uil-images") if card is mounted and app has * appropriate permission. Else - Android defines cache directory on device's file system. * * @param context Application context * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images") * @return Cache {@link File directory} */ public static File getIndividualCacheDirectory(Context context, String cacheDir) { File appCacheDir = getCacheDirectory(context); File individualCacheDir = new File(appCacheDir, cacheDir); if (!individualCacheDir.exists()) { if (!individualCacheDir.mkdir()) { individualCacheDir = appCacheDir; } } return individualCacheDir; } /** * Returns specified application cache directory. Cache directory will be created on SD card by defined path if card * is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system. * * @param context Application context * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images") * @return Cache {@link File directory} */ public static File getOwnCacheDirectory(Context context, String cacheDir) { File appCacheDir = null; if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) { appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir); } if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) { appCacheDir = context.getCacheDir(); } return appCacheDir; } /** * Returns specified application cache directory. Cache directory will be created on SD card by defined path if card * is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system. * * @param context Application context * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images") * @return Cache {@link File directory} */ public static File getOwnCacheDirectory(Context context, String cacheDir, boolean preferExternal) { File appCacheDir = null; if (preferExternal && MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) { appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir); } if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) { appCacheDir = context.getCacheDir(); } return appCacheDir; } private static File getExternalCacheDir(Context context) { File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"); File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache"); if (!appCacheDir.exists()) { if (!appCacheDir.mkdirs()) { L.w("Unable to create external cache directory"); return null; } try { new File(appCacheDir, ".nomedia").createNewFile(); } catch (IOException e) { L.i("Can't create \".nomedia\" file in application external cache directory"); } } return appCacheDir; } private static boolean hasExternalStoragePermission(Context context) { int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION); return perm == PackageManager.PERMISSION_GRANTED; } } ================================================ FILE: library/src/test/java/com/nostra13/universalimageloader/core/assist/ImageSizeTest.java ================================================ package com.nostra13.universalimageloader.core.assist; import android.app.Activity; import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import com.nostra13.universalimageloader.core.imageaware.ImageViewAware; import com.nostra13.universalimageloader.utils.ImageSizeUtils; import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class ImageSizeTest { private Activity mActivity; private ImageView mView; private ImageAware mImageAware; @Before public void setUp() throws Exception { mActivity = new Activity(); // Make and set view with some prelim values to test mView = new TestImageView(mActivity); mView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mView.measure(View.MeasureSpec.makeMeasureSpec(250, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(250, View.MeasureSpec.EXACTLY)); mImageAware = new ImageViewAware(mView); } @Test public void testGetImageSizeScaleTo_useImageActualSize() throws Exception { // We layout the view to give it a width and height mView.measure(View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY)); mView.layout(0, 0, 200, 200); ImageSize expected = new ImageSize(200, 200); ImageSize result = ImageSizeUtils.defineTargetSizeForView(mImageAware, new ImageSize(590, 590)); Assertions.assertThat(result).isNotNull(); Assertions.assertThat(result.getWidth()).isEqualTo(expected.getWidth()); Assertions.assertThat(result.getHeight()).isEqualTo(expected.getHeight()); } /** * This will make sure the view falls back to the ViewParams/Max/Or Config if wrap content so that it is never * shrunk to the first image size. In this case it falls back to the config size * * @throws Exception */ @Test public void testGetImageSizeScaleTo_dontUseImageActualSizeWithWrapContent() throws Exception { //Set it to wrap content so that it will fall back to mView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); mView.measure(View.MeasureSpec.makeMeasureSpec(250, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(250, View.MeasureSpec.EXACTLY)); // We layout the view to give it a width and height mView.layout(0, 0, 200, 200); ImageSize expected = new ImageSize(500, 500); ImageSize result = ImageSizeUtils.defineTargetSizeForView(mImageAware, new ImageSize(500, 500)); Assertions.assertThat(result).isNotNull().isEqualsToByComparingFields(expected); } @Test public void testGetImageSizeScaleTo_useImageLayoutParams() throws Exception { // Set a defined width mView.setLayoutParams(new FrameLayout.LayoutParams(300, 300)); ImageSize expected = new ImageSize(300, 300); ImageSize result = ImageSizeUtils.defineTargetSizeForView(mImageAware, new ImageSize(500, 500)); Assertions.assertThat(result).isNotNull().isEqualsToByComparingFields(expected); } @Test public void testGetImageSizeScaleTo_useImageConfigMaxSize() throws Exception { ImageSize expected = new ImageSize(500, 500); ImageSize result = ImageSizeUtils.defineTargetSizeForView(mImageAware, new ImageSize(500, 500)); Assertions.assertThat(result).isNotNull().isEqualsToByComparingFields(expected); } @Test public void testComputeImageSampleSize_fitInside() throws Exception { final ViewScaleType scaleType = ViewScaleType.FIT_INSIDE; int result; ImageSize srcSize = new ImageSize(300, 100); ImageSize targetSize = new ImageSize(30, 10); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(10); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(8); srcSize = new ImageSize(300, 100); targetSize = new ImageSize(200, 200); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(1); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(1); srcSize = new ImageSize(300, 100); targetSize = new ImageSize(55, 40); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(5); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(4); srcSize = new ImageSize(300, 100); targetSize = new ImageSize(30, 40); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(10); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(8); srcSize = new ImageSize(5000, 70); targetSize = new ImageSize(2000, 30); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(3); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(4); } @Test public void testComputeImageSampleSize_centerCrop() throws Exception { final ViewScaleType scaleType = ViewScaleType.CROP; int result; ImageSize srcSize = new ImageSize(300, 100); ImageSize targetSize = new ImageSize(30, 10); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(10); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(8); srcSize = new ImageSize(300, 100); targetSize = new ImageSize(200, 200); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(1); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(1); srcSize = new ImageSize(300, 100); targetSize = new ImageSize(55, 40); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(2); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(2); srcSize = new ImageSize(300, 100); targetSize = new ImageSize(30, 30); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(3); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(2); srcSize = new ImageSize(5000, 70); targetSize = new ImageSize(300, 30); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false); Assertions.assertThat(result).isEqualTo(3); result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true); Assertions.assertThat(result).isEqualTo(4); } /** Fixes {@link NoSuchMethodError} for ImageView#onLayout(...) */ private class TestImageView extends ImageView { TestImageView(Context activity) { super(activity); } @Override public void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); } } } ================================================ FILE: library/src/test/java/com/nostra13/universalimageloader/core/download/BaseImageDownloaderTest.java ================================================ package com.nostra13.universalimageloader.core.download; import org.assertj.core.api.Assertions; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme; @RunWith(RobolectricTestRunner.class) public class BaseImageDownloaderTest { @Test public void testSchemeHttp() throws Exception { String uri = "http://image.com/1.png"; Scheme result = Scheme.ofUri(uri); Scheme expected = Scheme.HTTP; Assertions.assertThat(result).isEqualTo(expected); } @Test public void testSchemeHttps() throws Exception { String uri = "https://image.com/1.png"; Scheme result = Scheme.ofUri(uri); Scheme expected = Scheme.HTTPS; Assertions.assertThat(result).isEqualTo(expected); } @Test public void testSchemeContent() throws Exception { String uri = "content://path/to/content"; Scheme result = Scheme.ofUri(uri); Scheme expected = Scheme.CONTENT; Assertions.assertThat(result).isEqualTo(expected); } @Test public void testSchemeAssets() throws Exception { String uri = "assets://folder/1.png"; Scheme result = Scheme.ofUri(uri); Scheme expected = Scheme.ASSETS; Assertions.assertThat(result).isEqualTo(expected); } @Test public void testSchemeDrawables() throws Exception { String uri = "drawable://123456890"; Scheme result = Scheme.ofUri(uri); Scheme expected = Scheme.DRAWABLE; Assertions.assertThat(result).isEqualTo(expected); } @Test public void testSchemeFile() throws Exception { String uri = "file://path/on/the/device/1.png"; Scheme result = Scheme.ofUri(uri); Scheme expected = Scheme.FILE; Assertions.assertThat(result).isEqualTo(expected); } @Test public void testSchemeUnknown() throws Exception { String uri = "other://image.com/1.png"; Scheme result = Scheme.ofUri(uri); Scheme expected = Scheme.UNKNOWN; Assertions.assertThat(result).isEqualTo(expected); } } ================================================ FILE: sample/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 28 buildToolsVersion "28.0.3" defaultConfig { applicationId "com.nostra13.universalimageloader" minSdkVersion 16 targetSdkVersion 28 versionCode 40 versionName "1.9.5" } useLibrary 'org.apache.http.legacy' lintOptions { abortOnError false } } dependencies { implementation project(':library') implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.squareup.okhttp:okhttp:2.4.0' } File propFile = file('signing.properties'); if (propFile.exists()) { def Properties props = new Properties() props.load(new FileInputStream(propFile)) if (props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') && props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) { android.signingConfigs.release.storeFile = file(props['STORE_FILE']) android.signingConfigs.release.storePassword = props['STORE_PASSWORD'] android.signingConfigs.release.keyAlias = props['KEY_ALIAS'] android.signingConfigs.release.keyPassword = props['KEY_PASSWORD'] } else { android.buildTypes.release.signingConfig = null } } else { android.buildTypes.release.signingConfig = null } ================================================ FILE: sample/gradle.properties ================================================ POM_NAME=Universal Image Loader Sample POM_ARTIFACT_ID=universal-image-loader-sample POM_PACKAGING=apk ================================================ FILE: sample/project.properties ================================================ # This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system use, # "ant.properties", and override values to adapt the script to your # project structure. # Project target. target=android-21 ================================================ FILE: sample/src/main/AndroidManifest.xml ================================================ ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/Constants.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public final class Constants { public static final String[] IMAGES = new String[] { // Heavy images "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg", "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s1024/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg", "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s1024/Another%252520Rockaway%252520Sunset.jpg", "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s1024/Antelope%252520Butte.jpg", "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s1024/Antelope%252520Hallway.jpg", "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s1024/Antelope%252520Walls.jpg", "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s1024/Apre%2525CC%252580s%252520la%252520Pluie.jpg", "https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s1024/Backlit%252520Cloud.jpg", "https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s1024/Bee%252520and%252520Flower.jpg", "https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s1024/Bonzai%252520Rock%252520Sunset.jpg", "https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s1024/Caterpillar.jpg", "https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s1024/Chess.jpg", "https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s1024/Chihuly.jpg", "https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s1024/Closed%252520Door.jpg", "https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s1024/Colorado%252520River%252520Sunset.jpg", "https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s1024/Colors%252520of%252520Autumn.jpg", "https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s1024/Countryside.jpg", "https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s1024/Death%252520Valley%252520-%252520Dunes.jpg", "https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s1024/Delicate%252520Arch.jpg", "https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s1024/Despair.jpg", "https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s1024/Eagle%252520Fall%252520Sunrise.jpg", "https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s1024/Electric%252520Storm.jpg", "https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s1024/False%252520Kiva.jpg", "https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s1024/Fitzgerald%252520Streaks.jpg", "https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s1024/Foggy%252520Sunset.jpg", "https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s1024/Frantic.jpg", "https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s1024/Golden%252520Gate%252520Afternoon.jpg", "https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s1024/Golden%252520Gate%252520Fog.jpg", "https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s1024/Golden%252520Grass.jpg", "https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s1024/Grand%252520Teton.jpg", "https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s1024/Grass%252520Closeup.jpg", "https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s1024/Green%252520Grass.jpg", "https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s1024/Hanging%252520Leaf.jpg", "https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s1024/Highway%2525201.jpg", "https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s1024/Horseshoe%252520Bend%252520Sunset.jpg", "https://lh4.googleusercontent.com/-JhFi4fb_Pqw/URquuX-QXbI/AAAAAAAAAbs/IXpYUxuweYM/s1024/Horseshoe%252520Bend.jpg", "https://lh5.googleusercontent.com/-UGgssvFRJ7g/URquueyJzGI/AAAAAAAAAbs/yYIBlLT0toM/s1024/Into%252520the%252520Blue.jpg", "https://lh3.googleusercontent.com/-CH7KoupI7uI/URquu0FF__I/AAAAAAAAAbs/R7GDmI7v_G0/s1024/Jelly%252520Fish%2525202.jpg", "https://lh4.googleusercontent.com/-pwuuw6yhg8U/URquvPxR3FI/AAAAAAAAAbs/VNGk6f-tsGE/s1024/Jelly%252520Fish%2525203.jpg", "https://lh5.googleusercontent.com/-GoUQVw1fnFw/URquv6xbC0I/AAAAAAAAAbs/zEUVTQQ43Zc/s1024/Kauai.jpg", "https://lh6.googleusercontent.com/-8QdYYQEpYjw/URquwvdh88I/AAAAAAAAAbs/cktDy-ysfHo/s1024/Kyoto%252520Sunset.jpg", "https://lh4.googleusercontent.com/-vPeekyDjOE0/URquwzJ28qI/AAAAAAAAAbs/qxcyXULsZrg/s1024/Lake%252520Tahoe%252520Colors.jpg", "https://lh4.googleusercontent.com/-xBPxWpD4yxU/URquxWHk8AI/AAAAAAAAAbs/ARDPeDYPiMY/s1024/Lava%252520from%252520the%252520Sky.jpg", "https://lh3.googleusercontent.com/-897VXrJB6RE/URquxxxd-5I/AAAAAAAAAbs/j-Cz4T4YvIw/s1024/Leica%25252050mm%252520Summilux.jpg", "https://lh5.googleusercontent.com/-qSJ4D4iXzGo/URquyDWiJ1I/AAAAAAAAAbs/k2pBXeWehOA/s1024/Leica%25252050mm%252520Summilux.jpg", "https://lh6.googleusercontent.com/-dwlPg83vzLg/URquylTVuFI/AAAAAAAAAbs/G6SyQ8b4YsI/s1024/Leica%252520M8%252520%252528Front%252529.jpg", "https://lh3.googleusercontent.com/-R3_EYAyJvfk/URquzQBv8eI/AAAAAAAAAbs/b9xhpUM3pEI/s1024/Light%252520to%252520Sand.jpg", "https://lh3.googleusercontent.com/-fHY5h67QPi0/URqu0Cp4J1I/AAAAAAAAAbs/0lG6m94Z6vM/s1024/Little%252520Bit%252520of%252520Paradise.jpg", "https://lh5.googleusercontent.com/-TzF_LwrCnRM/URqu0RddPOI/AAAAAAAAAbs/gaj2dLiuX0s/s1024/Lone%252520Pine%252520Sunset.jpg", "https://lh3.googleusercontent.com/-4HdpJ4_DXU4/URqu046dJ9I/AAAAAAAAAbs/eBOodtk2_uk/s1024/Lonely%252520Rock.jpg", "https://lh6.googleusercontent.com/-erbF--z-W4s/URqu1ajSLkI/AAAAAAAAAbs/xjDCDO1INzM/s1024/Longue%252520Vue.jpg", "https://lh6.googleusercontent.com/-0CXJRdJaqvc/URqu1opNZNI/AAAAAAAAAbs/PFB2oPUU7Lk/s1024/Look%252520Me%252520in%252520the%252520Eye.jpg", "https://lh3.googleusercontent.com/-D_5lNxnDN6g/URqu2Tk7HVI/AAAAAAAAAbs/p0ddca9W__Y/s1024/Lost%252520in%252520a%252520Field.jpg", "https://lh6.googleusercontent.com/-flsqwMrIk2Q/URqu24PcmjI/AAAAAAAAAbs/5ocIH85XofM/s1024/Marshall%252520Beach%252520Sunset.jpg", "https://lh4.googleusercontent.com/-Y4lgryEVTmU/URqu28kG3gI/AAAAAAAAAbs/OjXpekqtbJ4/s1024/Mono%252520Lake%252520Blue.jpg", "https://lh4.googleusercontent.com/-AaHAJPmcGYA/URqu3PIldHI/AAAAAAAAAbs/lcTqk1SIcRs/s1024/Monument%252520Valley%252520Overlook.jpg", "https://lh4.googleusercontent.com/-vKxfdQ83dQA/URqu31Yq_BI/AAAAAAAAAbs/OUoGk_2AyfM/s1024/Moving%252520Rock.jpg", "https://lh5.googleusercontent.com/-CG62QiPpWXg/URqu4ia4vRI/AAAAAAAAAbs/0YOdqLAlcAc/s1024/Napali%252520Coast.jpg", "https://lh6.googleusercontent.com/-wdGrP5PMmJQ/URqu5PZvn7I/AAAAAAAAAbs/m0abEcdPXe4/s1024/One%252520Wheel.jpg", "https://lh6.googleusercontent.com/-6WS5DoCGuOA/URqu5qx1UgI/AAAAAAAAAbs/giMw2ixPvrY/s1024/Open%252520Sky.jpg", "https://lh6.googleusercontent.com/-u8EHKj8G8GQ/URqu55sM6yI/AAAAAAAAAbs/lIXX_GlTdmI/s1024/Orange%252520Sunset.jpg", "https://lh6.googleusercontent.com/-74Z5qj4bTDE/URqu6LSrJrI/AAAAAAAAAbs/XzmVkw90szQ/s1024/Orchid.jpg", "https://lh6.googleusercontent.com/-lEQE4h6TePE/URqu6t_lSkI/AAAAAAAAAbs/zvGYKOea_qY/s1024/Over%252520there.jpg", "https://lh5.googleusercontent.com/-cauH-53JH2M/URqu66v_USI/AAAAAAAAAbs/EucwwqclfKQ/s1024/Plumes.jpg", "https://lh3.googleusercontent.com/-eDLT2jHDoy4/URqu7axzkAI/AAAAAAAAAbs/iVZE-xJ7lZs/s1024/Rainbokeh.jpg", "https://lh5.googleusercontent.com/-j1NLqEFIyco/URqu8L1CGcI/AAAAAAAAAbs/aqZkgX66zlI/s1024/Rainbow.jpg", "https://lh5.googleusercontent.com/-DRnqmK0t4VU/URqu8XYN9yI/AAAAAAAAAbs/LgvF_592WLU/s1024/Rice%252520Fields.jpg", "https://lh3.googleusercontent.com/-hwh1v3EOGcQ/URqu8qOaKwI/AAAAAAAAAbs/IljRJRnbJGw/s1024/Rockaway%252520Fire%252520Sky.jpg", "https://lh5.googleusercontent.com/-wjV6FQk7tlk/URqu9jCQ8sI/AAAAAAAAAbs/RyYUpdo-c9o/s1024/Rockaway%252520Flow.jpg", "https://lh6.googleusercontent.com/-6cAXNfo7D20/URqu-BdzgPI/AAAAAAAAAbs/OmsYllzJqwo/s1024/Rockaway%252520Sunset%252520Sky.jpg", "https://lh3.googleusercontent.com/-sl8fpGPS-RE/URqu_BOkfgI/AAAAAAAAAbs/Dg2Fv-JxOeg/s1024/Russian%252520Ridge%252520Sunset.jpg", "https://lh6.googleusercontent.com/-gVtY36mMBIg/URqu_q91lkI/AAAAAAAAAbs/3CiFMBcy5MA/s1024/Rust%252520Knot.jpg", "https://lh6.googleusercontent.com/-GHeImuHqJBE/URqu_FKfVLI/AAAAAAAAAbs/axuEJeqam7Q/s1024/Sailing%252520Stones.jpg", "https://lh3.googleusercontent.com/-hBbYZjTOwGc/URqu_ycpIrI/AAAAAAAAAbs/nAdJUXnGJYE/s1024/Seahorse.jpg", "https://lh3.googleusercontent.com/-Iwi6-i6IexY/URqvAYZHsVI/AAAAAAAAAbs/5ETWl4qXsFE/s1024/Shinjuku%252520Street.jpg", "https://lh6.googleusercontent.com/-amhnySTM_MY/URqvAlb5KoI/AAAAAAAAAbs/pFCFgzlKsn0/s1024/Sierra%252520Heavens.jpg", "https://lh5.googleusercontent.com/-dJgjepFrYSo/URqvBVJZrAI/AAAAAAAAAbs/v-F5QWpYO6s/s1024/Sierra%252520Sunset.jpg", "https://lh4.googleusercontent.com/-Z4zGiC5nWdc/URqvBdEwivI/AAAAAAAAAbs/ZRZR1VJ84QA/s1024/Sin%252520Lights.jpg", "https://lh4.googleusercontent.com/-_0cYiWW8ccY/URqvBz3iM4I/AAAAAAAAAbs/9N_Wq8MhLTY/s1024/Starry%252520Lake.jpg", "https://lh3.googleusercontent.com/-A9LMoRyuQUA/URqvCYx_JoI/AAAAAAAAAbs/s7sde1Bz9cI/s1024/Starry%252520Night.jpg", "https://lh3.googleusercontent.com/-KtLJ3k858eY/URqvC_2h_bI/AAAAAAAAAbs/zzEBImwDA_g/s1024/Stream.jpg", "https://lh5.googleusercontent.com/-dFB7Lad6RcA/URqvDUftwWI/AAAAAAAAAbs/BrhoUtXTN7o/s1024/Strip%252520Sunset.jpg", "https://lh5.googleusercontent.com/-at6apgFiN20/URqvDyffUZI/AAAAAAAAAbs/clABCx171bE/s1024/Sunset%252520Hills.jpg", "https://lh4.googleusercontent.com/-7-EHhtQthII/URqvEYTk4vI/AAAAAAAAAbs/QSJZoB3YjVg/s1024/Tenaya%252520Lake%2525202.jpg", "https://lh6.googleusercontent.com/-8MrjV_a-Pok/URqvFC5repI/AAAAAAAAAbs/9inKTg9fbCE/s1024/Tenaya%252520Lake.jpg", "https://lh5.googleusercontent.com/-B1HW-z4zwao/URqvFWYRwUI/AAAAAAAAAbs/8Peli53Bs8I/s1024/The%252520Cave%252520BW.jpg", "https://lh3.googleusercontent.com/-PO4E-xZKAnQ/URqvGRqjYkI/AAAAAAAAAbs/42nyADFsXag/s1024/The%252520Fisherman.jpg", "https://lh4.googleusercontent.com/-iLyZlzfdy7s/URqvG0YScdI/AAAAAAAAAbs/1J9eDKmkXtk/s1024/The%252520Night%252520is%252520Coming.jpg", "https://lh6.googleusercontent.com/-G-k7YkkUco0/URqvHhah6fI/AAAAAAAAAbs/_taQQG7t0vo/s1024/The%252520Road.jpg", "https://lh6.googleusercontent.com/-h-ALJt7kSus/URqvIThqYfI/AAAAAAAAAbs/ejiv35olWS8/s1024/Tokyo%252520Heights.jpg", "https://lh5.googleusercontent.com/-Hy9k-TbS7xg/URqvIjQMOxI/AAAAAAAAAbs/RSpmmOATSkg/s1024/Tokyo%252520Highway.jpg", "https://lh6.googleusercontent.com/-83oOvMb4OZs/URqvJL0T7lI/AAAAAAAAAbs/c5TECZ6RONM/s1024/Tokyo%252520Smog.jpg", "https://lh3.googleusercontent.com/-FB-jfgREEfI/URqvJI3EXAI/AAAAAAAAAbs/XfyweiRF4v8/s1024/Tufa%252520at%252520Night.jpg", "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s1024/Valley%252520Sunset.jpg", "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s1024/Windmill%252520Sunrise.jpg", "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s1024/Windmill.jpg", "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s1024/Windmills.jpg", "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s1024/Yet%252520Another%252520Rockaway%252520Sunset.jpg", "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s1024/Yosemite%252520Tree.jpg", // Light images "http://tabletpcssource.com/wp-content/uploads/2011/05/android-logo.png", "http://simpozia.com/pages/images/stories/windows-icon.png", "http://radiotray.sourceforge.net/radio.png", "http://www.bandwidthblog.com/wp-content/uploads/2011/11/twitter-logo.png", "http://weloveicons.s3.amazonaws.com/icons/100907_itunes1.png", "http://weloveicons.s3.amazonaws.com/icons/100929_applications.png", "http://www.idyllicmusic.com/index_files/get_apple-iphone.png", "http://www.frenchrevolutionfood.com/wp-content/uploads/2009/04/Twitter-Bird.png", "http://3.bp.blogspot.com/-ka5MiRGJ_S4/TdD9OoF6bmI/AAAAAAAAE8k/7ydKtptUtSg/s1600/Google_Sky%2BMaps_Android.png", "http://www.desiredsoft.com/images/icon_webhosting.png", "http://goodereader.com/apps/wp-content/uploads/downloads/thumbnails/2012/01/hi-256-0-99dda8c730196ab93c67f0659d5b8489abdeb977.png", "http://1.bp.blogspot.com/-mlaJ4p_3rBU/TdD9OWxN8II/AAAAAAAAE8U/xyynWwr3_4Q/s1600/antivitus_free.png", "http://cdn3.iconfinder.com/data/icons/transformers/computer.png", "http://cdn.geekwire.com/wp-content/uploads/2011/04/firefox.png?7794fe", "https://ssl.gstatic.com/android/market/com.rovio.angrybirdsseasons/hi-256-9-347dae230614238a639d21508ae492302340b2ba", "http://androidblaze.com/wp-content/uploads/2011/12/tablet-pc-256x256.jpg", "http://www.theblaze.com/wp-content/uploads/2011/08/Apple.png", "http://1.bp.blogspot.com/-y-HQwQ4Kuu0/TdD9_iKIY7I/AAAAAAAAE88/3G4xiclDZD0/s1600/Twitter_Android.png", "http://3.bp.blogspot.com/-nAf4IMJGpc8/TdD9OGNUHHI/AAAAAAAAE8E/VM9yU_lIgZ4/s1600/Adobe%2BReader_Android.png", "http://cdn.geekwire.com/wp-content/uploads/2011/05/oovoo-android.png?7794fe", "http://icons.iconarchive.com/icons/kocco/ndroid/128/android-market-2-icon.png", "http://thecustomizewindows.com/wp-content/uploads/2011/11/Nicest-Android-Live-Wallpapers.png", "http://c.wrzuta.pl/wm16596/a32f1a47002ab3a949afeb4f", "http://macprovid.vo.llnwd.net/o43/hub/media/1090/6882/01_headline_Muse.jpg", // Special cases "http://cdn.urbanislandz.com/wp-content/uploads/2011/10/MMSposter-large.jpg", // Very large image "http://www.ioncannon.net/wp-content/uploads/2011/06/test9.webp", // WebP image "http://4.bp.blogspot.com/-LEvwF87bbyU/Uicaskm-g6I/AAAAAAAAZ2c/V-WZZAvFg5I/s800/Pesto+Guacamole+500w+0268.jpg", // Image with "Mark has been invalidated" problem "file:///sdcard/Universal Image Loader @#&=+-_.,!()~'%20.png", // Image from SD card with encoded symbols "assets://Living Things @#&=+-_.,!()~'%20.jpg", // Image from assets "drawable://" + R.drawable.ic_launcher, // Image from drawables "http://upload.wikimedia.org/wikipedia/ru/b/b6/Как_кот_с_мышами_воевал.png", // Link with UTF-8 "https://www.eff.org/sites/default/files/chrome150_0.jpg", // Image from HTTPS "http://bit.ly/soBiXr", // Redirect link "http://img001.us.expono.com/100001/100001-1bc30-2d736f_m.jpg", // EXIF "", // Empty link "http://wrong.site.com/corruptedLink", // Wrong link }; private Constants() { } public static class Config { public static final boolean DEVELOPER_MODE = false; } public static class Extra { public static final String FRAGMENT_INDEX = "com.nostra13.example.universalimageloader.FRAGMENT_INDEX"; public static final String IMAGE_POSITION = "com.nostra13.example.universalimageloader.IMAGE_POSITION"; } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/UILApplication.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample; import android.annotation.TargetApi; import android.app.Application; import android.content.Context; import android.os.Build; import android.os.StrictMode; import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class UILApplication extends Application { @TargetApi(Build.VERSION_CODES.GINGERBREAD) @SuppressWarnings("unused") @Override public void onCreate() { if (Constants.Config.DEVELOPER_MODE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyDialog().build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyDeath().build()); } super.onCreate(); initImageLoader(getApplicationContext()); } public static void initImageLoader(Context context) { // This configuration tuning is custom. You can tune every option, you may tune some of them, // or you can create default configuration by // ImageLoaderConfiguration.createDefault(this); // method. ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(context); config.threadPriority(Thread.NORM_PRIORITY - 2); config.denyCacheImageMultipleSizesInMemory(); config.diskCacheFileNameGenerator(new Md5FileNameGenerator()); config.diskCacheSize(50 * 1024 * 1024); // 50 MiB config.tasksProcessingOrder(QueueProcessingType.LIFO); config.writeDebugLogs(); // Remove for release app // Initialize ImageLoader with configuration. ImageLoader.getInstance().init(config.build()); } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/activity/ComplexImageActivity.java ================================================ /******************************************************************************* * Copyright 2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.activity; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import com.nostra13.universalimageloader.sample.R; import com.nostra13.universalimageloader.sample.fragment.ImageGridFragment; import com.nostra13.universalimageloader.sample.fragment.ImageListFragment; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class ComplexImageActivity extends FragmentActivity { private static final String STATE_POSITION = "STATE_POSITION"; private ViewPager pager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ac_complex); int pagerPosition = savedInstanceState == null ? 0 : savedInstanceState.getInt(STATE_POSITION); pager = (ViewPager) findViewById(R.id.pager); pager.setAdapter(new ImagePagerAdapter(getSupportFragmentManager())); pager.setCurrentItem(pagerPosition); } @Override public void onSaveInstanceState(Bundle outState) { outState.putInt(STATE_POSITION, pager.getCurrentItem()); } private class ImagePagerAdapter extends FragmentPagerAdapter { Fragment listFragment; Fragment gridFragment; ImagePagerAdapter(FragmentManager fm) { super(fm); listFragment = new ImageListFragment(); gridFragment = new ImageGridFragment(); } @Override public int getCount() { return 2; } @Override public Fragment getItem(int position) { switch (position) { case 0: return listFragment; case 1: return gridFragment; default: return null; } } @Override public CharSequence getPageTitle(int position) { switch (position) { case 0: return getString(R.string.title_list); case 1: return getString(R.string.title_grid); default: return null; } } } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/activity/HomeActivity.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.activity; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.sample.Constants; import com.nostra13.universalimageloader.sample.R; import com.nostra13.universalimageloader.sample.fragment.ImageGalleryFragment; import com.nostra13.universalimageloader.sample.fragment.ImageGridFragment; import com.nostra13.universalimageloader.sample.fragment.ImageListFragment; import com.nostra13.universalimageloader.sample.fragment.ImagePagerFragment; import com.nostra13.universalimageloader.utils.L; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class HomeActivity extends Activity { private static final String TEST_FILE_NAME = "Universal Image Loader @#&=+-_.,!()~'%20.png"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ac_home); File testImageOnSdCard = new File("/mnt/sdcard", TEST_FILE_NAME); if (!testImageOnSdCard.exists()) { copyTestImageToSdCard(testImageOnSdCard); } } public void onImageListClick(View view) { Intent intent = new Intent(this, SimpleImageActivity.class); intent.putExtra(Constants.Extra.FRAGMENT_INDEX, ImageListFragment.INDEX); startActivity(intent); } public void onImageGridClick(View view) { Intent intent = new Intent(this, SimpleImageActivity.class); intent.putExtra(Constants.Extra.FRAGMENT_INDEX, ImageGridFragment.INDEX); startActivity(intent); } public void onImagePagerClick(View view) { Intent intent = new Intent(this, SimpleImageActivity.class); intent.putExtra(Constants.Extra.FRAGMENT_INDEX, ImagePagerFragment.INDEX); startActivity(intent); } public void onImageGalleryClick(View view) { Intent intent = new Intent(this, SimpleImageActivity.class); intent.putExtra(Constants.Extra.FRAGMENT_INDEX, ImageGalleryFragment.INDEX); startActivity(intent); } public void onFragmentsClick(View view) { Intent intent = new Intent(this, ComplexImageActivity.class); startActivity(intent); } @Override public void onBackPressed() { ImageLoader.getInstance().stop(); super.onBackPressed(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.item_clear_memory_cache: ImageLoader.getInstance().clearMemoryCache(); return true; case R.id.item_clear_disc_cache: ImageLoader.getInstance().clearDiskCache(); return true; default: return false; } } private void copyTestImageToSdCard(final File testImageOnSdCard) { new Thread(new Runnable() { @Override public void run() { try { InputStream is = getAssets().open(TEST_FILE_NAME); FileOutputStream fos = new FileOutputStream(testImageOnSdCard); byte[] buffer = new byte[8192]; int read; try { while ((read = is.read(buffer)) != -1) { fos.write(buffer, 0, read); } } finally { fos.flush(); fos.close(); is.close(); } } catch (IOException e) { L.w("Can't copy test image onto SD card"); } } }).start(); } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/activity/SimpleImageActivity.java ================================================ /******************************************************************************* * Copyright 2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.activity; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import com.nostra13.universalimageloader.sample.Constants; import com.nostra13.universalimageloader.sample.R; import com.nostra13.universalimageloader.sample.fragment.ImageGalleryFragment; import com.nostra13.universalimageloader.sample.fragment.ImageGridFragment; import com.nostra13.universalimageloader.sample.fragment.ImageListFragment; import com.nostra13.universalimageloader.sample.fragment.ImagePagerFragment; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class SimpleImageActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); int frIndex = getIntent().getIntExtra(Constants.Extra.FRAGMENT_INDEX, 0); Fragment fr; String tag; int titleRes; switch (frIndex) { default: case ImageListFragment.INDEX: tag = ImageListFragment.class.getSimpleName(); fr = getSupportFragmentManager().findFragmentByTag(tag); if (fr == null) { fr = new ImageListFragment(); } titleRes = R.string.ac_name_image_list; break; case ImageGridFragment.INDEX: tag = ImageGridFragment.class.getSimpleName(); fr = getSupportFragmentManager().findFragmentByTag(tag); if (fr == null) { fr = new ImageGridFragment(); } titleRes = R.string.ac_name_image_grid; break; case ImagePagerFragment.INDEX: tag = ImagePagerFragment.class.getSimpleName(); fr = getSupportFragmentManager().findFragmentByTag(tag); if (fr == null) { fr = new ImagePagerFragment(); fr.setArguments(getIntent().getExtras()); } titleRes = R.string.ac_name_image_pager; break; case ImageGalleryFragment.INDEX: tag = ImageGalleryFragment.class.getSimpleName(); fr = getSupportFragmentManager().findFragmentByTag(tag); if (fr == null) { fr = new ImageGalleryFragment(); } titleRes = R.string.ac_name_image_gallery; break; } setTitle(titleRes); getSupportFragmentManager().beginTransaction().replace(android.R.id.content, fr, tag).commit(); } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/ext/Base64ImageDownloader.java ================================================ /******************************************************************************* * Copyright 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.ext; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.util.Base64; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * Downloader supporting "base64://..." URIs. * E.g.: "base64://data:image/jpeg;base64,/9j/4AAQSkZ..." * * @author mrleolink, Sergey Tarasevich (nostra13[at]gmail[dot]com) */ @TargetApi(Build.VERSION_CODES.FROYO) public class Base64ImageDownloader extends BaseImageDownloader { public static final String BASE64_SCHEME = "base64"; public static final String BASE64_URI_PREFIX = BASE64_SCHEME + "://"; public static final String BASE64_DATA_PREFIX = "base64,"; public Base64ImageDownloader(Context context) { super(context); } public Base64ImageDownloader(Context context, int connectTimeout, int readTimeout) { super(context, connectTimeout, readTimeout); } @Override public InputStream getStreamFromOtherSource(String imageUri, Object extra) throws IOException { if (imageUri.startsWith(BASE64_URI_PREFIX)) { return getStreamFormBase64(imageUri, extra); } return super.getStreamFromOtherSource(imageUri, extra); } protected InputStream getStreamFormBase64(String imageUri, Object extra) { int dataStartIndex = imageUri.indexOf(BASE64_DATA_PREFIX) + BASE64_DATA_PREFIX.length(); String base64 = imageUri.substring(dataStartIndex); return new ByteArrayInputStream(Base64.decode(base64, Base64.DEFAULT)); } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/ext/BrokenJpegImageDecoder.java ================================================ package com.nostra13.universalimageloader.sample.ext; import com.nostra13.universalimageloader.core.decode.BaseImageDecoder; import com.nostra13.universalimageloader.core.decode.ImageDecodingInfo; import java.io.IOException; import java.io.InputStream; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class BrokenJpegImageDecoder extends BaseImageDecoder { public BrokenJpegImageDecoder(boolean loggingEnabled) { super(loggingEnabled); } @Override protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException { InputStream stream = decodingInfo.getDownloader() .getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader()); return stream == null ? null : new JpegClosedInputStream(stream); } private class JpegClosedInputStream extends InputStream { private static final int JPEG_EOI_1 = 0xFF; private static final int JPEG_EOI_2 = 0xD9; private final InputStream inputStream; private int bytesPastEnd; private JpegClosedInputStream(InputStream inputStream) { this.inputStream = inputStream; bytesPastEnd = 0; } @Override public int read() throws IOException { int buffer = inputStream.read(); if (buffer == -1) { if (bytesPastEnd > 0) { buffer = JPEG_EOI_2; } else { ++bytesPastEnd; buffer = JPEG_EOI_1; } } return buffer; } } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/ext/HttpClientImageDownloader.java ================================================ /******************************************************************************* * 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.ext; import android.content.Context; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.entity.BufferedHttpEntity; import java.io.IOException; import java.io.InputStream; /** * Implementation of ImageDownloader which uses {@link HttpClient} for image stream retrieving. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class HttpClientImageDownloader extends BaseImageDownloader { private HttpClient httpClient; public HttpClientImageDownloader(Context context, HttpClient httpClient) { super(context); this.httpClient = httpClient; } @Override protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException { HttpGet httpRequest = new HttpGet(imageUri); HttpResponse response = httpClient.execute(httpRequest); HttpEntity entity = response.getEntity(); BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity); return bufHttpEntity.getContent(); } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/ext/OkHttpImageDownloader.java ================================================ /******************************************************************************* * 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.ext; import android.content.Context; import com.nostra13.universalimageloader.core.assist.ContentLengthInputStream; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.ResponseBody; import java.io.IOException; import java.io.InputStream; /** * Implementation of ImageDownloader which uses {@link com.squareup.okhttp.OkHttpClient} for image stream retrieving. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @author Leo Link (mr[dot]leolink[at]gmail[dot]com) */ public class OkHttpImageDownloader extends BaseImageDownloader { private OkHttpClient client; public OkHttpImageDownloader(Context context, OkHttpClient client) { super(context); this.client = client; } @Override protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException { Request request = new Request.Builder().url(imageUri).build(); ResponseBody responseBody = client.newCall(request).execute().body(); InputStream inputStream = responseBody.byteStream(); int contentLength = (int) responseBody.contentLength(); return new ContentLengthInputStream(inputStream, contentLength); } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/ext/OldRoundedBitmapDisplayer.java ================================================ /******************************************************************************* * 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.ext; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.widget.ImageView; import com.nostra13.universalimageloader.core.assist.LoadedFrom; import com.nostra13.universalimageloader.core.display.BitmapDisplayer; import com.nostra13.universalimageloader.core.imageaware.ImageAware; import com.nostra13.universalimageloader.core.imageaware.ImageViewAware; import com.nostra13.universalimageloader.utils.L; /** * Displays bitmap with rounded corners. This implementation works only with ImageViews wrapped in ImageViewAware.
    * NOTE: It's strongly recommended your {@link ImageView} has defined width (layout_width) and height * (layout_height) .
    * NOTE: New {@link Bitmap} object is created for displaying. So this class needs more memory and can cause * {@link OutOfMemoryError}. * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class OldRoundedBitmapDisplayer implements BitmapDisplayer { private final int roundPixels; public OldRoundedBitmapDisplayer(int roundPixels) { this.roundPixels = roundPixels; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { if (!(imageAware instanceof ImageViewAware)) { throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected."); } Bitmap roundedBitmap = roundCorners(bitmap, (ImageViewAware) imageAware, roundPixels); imageAware.setImageBitmap(roundedBitmap); } /** * Process incoming {@linkplain Bitmap} to make rounded corners according to target * {@link com.nostra13.universalimageloader.core.imageaware.ImageViewAware}.
    * This method doesn't display result bitmap in {@link ImageView} * * @param bitmap Incoming Bitmap to process * @param imageAware Target {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware} to * display bitmap in * @param roundPixels Rounded pixels of corner * @return Result bitmap with rounded corners */ public static Bitmap roundCorners(Bitmap bitmap, ImageViewAware imageAware, int roundPixels) { ImageView imageView = imageAware.getWrappedView(); if (imageView == null) { L.w("View is collected probably. Can't round bitmap corners without view properties."); return bitmap; } Bitmap roundBitmap; int bw = bitmap.getWidth(); int bh = bitmap.getHeight(); int vw = imageAware.getWidth(); int vh = imageAware.getHeight(); if (vw <= 0) vw = bw; if (vh <= 0) vh = bh; final ImageView.ScaleType scaleType = imageView.getScaleType(); if (scaleType == null) { return bitmap; } int width, height; Rect srcRect; Rect destRect; switch (scaleType) { case CENTER_INSIDE: float vRation = (float) vw / vh; float bRation = (float) bw / bh; int destWidth; int destHeight; if (vRation > bRation) { destHeight = Math.min(vh, bh); destWidth = (int) (bw / ((float) bh / destHeight)); } else { destWidth = Math.min(vw, bw); destHeight = (int) (bh / ((float) bw / destWidth)); } int x = (vw - destWidth) / 2; int y = (vh - destHeight) / 2; srcRect = new Rect(0, 0, bw, bh); destRect = new Rect(x, y, x + destWidth, y + destHeight); width = vw; height = vh; break; case FIT_CENTER: case FIT_START: case FIT_END: default: vRation = (float) vw / vh; bRation = (float) bw / bh; if (vRation > bRation) { width = (int) (bw / ((float) bh / vh)); height = vh; } else { width = vw; height = (int) (bh / ((float) bw / vw)); } srcRect = new Rect(0, 0, bw, bh); destRect = new Rect(0, 0, width, height); break; case CENTER_CROP: vRation = (float) vw / vh; bRation = (float) bw / bh; int srcWidth; int srcHeight; if (vRation > bRation) { srcWidth = bw; srcHeight = (int) (vh * ((float) bw / vw)); x = 0; y = (bh - srcHeight) / 2; } else { srcWidth = (int) (vw * ((float) bh / vh)); srcHeight = bh; x = (bw - srcWidth) / 2; y = 0; } width = srcWidth;// Math.min(vw, bw); height = srcHeight;//Math.min(vh, bh); srcRect = new Rect(x, y, x + srcWidth, y + srcHeight); destRect = new Rect(0, 0, width, height); break; case FIT_XY: width = vw; height = vh; srcRect = new Rect(0, 0, bw, bh); destRect = new Rect(0, 0, width, height); break; case CENTER: case MATRIX: width = Math.min(vw, bw); height = Math.min(vh, bh); x = (bw - width) / 2; y = (bh - height) / 2; srcRect = new Rect(x, y, x + width, y + height); destRect = new Rect(0, 0, width, height); break; } try { roundBitmap = getRoundedCornerBitmap(bitmap, roundPixels, srcRect, destRect, width, height); } catch (OutOfMemoryError e) { L.e(e, "Can't create bitmap with rounded corners. Not enough memory."); roundBitmap = bitmap; } return roundBitmap; } private static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int roundPixels, Rect srcRect, Rect destRect, int width, int height) { Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(output); final Paint paint = new Paint(); final RectF destRectF = new RectF(destRect); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(0xFF000000); canvas.drawRoundRect(destRectF, roundPixels, roundPixels, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(bitmap, srcRect, destRectF, paint); return output; } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/AbsListViewBaseFragment.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.fragment; import android.content.Intent; import android.view.Menu; import android.view.MenuItem; import android.widget.AbsListView; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.listener.PauseOnScrollListener; import com.nostra13.universalimageloader.sample.Constants; import com.nostra13.universalimageloader.sample.R; import com.nostra13.universalimageloader.sample.activity.SimpleImageActivity; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public abstract class AbsListViewBaseFragment extends BaseFragment { protected static final String STATE_PAUSE_ON_SCROLL = "STATE_PAUSE_ON_SCROLL"; protected static final String STATE_PAUSE_ON_FLING = "STATE_PAUSE_ON_FLING"; protected AbsListView listView; protected boolean pauseOnScroll = false; protected boolean pauseOnFling = true; @Override public void onResume() { super.onResume(); applyScrollListener(); } @Override public void onPrepareOptionsMenu(Menu menu) { MenuItem pauseOnScrollItem = menu.findItem(R.id.item_pause_on_scroll); pauseOnScrollItem.setVisible(true); pauseOnScrollItem.setChecked(pauseOnScroll); MenuItem pauseOnFlingItem = menu.findItem(R.id.item_pause_on_fling); pauseOnFlingItem.setVisible(true); pauseOnFlingItem.setChecked(pauseOnFling); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.item_pause_on_scroll: pauseOnScroll = !pauseOnScroll; item.setChecked(pauseOnScroll); applyScrollListener(); return true; case R.id.item_pause_on_fling: pauseOnFling = !pauseOnFling; item.setChecked(pauseOnFling); applyScrollListener(); return true; default: return super.onOptionsItemSelected(item); } } protected void startImagePagerActivity(int position) { Intent intent = new Intent(getActivity(), SimpleImageActivity.class); intent.putExtra(Constants.Extra.FRAGMENT_INDEX, ImagePagerFragment.INDEX); intent.putExtra(Constants.Extra.IMAGE_POSITION, position); startActivity(intent); } private void applyScrollListener() { listView.setOnScrollListener(new PauseOnScrollListener(ImageLoader.getInstance(), pauseOnScroll, pauseOnFling)); } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/BaseFragment.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.fragment; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.sample.R; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public abstract class BaseFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.main_menu, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.item_clear_memory_cache: ImageLoader.getInstance().clearMemoryCache(); return true; case R.id.item_clear_disc_cache: ImageLoader.getInstance().clearDiskCache(); return true; default: return false; } } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/ImageGalleryFragment.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.fragment; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.Gallery; import android.widget.ImageView; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer; import com.nostra13.universalimageloader.sample.Constants; import com.nostra13.universalimageloader.sample.R; import com.nostra13.universalimageloader.sample.activity.SimpleImageActivity; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class ImageGalleryFragment extends BaseFragment { public static final int INDEX = 3; @SuppressWarnings("deprecation") @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fr_image_gallery, container, false); Gallery gallery = (Gallery) rootView.findViewById(R.id.gallery); gallery.setAdapter(new ImageAdapter(getActivity())); gallery.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { startImagePagerActivity(position); } }); return rootView; } private void startImagePagerActivity(int position) { Intent intent = new Intent(getActivity(), SimpleImageActivity.class); intent.putExtra(Constants.Extra.FRAGMENT_INDEX, ImagePagerFragment.INDEX); intent.putExtra(Constants.Extra.IMAGE_POSITION, position); startActivity(intent); } private static class ImageAdapter extends BaseAdapter { private static final String[] IMAGE_URLS = Constants.IMAGES; private LayoutInflater inflater; private DisplayImageOptions options; ImageAdapter(Context context) { inflater = LayoutInflater.from(context); options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.ic_stub) .showImageForEmptyUri(R.drawable.ic_empty) .showImageOnFail(R.drawable.ic_error) .cacheInMemory(true) .cacheOnDisk(true) .considerExifParams(true) .bitmapConfig(Bitmap.Config.RGB_565) .displayer(new RoundedBitmapDisplayer(20)) .build(); } @Override public int getCount() { return IMAGE_URLS.length; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView = (ImageView) convertView; if (imageView == null) { imageView = (ImageView) inflater.inflate(R.layout.item_gallery_image, parent, false); } ImageLoader.getInstance().displayImage(IMAGE_URLS[position], imageView, options); return imageView; } } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/ImageGridFragment.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.fragment; import android.content.Context; import android.graphics.Bitmap; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.ImageView; import android.widget.ProgressBar; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import com.nostra13.universalimageloader.sample.Constants; import com.nostra13.universalimageloader.sample.R; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class ImageGridFragment extends AbsListViewBaseFragment { public static final int INDEX = 1; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fr_image_grid, container, false); listView = (GridView) rootView.findViewById(R.id.grid); ((GridView) listView).setAdapter(new ImageAdapter(getActivity())); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { startImagePagerActivity(position); } }); return rootView; } private static class ImageAdapter extends BaseAdapter { private static final String[] IMAGE_URLS = Constants.IMAGES; private LayoutInflater inflater; private DisplayImageOptions options; ImageAdapter(Context context) { inflater = LayoutInflater.from(context); options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.ic_stub) .showImageForEmptyUri(R.drawable.ic_empty) .showImageOnFail(R.drawable.ic_error) .cacheInMemory(true) .cacheOnDisk(true) .considerExifParams(true) .bitmapConfig(Bitmap.Config.RGB_565) .build(); } @Override public int getCount() { return IMAGE_URLS.length; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; View view = convertView; if (view == null) { view = inflater.inflate(R.layout.item_grid_image, parent, false); holder = new ViewHolder(); assert view != null; holder.imageView = (ImageView) view.findViewById(R.id.image); holder.progressBar = (ProgressBar) view.findViewById(R.id.progress); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } ImageLoader.getInstance() .displayImage(IMAGE_URLS[position], holder.imageView, options, new SimpleImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { holder.progressBar.setProgress(0); holder.progressBar.setVisibility(View.VISIBLE); } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { holder.progressBar.setVisibility(View.GONE); } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { holder.progressBar.setVisibility(View.GONE); } }, new ImageLoadingProgressListener() { @Override public void onProgressUpdate(String imageUri, View view, int current, int total) { holder.progressBar.setProgress(Math.round(100.0f * current / total)); } }); return view; } } static class ViewHolder { ImageView imageView; ProgressBar progressBar; } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/ImageListFragment.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.fragment; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.display.CircleBitmapDisplayer; import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import com.nostra13.universalimageloader.sample.Constants; import com.nostra13.universalimageloader.sample.R; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class ImageListFragment extends AbsListViewBaseFragment { public static final int INDEX = 0; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fr_image_list, container, false); listView = (ListView) rootView.findViewById(android.R.id.list); ((ListView) listView).setAdapter(new ImageAdapter(getActivity())); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { startImagePagerActivity(position); } }); return rootView; } @Override public void onDestroy() { super.onDestroy(); AnimateFirstDisplayListener.displayedImages.clear(); } private static class ImageAdapter extends BaseAdapter { private static final String[] IMAGE_URLS = Constants.IMAGES; private LayoutInflater inflater; private ImageLoadingListener animateFirstListener = new AnimateFirstDisplayListener(); private DisplayImageOptions options; ImageAdapter(Context context) { inflater = LayoutInflater.from(context); options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.ic_stub) .showImageForEmptyUri(R.drawable.ic_empty) .showImageOnFail(R.drawable.ic_error) .cacheInMemory(true) .cacheOnDisk(true) .considerExifParams(true) .displayer(new CircleBitmapDisplayer(Color.WHITE, 5)) .build(); } @Override public int getCount() { return IMAGE_URLS.length; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { View view = convertView; final ViewHolder holder; if (convertView == null) { view = inflater.inflate(R.layout.item_list_image, parent, false); holder = new ViewHolder(); holder.text = (TextView) view.findViewById(R.id.text); holder.image = (ImageView) view.findViewById(R.id.image); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } holder.text.setText("Item " + (position + 1)); ImageLoader.getInstance().displayImage(IMAGE_URLS[position], holder.image, options, animateFirstListener); return view; } } static class ViewHolder { TextView text; ImageView image; } private static class AnimateFirstDisplayListener extends SimpleImageLoadingListener { static final List displayedImages = Collections.synchronizedList(new LinkedList()); @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { if (loadedImage != null) { ImageView imageView = (ImageView) view; boolean firstDisplay = !displayedImages.contains(imageUri); if (firstDisplay) { FadeInBitmapDisplayer.animate(imageView, 500); displayedImages.add(imageUri); } } } } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/ImagePagerFragment.java ================================================ /******************************************************************************* * Copyright 2011-2014 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.fragment; import android.content.Context; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.Toast; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import com.nostra13.universalimageloader.sample.Constants; import com.nostra13.universalimageloader.sample.R; /** * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class ImagePagerFragment extends BaseFragment { public static final int INDEX = 2; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fr_image_pager, container, false); ViewPager pager = (ViewPager) rootView.findViewById(R.id.pager); pager.setAdapter(new ImageAdapter(getActivity())); pager.setCurrentItem(getArguments().getInt(Constants.Extra.IMAGE_POSITION, 0)); return rootView; } private static class ImageAdapter extends PagerAdapter { private static final String[] IMAGE_URLS = Constants.IMAGES; private LayoutInflater inflater; private DisplayImageOptions options; ImageAdapter(Context context) { inflater = LayoutInflater.from(context); options = new DisplayImageOptions.Builder() .showImageForEmptyUri(R.drawable.ic_empty) .showImageOnFail(R.drawable.ic_error) .resetViewBeforeLoading(true) .cacheOnDisk(true) .imageScaleType(ImageScaleType.EXACTLY) .bitmapConfig(Bitmap.Config.RGB_565) .considerExifParams(true) .displayer(new FadeInBitmapDisplayer(300)) .build(); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } @Override public int getCount() { return IMAGE_URLS.length; } @Override public Object instantiateItem(ViewGroup view, int position) { View imageLayout = inflater.inflate(R.layout.item_pager_image, view, false); assert imageLayout != null; ImageView imageView = (ImageView) imageLayout.findViewById(R.id.image); final ProgressBar spinner = (ProgressBar) imageLayout.findViewById(R.id.loading); ImageLoader.getInstance().displayImage(IMAGE_URLS[position], imageView, options, new SimpleImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { spinner.setVisibility(View.VISIBLE); } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { String message = null; switch (failReason.getType()) { case IO_ERROR: message = "Input/Output error"; break; case DECODING_ERROR: message = "Image can't be decoded"; break; case NETWORK_DENIED: message = "Downloads are denied"; break; case OUT_OF_MEMORY: message = "Out Of Memory error"; break; case UNKNOWN: message = "Unknown error"; break; } Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show(); spinner.setVisibility(View.GONE); } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { spinner.setVisibility(View.GONE); } }); view.addView(imageLayout, 0); return imageLayout; } @Override public boolean isViewFromObject(View view, Object object) { return view.equals(object); } @Override public void restoreState(Parcelable state, ClassLoader loader) { } @Override public Parcelable saveState() { return null; } } } ================================================ FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/widget/UILWidgetProvider.java ================================================ /******************************************************************************* * Copyright 2011-2013 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. *******************************************************************************/ package com.nostra13.universalimageloader.sample.widget; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; import android.graphics.Bitmap; import android.view.View; import android.widget.RemoteViews; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import com.nostra13.universalimageloader.sample.R; import com.nostra13.universalimageloader.sample.UILApplication; import static com.nostra13.universalimageloader.sample.Constants.IMAGES; /** * Example widget provider * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) */ public class UILWidgetProvider extends AppWidgetProvider { private static DisplayImageOptions displayOptions; static { displayOptions = DisplayImageOptions.createSimple(); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { UILApplication.initImageLoader(context); final int widgetCount = appWidgetIds.length; for (int i = 0; i < widgetCount; i++) { int appWidgetId = appWidgetIds[i]; updateAppWidget(context, appWidgetManager, appWidgetId); } } static void updateAppWidget(Context context, final AppWidgetManager appWidgetManager, final int appWidgetId) { final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget); ImageSize minImageSize = new ImageSize(70, 70); // 70 - approximate size of ImageView in widget ImageLoader.getInstance() .loadImage(IMAGES[0], minImageSize, displayOptions, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { views.setImageViewBitmap(R.id.image_left, loadedImage); appWidgetManager.updateAppWidget(appWidgetId, views); } }); ImageLoader.getInstance() .loadImage(IMAGES[1], minImageSize, displayOptions, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { views.setImageViewBitmap(R.id.image_right, loadedImage); appWidgetManager.updateAppWidget(appWidgetId, views); } }); } } ================================================ FILE: sample/src/main/res/layout/ac_complex.xml ================================================ ================================================ FILE: sample/src/main/res/layout/ac_home.xml ================================================