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. + *

+ * 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: + *

+ *

+ * You can create instance: + *