From ad1819c10ecef2884250f70746e023a995eeb5dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Tue, 31 Mar 2015 20:16:11 +0200 Subject: [PATCH] Replace UniversalImageLoader submodule by checked in code --- .gitmodules | 4 - extern/UniversalImageLoader | 1 - extern/UniversalImageLoader/LICENSE | 201 +++ .../library/AndroidManifest.xml | 11 + .../library/project.properties | 12 + .../cache/disc/DiscCacheAware.java | 87 ++ .../cache/disc/DiskCache.java | 25 + .../cache/disc/impl/BaseDiscCache.java | 189 +++ .../cache/disc/impl/LimitedAgeDiscCache.java | 127 ++ .../cache/disc/impl/UnlimitedDiscCache.java | 52 + .../cache/disc/impl/ext/DiskLruCache.java | 974 ++++++++++++++ .../cache/disc/impl/ext/LruDiscCache.java | 238 ++++ .../cache/disc/impl/ext/StrictLineReader.java | 191 +++ .../cache/disc/impl/ext/Util.java | 76 ++ .../cache/disc/naming/FileNameGenerator.java | 28 + .../naming/HashCodeFileNameGenerator.java | 29 + .../disc/naming/Md5FileNameGenerator.java | 53 + .../cache/memory/BaseMemoryCache.java | 71 + .../cache/memory/LimitedMemoryCache.java | 112 ++ .../cache/memory/MemoryCache.java | 27 + .../cache/memory/MemoryCacheAware.java | 49 + .../memory/impl/FIFOLimitedMemoryCache.java | 84 ++ .../memory/impl/FuzzyKeyMemoryCache.java | 81 ++ .../memory/impl/LRULimitedMemoryCache.java | 103 ++ .../impl/LargestLimitedMemoryCache.java | 109 ++ .../memory/impl/LimitedAgeMemoryCache.java | 88 ++ .../cache/memory/impl/LruMemoryCache.java | 144 ++ .../impl/UsingFreqLimitedMemoryCache.java | 122 ++ .../cache/memory/impl/WeakMemoryCache.java | 37 + .../core/DefaultConfigurationFactory.java | 160 +++ .../core/DisplayBitmapTask.java | 81 ++ .../core/DisplayImageOptions.java | 509 +++++++ .../core/ImageLoader.java | 763 +++++++++++ .../core/ImageLoaderConfiguration.java | 655 +++++++++ .../core/ImageLoaderEngine.java | 217 +++ .../core/ImageLoadingInfo.java | 58 + .../core/LoadAndDisplayImageTask.java | 472 +++++++ .../core/ProcessAndDisplayImageTask.java | 59 + .../core/assist/ContentLengthInputStream.java | 82 ++ .../core/assist/FailReason.java | 65 + .../core/assist/FlushedInputStream.java | 34 + .../core/assist/ImageScaleType.java | 78 ++ .../core/assist/ImageSize.java | 69 + .../core/assist/LoadedFrom.java | 10 + .../core/assist/QueueProcessingType.java | 26 + .../core/assist/ViewScaleType.java | 73 + .../core/assist/deque/BlockingDeque.java | 616 +++++++++ .../core/assist/deque/Deque.java | 550 ++++++++ .../assist/deque/LIFOLinkedBlockingDeque.java | 47 + .../assist/deque/LinkedBlockingDeque.java | 1169 +++++++++++++++++ .../core/decode/BaseImageDecoder.java | 245 ++++ .../core/decode/ImageDecoder.java | 39 + .../core/decode/ImageDecodingInfo.java | 150 +++ .../core/display/BitmapDisplayer.java | 43 + .../core/display/FadeInBitmapDisplayer.java | 86 ++ .../core/display/RoundedBitmapDisplayer.java | 118 ++ .../RoundedVignetteBitmapDisplayer.java | 74 ++ .../core/display/SimpleBitmapDisplayer.java | 33 + .../core/download/BaseImageDownloader.java | 247 ++++ .../core/download/ImageDownloader.java | 90 ++ .../core/imageaware/ImageAware.java | 114 ++ .../core/imageaware/ImageViewAware.java | 143 ++ .../core/imageaware/NonViewAware.java | 92 ++ .../core/imageaware/ViewAware.java | 184 +++ .../core/listener/ImageLoadingListener.java | 67 + .../ImageLoadingProgressListener.java | 37 + .../core/listener/PauseOnScrollListener.java | 98 ++ .../listener/SimpleImageLoadingListener.java | 50 + .../core/process/BitmapProcessor.java | 40 + .../utils/DiskCacheUtils.java | 49 + .../utils/ImageSizeUtils.java | 215 +++ .../universalimageloader/utils/IoUtils.java | 132 ++ .../universalimageloader/utils/L.java | 113 ++ .../utils/MemoryCacheUtils.java | 110 ++ .../utils/StorageUtils.java | 147 +++ 75 files changed, 11829 insertions(+), 5 deletions(-) delete mode 160000 extern/UniversalImageLoader create mode 100644 extern/UniversalImageLoader/LICENSE create mode 100644 extern/UniversalImageLoader/library/AndroidManifest.xml create mode 100644 extern/UniversalImageLoader/library/project.properties create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/DiscCacheAware.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/DiskCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/BaseDiscCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/LimitedAgeDiscCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/UnlimitedDiscCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/DiskLruCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/LruDiscCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/StrictLineReader.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/Util.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/FileNameGenerator.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/HashCodeFileNameGenerator.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/Md5FileNameGenerator.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/BaseMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/LimitedMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/MemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/MemoryCacheAware.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/FIFOLimitedMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/FuzzyKeyMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LRULimitedMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LargestLimitedMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LimitedAgeMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LruMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/UsingFreqLimitedMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/WeakMemoryCache.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DefaultConfigurationFactory.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayImageOptions.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoader.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoadingInfo.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ProcessAndDisplayImageTask.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ContentLengthInputStream.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FailReason.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageScaleType.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageSize.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/LoadedFrom.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/QueueProcessingType.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ViewScaleType.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/BlockingDeque.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/Deque.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/LIFOLinkedBlockingDeque.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/LinkedBlockingDeque.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/BaseImageDecoder.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/ImageDecoder.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/ImageDecodingInfo.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/BitmapDisplayer.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/FadeInBitmapDisplayer.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedBitmapDisplayer.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedVignetteBitmapDisplayer.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/SimpleBitmapDisplayer.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/ImageDownloader.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageAware.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/NonViewAware.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ViewAware.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingListener.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/PauseOnScrollListener.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/SimpleImageLoadingListener.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/process/BitmapProcessor.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/DiskCacheUtils.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/ImageSizeUtils.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/IoUtils.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/L.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/MemoryCacheUtils.java create mode 100644 extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/StorageUtils.java diff --git a/.gitmodules b/.gitmodules index 47817e9d6..e05afb8d0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,3 @@ -[submodule "extern/Universal-Image-Loader"] - path = extern/UniversalImageLoader - url = https://github.com/nostra13/Android-Universal-Image-Loader - ignore = dirty [submodule "extern/MemorizingTrustManager"] path = extern/MemorizingTrustManager url = https://github.com/ge0rg/MemorizingTrustManager.git diff --git a/extern/UniversalImageLoader b/extern/UniversalImageLoader deleted file mode 160000 index 7dd735871..000000000 --- a/extern/UniversalImageLoader +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7dd735871fec70d4a93837a26503470804b7d7e4 diff --git a/extern/UniversalImageLoader/LICENSE b/extern/UniversalImageLoader/LICENSE new file mode 100644 index 000000000..f49a4e16e --- /dev/null +++ b/extern/UniversalImageLoader/LICENSE @@ -0,0 +1,201 @@ + 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. \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/AndroidManifest.xml b/extern/UniversalImageLoader/library/AndroidManifest.xml new file mode 100644 index 000000000..359e84264 --- /dev/null +++ b/extern/UniversalImageLoader/library/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/project.properties b/extern/UniversalImageLoader/library/project.properties new file mode 100644 index 000000000..8e4bc5fdc --- /dev/null +++ b/extern/UniversalImageLoader/library/project.properties @@ -0,0 +1,12 @@ +# 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-19 +android.library=true diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/DiscCacheAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/DiscCacheAware.java new file mode 100644 index 000000000..138fcfd8f --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/DiscCacheAware.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * 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; + +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.0.0 + * @deprecated Use {@link DiskCache DiskCache} instead + */ +@Deprecated +public interface DiscCacheAware { + + /** + * 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. + * + * @param imageUri Original image URI + * @param imageStream Input stream of image + * @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 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(); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/DiskCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/DiskCache.java new file mode 100644 index 000000000..ccc698617 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/DiskCache.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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; + +/** + * Interface for disk cache + * + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) + * @since 1.9.2 + */ +public interface DiskCache extends DiscCacheAware { +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/BaseDiscCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/BaseDiscCache.java new file mode 100644 index 000000000..e81f0d543 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/BaseDiscCache.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * 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.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 BaseDiscCache 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 BaseDiscCache(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 BaseDiscCache(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 BaseDiscCache(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 { + IoUtils.closeSilently(imageStream); + 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; + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/LimitedAgeDiscCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/LimitedAgeDiscCache.java new file mode 100644 index 000000000..387a3a7e7 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/LimitedAgeDiscCache.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * 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.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 LimitedAgeDiscCache extends BaseDiscCache { + + 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 LimitedAgeDiscCache(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 LimitedAgeDiscCache(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 LimitedAgeDiscCache(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); + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/UnlimitedDiscCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/UnlimitedDiscCache.java new file mode 100644 index 000000000..d75222295 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/UnlimitedDiscCache.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * 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.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 UnlimitedDiscCache extends BaseDiscCache { + /** @param cacheDir Directory for file caching */ + public UnlimitedDiscCache(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 UnlimitedDiscCache(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 UnlimitedDiscCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) { + super(cacheDir, reserveCacheDir, fileNameGenerator); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/DiskLruCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/DiskLruCache.java new file mode 100644 index 000000000..6e18f31ff --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/DiskLruCache.java @@ -0,0 +1,974 @@ +/* + * 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"); + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/LruDiscCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/LruDiscCache.java new file mode 100644 index 000000000..42079eab7 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/LruDiscCache.java @@ -0,0 +1,238 @@ +/******************************************************************************* + * 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 LruDiscCache 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 LruDiscCache(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 LruDiscCache(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; + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/StrictLineReader.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/StrictLineReader.java new file mode 100644 index 000000000..0f3e0f56e --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/StrictLineReader.java @@ -0,0 +1,191 @@ +/* + * 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; + } +} + diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/Util.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/Util.java new file mode 100644 index 000000000..fd7a4ba17 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/Util.java @@ -0,0 +1,76 @@ +/* + * 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) { + } + } + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/FileNameGenerator.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/FileNameGenerator.java new file mode 100644 index 000000000..20e1d664b --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/FileNameGenerator.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/HashCodeFileNameGenerator.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/HashCodeFileNameGenerator.java new file mode 100644 index 000000000..a284b53b0 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/HashCodeFileNameGenerator.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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()); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/Md5FileNameGenerator.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/Md5FileNameGenerator.java new file mode 100644 index 000000000..9d50bc6cd --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/Md5FileNameGenerator.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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; + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/BaseMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/BaseMemoryCache.java new file mode 100644 index 000000000..a1cc0b779 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/BaseMemoryCache.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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.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); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/LimitedMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/LimitedMemoryCache.java new file mode 100644 index 000000000..82ae1f1f9 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/LimitedMemoryCache.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * 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.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(); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/MemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/MemoryCache.java new file mode 100644 index 000000000..47bac924e --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/MemoryCache.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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; + +/** + * Interface for memory cache + * + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) + * @since 1.9.2 + */ +public interface MemoryCache extends MemoryCacheAware { +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/MemoryCacheAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/MemoryCacheAware.java new file mode 100644 index 000000000..d52524b5e --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/MemoryCacheAware.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.memory; + +import java.util.Collection; + +/** + * Interface for memory cache + * + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) + * @since 1.0.0 + * @deprecated Use {@link com.nostra13.universalimageloader.cache.memory.MemoryCache MemoryCache} + * instead + */ +@Deprecated +public interface MemoryCacheAware { + /** + * 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(K key, V value); + + /** Returns value by key. If there is no value for key then null will be returned. */ + V get(K key); + + /** Removes item by key */ + V remove(K key); + + /** Returns all keys of cache */ + Collection keys(); + + /** Remove all items from cache */ + void clear(); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/FIFOLimitedMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/FIFOLimitedMemoryCache.java new file mode 100644 index 000000000..1d822b7e6 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/FIFOLimitedMemoryCache.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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.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); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/FuzzyKeyMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/FuzzyKeyMemoryCache.java new file mode 100644 index 000000000..d9e1d96d4 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/FuzzyKeyMemoryCache.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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.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(); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LRULimitedMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LRULimitedMemoryCache.java new file mode 100644 index 000000000..75c0d5e8c --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LRULimitedMemoryCache.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * 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.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); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LargestLimitedMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LargestLimitedMemoryCache.java new file mode 100644 index 000000000..105aa559b --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LargestLimitedMemoryCache.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * 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.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); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LimitedAgeMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LimitedAgeMemoryCache.java new file mode 100644 index 000000000..7327bf2e6 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LimitedAgeMemoryCache.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * 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.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(); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LruMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LruMemoryCache.java new file mode 100644 index 000000000..134a4f838 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/LruMemoryCache.java @@ -0,0 +1,144 @@ +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); + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/UsingFreqLimitedMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/UsingFreqLimitedMemoryCache.java new file mode 100644 index 000000000..cbf66358d --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/UsingFreqLimitedMemoryCache.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * 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.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); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/WeakMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/WeakMemoryCache.java new file mode 100644 index 000000000..60914d65f --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/impl/WeakMemoryCache.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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.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); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DefaultConfigurationFactory.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DefaultConfigurationFactory.java new file mode 100644 index 000000000..3157b11c1 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DefaultConfigurationFactory.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * 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 android.content.Context; +import com.nostra13.universalimageloader.cache.disc.DiskCache; +import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache; +import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiscCache; +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 LruDiscCache(individualCacheDir, reserveCacheDir, diskCacheFileNameGenerator, diskCacheSize, + diskCacheFileCount); + } catch (IOException e) { + L.e(e); + // continue and create unlimited cache + } + } + File cacheDir = StorageUtils.getCacheDirectory(context); + return new UnlimitedDiscCache(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(int memoryCacheSize) { + if (memoryCacheSize == 0) { + memoryCacheSize = (int) (Runtime.getRuntime().maxMemory() / 8); + } + return new LruMemoryCache(memoryCacheSize); + } + + /** 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; + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java new file mode 100644 index 000000000..341321e13 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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 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); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayImageOptions.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayImageOptions.java new file mode 100644 index 000000000..b8ff9fd1c --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayImageOptions.java @@ -0,0 +1,509 @@ +/******************************************************************************* + * 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 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}.{@link Builder#Builder() 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; + + public Builder() { + decodingOptions.inPurgeable = true; + decodingOptions.inInputShareable = true; + } + + /** + * 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(); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoader.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoader.java new file mode 100644 index 000000000..6a745933d --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoader.java @@ -0,0 +1,763 @@ +/******************************************************************************* + * 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 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 final ImageLoadingListener emptyListener = 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) { + checkConfiguration(); + if (imageAware == null) { + throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); + } + if (listener == null) { + listener = emptyListener; + } + 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; + } + + ImageSize 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.
    + * 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); + } + } + + /** + * 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() { + engine.pause(); + } + + /** Resumes waiting "load&display" tasks */ + public void resume() { + engine.resume(); + } + + /** + * 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() { + engine.stop(); + } + + /** + * {@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 (configuration != null) L.d(LOG_DESTROY); + stop(); + configuration.diskCache.close(); + engine = null; + configuration = null; + } + + 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; + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java new file mode 100644 index 000000000..1fa2e8a00 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java @@ -0,0 +1,655 @@ +/******************************************************************************* + * 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 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(int)}
    • + *
    • diskCache = {@link com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache}
    • + *
    • 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 - 1; + /** {@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.LruDiscCache LruDiscCache} + * 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.LruDiscCache LruDiscCache} + * 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.UnlimitedDiscCache + * BaseDiscCache}. 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(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; + } + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java new file mode 100644 index 000000000..b25f6c9d7 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * 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 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; + +/** + * {@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(); + initExecutorsIfNeed(); + if (isImageCachedOnDisk) { + taskExecutorForCachedImages.execute(task); + } else { + taskExecutor.execute(task); + } + } + }); + } + + /** Submits task to execution pool */ + void submit(ProcessAndDisplayImageTask task) { + initExecutorsIfNeed(); + taskExecutorForCachedImages.execute(task); + } + + 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(); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoadingInfo.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoadingInfo.java new file mode 100644 index 000000000..2950fc771 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoadingInfo.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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; + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java new file mode 100644 index 000000000..24cbc497f --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java @@ -0,0 +1,472 @@ +/******************************************************************************* + * 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 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_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()) { + 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()); + return configuration.diskCache.save(uri, is, this); + } + + /** 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 { + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ProcessAndDisplayImageTask.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ProcessAndDisplayImageTask.java new file mode 100644 index 000000000..d7d759d8a --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ProcessAndDisplayImageTask.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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 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); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ContentLengthInputStream.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ContentLengthInputStream.java new file mode 100644 index 000000000..786b2d5aa --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ContentLengthInputStream.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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.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(); + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FailReason.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FailReason.java new file mode 100644 index 000000000..f78491e58 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FailReason.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * 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; + +/** + * 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 + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java new file mode 100644 index 000000000..b2e450607 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java @@ -0,0 +1,34 @@ +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; + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageScaleType.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageScaleType.java new file mode 100644 index 000000000..b3eb89bd2 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageScaleType.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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; + +/** + * 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 +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageSize.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageSize.java new file mode 100644 index 000000000..54c97ca64 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageSize.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * 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(); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/LoadedFrom.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/LoadedFrom.java new file mode 100644 index 000000000..6889ce45a --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/LoadedFrom.java @@ -0,0 +1,10 @@ +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 +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/QueueProcessingType.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/QueueProcessingType.java new file mode 100644 index 000000000..0bbca5868 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/QueueProcessingType.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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 +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ViewScaleType.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ViewScaleType.java new file mode 100644 index 000000000..c618cfba8 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ViewScaleType.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * 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; + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/BlockingDeque.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/BlockingDeque.java new file mode 100644 index 000000000..571eb6ca5 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/BlockingDeque.java @@ -0,0 +1,616 @@ +/* + * 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. + * + * @since 1.6 + * @author Doug Lea + * @param the type of elements held in this collection + */ +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); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/Deque.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/Deque.java new file mode 100644 index 000000000..1b359ffdf --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/Deque.java @@ -0,0 +1,550 @@ +/* + * 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. + * + * @author Doug Lea + * @author Josh Bloch + * @since 1.6 + * @param the type of elements held in this collection + */ + +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(); + +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/LIFOLinkedBlockingDeque.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/LIFOLinkedBlockingDeque.java new file mode 100644 index 000000000..0a2cf02c8 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/LIFOLinkedBlockingDeque.java @@ -0,0 +1,47 @@ +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(); + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/LinkedBlockingDeque.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/LinkedBlockingDeque.java new file mode 100644 index 000000000..75d6994fa --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/LinkedBlockingDeque.java @@ -0,0 +1,1169 @@ +/* + * 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. + * + * @since 1.6 + * @author Doug Lea + * @param the type of elements held in this collection + */ +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). + * + * @serialData The capacity (int), followed by elements (each an + * {@code Object}) in the proper order, followed by a null + * @param s the stream + */ + 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); + } + } + +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/BaseImageDecoder.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/BaseImageDecoder.java new file mode 100644 index 000000000..da3ce0efe --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/BaseImageDecoder.java @@ -0,0 +1,245 @@ +/******************************************************************************* + * 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.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_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); + 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 { + try { + imageStream.reset(); + } catch (IOException e) { + IoUtils.closeSilently(imageStream); + imageStream = getImageStream(decodingInfo); + } + return imageStream; + } + + 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; + } + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/ImageDecoder.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/ImageDecoder.java new file mode 100644 index 000000000..69fb01842 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/ImageDecoder.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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; +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/ImageDecodingInfo.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/ImageDecodingInfo.java new file mode 100644 index 000000000..fc4400bea --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/decode/ImageDecodingInfo.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * 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.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; + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/BitmapDisplayer.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/BitmapDisplayer.java new file mode 100644 index 000000000..77b800f4a --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/BitmapDisplayer.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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; + +/** + * 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); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/FadeInBitmapDisplayer.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/FadeInBitmapDisplayer.java new file mode 100644 index 000000000..e60a18e91 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/FadeInBitmapDisplayer.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright 2011-2013 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); + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedBitmapDisplayer.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedBitmapDisplayer.java new file mode 100644 index 000000000..e265e2bc5 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedBitmapDisplayer.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * 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.*; +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); + } + + @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); + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedVignetteBitmapDisplayer.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedVignetteBitmapDisplayer.java new file mode 100644 index 000000000..bea59d354 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedVignetteBitmapDisplayer.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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)); + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/SimpleBitmapDisplayer.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/SimpleBitmapDisplayer.java new file mode 100644 index 000000000..8aae7de6b --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/SimpleBitmapDisplayer.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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); + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java new file mode 100644 index 000000000..964b51cef --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java @@ -0,0 +1,247 @@ +/******************************************************************************* + * 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 android.content.ContentResolver; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.CompressFormat; +import android.net.Uri; +import android.provider.ContactsContract; +import android.provider.MediaStore; +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 = context.getApplicationContext(); + this.connectTimeout = DEFAULT_HTTP_CONNECT_TIMEOUT; + this.readTimeout = 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; + } + return new ContentLengthInputStream(new BufferedInputStream(imageStream, BUFFER_SIZE), conn.getContentLength()); + } + + /** + * 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); + return new ContentLengthInputStream(new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE), + (int) new File(filePath).length()); + } + + /** + * 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 (isVideoUri(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 ContactsContract.Contacts.openContactPhotoInputStream(res, uri); + } + + return res.openInputStream(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 isVideoUri(Uri uri) { + String mimeType = context.getContentResolver().getType(uri); + + if (mimeType == null) { + return false; + } + + return mimeType.startsWith("video/"); + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/ImageDownloader.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/ImageDownloader.java new file mode 100644 index 000000000..3fa0fb5cc --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/ImageDownloader.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * 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()); + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageAware.java new file mode 100644 index 000000000..71531fdd9 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageAware.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * 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.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); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java new file mode 100644 index 000000000..832872dcd --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * 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.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 = getImageViewFieldValue(imageView, "mMaxWidth"); // Check maxWidth parameter + } + } + 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 = getImageViewFieldValue(imageView, "mMaxHeight"); // Check maxHeight parameter + } + } + 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); + } + + private static int getImageViewFieldValue(Object object, String fieldName) { + int value = 0; + try { + Field field = ImageView.class.getDeclaredField(fieldName); + field.setAccessible(true); + int fieldValue = (Integer) field.get(object); + if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) { + value = fieldValue; + } + } catch (Exception e) { + L.e(e); + } + return value; + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/NonViewAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/NonViewAware.java new file mode 100644 index 000000000..1918b5ec9 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/NonViewAware.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * 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.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; + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ViewAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ViewAware.java new file mode 100644 index 000000000..af6871531 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ViewAware.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * 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); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingListener.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingListener.java new file mode 100644 index 000000000..7be04fbcf --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingListener.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener.java new file mode 100644 index 000000000..c9ce6848c --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/PauseOnScrollListener.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/PauseOnScrollListener.java new file mode 100644 index 000000000..870822f65 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/PauseOnScrollListener.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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); + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/SimpleImageLoadingListener.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/SimpleImageLoadingListener.java new file mode 100644 index 000000000..3e42223d5 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/SimpleImageLoadingListener.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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 + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/process/BitmapProcessor.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/process/BitmapProcessor.java new file mode 100644 index 000000000..962605dbd --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/process/BitmapProcessor.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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); +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/DiskCacheUtils.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/DiskCacheUtils.java new file mode 100644 index 000000000..2522cd6b4 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/DiskCacheUtils.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.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(); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/ImageSizeUtils.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/ImageSizeUtils.java new file mode 100644 index 000000000..1dafddc2e --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/ImageSizeUtils.java @@ -0,0 +1,215 @@ +/******************************************************************************* + * 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.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(); + + int height = imageAware.getHeight(); + if (height <= 0) height = maxImageSize.getHeight(); + + return new ImageSize(width, height); + } + + /** + * Computes sample size for downscaling image size (srcSize) to view size (targetSize). This sample + * size is used during + * {@linkplain BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options) + * decoding image} to bitmap.
    + *
    + * Examples:
    + *

    + *

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

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

    + *

    +	 * srcSize(40x40), targetSize(10x10) -> scale = 0.25
    +	 *
    +	 * srcSize(10x10), targetSize(20x20), stretch = false -> scale = 1
    +	 * srcSize(10x10), targetSize(20x20), stretch = true  -> scale = 2
    +	 *
    +	 * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> scale = 0.2
    +	 * srcSize(100x100), targetSize(20x40), viewScaleType = CROP       -> scale = 0.4
    +	 * 
    + * + * @param srcSize Source (image) size + * @param targetSize Target (view) size + * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view + * @param stretch Whether source size should be stretched if target size is larger than source size. If false + * then result scale value can't be greater than 1. + * @return Computed scale + */ + public static float computeImageScale(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType, + boolean stretch) { + final int srcWidth = srcSize.getWidth(); + final int srcHeight = srcSize.getHeight(); + final int targetWidth = targetSize.getWidth(); + final int targetHeight = targetSize.getHeight(); + + final float widthScale = (float) srcWidth / targetWidth; + final float heightScale = (float) srcHeight / targetHeight; + + final int destWidth; + final int destHeight; + if ((viewScaleType == ViewScaleType.FIT_INSIDE && widthScale >= heightScale) || (viewScaleType == ViewScaleType.CROP && widthScale < heightScale)) { + destWidth = targetWidth; + destHeight = (int) (srcHeight / widthScale); + } else { + destWidth = (int) (srcWidth / heightScale); + destHeight = targetHeight; + } + + float scale = 1; + if ((!stretch && destWidth < srcWidth && destHeight < srcHeight) || (stretch && destWidth != srcWidth && destHeight != srcHeight)) { + scale = (float) destWidth / srcWidth; + } + + return scale; + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/IoUtils.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/IoUtils.java new file mode 100644 index 000000000..fb900fb1f --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/IoUtils.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * 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.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 e) { + // Do nothing + } finally { + closeSilently(is); + } + } + + public static void closeSilently(Closeable closeable) { + try { + closeable.close(); + } catch (Exception e) { + // Do nothing + } + } + + /** 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); + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/L.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/L.java new file mode 100644 index 000000000..85d0e5033 --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/L.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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.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); + } +} \ No newline at end of file diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/MemoryCacheUtils.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/MemoryCacheUtils.java new file mode 100644 index 000000000..e7f93a59c --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/MemoryCacheUtils.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * 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.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); + } + } +} diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/StorageUtils.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/StorageUtils.java new file mode 100644 index 000000000..ac57eefec --- /dev/null +++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/StorageUtils.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * 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.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 = ""; + } + 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) { + File cacheDir = getCacheDirectory(context); + File individualCacheDir = new File(cacheDir, INDIVIDUAL_DIR_NAME); + if (!individualCacheDir.exists()) { + if (!individualCacheDir.mkdir()) { + individualCacheDir = cacheDir; + } + } + 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; + } + + 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; + } +}