Commit 724a0c23ec7479d4d5bf59c47c52e68510d20b91

Authored by Penley
1 parent 3af87bc3

init uil

Showing 73 changed files with 11823 additions and 0 deletions
  1 +/build
  2 +uil.iml
\ No newline at end of file
... ...
  1 +/*
  2 + * Copyright (c) 2016. wugian
  3 + * Licensed under the Apache License, Version 2.0 (the "License");
  4 + * you may not use this file except in compliance with the License.
  5 + * You may obtain a copy of the License at
  6 + *
  7 + * http://www.apache.org/licenses/LICENSE-2.0
  8 + *
  9 + * Unless required by applicable law or agreed to in writing, software
  10 + * distributed under the License is distributed on an "AS IS" BASIS,
  11 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 + * See the License for the specific language governing permissions and
  13 + * limitations under the License.
  14 + *
  15 + */
  16 +
  17 +apply plugin: 'com.android.library'
  18 +
  19 +android {
  20 + compileSdkVersion 25
  21 + buildToolsVersion "25.0.0"
  22 +
  23 + defaultConfig {
  24 + minSdkVersion 18
  25 + targetSdkVersion 25
  26 + versionCode 1
  27 + versionName "1.0"
  28 +
  29 + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  30 +
  31 + }
  32 + buildTypes {
  33 + release {
  34 + minifyEnabled false
  35 + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  36 + }
  37 + }
  38 +}
  39 +
  40 +dependencies {
  41 + compile fileTree(dir: 'libs', include: ['*.jar'])
  42 +}
... ...
  1 +# Add project specific ProGuard rules here.
  2 +# By default, the flags in this file are appended to flags specified
  3 +# in D:\develope-file\android-sdk/tools/proguard/proguard-android.txt
  4 +# You can edit the include path and order by changing the proguardFiles
  5 +# directive in build.gradle.
  6 +#
  7 +# For more details, see
  8 +# http://developer.android.com/guide/developing/tools/proguard.html
  9 +
  10 +# Add any project specific keep options here:
  11 +
  12 +# If your project uses WebView with JS, uncomment the following
  13 +# and specify the fully qualified class name to the JavaScript interface
  14 +# class:
  15 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  16 +# public *;
  17 +#}
... ...
  1 +<!--
  2 + ~ Copyright (c) 2016. wugian
  3 + ~ Licensed under the Apache License, Version 2.0 (the "License");
  4 + ~ you may not use this file except in compliance with the License.
  5 + ~ You may obtain a copy of the License at
  6 + ~
  7 + ~ http://www.apache.org/licenses/LICENSE-2.0
  8 + ~
  9 + ~ Unless required by applicable law or agreed to in writing, software
  10 + ~ distributed under the License is distributed on an "AS IS" BASIS,
  11 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 + ~ See the License for the specific language governing permissions and
  13 + ~ limitations under the License.
  14 + ~
  15 + -->
  16 +
  17 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  18 + package="com.nostra13.universalimageloader">
  19 +
  20 + <application android:allowBackup="true"
  21 + android:supportsRtl="true"
  22 + >
  23 +
  24 + </application>
  25 +
  26 +</manifest>
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.utils.IoUtils;
  20 +
  21 +import java.io.File;
  22 +import java.io.IOException;
  23 +import java.io.InputStream;
  24 +
  25 +/**
  26 + * Interface for disk cache
  27 + *
  28 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  29 + * @since 1.9.2
  30 + */
  31 +public interface DiskCache {
  32 + /**
  33 + * Returns root directory of disk cache
  34 + *
  35 + * @return Root directory of disk cache
  36 + */
  37 + File getDirectory();
  38 +
  39 + /**
  40 + * Returns file of cached image
  41 + *
  42 + * @param imageUri Original image URI
  43 + * @return File of cached image or <b>null</b> if image wasn't cached
  44 + */
  45 + File get(String imageUri);
  46 +
  47 + /**
  48 + * Saves image stream in disk cache.
  49 + * Incoming image stream shouldn't be closed in this method.
  50 + *
  51 + * @param imageUri Original image URI
  52 + * @param imageStream Input stream of image (shouldn't be closed in this method)
  53 + * @param listener Listener for saving progress, can be ignored if you don't use
  54 + * {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
  55 + * progress listener} in ImageLoader calls
  56 + * @return <b>true</b> - if image was saved successfully; <b>false</b> - if image wasn't saved in disk cache.
  57 + * @throws IOException
  58 + */
  59 + boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException;
  60 +
  61 + /**
  62 + * Saves image bitmap in disk cache.
  63 + *
  64 + * @param imageUri Original image URI
  65 + * @param bitmap Image bitmap
  66 + * @return <b>true</b> - if bitmap was saved successfully; <b>false</b> - if bitmap wasn't saved in disk cache.
  67 + * @throws IOException
  68 + */
  69 + boolean save(String imageUri, Bitmap bitmap) throws IOException;
  70 +
  71 + /**
  72 + * Removes image file associated with incoming URI
  73 + *
  74 + * @param imageUri Image URI
  75 + * @return <b>true</b> - if image file is deleted successfully; <b>false</b> - if image file doesn't exist for
  76 + * incoming URI or image file can't be deleted.
  77 + */
  78 + boolean remove(String imageUri);
  79 +
  80 + /** Closes disk cache, releases resources. */
  81 + void close();
  82 +
  83 + /** Clears disk cache. */
  84 + void clear();
  85 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  20 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  21 +import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
  22 +import com.nostra13.universalimageloader.utils.IoUtils;
  23 +
  24 +import java.io.BufferedOutputStream;
  25 +import java.io.File;
  26 +import java.io.FileOutputStream;
  27 +import java.io.IOException;
  28 +import java.io.InputStream;
  29 +import java.io.OutputStream;
  30 +
  31 +/**
  32 + * Base disk cache.
  33 + *
  34 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  35 + * @see FileNameGenerator
  36 + * @since 1.0.0
  37 + */
  38 +public abstract class BaseDiskCache implements DiskCache {
  39 + /** {@value */
  40 + public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb
  41 + /** {@value */
  42 + public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
  43 + /** {@value */
  44 + public static final int DEFAULT_COMPRESS_QUALITY = 100;
  45 +
  46 + private static final String ERROR_ARG_NULL = " argument must be not null";
  47 + private static final String TEMP_IMAGE_POSTFIX = ".tmp";
  48 +
  49 + protected final File cacheDir;
  50 + protected final File reserveCacheDir;
  51 +
  52 + protected final FileNameGenerator fileNameGenerator;
  53 +
  54 + protected int bufferSize = DEFAULT_BUFFER_SIZE;
  55 +
  56 + protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
  57 + protected int compressQuality = DEFAULT_COMPRESS_QUALITY;
  58 +
  59 + /** @param cacheDir Directory for file caching */
  60 + public BaseDiskCache(File cacheDir) {
  61 + this(cacheDir, null);
  62 + }
  63 +
  64 + /**
  65 + * @param cacheDir Directory for file caching
  66 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  67 + */
  68 + public BaseDiskCache(File cacheDir, File reserveCacheDir) {
  69 + this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator());
  70 + }
  71 +
  72 + /**
  73 + * @param cacheDir Directory for file caching
  74 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  75 + * @param fileNameGenerator {@linkplain FileNameGenerator
  76 + * Name generator} for cached files
  77 + */
  78 + public BaseDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
  79 + if (cacheDir == null) {
  80 + throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL);
  81 + }
  82 + if (fileNameGenerator == null) {
  83 + throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL);
  84 + }
  85 +
  86 + this.cacheDir = cacheDir;
  87 + this.reserveCacheDir = reserveCacheDir;
  88 + this.fileNameGenerator = fileNameGenerator;
  89 + }
  90 +
  91 + @Override
  92 + public File getDirectory() {
  93 + return cacheDir;
  94 + }
  95 +
  96 + @Override
  97 + public File get(String imageUri) {
  98 + return getFile(imageUri);
  99 + }
  100 +
  101 + @Override
  102 + public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
  103 + File imageFile = getFile(imageUri);
  104 + File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
  105 + boolean loaded = false;
  106 + try {
  107 + OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
  108 + try {
  109 + loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize);
  110 + } finally {
  111 + IoUtils.closeSilently(os);
  112 + }
  113 + } finally {
  114 + if (loaded && !tmpFile.renameTo(imageFile)) {
  115 + loaded = false;
  116 + }
  117 + if (!loaded) {
  118 + tmpFile.delete();
  119 + }
  120 + }
  121 + return loaded;
  122 + }
  123 +
  124 + @Override
  125 + public boolean save(String imageUri, Bitmap bitmap) throws IOException {
  126 + File imageFile = getFile(imageUri);
  127 + File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
  128 + OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
  129 + boolean savedSuccessfully = false;
  130 + try {
  131 + savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);
  132 + } finally {
  133 + IoUtils.closeSilently(os);
  134 + if (savedSuccessfully && !tmpFile.renameTo(imageFile)) {
  135 + savedSuccessfully = false;
  136 + }
  137 + if (!savedSuccessfully) {
  138 + tmpFile.delete();
  139 + }
  140 + }
  141 + bitmap.recycle();
  142 + return savedSuccessfully;
  143 + }
  144 +
  145 + @Override
  146 + public boolean remove(String imageUri) {
  147 + return getFile(imageUri).delete();
  148 + }
  149 +
  150 + @Override
  151 + public void close() {
  152 + // Nothing to do
  153 + }
  154 +
  155 + @Override
  156 + public void clear() {
  157 + File[] files = cacheDir.listFiles();
  158 + if (files != null) {
  159 + for (File f : files) {
  160 + f.delete();
  161 + }
  162 + }
  163 + }
  164 +
  165 + /** Returns file object (not null) for incoming image URI. File object can reference to non-existing file. */
  166 + protected File getFile(String imageUri) {
  167 + String fileName = fileNameGenerator.generate(imageUri);
  168 + File dir = cacheDir;
  169 + if (!cacheDir.exists() && !cacheDir.mkdirs()) {
  170 + if (reserveCacheDir != null && (reserveCacheDir.exists() || reserveCacheDir.mkdirs())) {
  171 + dir = reserveCacheDir;
  172 + }
  173 + }
  174 + return new File(dir, fileName);
  175 + }
  176 +
  177 + public void setBufferSize(int bufferSize) {
  178 + this.bufferSize = bufferSize;
  179 + }
  180 +
  181 + public void setCompressFormat(Bitmap.CompressFormat compressFormat) {
  182 + this.compressFormat = compressFormat;
  183 + }
  184 +
  185 + public void setCompressQuality(int compressQuality) {
  186 + this.compressQuality = compressQuality;
  187 + }
  188 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  20 +import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
  21 +import com.nostra13.universalimageloader.utils.IoUtils;
  22 +
  23 +import java.io.File;
  24 +import java.io.IOException;
  25 +import java.io.InputStream;
  26 +import java.util.Collections;
  27 +import java.util.HashMap;
  28 +import java.util.Map;
  29 +
  30 +/**
  31 + * Cache which deletes files which were loaded more than defined time. Cache size is unlimited.
  32 + *
  33 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  34 + * @since 1.3.1
  35 + */
  36 +public class LimitedAgeDiskCache extends BaseDiskCache {
  37 +
  38 + private final long maxFileAge;
  39 +
  40 + private final Map<File, Long> loadingDates = Collections.synchronizedMap(new HashMap<File, Long>());
  41 +
  42 + /**
  43 + * @param cacheDir Directory for file caching
  44 + * @param maxAge Max file age (in seconds). If file age will exceed this value then it'll be removed on next
  45 + * treatment (and therefore be reloaded).
  46 + */
  47 + public LimitedAgeDiskCache(File cacheDir, long maxAge) {
  48 + this(cacheDir, null, DefaultConfigurationFactory.createFileNameGenerator(), maxAge);
  49 + }
  50 +
  51 + /**
  52 + * @param cacheDir Directory for file caching
  53 + * @param maxAge Max file age (in seconds). If file age will exceed this value then it'll be removed on next
  54 + * treatment (and therefore be reloaded).
  55 + */
  56 + public LimitedAgeDiskCache(File cacheDir, File reserveCacheDir, long maxAge) {
  57 + this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator(), maxAge);
  58 + }
  59 +
  60 + /**
  61 + * @param cacheDir Directory for file caching
  62 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  63 + * @param fileNameGenerator Name generator for cached files
  64 + * @param maxAge Max file age (in seconds). If file age will exceed this value then it'll be removed on next
  65 + * treatment (and therefore be reloaded).
  66 + */
  67 + public LimitedAgeDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long maxAge) {
  68 + super(cacheDir, reserveCacheDir, fileNameGenerator);
  69 + this.maxFileAge = maxAge * 1000; // to milliseconds
  70 + }
  71 +
  72 + @Override
  73 + public File get(String imageUri) {
  74 + File file = super.get(imageUri);
  75 + if (file != null && file.exists()) {
  76 + boolean cached;
  77 + Long loadingDate = loadingDates.get(file);
  78 + if (loadingDate == null) {
  79 + cached = false;
  80 + loadingDate = file.lastModified();
  81 + } else {
  82 + cached = true;
  83 + }
  84 +
  85 + if (System.currentTimeMillis() - loadingDate > maxFileAge) {
  86 + file.delete();
  87 + loadingDates.remove(file);
  88 + } else if (!cached) {
  89 + loadingDates.put(file, loadingDate);
  90 + }
  91 + }
  92 + return file;
  93 + }
  94 +
  95 + @Override
  96 + public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
  97 + boolean saved = super.save(imageUri, imageStream, listener);
  98 + rememberUsage(imageUri);
  99 + return saved;
  100 + }
  101 +
  102 + @Override
  103 + public boolean save(String imageUri, Bitmap bitmap) throws IOException {
  104 + boolean saved = super.save(imageUri, bitmap);
  105 + rememberUsage(imageUri);
  106 + return saved;
  107 + }
  108 +
  109 + @Override
  110 + public boolean remove(String imageUri) {
  111 + loadingDates.remove(getFile(imageUri));
  112 + return super.remove(imageUri);
  113 + }
  114 +
  115 + @Override
  116 + public void clear() {
  117 + super.clear();
  118 + loadingDates.clear();
  119 + }
  120 +
  121 + private void rememberUsage(String imageUri) {
  122 + File file = getFile(imageUri);
  123 + long currentTime = System.currentTimeMillis();
  124 + file.setLastModified(currentTime);
  125 + loadingDates.put(file, currentTime);
  126 + }
  127 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.impl;
  17 +
  18 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  19 +
  20 +import java.io.File;
  21 +
  22 +/**
  23 + * Default implementation of {@linkplain com.nostra13.universalimageloader.cache.disc.DiskCache disk cache}.
  24 + * Cache size is unlimited.
  25 + *
  26 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  27 + * @since 1.0.0
  28 + */
  29 +public class UnlimitedDiskCache extends BaseDiskCache {
  30 + /** @param cacheDir Directory for file caching */
  31 + public UnlimitedDiskCache(File cacheDir) {
  32 + super(cacheDir);
  33 + }
  34 +
  35 + /**
  36 + * @param cacheDir Directory for file caching
  37 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  38 + */
  39 + public UnlimitedDiskCache(File cacheDir, File reserveCacheDir) {
  40 + super(cacheDir, reserveCacheDir);
  41 + }
  42 +
  43 + /**
  44 + * @param cacheDir Directory for file caching
  45 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  46 + * @param fileNameGenerator {@linkplain FileNameGenerator
  47 + * Name generator} for cached files
  48 + */
  49 + public UnlimitedDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
  50 + super(cacheDir, reserveCacheDir, fileNameGenerator);
  51 + }
  52 +}
... ...
  1 +/*
  2 + * Copyright (C) 2011 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.nostra13.universalimageloader.cache.disc.impl.ext;
  17 +
  18 +import java.io.BufferedWriter;
  19 +import java.io.Closeable;
  20 +import java.io.EOFException;
  21 +import java.io.File;
  22 +import java.io.FileInputStream;
  23 +import java.io.FileNotFoundException;
  24 +import java.io.FileOutputStream;
  25 +import java.io.FilterOutputStream;
  26 +import java.io.IOException;
  27 +import java.io.InputStream;
  28 +import java.io.InputStreamReader;
  29 +import java.io.OutputStream;
  30 +import java.io.OutputStreamWriter;
  31 +import java.io.Writer;
  32 +import java.util.ArrayList;
  33 +import java.util.Iterator;
  34 +import java.util.LinkedHashMap;
  35 +import java.util.Map;
  36 +import java.util.concurrent.Callable;
  37 +import java.util.concurrent.LinkedBlockingQueue;
  38 +import java.util.concurrent.ThreadPoolExecutor;
  39 +import java.util.concurrent.TimeUnit;
  40 +import java.util.regex.Matcher;
  41 +import java.util.regex.Pattern;
  42 +
  43 +/**
  44 + * A cache that uses a bounded amount of space on a filesystem. Each cache
  45 + * entry has a string key and a fixed number of values. Each key must match
  46 + * the regex <strong>[a-z0-9_-]{1,64}</strong>. Values are byte sequences,
  47 + * accessible as streams or files. Each value must be between {@code 0} and
  48 + * {@code Integer.MAX_VALUE} bytes in length.
  49 + *
  50 + * <p>The cache stores its data in a directory on the filesystem. This
  51 + * directory must be exclusive to the cache; the cache may delete or overwrite
  52 + * files from its directory. It is an error for multiple processes to use the
  53 + * same cache directory at the same time.
  54 + *
  55 + * <p>This cache limits the number of bytes that it will store on the
  56 + * filesystem. When the number of stored bytes exceeds the limit, the cache will
  57 + * remove entries in the background until the limit is satisfied. The limit is
  58 + * not strict: the cache may temporarily exceed it while waiting for files to be
  59 + * deleted. The limit does not include filesystem overhead or the cache
  60 + * journal so space-sensitive applications should set a conservative limit.
  61 + *
  62 + * <p>Clients call {@link #edit} to create or update the values of an entry. An
  63 + * entry may have only one editor at one time; if a value is not available to be
  64 + * edited then {@link #edit} will return null.
  65 + * <ul>
  66 + * <li>When an entry is being <strong>created</strong> it is necessary to
  67 + * supply a full set of values; the empty value should be used as a
  68 + * placeholder if necessary.
  69 + * <li>When an entry is being <strong>edited</strong>, it is not necessary
  70 + * to supply data for every value; values default to their previous
  71 + * value.
  72 + * </ul>
  73 + * Every {@link #edit} call must be matched by a call to {@link Editor#commit}
  74 + * or {@link Editor#abort}. Committing is atomic: a read observes the full set
  75 + * of values as they were before or after the commit, but never a mix of values.
  76 + *
  77 + * <p>Clients call {@link #get} to read a snapshot of an entry. The read will
  78 + * observe the value at the time that {@link #get} was called. Updates and
  79 + * removals after the call do not impact ongoing reads.
  80 + *
  81 + * <p>This class is tolerant of some I/O errors. If files are missing from the
  82 + * filesystem, the corresponding entries will be dropped from the cache. If
  83 + * an error occurs while writing a cache value, the edit will fail silently.
  84 + * Callers should handle other problems by catching {@code IOException} and
  85 + * responding appropriately.
  86 + */
  87 +final class DiskLruCache implements Closeable {
  88 + static final String JOURNAL_FILE = "journal";
  89 + static final String JOURNAL_FILE_TEMP = "journal.tmp";
  90 + static final String JOURNAL_FILE_BACKUP = "journal.bkp";
  91 + static final String MAGIC = "libcore.io.DiskLruCache";
  92 + static final String VERSION_1 = "1";
  93 + static final long ANY_SEQUENCE_NUMBER = -1;
  94 + static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,64}");
  95 + private static final String CLEAN = "CLEAN";
  96 + private static final String DIRTY = "DIRTY";
  97 + private static final String REMOVE = "REMOVE";
  98 + private static final String READ = "READ";
  99 +
  100 + /*
  101 + * This cache uses a journal file named "journal". A typical journal file
  102 + * looks like this:
  103 + * libcore.io.DiskLruCache
  104 + * 1
  105 + * 100
  106 + * 2
  107 + *
  108 + * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
  109 + * DIRTY 335c4c6028171cfddfbaae1a9c313c52
  110 + * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
  111 + * REMOVE 335c4c6028171cfddfbaae1a9c313c52
  112 + * DIRTY 1ab96a171faeeee38496d8b330771a7a
  113 + * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
  114 + * READ 335c4c6028171cfddfbaae1a9c313c52
  115 + * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
  116 + *
  117 + * The first five lines of the journal form its header. They are the
  118 + * constant string "libcore.io.DiskLruCache", the disk cache's version,
  119 + * the application's version, the value count, and a blank line.
  120 + *
  121 + * Each of the subsequent lines in the file is a record of the state of a
  122 + * cache entry. Each line contains space-separated values: a state, a key,
  123 + * and optional state-specific values.
  124 + * o DIRTY lines track that an entry is actively being created or updated.
  125 + * Every successful DIRTY action should be followed by a CLEAN or REMOVE
  126 + * action. DIRTY lines without a matching CLEAN or REMOVE indicate that
  127 + * temporary files may need to be deleted.
  128 + * o CLEAN lines track a cache entry that has been successfully published
  129 + * and may be read. A publish line is followed by the lengths of each of
  130 + * its values.
  131 + * o READ lines track accesses for LRU.
  132 + * o REMOVE lines track entries that have been deleted.
  133 + *
  134 + * The journal file is appended to as cache operations occur. The journal may
  135 + * occasionally be compacted by dropping redundant lines. A temporary file named
  136 + * "journal.tmp" will be used during compaction; that file should be deleted if
  137 + * it exists when the cache is opened.
  138 + */
  139 +
  140 + private final File directory;
  141 + private final File journalFile;
  142 + private final File journalFileTmp;
  143 + private final File journalFileBackup;
  144 + private final int appVersion;
  145 + private long maxSize;
  146 + private int maxFileCount;
  147 + private final int valueCount;
  148 + private long size = 0;
  149 + private int fileCount = 0;
  150 + private Writer journalWriter;
  151 + private final LinkedHashMap<String, Entry> lruEntries =
  152 + new LinkedHashMap<String, Entry>(0, 0.75f, true);
  153 + private int redundantOpCount;
  154 +
  155 + /**
  156 + * To differentiate between old and current snapshots, each entry is given
  157 + * a sequence number each time an edit is committed. A snapshot is stale if
  158 + * its sequence number is not equal to its entry's sequence number.
  159 + */
  160 + private long nextSequenceNumber = 0;
  161 +
  162 + /** This cache uses a single background thread to evict entries. */
  163 + final ThreadPoolExecutor executorService =
  164 + new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
  165 + private final Callable<Void> cleanupCallable = new Callable<Void>() {
  166 + public Void call() throws Exception {
  167 + synchronized (DiskLruCache.this) {
  168 + if (journalWriter == null) {
  169 + return null; // Closed.
  170 + }
  171 + trimToSize();
  172 + trimToFileCount();
  173 + if (journalRebuildRequired()) {
  174 + rebuildJournal();
  175 + redundantOpCount = 0;
  176 + }
  177 + }
  178 + return null;
  179 + }
  180 + };
  181 +
  182 + private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount) {
  183 + this.directory = directory;
  184 + this.appVersion = appVersion;
  185 + this.journalFile = new File(directory, JOURNAL_FILE);
  186 + this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP);
  187 + this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
  188 + this.valueCount = valueCount;
  189 + this.maxSize = maxSize;
  190 + this.maxFileCount = maxFileCount;
  191 + }
  192 +
  193 + /**
  194 + * Opens the cache in {@code directory}, creating a cache if none exists
  195 + * there.
  196 + *
  197 + * @param directory a writable directory
  198 + * @param valueCount the number of values per cache entry. Must be positive.
  199 + * @param maxSize the maximum number of bytes this cache should use to store
  200 + * @param maxFileCount the maximum file count this cache should store
  201 + * @throws IOException if reading or writing the cache directory fails
  202 + */
  203 + public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount)
  204 + throws IOException {
  205 + if (maxSize <= 0) {
  206 + throw new IllegalArgumentException("maxSize <= 0");
  207 + }
  208 + if (maxFileCount <= 0) {
  209 + throw new IllegalArgumentException("maxFileCount <= 0");
  210 + }
  211 + if (valueCount <= 0) {
  212 + throw new IllegalArgumentException("valueCount <= 0");
  213 + }
  214 +
  215 + // If a bkp file exists, use it instead.
  216 + File backupFile = new File(directory, JOURNAL_FILE_BACKUP);
  217 + if (backupFile.exists()) {
  218 + File journalFile = new File(directory, JOURNAL_FILE);
  219 + // If journal file also exists just delete backup file.
  220 + if (journalFile.exists()) {
  221 + backupFile.delete();
  222 + } else {
  223 + renameTo(backupFile, journalFile, false);
  224 + }
  225 + }
  226 +
  227 + // Prefer to pick up where we left off.
  228 + DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount);
  229 + if (cache.journalFile.exists()) {
  230 + try {
  231 + cache.readJournal();
  232 + cache.processJournal();
  233 + cache.journalWriter = new BufferedWriter(
  234 + new OutputStreamWriter(new FileOutputStream(cache.journalFile, true), Util.US_ASCII));
  235 + return cache;
  236 + } catch (IOException journalIsCorrupt) {
  237 + System.out
  238 + .println("DiskLruCache "
  239 + + directory
  240 + + " is corrupt: "
  241 + + journalIsCorrupt.getMessage()
  242 + + ", removing");
  243 + cache.delete();
  244 + }
  245 + }
  246 +
  247 + // Create a new empty cache.
  248 + directory.mkdirs();
  249 + cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount);
  250 + cache.rebuildJournal();
  251 + return cache;
  252 + }
  253 +
  254 + private void readJournal() throws IOException {
  255 + StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII);
  256 + try {
  257 + String magic = reader.readLine();
  258 + String version = reader.readLine();
  259 + String appVersionString = reader.readLine();
  260 + String valueCountString = reader.readLine();
  261 + String blank = reader.readLine();
  262 + if (!MAGIC.equals(magic)
  263 + || !VERSION_1.equals(version)
  264 + || !Integer.toString(appVersion).equals(appVersionString)
  265 + || !Integer.toString(valueCount).equals(valueCountString)
  266 + || !"".equals(blank)) {
  267 + throw new IOException("unexpected journal header: [" + magic + ", " + version + ", "
  268 + + valueCountString + ", " + blank + "]");
  269 + }
  270 +
  271 + int lineCount = 0;
  272 + while (true) {
  273 + try {
  274 + readJournalLine(reader.readLine());
  275 + lineCount++;
  276 + } catch (EOFException endOfJournal) {
  277 + break;
  278 + }
  279 + }
  280 + redundantOpCount = lineCount - lruEntries.size();
  281 + } finally {
  282 + Util.closeQuietly(reader);
  283 + }
  284 + }
  285 +
  286 + private void readJournalLine(String line) throws IOException {
  287 + int firstSpace = line.indexOf(' ');
  288 + if (firstSpace == -1) {
  289 + throw new IOException("unexpected journal line: " + line);
  290 + }
  291 +
  292 + int keyBegin = firstSpace + 1;
  293 + int secondSpace = line.indexOf(' ', keyBegin);
  294 + final String key;
  295 + if (secondSpace == -1) {
  296 + key = line.substring(keyBegin);
  297 + if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) {
  298 + lruEntries.remove(key);
  299 + return;
  300 + }
  301 + } else {
  302 + key = line.substring(keyBegin, secondSpace);
  303 + }
  304 +
  305 + Entry entry = lruEntries.get(key);
  306 + if (entry == null) {
  307 + entry = new Entry(key);
  308 + lruEntries.put(key, entry);
  309 + }
  310 +
  311 + if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) {
  312 + String[] parts = line.substring(secondSpace + 1).split(" ");
  313 + entry.readable = true;
  314 + entry.currentEditor = null;
  315 + entry.setLengths(parts);
  316 + } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) {
  317 + entry.currentEditor = new Editor(entry);
  318 + } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) {
  319 + // This work was already done by calling lruEntries.get().
  320 + } else {
  321 + throw new IOException("unexpected journal line: " + line);
  322 + }
  323 + }
  324 +
  325 + /**
  326 + * Computes the initial size and collects garbage as a part of opening the
  327 + * cache. Dirty entries are assumed to be inconsistent and will be deleted.
  328 + */
  329 + private void processJournal() throws IOException {
  330 + deleteIfExists(journalFileTmp);
  331 + for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {
  332 + Entry entry = i.next();
  333 + if (entry.currentEditor == null) {
  334 + for (int t = 0; t < valueCount; t++) {
  335 + size += entry.lengths[t];
  336 + fileCount++;
  337 + }
  338 + } else {
  339 + entry.currentEditor = null;
  340 + for (int t = 0; t < valueCount; t++) {
  341 + deleteIfExists(entry.getCleanFile(t));
  342 + deleteIfExists(entry.getDirtyFile(t));
  343 + }
  344 + i.remove();
  345 + }
  346 + }
  347 + }
  348 +
  349 + /**
  350 + * Creates a new journal that omits redundant information. This replaces the
  351 + * current journal if it exists.
  352 + */
  353 + private synchronized void rebuildJournal() throws IOException {
  354 + if (journalWriter != null) {
  355 + journalWriter.close();
  356 + }
  357 +
  358 + Writer writer = new BufferedWriter(
  359 + new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII));
  360 + try {
  361 + writer.write(MAGIC);
  362 + writer.write("\n");
  363 + writer.write(VERSION_1);
  364 + writer.write("\n");
  365 + writer.write(Integer.toString(appVersion));
  366 + writer.write("\n");
  367 + writer.write(Integer.toString(valueCount));
  368 + writer.write("\n");
  369 + writer.write("\n");
  370 +
  371 + for (Entry entry : lruEntries.values()) {
  372 + if (entry.currentEditor != null) {
  373 + writer.write(DIRTY + ' ' + entry.key + '\n');
  374 + } else {
  375 + writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
  376 + }
  377 + }
  378 + } finally {
  379 + writer.close();
  380 + }
  381 +
  382 + if (journalFile.exists()) {
  383 + renameTo(journalFile, journalFileBackup, true);
  384 + }
  385 + renameTo(journalFileTmp, journalFile, false);
  386 + journalFileBackup.delete();
  387 +
  388 + journalWriter = new BufferedWriter(
  389 + new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII));
  390 + }
  391 +
  392 + private static void deleteIfExists(File file) throws IOException {
  393 + if (file.exists() && !file.delete()) {
  394 + throw new IOException();
  395 + }
  396 + }
  397 +
  398 + private static void renameTo(File from, File to, boolean deleteDestination) throws IOException {
  399 + if (deleteDestination) {
  400 + deleteIfExists(to);
  401 + }
  402 + if (!from.renameTo(to)) {
  403 + throw new IOException();
  404 + }
  405 + }
  406 +
  407 + /**
  408 + * Returns a snapshot of the entry named {@code key}, or null if it doesn't
  409 + * exist is not currently readable. If a value is returned, it is moved to
  410 + * the head of the LRU queue.
  411 + */
  412 + public synchronized Snapshot get(String key) throws IOException {
  413 + checkNotClosed();
  414 + validateKey(key);
  415 + Entry entry = lruEntries.get(key);
  416 + if (entry == null) {
  417 + return null;
  418 + }
  419 +
  420 + if (!entry.readable) {
  421 + return null;
  422 + }
  423 +
  424 + // Open all streams eagerly to guarantee that we see a single published
  425 + // snapshot. If we opened streams lazily then the streams could come
  426 + // from different edits.
  427 + File[] files = new File[valueCount];
  428 + InputStream[] ins = new InputStream[valueCount];
  429 + try {
  430 + File file;
  431 + for (int i = 0; i < valueCount; i++) {
  432 + file = entry.getCleanFile(i);
  433 + files[i] = file;
  434 + ins[i] = new FileInputStream(file);
  435 + }
  436 + } catch (FileNotFoundException e) {
  437 + // A file must have been deleted manually!
  438 + for (int i = 0; i < valueCount; i++) {
  439 + if (ins[i] != null) {
  440 + Util.closeQuietly(ins[i]);
  441 + } else {
  442 + break;
  443 + }
  444 + }
  445 + return null;
  446 + }
  447 +
  448 + redundantOpCount++;
  449 + journalWriter.append(READ + ' ' + key + '\n');
  450 + if (journalRebuildRequired()) {
  451 + executorService.submit(cleanupCallable);
  452 + }
  453 +
  454 + return new Snapshot(key, entry.sequenceNumber, files, ins, entry.lengths);
  455 + }
  456 +
  457 + /**
  458 + * Returns an editor for the entry named {@code key}, or null if another
  459 + * edit is in progress.
  460 + */
  461 + public Editor edit(String key) throws IOException {
  462 + return edit(key, ANY_SEQUENCE_NUMBER);
  463 + }
  464 +
  465 + private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
  466 + checkNotClosed();
  467 + validateKey(key);
  468 + Entry entry = lruEntries.get(key);
  469 + if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null
  470 + || entry.sequenceNumber != expectedSequenceNumber)) {
  471 + return null; // Snapshot is stale.
  472 + }
  473 + if (entry == null) {
  474 + entry = new Entry(key);
  475 + lruEntries.put(key, entry);
  476 + } else if (entry.currentEditor != null) {
  477 + return null; // Another edit is in progress.
  478 + }
  479 +
  480 + Editor editor = new Editor(entry);
  481 + entry.currentEditor = editor;
  482 +
  483 + // Flush the journal before creating files to prevent file leaks.
  484 + journalWriter.write(DIRTY + ' ' + key + '\n');
  485 + journalWriter.flush();
  486 + return editor;
  487 + }
  488 +
  489 + /** Returns the directory where this cache stores its data. */
  490 + public File getDirectory() {
  491 + return directory;
  492 + }
  493 +
  494 + /**
  495 + * Returns the maximum number of bytes that this cache should use to store
  496 + * its data.
  497 + */
  498 + public synchronized long getMaxSize() {
  499 + return maxSize;
  500 + }
  501 +
  502 + /** Returns the maximum number of files that this cache should store */
  503 + public synchronized int getMaxFileCount() {
  504 + return maxFileCount;
  505 + }
  506 +
  507 + /**
  508 + * Changes the maximum number of bytes the cache can store and queues a job
  509 + * to trim the existing store, if necessary.
  510 + */
  511 + public synchronized void setMaxSize(long maxSize) {
  512 + this.maxSize = maxSize;
  513 + executorService.submit(cleanupCallable);
  514 + }
  515 +
  516 + /**
  517 + * Returns the number of bytes currently being used to store the values in
  518 + * this cache. This may be greater than the max size if a background
  519 + * deletion is pending.
  520 + */
  521 + public synchronized long size() {
  522 + return size;
  523 + }
  524 +
  525 + /**
  526 + * Returns the number of files currently being used to store the values in
  527 + * this cache. This may be greater than the max file count if a background
  528 + * deletion is pending.
  529 + */
  530 + public synchronized long fileCount() {
  531 + return fileCount;
  532 + }
  533 +
  534 + private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
  535 + Entry entry = editor.entry;
  536 + if (entry.currentEditor != editor) {
  537 + throw new IllegalStateException();
  538 + }
  539 +
  540 + // If this edit is creating the entry for the first time, every index must have a value.
  541 + if (success && !entry.readable) {
  542 + for (int i = 0; i < valueCount; i++) {
  543 + if (!editor.written[i]) {
  544 + editor.abort();
  545 + throw new IllegalStateException("Newly created entry didn't create value for index " + i);
  546 + }
  547 + if (!entry.getDirtyFile(i).exists()) {
  548 + editor.abort();
  549 + return;
  550 + }
  551 + }
  552 + }
  553 +
  554 + for (int i = 0; i < valueCount; i++) {
  555 + File dirty = entry.getDirtyFile(i);
  556 + if (success) {
  557 + if (dirty.exists()) {
  558 + File clean = entry.getCleanFile(i);
  559 + dirty.renameTo(clean);
  560 + long oldLength = entry.lengths[i];
  561 + long newLength = clean.length();
  562 + entry.lengths[i] = newLength;
  563 + size = size - oldLength + newLength;
  564 + fileCount++;
  565 + }
  566 + } else {
  567 + deleteIfExists(dirty);
  568 + }
  569 + }
  570 +
  571 + redundantOpCount++;
  572 + entry.currentEditor = null;
  573 + if (entry.readable | success) {
  574 + entry.readable = true;
  575 + journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
  576 + if (success) {
  577 + entry.sequenceNumber = nextSequenceNumber++;
  578 + }
  579 + } else {
  580 + lruEntries.remove(entry.key);
  581 + journalWriter.write(REMOVE + ' ' + entry.key + '\n');
  582 + }
  583 + journalWriter.flush();
  584 +
  585 + if (size > maxSize || fileCount > maxFileCount || journalRebuildRequired()) {
  586 + executorService.submit(cleanupCallable);
  587 + }
  588 + }
  589 +
  590 + /**
  591 + * We only rebuild the journal when it will halve the size of the journal
  592 + * and eliminate at least 2000 ops.
  593 + */
  594 + private boolean journalRebuildRequired() {
  595 + final int redundantOpCompactThreshold = 2000;
  596 + return redundantOpCount >= redundantOpCompactThreshold //
  597 + && redundantOpCount >= lruEntries.size();
  598 + }
  599 +
  600 + /**
  601 + * Drops the entry for {@code key} if it exists and can be removed. Entries
  602 + * actively being edited cannot be removed.
  603 + *
  604 + * @return true if an entry was removed.
  605 + */
  606 + public synchronized boolean remove(String key) throws IOException {
  607 + checkNotClosed();
  608 + validateKey(key);
  609 + Entry entry = lruEntries.get(key);
  610 + if (entry == null || entry.currentEditor != null) {
  611 + return false;
  612 + }
  613 +
  614 + for (int i = 0; i < valueCount; i++) {
  615 + File file = entry.getCleanFile(i);
  616 + if (file.exists() && !file.delete()) {
  617 + throw new IOException("failed to delete " + file);
  618 + }
  619 + size -= entry.lengths[i];
  620 + fileCount--;
  621 + entry.lengths[i] = 0;
  622 + }
  623 +
  624 + redundantOpCount++;
  625 + journalWriter.append(REMOVE + ' ' + key + '\n');
  626 + lruEntries.remove(key);
  627 +
  628 + if (journalRebuildRequired()) {
  629 + executorService.submit(cleanupCallable);
  630 + }
  631 +
  632 + return true;
  633 + }
  634 +
  635 + /** Returns true if this cache has been closed. */
  636 + public synchronized boolean isClosed() {
  637 + return journalWriter == null;
  638 + }
  639 +
  640 + private void checkNotClosed() {
  641 + if (journalWriter == null) {
  642 + throw new IllegalStateException("cache is closed");
  643 + }
  644 + }
  645 +
  646 + /** Force buffered operations to the filesystem. */
  647 + public synchronized void flush() throws IOException {
  648 + checkNotClosed();
  649 + trimToSize();
  650 + trimToFileCount();
  651 + journalWriter.flush();
  652 + }
  653 +
  654 + /** Closes this cache. Stored values will remain on the filesystem. */
  655 + public synchronized void close() throws IOException {
  656 + if (journalWriter == null) {
  657 + return; // Already closed.
  658 + }
  659 + for (Entry entry : new ArrayList<Entry>(lruEntries.values())) {
  660 + if (entry.currentEditor != null) {
  661 + entry.currentEditor.abort();
  662 + }
  663 + }
  664 + trimToSize();
  665 + trimToFileCount();
  666 + journalWriter.close();
  667 + journalWriter = null;
  668 + }
  669 +
  670 + private void trimToSize() throws IOException {
  671 + while (size > maxSize) {
  672 + Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();
  673 + remove(toEvict.getKey());
  674 + }
  675 + }
  676 +
  677 + private void trimToFileCount() throws IOException {
  678 + while (fileCount > maxFileCount) {
  679 + Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();
  680 + remove(toEvict.getKey());
  681 + }
  682 + }
  683 +
  684 + /**
  685 + * Closes the cache and deletes all of its stored values. This will delete
  686 + * all files in the cache directory including files that weren't created by
  687 + * the cache.
  688 + */
  689 + public void delete() throws IOException {
  690 + close();
  691 + Util.deleteContents(directory);
  692 + }
  693 +
  694 + private void validateKey(String key) {
  695 + Matcher matcher = LEGAL_KEY_PATTERN.matcher(key);
  696 + if (!matcher.matches()) {
  697 + throw new IllegalArgumentException("keys must match regex [a-z0-9_-]{1,64}: \"" + key + "\"");
  698 + }
  699 + }
  700 +
  701 + private static String inputStreamToString(InputStream in) throws IOException {
  702 + return Util.readFully(new InputStreamReader(in, Util.UTF_8));
  703 + }
  704 +
  705 + /** A snapshot of the values for an entry. */
  706 + public final class Snapshot implements Closeable {
  707 + private final String key;
  708 + private final long sequenceNumber;
  709 + private File[] files;
  710 + private final InputStream[] ins;
  711 + private final long[] lengths;
  712 +
  713 + private Snapshot(String key, long sequenceNumber, File[] files, InputStream[] ins, long[] lengths) {
  714 + this.key = key;
  715 + this.sequenceNumber = sequenceNumber;
  716 + this.files = files;
  717 + this.ins = ins;
  718 + this.lengths = lengths;
  719 + }
  720 +
  721 + /**
  722 + * Returns an editor for this snapshot's entry, or null if either the
  723 + * entry has changed since this snapshot was created or if another edit
  724 + * is in progress.
  725 + */
  726 + public Editor edit() throws IOException {
  727 + return DiskLruCache.this.edit(key, sequenceNumber);
  728 + }
  729 +
  730 + /** Returns file with the value for {@code index}. */
  731 + public File getFile(int index) {
  732 + return files[index];
  733 + }
  734 +
  735 + /** Returns the unbuffered stream with the value for {@code index}. */
  736 + public InputStream getInputStream(int index) {
  737 + return ins[index];
  738 + }
  739 +
  740 + /** Returns the string value for {@code index}. */
  741 + public String getString(int index) throws IOException {
  742 + return inputStreamToString(getInputStream(index));
  743 + }
  744 +
  745 + /** Returns the byte length of the value for {@code index}. */
  746 + public long getLength(int index) {
  747 + return lengths[index];
  748 + }
  749 +
  750 + public void close() {
  751 + for (InputStream in : ins) {
  752 + Util.closeQuietly(in);
  753 + }
  754 + }
  755 + }
  756 +
  757 + private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() {
  758 + @Override
  759 + public void write(int b) throws IOException {
  760 + // Eat all writes silently. Nom nom.
  761 + }
  762 + };
  763 +
  764 + /** Edits the values for an entry. */
  765 + public final class Editor {
  766 + private final Entry entry;
  767 + private final boolean[] written;
  768 + private boolean hasErrors;
  769 + private boolean committed;
  770 +
  771 + private Editor(Entry entry) {
  772 + this.entry = entry;
  773 + this.written = (entry.readable) ? null : new boolean[valueCount];
  774 + }
  775 +
  776 + /**
  777 + * Returns an unbuffered input stream to read the last committed value,
  778 + * or null if no value has been committed.
  779 + */
  780 + public InputStream newInputStream(int index) throws IOException {
  781 + synchronized (DiskLruCache.this) {
  782 + if (entry.currentEditor != this) {
  783 + throw new IllegalStateException();
  784 + }
  785 + if (!entry.readable) {
  786 + return null;
  787 + }
  788 + try {
  789 + return new FileInputStream(entry.getCleanFile(index));
  790 + } catch (FileNotFoundException e) {
  791 + return null;
  792 + }
  793 + }
  794 + }
  795 +
  796 + /**
  797 + * Returns the last committed value as a string, or null if no value
  798 + * has been committed.
  799 + */
  800 + public String getString(int index) throws IOException {
  801 + InputStream in = newInputStream(index);
  802 + return in != null ? inputStreamToString(in) : null;
  803 + }
  804 +
  805 + /**
  806 + * Returns a new unbuffered output stream to write the value at
  807 + * {@code index}. If the underlying output stream encounters errors
  808 + * when writing to the filesystem, this edit will be aborted when
  809 + * {@link #commit} is called. The returned output stream does not throw
  810 + * IOExceptions.
  811 + */
  812 + public OutputStream newOutputStream(int index) throws IOException {
  813 + synchronized (DiskLruCache.this) {
  814 + if (entry.currentEditor != this) {
  815 + throw new IllegalStateException();
  816 + }
  817 + if (!entry.readable) {
  818 + written[index] = true;
  819 + }
  820 + File dirtyFile = entry.getDirtyFile(index);
  821 + FileOutputStream outputStream;
  822 + try {
  823 + outputStream = new FileOutputStream(dirtyFile);
  824 + } catch (FileNotFoundException e) {
  825 + // Attempt to recreate the cache directory.
  826 + directory.mkdirs();
  827 + try {
  828 + outputStream = new FileOutputStream(dirtyFile);
  829 + } catch (FileNotFoundException e2) {
  830 + // We are unable to recover. Silently eat the writes.
  831 + return NULL_OUTPUT_STREAM;
  832 + }
  833 + }
  834 + return new FaultHidingOutputStream(outputStream);
  835 + }
  836 + }
  837 +
  838 + /** Sets the value at {@code index} to {@code value}. */
  839 + public void set(int index, String value) throws IOException {
  840 + Writer writer = null;
  841 + try {
  842 + writer = new OutputStreamWriter(newOutputStream(index), Util.UTF_8);
  843 + writer.write(value);
  844 + } finally {
  845 + Util.closeQuietly(writer);
  846 + }
  847 + }
  848 +
  849 + /**
  850 + * Commits this edit so it is visible to readers. This releases the
  851 + * edit lock so another edit may be started on the same key.
  852 + */
  853 + public void commit() throws IOException {
  854 + if (hasErrors) {
  855 + completeEdit(this, false);
  856 + remove(entry.key); // The previous entry is stale.
  857 + } else {
  858 + completeEdit(this, true);
  859 + }
  860 + committed = true;
  861 + }
  862 +
  863 + /**
  864 + * Aborts this edit. This releases the edit lock so another edit may be
  865 + * started on the same key.
  866 + */
  867 + public void abort() throws IOException {
  868 + completeEdit(this, false);
  869 + }
  870 +
  871 + public void abortUnlessCommitted() {
  872 + if (!committed) {
  873 + try {
  874 + abort();
  875 + } catch (IOException ignored) {
  876 + }
  877 + }
  878 + }
  879 +
  880 + private class FaultHidingOutputStream extends FilterOutputStream {
  881 + private FaultHidingOutputStream(OutputStream out) {
  882 + super(out);
  883 + }
  884 +
  885 + @Override public void write(int oneByte) {
  886 + try {
  887 + out.write(oneByte);
  888 + } catch (IOException e) {
  889 + hasErrors = true;
  890 + }
  891 + }
  892 +
  893 + @Override public void write(byte[] buffer, int offset, int length) {
  894 + try {
  895 + out.write(buffer, offset, length);
  896 + } catch (IOException e) {
  897 + hasErrors = true;
  898 + }
  899 + }
  900 +
  901 + @Override public void close() {
  902 + try {
  903 + out.close();
  904 + } catch (IOException e) {
  905 + hasErrors = true;
  906 + }
  907 + }
  908 +
  909 + @Override public void flush() {
  910 + try {
  911 + out.flush();
  912 + } catch (IOException e) {
  913 + hasErrors = true;
  914 + }
  915 + }
  916 + }
  917 + }
  918 +
  919 + private final class Entry {
  920 + private final String key;
  921 +
  922 + /** Lengths of this entry's files. */
  923 + private final long[] lengths;
  924 +
  925 + /** True if this entry has ever been published. */
  926 + private boolean readable;
  927 +
  928 + /** The ongoing edit or null if this entry is not being edited. */
  929 + private Editor currentEditor;
  930 +
  931 + /** The sequence number of the most recently committed edit to this entry. */
  932 + private long sequenceNumber;
  933 +
  934 + private Entry(String key) {
  935 + this.key = key;
  936 + this.lengths = new long[valueCount];
  937 + }
  938 +
  939 + public String getLengths() throws IOException {
  940 + StringBuilder result = new StringBuilder();
  941 + for (long size : lengths) {
  942 + result.append(' ').append(size);
  943 + }
  944 + return result.toString();
  945 + }
  946 +
  947 + /** Set lengths using decimal numbers like "10123". */
  948 + private void setLengths(String[] strings) throws IOException {
  949 + if (strings.length != valueCount) {
  950 + throw invalidLengths(strings);
  951 + }
  952 +
  953 + try {
  954 + for (int i = 0; i < strings.length; i++) {
  955 + lengths[i] = Long.parseLong(strings[i]);
  956 + }
  957 + } catch (NumberFormatException e) {
  958 + throw invalidLengths(strings);
  959 + }
  960 + }
  961 +
  962 + private IOException invalidLengths(String[] strings) throws IOException {
  963 + throw new IOException("unexpected journal line: " + java.util.Arrays.toString(strings));
  964 + }
  965 +
  966 + public File getCleanFile(int i) {
  967 + return new File(directory, key + "." + i);
  968 + }
  969 +
  970 + public File getDirtyFile(int i) {
  971 + return new File(directory, key + "." + i + ".tmp");
  972 + }
  973 + }
  974 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.impl.ext;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  20 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  21 +import com.nostra13.universalimageloader.utils.IoUtils;
  22 +import com.nostra13.universalimageloader.utils.L;
  23 +
  24 +import java.io.BufferedOutputStream;
  25 +import java.io.File;
  26 +import java.io.IOException;
  27 +import java.io.InputStream;
  28 +import java.io.OutputStream;
  29 +
  30 +/**
  31 + * Disk cache based on "Least-Recently Used" principle. Adapter pattern, adapts
  32 + * {@link DiskLruCache DiskLruCache} to
  33 + * {@link DiskCache DiskCache}
  34 + *
  35 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  36 + * @see FileNameGenerator
  37 + * @since 1.9.2
  38 + */
  39 +public class LruDiskCache implements DiskCache {
  40 + /** {@value */
  41 + public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb
  42 + /** {@value */
  43 + public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
  44 + /** {@value */
  45 + public static final int DEFAULT_COMPRESS_QUALITY = 100;
  46 +
  47 + private static final String ERROR_ARG_NULL = " argument must be not null";
  48 + private static final String ERROR_ARG_NEGATIVE = " argument must be positive number";
  49 +
  50 + protected DiskLruCache cache;
  51 + private File reserveCacheDir;
  52 +
  53 + protected final FileNameGenerator fileNameGenerator;
  54 +
  55 + protected int bufferSize = DEFAULT_BUFFER_SIZE;
  56 +
  57 + protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
  58 + protected int compressQuality = DEFAULT_COMPRESS_QUALITY;
  59 +
  60 + /**
  61 + * @param cacheDir Directory for file caching
  62 + * @param fileNameGenerator {@linkplain FileNameGenerator
  63 + * Name generator} for cached files. Generated names must match the regex
  64 + * <strong>[a-z0-9_-]{1,64}</strong>
  65 + * @param cacheMaxSize Max cache size in bytes. <b>0</b> means cache size is unlimited.
  66 + * @throws IOException if cache can't be initialized (e.g. "No space left on device")
  67 + */
  68 + public LruDiskCache(File cacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize) throws IOException {
  69 + this(cacheDir, null, fileNameGenerator, cacheMaxSize, 0);
  70 + }
  71 +
  72 + /**
  73 + * @param cacheDir Directory for file caching
  74 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  75 + * @param fileNameGenerator {@linkplain FileNameGenerator
  76 + * Name generator} for cached files. Generated names must match the regex
  77 + * <strong>[a-z0-9_-]{1,64}</strong>
  78 + * @param cacheMaxSize Max cache size in bytes. <b>0</b> means cache size is unlimited.
  79 + * @param cacheMaxFileCount Max file count in cache. <b>0</b> means file count is unlimited.
  80 + * @throws IOException if cache can't be initialized (e.g. "No space left on device")
  81 + */
  82 + public LruDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize,
  83 + int cacheMaxFileCount) throws IOException {
  84 + if (cacheDir == null) {
  85 + throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL);
  86 + }
  87 + if (cacheMaxSize < 0) {
  88 + throw new IllegalArgumentException("cacheMaxSize" + ERROR_ARG_NEGATIVE);
  89 + }
  90 + if (cacheMaxFileCount < 0) {
  91 + throw new IllegalArgumentException("cacheMaxFileCount" + ERROR_ARG_NEGATIVE);
  92 + }
  93 + if (fileNameGenerator == null) {
  94 + throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL);
  95 + }
  96 +
  97 + if (cacheMaxSize == 0) {
  98 + cacheMaxSize = Long.MAX_VALUE;
  99 + }
  100 + if (cacheMaxFileCount == 0) {
  101 + cacheMaxFileCount = Integer.MAX_VALUE;
  102 + }
  103 +
  104 + this.reserveCacheDir = reserveCacheDir;
  105 + this.fileNameGenerator = fileNameGenerator;
  106 + initCache(cacheDir, reserveCacheDir, cacheMaxSize, cacheMaxFileCount);
  107 + }
  108 +
  109 + private void initCache(File cacheDir, File reserveCacheDir, long cacheMaxSize, int cacheMaxFileCount)
  110 + throws IOException {
  111 + try {
  112 + cache = DiskLruCache.open(cacheDir, 1, 1, cacheMaxSize, cacheMaxFileCount);
  113 + } catch (IOException e) {
  114 + L.e(e);
  115 + if (reserveCacheDir != null) {
  116 + initCache(reserveCacheDir, null, cacheMaxSize, cacheMaxFileCount);
  117 + }
  118 + if (cache == null) {
  119 + throw e; //new RuntimeException("Can't initialize disk cache", e);
  120 + }
  121 + }
  122 + }
  123 +
  124 + @Override
  125 + public File getDirectory() {
  126 + return cache.getDirectory();
  127 + }
  128 +
  129 + @Override
  130 + public File get(String imageUri) {
  131 + DiskLruCache.Snapshot snapshot = null;
  132 + try {
  133 + snapshot = cache.get(getKey(imageUri));
  134 + return snapshot == null ? null : snapshot.getFile(0);
  135 + } catch (IOException e) {
  136 + L.e(e);
  137 + return null;
  138 + } finally {
  139 + if (snapshot != null) {
  140 + snapshot.close();
  141 + }
  142 + }
  143 + }
  144 +
  145 + @Override
  146 + public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
  147 + DiskLruCache.Editor editor = cache.edit(getKey(imageUri));
  148 + if (editor == null) {
  149 + return false;
  150 + }
  151 +
  152 + OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize);
  153 + boolean copied = false;
  154 + try {
  155 + copied = IoUtils.copyStream(imageStream, os, listener, bufferSize);
  156 + } finally {
  157 + IoUtils.closeSilently(os);
  158 + if (copied) {
  159 + editor.commit();
  160 + } else {
  161 + editor.abort();
  162 + }
  163 + }
  164 + return copied;
  165 + }
  166 +
  167 + @Override
  168 + public boolean save(String imageUri, Bitmap bitmap) throws IOException {
  169 + DiskLruCache.Editor editor = cache.edit(getKey(imageUri));
  170 + if (editor == null) {
  171 + return false;
  172 + }
  173 +
  174 + OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize);
  175 + boolean savedSuccessfully = false;
  176 + try {
  177 + savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);
  178 + } finally {
  179 + IoUtils.closeSilently(os);
  180 + }
  181 + if (savedSuccessfully) {
  182 + editor.commit();
  183 + } else {
  184 + editor.abort();
  185 + }
  186 + return savedSuccessfully;
  187 + }
  188 +
  189 + @Override
  190 + public boolean remove(String imageUri) {
  191 + try {
  192 + return cache.remove(getKey(imageUri));
  193 + } catch (IOException e) {
  194 + L.e(e);
  195 + return false;
  196 + }
  197 + }
  198 +
  199 + @Override
  200 + public void close() {
  201 + try {
  202 + cache.close();
  203 + } catch (IOException e) {
  204 + L.e(e);
  205 + }
  206 + cache = null;
  207 + }
  208 +
  209 + @Override
  210 + public void clear() {
  211 + try {
  212 + cache.delete();
  213 + } catch (IOException e) {
  214 + L.e(e);
  215 + }
  216 + try {
  217 + initCache(cache.getDirectory(), reserveCacheDir, cache.getMaxSize(), cache.getMaxFileCount());
  218 + } catch (IOException e) {
  219 + L.e(e);
  220 + }
  221 + }
  222 +
  223 + private String getKey(String imageUri) {
  224 + return fileNameGenerator.generate(imageUri);
  225 + }
  226 +
  227 + public void setBufferSize(int bufferSize) {
  228 + this.bufferSize = bufferSize;
  229 + }
  230 +
  231 + public void setCompressFormat(Bitmap.CompressFormat compressFormat) {
  232 + this.compressFormat = compressFormat;
  233 + }
  234 +
  235 + public void setCompressQuality(int compressQuality) {
  236 + this.compressQuality = compressQuality;
  237 + }
  238 +}
... ...
  1 +/*
  2 + * Copyright (C) 2012 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.nostra13.universalimageloader.cache.disc.impl.ext;
  17 +
  18 +import java.io.ByteArrayOutputStream;
  19 +import java.io.Closeable;
  20 +import java.io.EOFException;
  21 +import java.io.IOException;
  22 +import java.io.InputStream;
  23 +import java.io.UnsupportedEncodingException;
  24 +import java.nio.charset.Charset;
  25 +
  26 +/**
  27 + * Buffers input from an {@link InputStream} for reading lines.
  28 + *
  29 + * <p>This class is used for buffered reading of lines. For purposes of this class, a line ends
  30 + * with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
  31 + * line at end of input is invalid and will be ignored, the caller may use {@code
  32 + * hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
  33 + *
  34 + * <p>This class is intended for reading input that strictly consists of lines, such as line-based
  35 + * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
  36 + * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
  37 + * end-of-input reporting and a more restrictive definition of a line.
  38 + *
  39 + * <p>This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
  40 + * and 10, respectively, and the representation of no other character contains these values.
  41 + * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
  42 + * The default charset is US_ASCII.
  43 + */
  44 +class StrictLineReader implements Closeable {
  45 + private static final byte CR = (byte) '\r';
  46 + private static final byte LF = (byte) '\n';
  47 +
  48 + private final InputStream in;
  49 + private final Charset charset;
  50 +
  51 + /*
  52 + * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
  53 + * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
  54 + * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
  55 + * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
  56 + */
  57 + private byte[] buf;
  58 + private int pos;
  59 + private int end;
  60 +
  61 + /**
  62 + * Constructs a new {@code LineReader} with the specified charset and the default capacity.
  63 + *
  64 + * @param in the {@code InputStream} to read data from.
  65 + * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
  66 + * supported.
  67 + * @throws NullPointerException if {@code in} or {@code charset} is null.
  68 + * @throws IllegalArgumentException if the specified charset is not supported.
  69 + */
  70 + public StrictLineReader(InputStream in, Charset charset) {
  71 + this(in, 8192, charset);
  72 + }
  73 +
  74 + /**
  75 + * Constructs a new {@code LineReader} with the specified capacity and charset.
  76 + *
  77 + * @param in the {@code InputStream} to read data from.
  78 + * @param capacity the capacity of the buffer.
  79 + * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
  80 + * supported.
  81 + * @throws NullPointerException if {@code in} or {@code charset} is null.
  82 + * @throws IllegalArgumentException if {@code capacity} is negative or zero
  83 + * or the specified charset is not supported.
  84 + */
  85 + public StrictLineReader(InputStream in, int capacity, Charset charset) {
  86 + if (in == null || charset == null) {
  87 + throw new NullPointerException();
  88 + }
  89 + if (capacity < 0) {
  90 + throw new IllegalArgumentException("capacity <= 0");
  91 + }
  92 + if (!(charset.equals(Util.US_ASCII))) {
  93 + throw new IllegalArgumentException("Unsupported encoding");
  94 + }
  95 +
  96 + this.in = in;
  97 + this.charset = charset;
  98 + buf = new byte[capacity];
  99 + }
  100 +
  101 + /**
  102 + * Closes the reader by closing the underlying {@code InputStream} and
  103 + * marking this reader as closed.
  104 + *
  105 + * @throws IOException for errors when closing the underlying {@code InputStream}.
  106 + */
  107 + public void close() throws IOException {
  108 + synchronized (in) {
  109 + if (buf != null) {
  110 + buf = null;
  111 + in.close();
  112 + }
  113 + }
  114 + }
  115 +
  116 + /**
  117 + * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
  118 + * this end of line marker is not included in the result.
  119 + *
  120 + * @return the next line from the input.
  121 + * @throws IOException for underlying {@code InputStream} errors.
  122 + * @throws EOFException for the end of source stream.
  123 + */
  124 + public String readLine() throws IOException {
  125 + synchronized (in) {
  126 + if (buf == null) {
  127 + throw new IOException("LineReader is closed");
  128 + }
  129 +
  130 + // Read more data if we are at the end of the buffered data.
  131 + // Though it's an error to read after an exception, we will let {@code fillBuf()}
  132 + // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
  133 + if (pos >= end) {
  134 + fillBuf();
  135 + }
  136 + // Try to find LF in the buffered data and return the line if successful.
  137 + for (int i = pos; i != end; ++i) {
  138 + if (buf[i] == LF) {
  139 + int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
  140 + String res = new String(buf, pos, lineEnd - pos, charset.name());
  141 + pos = i + 1;
  142 + return res;
  143 + }
  144 + }
  145 +
  146 + // Let's anticipate up to 80 characters on top of those already read.
  147 + ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
  148 + @Override
  149 + public String toString() {
  150 + int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
  151 + try {
  152 + return new String(buf, 0, length, charset.name());
  153 + } catch (UnsupportedEncodingException e) {
  154 + throw new AssertionError(e); // Since we control the charset this will never happen.
  155 + }
  156 + }
  157 + };
  158 +
  159 + while (true) {
  160 + out.write(buf, pos, end - pos);
  161 + // Mark unterminated line in case fillBuf throws EOFException or IOException.
  162 + end = -1;
  163 + fillBuf();
  164 + // Try to find LF in the buffered data and return the line if successful.
  165 + for (int i = pos; i != end; ++i) {
  166 + if (buf[i] == LF) {
  167 + if (i != pos) {
  168 + out.write(buf, pos, i - pos);
  169 + }
  170 + pos = i + 1;
  171 + return out.toString();
  172 + }
  173 + }
  174 + }
  175 + }
  176 + }
  177 +
  178 + /**
  179 + * Reads new input data into the buffer. Call only with pos == end or end == -1,
  180 + * depending on the desired outcome if the function throws.
  181 + */
  182 + private void fillBuf() throws IOException {
  183 + int result = in.read(buf, 0, buf.length);
  184 + if (result == -1) {
  185 + throw new EOFException();
  186 + }
  187 + pos = 0;
  188 + end = result;
  189 + }
  190 +}
  191 +
... ...
  1 +/*
  2 + * Copyright (C) 2010 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.nostra13.universalimageloader.cache.disc.impl.ext;
  17 +
  18 +import java.io.Closeable;
  19 +import java.io.File;
  20 +import java.io.IOException;
  21 +import java.io.Reader;
  22 +import java.io.StringWriter;
  23 +import java.nio.charset.Charset;
  24 +
  25 +/** Junk drawer of utility methods. */
  26 +final class Util {
  27 + static final Charset US_ASCII = Charset.forName("US-ASCII");
  28 + static final Charset UTF_8 = Charset.forName("UTF-8");
  29 +
  30 + private Util() {
  31 + }
  32 +
  33 + static String readFully(Reader reader) throws IOException {
  34 + try {
  35 + StringWriter writer = new StringWriter();
  36 + char[] buffer = new char[1024];
  37 + int count;
  38 + while ((count = reader.read(buffer)) != -1) {
  39 + writer.write(buffer, 0, count);
  40 + }
  41 + return writer.toString();
  42 + } finally {
  43 + reader.close();
  44 + }
  45 + }
  46 +
  47 + /**
  48 + * Deletes the contents of {@code dir}. Throws an IOException if any file
  49 + * could not be deleted, or if {@code dir} is not a readable directory.
  50 + */
  51 + static void deleteContents(File dir) throws IOException {
  52 + File[] files = dir.listFiles();
  53 + if (files == null) {
  54 + throw new IOException("not a readable directory: " + dir);
  55 + }
  56 + for (File file : files) {
  57 + if (file.isDirectory()) {
  58 + deleteContents(file);
  59 + }
  60 + if (!file.delete()) {
  61 + throw new IOException("failed to delete file: " + file);
  62 + }
  63 + }
  64 + }
  65 +
  66 + static void closeQuietly(/*Auto*/Closeable closeable) {
  67 + if (closeable != null) {
  68 + try {
  69 + closeable.close();
  70 + } catch (RuntimeException rethrown) {
  71 + throw rethrown;
  72 + } catch (Exception ignored) {
  73 + }
  74 + }
  75 + }
  76 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.naming;
  17 +
  18 +/**
  19 + * Generates names for files at disk cache
  20 + *
  21 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  22 + * @since 1.3.1
  23 + */
  24 +public interface FileNameGenerator {
  25 +
  26 + /** Generates unique file name for image defined by URI */
  27 + String generate(String imageUri);
  28 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.naming;
  17 +
  18 +/**
  19 + * Names image file as image URI {@linkplain String#hashCode() hashcode}
  20 + *
  21 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  22 + * @since 1.3.1
  23 + */
  24 +public class HashCodeFileNameGenerator implements FileNameGenerator {
  25 + @Override
  26 + public String generate(String imageUri) {
  27 + return String.valueOf(imageUri.hashCode());
  28 + }
  29 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.naming;
  17 +
  18 +import com.nostra13.universalimageloader.utils.L;
  19 +
  20 +import java.math.BigInteger;
  21 +import java.security.MessageDigest;
  22 +import java.security.NoSuchAlgorithmException;
  23 +
  24 +/**
  25 + * Names image file as MD5 hash of image URI
  26 + *
  27 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  28 + * @since 1.4.0
  29 + */
  30 +public class Md5FileNameGenerator implements FileNameGenerator {
  31 +
  32 + private static final String HASH_ALGORITHM = "MD5";
  33 + private static final int RADIX = 10 + 26; // 10 digits + 26 letters
  34 +
  35 + @Override
  36 + public String generate(String imageUri) {
  37 + byte[] md5 = getMD5(imageUri.getBytes());
  38 + BigInteger bi = new BigInteger(md5).abs();
  39 + return bi.toString(RADIX);
  40 + }
  41 +
  42 + private byte[] getMD5(byte[] data) {
  43 + byte[] hash = null;
  44 + try {
  45 + MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
  46 + digest.update(data);
  47 + hash = digest.digest();
  48 + } catch (NoSuchAlgorithmException e) {
  49 + L.e(e);
  50 + }
  51 + return hash;
  52 + }
  53 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import java.lang.ref.Reference;
  21 +import java.util.*;
  22 +
  23 +/**
  24 + * Base memory cache. Implements common functionality for memory cache. Provides object references (
  25 + * {@linkplain Reference not strong}) storing.
  26 + *
  27 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  28 + * @since 1.0.0
  29 + */
  30 +public abstract class BaseMemoryCache implements MemoryCache {
  31 +
  32 + /** Stores not strong references to objects */
  33 + private final Map<String, Reference<Bitmap>> softMap = Collections.synchronizedMap(new HashMap<String, Reference<Bitmap>>());
  34 +
  35 + @Override
  36 + public Bitmap get(String key) {
  37 + Bitmap result = null;
  38 + Reference<Bitmap> reference = softMap.get(key);
  39 + if (reference != null) {
  40 + result = reference.get();
  41 + }
  42 + return result;
  43 + }
  44 +
  45 + @Override
  46 + public boolean put(String key, Bitmap value) {
  47 + softMap.put(key, createReference(value));
  48 + return true;
  49 + }
  50 +
  51 + @Override
  52 + public Bitmap remove(String key) {
  53 + Reference<Bitmap> bmpRef = softMap.remove(key);
  54 + return bmpRef == null ? null : bmpRef.get();
  55 + }
  56 +
  57 + @Override
  58 + public Collection<String> keys() {
  59 + synchronized (softMap) {
  60 + return new HashSet<String>(softMap.keySet());
  61 + }
  62 + }
  63 +
  64 + @Override
  65 + public void clear() {
  66 + softMap.clear();
  67 + }
  68 +
  69 + /** Creates {@linkplain Reference not strong} reference of value */
  70 + protected abstract Reference<Bitmap> createReference(Bitmap value);
  71 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import com.nostra13.universalimageloader.utils.L;
  21 +
  22 +import java.util.Collections;
  23 +import java.util.LinkedList;
  24 +import java.util.List;
  25 +import java.util.concurrent.atomic.AtomicInteger;
  26 +
  27 +/**
  28 + * Limited cache. Provides object storing. Size of all stored bitmaps will not to exceed size limit (
  29 + * {@link #getSizeLimit()}).<br />
  30 + * <br />
  31 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  32 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  33 + *
  34 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  35 + * @see BaseMemoryCache
  36 + * @since 1.0.0
  37 + */
  38 +public abstract class LimitedMemoryCache extends BaseMemoryCache {
  39 +
  40 + private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16;
  41 + private static final int MAX_NORMAL_CACHE_SIZE = MAX_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024;
  42 +
  43 + private final int sizeLimit;
  44 +
  45 + private final AtomicInteger cacheSize;
  46 +
  47 + /**
  48 + * Contains strong references to stored objects. Each next object is added last. If hard cache size will exceed
  49 + * limit then first object is deleted (but it continue exist at {@link #softMap} and can be collected by GC at any
  50 + * time)
  51 + */
  52 + private final List<Bitmap> hardCache = Collections.synchronizedList(new LinkedList<Bitmap>());
  53 +
  54 + /** @param sizeLimit Maximum size for cache (in bytes) */
  55 + public LimitedMemoryCache(int sizeLimit) {
  56 + this.sizeLimit = sizeLimit;
  57 + cacheSize = new AtomicInteger();
  58 + if (sizeLimit > MAX_NORMAL_CACHE_SIZE) {
  59 + L.w("You set too large memory cache size (more than %1$d Mb)", MAX_NORMAL_CACHE_SIZE_IN_MB);
  60 + }
  61 + }
  62 +
  63 + @Override
  64 + public boolean put(String key, Bitmap value) {
  65 + boolean putSuccessfully = false;
  66 + // Try to add value to hard cache
  67 + int valueSize = getSize(value);
  68 + int sizeLimit = getSizeLimit();
  69 + int curCacheSize = cacheSize.get();
  70 + if (valueSize < sizeLimit) {
  71 + while (curCacheSize + valueSize > sizeLimit) {
  72 + Bitmap removedValue = removeNext();
  73 + if (hardCache.remove(removedValue)) {
  74 + curCacheSize = cacheSize.addAndGet(-getSize(removedValue));
  75 + }
  76 + }
  77 + hardCache.add(value);
  78 + cacheSize.addAndGet(valueSize);
  79 +
  80 + putSuccessfully = true;
  81 + }
  82 + // Add value to soft cache
  83 + super.put(key, value);
  84 + return putSuccessfully;
  85 + }
  86 +
  87 + @Override
  88 + public Bitmap remove(String key) {
  89 + Bitmap value = super.get(key);
  90 + if (value != null) {
  91 + if (hardCache.remove(value)) {
  92 + cacheSize.addAndGet(-getSize(value));
  93 + }
  94 + }
  95 + return super.remove(key);
  96 + }
  97 +
  98 + @Override
  99 + public void clear() {
  100 + hardCache.clear();
  101 + cacheSize.set(0);
  102 + super.clear();
  103 + }
  104 +
  105 + protected int getSizeLimit() {
  106 + return sizeLimit;
  107 + }
  108 +
  109 + protected abstract int getSize(Bitmap value);
  110 +
  111 + protected abstract Bitmap removeNext();
  112 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import java.util.Collection;
  21 +
  22 +/**
  23 + * Interface for memory cache
  24 + *
  25 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  26 + * @since 1.9.2
  27 + */
  28 +public interface MemoryCache {
  29 + /**
  30 + * Puts value into cache by key
  31 + *
  32 + * @return <b>true</b> - if value was put into cache successfully, <b>false</b> - if value was <b>not</b> put into
  33 + * cache
  34 + */
  35 + boolean put(String key, Bitmap value);
  36 +
  37 + /** Returns value by key. If there is no value for key then null will be returned. */
  38 + Bitmap get(String key);
  39 +
  40 + /** Removes item by key */
  41 + Bitmap remove(String key);
  42 +
  43 + /** Returns all keys of cache */
  44 + Collection<String> keys();
  45 +
  46 + /** Remove all items from cache */
  47 + void clear();
  48 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +import java.util.Collections;
  24 +import java.util.LinkedList;
  25 +import java.util.List;
  26 +
  27 +/**
  28 + * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
  29 + * exceed size limit. When cache reaches limit size then cache clearing is processed by FIFO principle.<br />
  30 + * <br />
  31 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  32 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  33 + *
  34 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  35 + * @since 1.0.0
  36 + */
  37 +public class FIFOLimitedMemoryCache extends LimitedMemoryCache {
  38 +
  39 + private final List<Bitmap> queue = Collections.synchronizedList(new LinkedList<Bitmap>());
  40 +
  41 + public FIFOLimitedMemoryCache(int sizeLimit) {
  42 + super(sizeLimit);
  43 + }
  44 +
  45 + @Override
  46 + public boolean put(String key, Bitmap value) {
  47 + if (super.put(key, value)) {
  48 + queue.add(value);
  49 + return true;
  50 + } else {
  51 + return false;
  52 + }
  53 + }
  54 +
  55 + @Override
  56 + public Bitmap remove(String key) {
  57 + Bitmap value = super.get(key);
  58 + if (value != null) {
  59 + queue.remove(value);
  60 + }
  61 + return super.remove(key);
  62 + }
  63 +
  64 + @Override
  65 + public void clear() {
  66 + queue.clear();
  67 + super.clear();
  68 + }
  69 +
  70 + @Override
  71 + protected int getSize(Bitmap value) {
  72 + return value.getRowBytes() * value.getHeight();
  73 + }
  74 +
  75 + @Override
  76 + protected Bitmap removeNext() {
  77 + return queue.remove(0);
  78 + }
  79 +
  80 + @Override
  81 + protected Reference<Bitmap> createReference(Bitmap value) {
  82 + return new WeakReference<Bitmap>(value);
  83 + }
  84 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  21 +
  22 +import java.util.Collection;
  23 +import java.util.Comparator;
  24 +
  25 +/**
  26 + * Decorator for {@link MemoryCache}. Provides special feature for cache: some different keys are considered as
  27 + * equals (using {@link Comparator comparator}). And when you try to put some value into cache by key so entries with
  28 + * "equals" keys will be removed from cache before.<br />
  29 + * <b>NOTE:</b> Used for internal needs. Normally you don't need to use this class.
  30 + *
  31 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  32 + * @since 1.0.0
  33 + */
  34 +public class FuzzyKeyMemoryCache implements MemoryCache {
  35 +
  36 + private final MemoryCache cache;
  37 + private final Comparator<String> keyComparator;
  38 +
  39 + public FuzzyKeyMemoryCache(MemoryCache cache, Comparator<String> keyComparator) {
  40 + this.cache = cache;
  41 + this.keyComparator = keyComparator;
  42 + }
  43 +
  44 + @Override
  45 + public boolean put(String key, Bitmap value) {
  46 + // Search equal key and remove this entry
  47 + synchronized (cache) {
  48 + String keyToRemove = null;
  49 + for (String cacheKey : cache.keys()) {
  50 + if (keyComparator.compare(key, cacheKey) == 0) {
  51 + keyToRemove = cacheKey;
  52 + break;
  53 + }
  54 + }
  55 + if (keyToRemove != null) {
  56 + cache.remove(keyToRemove);
  57 + }
  58 + }
  59 + return cache.put(key, value);
  60 + }
  61 +
  62 + @Override
  63 + public Bitmap get(String key) {
  64 + return cache.get(key);
  65 + }
  66 +
  67 + @Override
  68 + public Bitmap remove(String key) {
  69 + return cache.remove(key);
  70 + }
  71 +
  72 + @Override
  73 + public void clear() {
  74 + cache.clear();
  75 + }
  76 +
  77 + @Override
  78 + public Collection<String> keys() {
  79 + return cache.keys();
  80 + }
  81 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +import java.util.Collections;
  24 +import java.util.Iterator;
  25 +import java.util.LinkedHashMap;
  26 +import java.util.Map;
  27 +import java.util.Map.Entry;
  28 +
  29 +/**
  30 + * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
  31 + * exceed size limit. When cache reaches limit size then the least recently used bitmap is deleted from cache.<br />
  32 + * <br />
  33 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  34 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  35 + *
  36 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  37 + * @since 1.3.0
  38 + */
  39 +public class LRULimitedMemoryCache extends LimitedMemoryCache {
  40 +
  41 + private static final int INITIAL_CAPACITY = 10;
  42 + private static final float LOAD_FACTOR = 1.1f;
  43 +
  44 + /** Cache providing Least-Recently-Used logic */
  45 + private final Map<String, Bitmap> lruCache = Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(INITIAL_CAPACITY, LOAD_FACTOR, true));
  46 +
  47 + /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
  48 + public LRULimitedMemoryCache(int maxSize) {
  49 + super(maxSize);
  50 + }
  51 +
  52 + @Override
  53 + public boolean put(String key, Bitmap value) {
  54 + if (super.put(key, value)) {
  55 + lruCache.put(key, value);
  56 + return true;
  57 + } else {
  58 + return false;
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + public Bitmap get(String key) {
  64 + lruCache.get(key); // call "get" for LRU logic
  65 + return super.get(key);
  66 + }
  67 +
  68 + @Override
  69 + public Bitmap remove(String key) {
  70 + lruCache.remove(key);
  71 + return super.remove(key);
  72 + }
  73 +
  74 + @Override
  75 + public void clear() {
  76 + lruCache.clear();
  77 + super.clear();
  78 + }
  79 +
  80 + @Override
  81 + protected int getSize(Bitmap value) {
  82 + return value.getRowBytes() * value.getHeight();
  83 + }
  84 +
  85 + @Override
  86 + protected Bitmap removeNext() {
  87 + Bitmap mostLongUsedValue = null;
  88 + synchronized (lruCache) {
  89 + Iterator<Entry<String, Bitmap>> it = lruCache.entrySet().iterator();
  90 + if (it.hasNext()) {
  91 + Entry<String, Bitmap> entry = it.next();
  92 + mostLongUsedValue = entry.getValue();
  93 + it.remove();
  94 + }
  95 + }
  96 + return mostLongUsedValue;
  97 + }
  98 +
  99 + @Override
  100 + protected Reference<Bitmap> createReference(Bitmap value) {
  101 + return new WeakReference<Bitmap>(value);
  102 + }
  103 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +import java.util.Collections;
  24 +import java.util.HashMap;
  25 +import java.util.Map;
  26 +import java.util.Map.Entry;
  27 +import java.util.Set;
  28 +
  29 +/**
  30 + * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
  31 + * exceed size limit. When cache reaches limit size then the bitmap which has the largest size is deleted from
  32 + * cache.<br />
  33 + * <br />
  34 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  35 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  36 + *
  37 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  38 + * @since 1.0.0
  39 + */
  40 +public class LargestLimitedMemoryCache extends LimitedMemoryCache {
  41 + /**
  42 + * Contains strong references to stored objects (keys) and sizes of the objects. If hard cache
  43 + * size will exceed limit then object with the largest size is deleted (but it continue exist at
  44 + * {@link #softMap} and can be collected by GC at any time)
  45 + */
  46 + private final Map<Bitmap, Integer> valueSizes = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());
  47 +
  48 + public LargestLimitedMemoryCache(int sizeLimit) {
  49 + super(sizeLimit);
  50 + }
  51 +
  52 + @Override
  53 + public boolean put(String key, Bitmap value) {
  54 + if (super.put(key, value)) {
  55 + valueSizes.put(value, getSize(value));
  56 + return true;
  57 + } else {
  58 + return false;
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + public Bitmap remove(String key) {
  64 + Bitmap value = super.get(key);
  65 + if (value != null) {
  66 + valueSizes.remove(value);
  67 + }
  68 + return super.remove(key);
  69 + }
  70 +
  71 + @Override
  72 + public void clear() {
  73 + valueSizes.clear();
  74 + super.clear();
  75 + }
  76 +
  77 + @Override
  78 + protected int getSize(Bitmap value) {
  79 + return value.getRowBytes() * value.getHeight();
  80 + }
  81 +
  82 + @Override
  83 + protected Bitmap removeNext() {
  84 + Integer maxSize = null;
  85 + Bitmap largestValue = null;
  86 + Set<Entry<Bitmap, Integer>> entries = valueSizes.entrySet();
  87 + synchronized (valueSizes) {
  88 + for (Entry<Bitmap, Integer> entry : entries) {
  89 + if (largestValue == null) {
  90 + largestValue = entry.getKey();
  91 + maxSize = entry.getValue();
  92 + } else {
  93 + Integer size = entry.getValue();
  94 + if (size > maxSize) {
  95 + maxSize = size;
  96 + largestValue = entry.getKey();
  97 + }
  98 + }
  99 + }
  100 + }
  101 + valueSizes.remove(largestValue);
  102 + return largestValue;
  103 + }
  104 +
  105 + @Override
  106 + protected Reference<Bitmap> createReference(Bitmap value) {
  107 + return new WeakReference<Bitmap>(value);
  108 + }
  109 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  21 +
  22 +import java.util.Collection;
  23 +import java.util.Collections;
  24 +import java.util.HashMap;
  25 +import java.util.Map;
  26 +
  27 +/**
  28 + * Decorator for {@link MemoryCache}. Provides special feature for cache: if some cached object age exceeds defined
  29 + * value then this object will be removed from cache.
  30 + *
  31 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  32 + * @see MemoryCache
  33 + * @since 1.3.1
  34 + */
  35 +public class LimitedAgeMemoryCache implements MemoryCache {
  36 +
  37 + private final MemoryCache cache;
  38 +
  39 + private final long maxAge;
  40 + private final Map<String, Long> loadingDates = Collections.synchronizedMap(new HashMap<String, Long>());
  41 +
  42 + /**
  43 + * @param cache Wrapped memory cache
  44 + * @param maxAge Max object age <b>(in seconds)</b>. If object age will exceed this value then it'll be removed from
  45 + * cache on next treatment (and therefore be reloaded).
  46 + */
  47 + public LimitedAgeMemoryCache(MemoryCache cache, long maxAge) {
  48 + this.cache = cache;
  49 + this.maxAge = maxAge * 1000; // to milliseconds
  50 + }
  51 +
  52 + @Override
  53 + public boolean put(String key, Bitmap value) {
  54 + boolean putSuccesfully = cache.put(key, value);
  55 + if (putSuccesfully) {
  56 + loadingDates.put(key, System.currentTimeMillis());
  57 + }
  58 + return putSuccesfully;
  59 + }
  60 +
  61 + @Override
  62 + public Bitmap get(String key) {
  63 + Long loadingDate = loadingDates.get(key);
  64 + if (loadingDate != null && System.currentTimeMillis() - loadingDate > maxAge) {
  65 + cache.remove(key);
  66 + loadingDates.remove(key);
  67 + }
  68 +
  69 + return cache.get(key);
  70 + }
  71 +
  72 + @Override
  73 + public Bitmap remove(String key) {
  74 + loadingDates.remove(key);
  75 + return cache.remove(key);
  76 + }
  77 +
  78 + @Override
  79 + public Collection<String> keys() {
  80 + return cache.keys();
  81 + }
  82 +
  83 + @Override
  84 + public void clear() {
  85 + cache.clear();
  86 + loadingDates.clear();
  87 + }
  88 +}
... ...
  1 +package com.nostra13.universalimageloader.cache.memory.impl;
  2 +
  3 +import android.graphics.Bitmap;
  4 +
  5 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  6 +
  7 +import java.util.Collection;
  8 +import java.util.HashSet;
  9 +import java.util.LinkedHashMap;
  10 +import java.util.Map;
  11 +
  12 +/**
  13 + * A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to
  14 + * 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
  15 + * become eligible for garbage collection.<br />
  16 + * <br />
  17 + * <b>NOTE:</b> This cache uses only strong references for stored Bitmaps.
  18 + *
  19 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  20 + * @since 1.8.1
  21 + */
  22 +public class LruMemoryCache implements MemoryCache {
  23 +
  24 + private final LinkedHashMap<String, Bitmap> map;
  25 +
  26 + private final int maxSize;
  27 + /** Size of this cache in bytes */
  28 + private int size;
  29 +
  30 + /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
  31 + public LruMemoryCache(int maxSize) {
  32 + if (maxSize <= 0) {
  33 + throw new IllegalArgumentException("maxSize <= 0");
  34 + }
  35 + this.maxSize = maxSize;
  36 + this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
  37 + }
  38 +
  39 + /**
  40 + * Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head
  41 + * of the queue. This returns null if a Bitmap is not cached.
  42 + */
  43 + @Override
  44 + public final Bitmap get(String key) {
  45 + if (key == null) {
  46 + throw new NullPointerException("key == null");
  47 + }
  48 +
  49 + synchronized (this) {
  50 + return map.get(key);
  51 + }
  52 + }
  53 +
  54 + /** Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue. */
  55 + @Override
  56 + public final boolean put(String key, Bitmap value) {
  57 + if (key == null || value == null) {
  58 + throw new NullPointerException("key == null || value == null");
  59 + }
  60 +
  61 + synchronized (this) {
  62 + size += sizeOf(key, value);
  63 + Bitmap previous = map.put(key, value);
  64 + if (previous != null) {
  65 + size -= sizeOf(key, previous);
  66 + }
  67 + }
  68 +
  69 + trimToSize(maxSize);
  70 + return true;
  71 + }
  72 +
  73 + /**
  74 + * Remove the eldest entries until the total of remaining entries is at or below the requested size.
  75 + *
  76 + * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements.
  77 + */
  78 + private void trimToSize(int maxSize) {
  79 + while (true) {
  80 + String key;
  81 + Bitmap value;
  82 + synchronized (this) {
  83 + if (size < 0 || (map.isEmpty() && size != 0)) {
  84 + throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
  85 + }
  86 +
  87 + if (size <= maxSize || map.isEmpty()) {
  88 + break;
  89 + }
  90 +
  91 + Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
  92 + if (toEvict == null) {
  93 + break;
  94 + }
  95 + key = toEvict.getKey();
  96 + value = toEvict.getValue();
  97 + map.remove(key);
  98 + size -= sizeOf(key, value);
  99 + }
  100 + }
  101 + }
  102 +
  103 + /** Removes the entry for {@code key} if it exists. */
  104 + @Override
  105 + public final Bitmap remove(String key) {
  106 + if (key == null) {
  107 + throw new NullPointerException("key == null");
  108 + }
  109 +
  110 + synchronized (this) {
  111 + Bitmap previous = map.remove(key);
  112 + if (previous != null) {
  113 + size -= sizeOf(key, previous);
  114 + }
  115 + return previous;
  116 + }
  117 + }
  118 +
  119 + @Override
  120 + public Collection<String> keys() {
  121 + synchronized (this) {
  122 + return new HashSet<String>(map.keySet());
  123 + }
  124 + }
  125 +
  126 + @Override
  127 + public void clear() {
  128 + trimToSize(-1); // -1 will evict 0-sized elements
  129 + }
  130 +
  131 + /**
  132 + * Returns the size {@code Bitmap} in bytes.
  133 + * <p/>
  134 + * An entry's size must not change while it is in the cache.
  135 + */
  136 + private int sizeOf(String key, Bitmap value) {
  137 + return value.getRowBytes() * value.getHeight();
  138 + }
  139 +
  140 + @Override
  141 + public synchronized final String toString() {
  142 + return String.format("LruCache[maxSize=%d]", maxSize);
  143 + }
  144 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +import java.util.Collections;
  24 +import java.util.HashMap;
  25 +import java.util.Map;
  26 +import java.util.Map.Entry;
  27 +import java.util.Set;
  28 +
  29 +/**
  30 + * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
  31 + * exceed size limit. When cache reaches limit size then the bitmap which used the least frequently is deleted from
  32 + * cache.<br />
  33 + * <br />
  34 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  35 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  36 + *
  37 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  38 + * @since 1.0.0
  39 + */
  40 +public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache {
  41 + /**
  42 + * Contains strong references to stored objects (keys) and last object usage date (in milliseconds). If hard cache
  43 + * size will exceed limit then object with the least frequently usage is deleted (but it continue exist at
  44 + * {@link #softMap} and can be collected by GC at any time)
  45 + */
  46 + private final Map<Bitmap, Integer> usingCounts = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());
  47 +
  48 + public UsingFreqLimitedMemoryCache(int sizeLimit) {
  49 + super(sizeLimit);
  50 + }
  51 +
  52 + @Override
  53 + public boolean put(String key, Bitmap value) {
  54 + if (super.put(key, value)) {
  55 + usingCounts.put(value, 0);
  56 + return true;
  57 + } else {
  58 + return false;
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + public Bitmap get(String key) {
  64 + Bitmap value = super.get(key);
  65 + // Increment usage count for value if value is contained in hardCahe
  66 + if (value != null) {
  67 + Integer usageCount = usingCounts.get(value);
  68 + if (usageCount != null) {
  69 + usingCounts.put(value, usageCount + 1);
  70 + }
  71 + }
  72 + return value;
  73 + }
  74 +
  75 + @Override
  76 + public Bitmap remove(String key) {
  77 + Bitmap value = super.get(key);
  78 + if (value != null) {
  79 + usingCounts.remove(value);
  80 + }
  81 + return super.remove(key);
  82 + }
  83 +
  84 + @Override
  85 + public void clear() {
  86 + usingCounts.clear();
  87 + super.clear();
  88 + }
  89 +
  90 + @Override
  91 + protected int getSize(Bitmap value) {
  92 + return value.getRowBytes() * value.getHeight();
  93 + }
  94 +
  95 + @Override
  96 + protected Bitmap removeNext() {
  97 + Integer minUsageCount = null;
  98 + Bitmap leastUsedValue = null;
  99 + Set<Entry<Bitmap, Integer>> entries = usingCounts.entrySet();
  100 + synchronized (usingCounts) {
  101 + for (Entry<Bitmap, Integer> entry : entries) {
  102 + if (leastUsedValue == null) {
  103 + leastUsedValue = entry.getKey();
  104 + minUsageCount = entry.getValue();
  105 + } else {
  106 + Integer lastValueUsage = entry.getValue();
  107 + if (lastValueUsage < minUsageCount) {
  108 + minUsageCount = lastValueUsage;
  109 + leastUsedValue = entry.getKey();
  110 + }
  111 + }
  112 + }
  113 + }
  114 + usingCounts.remove(leastUsedValue);
  115 + return leastUsedValue;
  116 + }
  117 +
  118 + @Override
  119 + protected Reference<Bitmap> createReference(Bitmap value) {
  120 + return new WeakReference<Bitmap>(value);
  121 + }
  122 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.BaseMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +
  24 +/**
  25 + * Memory cache with {@linkplain WeakReference weak references} to {@linkplain Bitmap bitmaps}<br />
  26 + * <br />
  27 + * <b>NOTE:</b> This cache uses only weak references for stored Bitmaps.
  28 + *
  29 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  30 + * @since 1.5.3
  31 + */
  32 +public class WeakMemoryCache extends BaseMemoryCache {
  33 + @Override
  34 + protected Reference<Bitmap> createReference(Bitmap value) {
  35 + return new WeakReference<Bitmap>(value);
  36 + }
  37 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.annotation.TargetApi;
  19 +import android.app.ActivityManager;
  20 +import android.content.Context;
  21 +import android.content.pm.ApplicationInfo;
  22 +import android.os.Build;
  23 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  24 +import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
  25 +import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache;
  26 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  27 +import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator;
  28 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  29 +import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache;
  30 +import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
  31 +import com.nostra13.universalimageloader.core.assist.deque.LIFOLinkedBlockingDeque;
  32 +import com.nostra13.universalimageloader.core.decode.BaseImageDecoder;
  33 +import com.nostra13.universalimageloader.core.decode.ImageDecoder;
  34 +import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
  35 +import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
  36 +import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
  37 +import com.nostra13.universalimageloader.core.download.ImageDownloader;
  38 +import com.nostra13.universalimageloader.utils.L;
  39 +import com.nostra13.universalimageloader.utils.StorageUtils;
  40 +
  41 +import java.io.File;
  42 +import java.io.IOException;
  43 +import java.util.concurrent.BlockingQueue;
  44 +import java.util.concurrent.Executor;
  45 +import java.util.concurrent.Executors;
  46 +import java.util.concurrent.LinkedBlockingQueue;
  47 +import java.util.concurrent.ThreadFactory;
  48 +import java.util.concurrent.ThreadPoolExecutor;
  49 +import java.util.concurrent.TimeUnit;
  50 +import java.util.concurrent.atomic.AtomicInteger;
  51 +
  52 +/**
  53 + * Factory for providing of default options for {@linkplain ImageLoaderConfiguration configuration}
  54 + *
  55 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  56 + * @since 1.5.6
  57 + */
  58 +public class DefaultConfigurationFactory {
  59 +
  60 + /** Creates default implementation of task executor */
  61 + public static Executor createExecutor(int threadPoolSize, int threadPriority,
  62 + QueueProcessingType tasksProcessingType) {
  63 + boolean lifo = tasksProcessingType == QueueProcessingType.LIFO;
  64 + BlockingQueue<Runnable> taskQueue =
  65 + lifo ? new LIFOLinkedBlockingDeque<Runnable>() : new LinkedBlockingQueue<Runnable>();
  66 + return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue,
  67 + createThreadFactory(threadPriority, "uil-pool-"));
  68 + }
  69 +
  70 + /** Creates default implementation of task distributor */
  71 + public static Executor createTaskDistributor() {
  72 + return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-"));
  73 + }
  74 +
  75 + /** Creates {@linkplain HashCodeFileNameGenerator default implementation} of FileNameGenerator */
  76 + public static FileNameGenerator createFileNameGenerator() {
  77 + return new HashCodeFileNameGenerator();
  78 + }
  79 +
  80 + /**
  81 + * Creates default implementation of {@link DiskCache} depends on incoming parameters
  82 + */
  83 + public static DiskCache createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator,
  84 + long diskCacheSize, int diskCacheFileCount) {
  85 + File reserveCacheDir = createReserveDiskCacheDir(context);
  86 + if (diskCacheSize > 0 || diskCacheFileCount > 0) {
  87 + File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context);
  88 + try {
  89 + return new LruDiskCache(individualCacheDir, reserveCacheDir, diskCacheFileNameGenerator, diskCacheSize,
  90 + diskCacheFileCount);
  91 + } catch (IOException e) {
  92 + L.e(e);
  93 + // continue and create unlimited cache
  94 + }
  95 + }
  96 + File cacheDir = StorageUtils.getCacheDirectory(context);
  97 + return new UnlimitedDiskCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator);
  98 + }
  99 +
  100 + /** Creates reserve disk cache folder which will be used if primary disk cache folder becomes unavailable */
  101 + private static File createReserveDiskCacheDir(Context context) {
  102 + File cacheDir = StorageUtils.getCacheDirectory(context, false);
  103 + File individualDir = new File(cacheDir, "uil-images");
  104 + if (individualDir.exists() || individualDir.mkdir()) {
  105 + cacheDir = individualDir;
  106 + }
  107 + return cacheDir;
  108 + }
  109 +
  110 + /**
  111 + * Creates default implementation of {@link MemoryCache} - {@link LruMemoryCache}<br />
  112 + * Default cache size = 1/8 of available app memory.
  113 + */
  114 + public static MemoryCache createMemoryCache(Context context, int memoryCacheSize) {
  115 + if (memoryCacheSize == 0) {
  116 + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  117 + int memoryClass = am.getMemoryClass();
  118 + if (hasHoneycomb() && isLargeHeap(context)) {
  119 + memoryClass = getLargeMemoryClass(am);
  120 + }
  121 + memoryCacheSize = 1024 * 1024 * memoryClass / 8;
  122 + }
  123 + return new LruMemoryCache(memoryCacheSize);
  124 + }
  125 +
  126 + private static boolean hasHoneycomb() {
  127 + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
  128 + }
  129 +
  130 + @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  131 + private static boolean isLargeHeap(Context context) {
  132 + return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0;
  133 + }
  134 +
  135 + @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  136 + private static int getLargeMemoryClass(ActivityManager am) {
  137 + return am.getLargeMemoryClass();
  138 + }
  139 +
  140 + /** Creates default implementation of {@link ImageDownloader} - {@link BaseImageDownloader} */
  141 + public static ImageDownloader createImageDownloader(Context context) {
  142 + return new BaseImageDownloader(context);
  143 + }
  144 +
  145 + /** Creates default implementation of {@link ImageDecoder} - {@link BaseImageDecoder} */
  146 + public static ImageDecoder createImageDecoder(boolean loggingEnabled) {
  147 + return new BaseImageDecoder(loggingEnabled);
  148 + }
  149 +
  150 + /** Creates default implementation of {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer} */
  151 + public static BitmapDisplayer createBitmapDisplayer() {
  152 + return new SimpleBitmapDisplayer();
  153 + }
  154 +
  155 + /** Creates default implementation of {@linkplain ThreadFactory thread factory} for task executor */
  156 + private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) {
  157 + return new DefaultThreadFactory(threadPriority, threadNamePrefix);
  158 + }
  159 +
  160 + private static class DefaultThreadFactory implements ThreadFactory {
  161 +
  162 + private static final AtomicInteger poolNumber = new AtomicInteger(1);
  163 +
  164 + private final ThreadGroup group;
  165 + private final AtomicInteger threadNumber = new AtomicInteger(1);
  166 + private final String namePrefix;
  167 + private final int threadPriority;
  168 +
  169 + DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
  170 + this.threadPriority = threadPriority;
  171 + group = Thread.currentThread().getThreadGroup();
  172 + namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
  173 + }
  174 +
  175 + @Override
  176 + public Thread newThread(Runnable r) {
  177 + Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
  178 + if (t.isDaemon()) t.setDaemon(false);
  179 + t.setPriority(threadPriority);
  180 + return t;
  181 + }
  182 + }
  183 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  20 +import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
  21 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  22 +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  23 +import com.nostra13.universalimageloader.utils.L;
  24 +
  25 +/**
  26 + * Displays bitmap in {@link ImageAware}. Must be called on UI thread.
  27 + *
  28 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  29 + * @see ImageLoadingListener
  30 + * @see BitmapDisplayer
  31 + * @since 1.3.1
  32 + */
  33 +final class DisplayBitmapTask implements Runnable {
  34 +
  35 + private static final String LOG_DISPLAY_IMAGE_IN_IMAGEAWARE = "Display image in ImageAware (loaded from %1$s) [%2$s]";
  36 + private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
  37 + private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";
  38 +
  39 + private final Bitmap bitmap;
  40 + private final String imageUri;
  41 + private final ImageAware imageAware;
  42 + private final String memoryCacheKey;
  43 + private final BitmapDisplayer displayer;
  44 + private final ImageLoadingListener listener;
  45 + private final ImageLoaderEngine engine;
  46 + private final LoadedFrom loadedFrom;
  47 +
  48 + public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, ImageLoaderEngine engine,
  49 + LoadedFrom loadedFrom) {
  50 + this.bitmap = bitmap;
  51 + imageUri = imageLoadingInfo.uri;
  52 + imageAware = imageLoadingInfo.imageAware;
  53 + memoryCacheKey = imageLoadingInfo.memoryCacheKey;
  54 + displayer = imageLoadingInfo.options.getDisplayer();
  55 + listener = imageLoadingInfo.listener;
  56 + this.engine = engine;
  57 + this.loadedFrom = loadedFrom;
  58 + }
  59 +
  60 + @Override
  61 + public void run() {
  62 + if (imageAware.isCollected()) {
  63 + L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
  64 + listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
  65 + } else if (isViewWasReused()) {
  66 + L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
  67 + listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
  68 + } else {
  69 + L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
  70 + displayer.display(bitmap, imageAware, loadedFrom);
  71 + engine.cancelDisplayTaskFor(imageAware);
  72 + listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
  73 + }
  74 + }
  75 +
  76 + /** Checks whether memory cache key (image URI) for current ImageAware is actual */
  77 + private boolean isViewWasReused() {
  78 + String currentCacheKey = engine.getLoadingUriForView(imageAware);
  79 + return !memoryCacheKey.equals(currentCacheKey);
  80 + }
  81 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.content.res.Resources;
  19 +import android.graphics.Bitmap;
  20 +import android.graphics.BitmapFactory.Options;
  21 +import android.graphics.drawable.Drawable;
  22 +import android.os.Handler;
  23 +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  24 +import com.nostra13.universalimageloader.core.assist.ImageScaleType;
  25 +import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
  26 +import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
  27 +import com.nostra13.universalimageloader.core.download.ImageDownloader;
  28 +import com.nostra13.universalimageloader.core.process.BitmapProcessor;
  29 +
  30 +/**
  31 + * Contains options for image display. Defines:
  32 + * <ul>
  33 + * <li>whether stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  34 + * image aware view} during image loading</li>
  35 + * <li>whether stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  36 + * image aware view} if empty URI is passed</li>
  37 + * <li>whether stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  38 + * image aware view} if image loading fails</li>
  39 + * <li>whether {@link com.nostra13.universalimageloader.core.imageaware.ImageAware image aware view} should be reset
  40 + * before image loading start</li>
  41 + * <li>whether loaded image will be cached in memory</li>
  42 + * <li>whether loaded image will be cached on disk</li>
  43 + * <li>image scale type</li>
  44 + * <li>decoding options (including bitmap decoding configuration)</li>
  45 + * <li>delay before loading of image</li>
  46 + * <li>whether consider EXIF parameters of image</li>
  47 + * <li>auxiliary object which will be passed to {@link ImageDownloader#getStream(String, Object) ImageDownloader}</li>
  48 + * <li>pre-processor for image Bitmap (before caching in memory)</li>
  49 + * <li>post-processor for image Bitmap (after caching in memory, before displaying)</li>
  50 + * <li>how decoded {@link Bitmap} will be displayed</li>
  51 + * </ul>
  52 + * <p/>
  53 + * You can create instance:
  54 + * <ul>
  55 + * <li>with {@link Builder}:<br />
  56 + * <b>i.e.</b> :
  57 + * <code>new {@link DisplayImageOptions}.{@link Builder#Builder() Builder()}.{@link Builder#cacheInMemory() cacheInMemory()}.
  58 + * {@link Builder#showImageOnLoading(int) showImageOnLoading()}.{@link Builder#build() build()}</code><br />
  59 + * </li>
  60 + * <li>or by static method: {@link #createSimple()}</li> <br />
  61 + *
  62 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  63 + * @since 1.0.0
  64 + */
  65 +public final class DisplayImageOptions {
  66 +
  67 + private final int imageResOnLoading;
  68 + private final int imageResForEmptyUri;
  69 + private final int imageResOnFail;
  70 + private final Drawable imageOnLoading;
  71 + private final Drawable imageForEmptyUri;
  72 + private final Drawable imageOnFail;
  73 + private final boolean resetViewBeforeLoading;
  74 + private final boolean cacheInMemory;
  75 + private final boolean cacheOnDisk;
  76 + private final ImageScaleType imageScaleType;
  77 + private final Options decodingOptions;
  78 + private final int delayBeforeLoading;
  79 + private final boolean considerExifParams;
  80 + private final Object extraForDownloader;
  81 + private final BitmapProcessor preProcessor;
  82 + private final BitmapProcessor postProcessor;
  83 + private final BitmapDisplayer displayer;
  84 + private final Handler handler;
  85 + private final boolean isSyncLoading;
  86 +
  87 + private DisplayImageOptions(Builder builder) {
  88 + imageResOnLoading = builder.imageResOnLoading;
  89 + imageResForEmptyUri = builder.imageResForEmptyUri;
  90 + imageResOnFail = builder.imageResOnFail;
  91 + imageOnLoading = builder.imageOnLoading;
  92 + imageForEmptyUri = builder.imageForEmptyUri;
  93 + imageOnFail = builder.imageOnFail;
  94 + resetViewBeforeLoading = builder.resetViewBeforeLoading;
  95 + cacheInMemory = builder.cacheInMemory;
  96 + cacheOnDisk = builder.cacheOnDisk;
  97 + imageScaleType = builder.imageScaleType;
  98 + decodingOptions = builder.decodingOptions;
  99 + delayBeforeLoading = builder.delayBeforeLoading;
  100 + considerExifParams = builder.considerExifParams;
  101 + extraForDownloader = builder.extraForDownloader;
  102 + preProcessor = builder.preProcessor;
  103 + postProcessor = builder.postProcessor;
  104 + displayer = builder.displayer;
  105 + handler = builder.handler;
  106 + isSyncLoading = builder.isSyncLoading;
  107 + }
  108 +
  109 + public boolean shouldShowImageOnLoading() {
  110 + return imageOnLoading != null || imageResOnLoading != 0;
  111 + }
  112 +
  113 + public boolean shouldShowImageForEmptyUri() {
  114 + return imageForEmptyUri != null || imageResForEmptyUri != 0;
  115 + }
  116 +
  117 + public boolean shouldShowImageOnFail() {
  118 + return imageOnFail != null || imageResOnFail != 0;
  119 + }
  120 +
  121 + public boolean shouldPreProcess() {
  122 + return preProcessor != null;
  123 + }
  124 +
  125 + public boolean shouldPostProcess() {
  126 + return postProcessor != null;
  127 + }
  128 +
  129 + public boolean shouldDelayBeforeLoading() {
  130 + return delayBeforeLoading > 0;
  131 + }
  132 +
  133 + public Drawable getImageOnLoading(Resources res) {
  134 + return imageResOnLoading != 0 ? res.getDrawable(imageResOnLoading) : imageOnLoading;
  135 + }
  136 +
  137 + public Drawable getImageForEmptyUri(Resources res) {
  138 + return imageResForEmptyUri != 0 ? res.getDrawable(imageResForEmptyUri) : imageForEmptyUri;
  139 + }
  140 +
  141 + public Drawable getImageOnFail(Resources res) {
  142 + return imageResOnFail != 0 ? res.getDrawable(imageResOnFail) : imageOnFail;
  143 + }
  144 +
  145 + public boolean isResetViewBeforeLoading() {
  146 + return resetViewBeforeLoading;
  147 + }
  148 +
  149 + public boolean isCacheInMemory() {
  150 + return cacheInMemory;
  151 + }
  152 +
  153 + public boolean isCacheOnDisk() {
  154 + return cacheOnDisk;
  155 + }
  156 +
  157 + public ImageScaleType getImageScaleType() {
  158 + return imageScaleType;
  159 + }
  160 +
  161 + public Options getDecodingOptions() {
  162 + return decodingOptions;
  163 + }
  164 +
  165 + public int getDelayBeforeLoading() {
  166 + return delayBeforeLoading;
  167 + }
  168 +
  169 + public boolean isConsiderExifParams() {
  170 + return considerExifParams;
  171 + }
  172 +
  173 + public Object getExtraForDownloader() {
  174 + return extraForDownloader;
  175 + }
  176 +
  177 + public BitmapProcessor getPreProcessor() {
  178 + return preProcessor;
  179 + }
  180 +
  181 + public BitmapProcessor getPostProcessor() {
  182 + return postProcessor;
  183 + }
  184 +
  185 + public BitmapDisplayer getDisplayer() {
  186 + return displayer;
  187 + }
  188 +
  189 + public Handler getHandler() {
  190 + return handler;
  191 + }
  192 +
  193 + boolean isSyncLoading() {
  194 + return isSyncLoading;
  195 + }
  196 +
  197 + /**
  198 + * Builder for {@link DisplayImageOptions}
  199 + *
  200 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  201 + */
  202 + public static class Builder {
  203 + private int imageResOnLoading = 0;
  204 + private int imageResForEmptyUri = 0;
  205 + private int imageResOnFail = 0;
  206 + private Drawable imageOnLoading = null;
  207 + private Drawable imageForEmptyUri = null;
  208 + private Drawable imageOnFail = null;
  209 + private boolean resetViewBeforeLoading = false;
  210 + private boolean cacheInMemory = false;
  211 + private boolean cacheOnDisk = false;
  212 + private ImageScaleType imageScaleType = ImageScaleType.IN_SAMPLE_POWER_OF_2;
  213 + private Options decodingOptions = new Options();
  214 + private int delayBeforeLoading = 0;
  215 + private boolean considerExifParams = false;
  216 + private Object extraForDownloader = null;
  217 + private BitmapProcessor preProcessor = null;
  218 + private BitmapProcessor postProcessor = null;
  219 + private BitmapDisplayer displayer = DefaultConfigurationFactory.createBitmapDisplayer();
  220 + private Handler handler = null;
  221 + private boolean isSyncLoading = false;
  222 +
  223 + public Builder() {
  224 + decodingOptions.inPurgeable = true;
  225 + decodingOptions.inInputShareable = true;
  226 + }
  227 +
  228 + /**
  229 + * Stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  230 + * image aware view} during image loading
  231 + *
  232 + * @param imageRes Stub image resource
  233 + * @deprecated Use {@link #showImageOnLoading(int)} instead
  234 + */
  235 + @Deprecated
  236 + public Builder showStubImage(int imageRes) {
  237 + imageResOnLoading = imageRes;
  238 + return this;
  239 + }
  240 +
  241 + /**
  242 + * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  243 + * image aware view} during image loading
  244 + *
  245 + * @param imageRes Image resource
  246 + */
  247 + public Builder showImageOnLoading(int imageRes) {
  248 + imageResOnLoading = imageRes;
  249 + return this;
  250 + }
  251 +
  252 + /**
  253 + * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  254 + * image aware view} during image loading.
  255 + * This option will be ignored if {@link Builder#showImageOnLoading(int)} is set.
  256 + */
  257 + public Builder showImageOnLoading(Drawable drawable) {
  258 + imageOnLoading = drawable;
  259 + return this;
  260 + }
  261 +
  262 + /**
  263 + * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  264 + * image aware view} if empty URI (null or empty
  265 + * string) will be passed to <b>ImageLoader.displayImage(...)</b> method.
  266 + *
  267 + * @param imageRes Image resource
  268 + */
  269 + public Builder showImageForEmptyUri(int imageRes) {
  270 + imageResForEmptyUri = imageRes;
  271 + return this;
  272 + }
  273 +
  274 + /**
  275 + * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  276 + * image aware view} if empty URI (null or empty
  277 + * string) will be passed to <b>ImageLoader.displayImage(...)</b> method.
  278 + * This option will be ignored if {@link Builder#showImageForEmptyUri(int)} is set.
  279 + */
  280 + public Builder showImageForEmptyUri(Drawable drawable) {
  281 + imageForEmptyUri = drawable;
  282 + return this;
  283 + }
  284 +
  285 + /**
  286 + * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  287 + * image aware view} if some error occurs during
  288 + * requested image loading/decoding.
  289 + *
  290 + * @param imageRes Image resource
  291 + */
  292 + public Builder showImageOnFail(int imageRes) {
  293 + imageResOnFail = imageRes;
  294 + return this;
  295 + }
  296 +
  297 + /**
  298 + * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  299 + * image aware view} if some error occurs during
  300 + * requested image loading/decoding.
  301 + * This option will be ignored if {@link Builder#showImageOnFail(int)} is set.
  302 + */
  303 + public Builder showImageOnFail(Drawable drawable) {
  304 + imageOnFail = drawable;
  305 + return this;
  306 + }
  307 +
  308 + /**
  309 + * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  310 + * image aware view} will be reset (set <b>null</b>) before image loading start
  311 + *
  312 + * @deprecated Use {@link #resetViewBeforeLoading(boolean) resetViewBeforeLoading(true)} instead
  313 + */
  314 + public Builder resetViewBeforeLoading() {
  315 + resetViewBeforeLoading = true;
  316 + return this;
  317 + }
  318 +
  319 + /**
  320 + * Sets whether {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  321 + * image aware view} will be reset (set <b>null</b>) before image loading start
  322 + */
  323 + public Builder resetViewBeforeLoading(boolean resetViewBeforeLoading) {
  324 + this.resetViewBeforeLoading = resetViewBeforeLoading;
  325 + return this;
  326 + }
  327 +
  328 + /**
  329 + * Loaded image will be cached in memory
  330 + *
  331 + * @deprecated Use {@link #cacheInMemory(boolean) cacheInMemory(true)} instead
  332 + */
  333 + @Deprecated
  334 + public Builder cacheInMemory() {
  335 + cacheInMemory = true;
  336 + return this;
  337 + }
  338 +
  339 + /** Sets whether loaded image will be cached in memory */
  340 + public Builder cacheInMemory(boolean cacheInMemory) {
  341 + this.cacheInMemory = cacheInMemory;
  342 + return this;
  343 + }
  344 +
  345 + /**
  346 + * Loaded image will be cached on disk
  347 + *
  348 + * @deprecated Use {@link #cacheOnDisk(boolean) cacheOnDisk(true)} instead
  349 + */
  350 + @Deprecated
  351 + public Builder cacheOnDisc() {
  352 + return cacheOnDisk(true);
  353 + }
  354 +
  355 + /**
  356 + * Sets whether loaded image will be cached on disk
  357 + *
  358 + * @deprecated Use {@link #cacheOnDisk(boolean)} instead
  359 + */
  360 + @Deprecated
  361 + public Builder cacheOnDisc(boolean cacheOnDisk) {
  362 + return cacheOnDisk(cacheOnDisk);
  363 + }
  364 +
  365 + /** Sets whether loaded image will be cached on disk */
  366 + public Builder cacheOnDisk(boolean cacheOnDisk) {
  367 + this.cacheOnDisk = cacheOnDisk;
  368 + return this;
  369 + }
  370 +
  371 + /**
  372 + * Sets {@linkplain ImageScaleType scale type} for decoding image. This parameter is used while define scale
  373 + * size for decoding image to Bitmap. Default value - {@link ImageScaleType#IN_SAMPLE_POWER_OF_2}
  374 + */
  375 + public Builder imageScaleType(ImageScaleType imageScaleType) {
  376 + this.imageScaleType = imageScaleType;
  377 + return this;
  378 + }
  379 +
  380 + /** Sets {@link Bitmap.Config bitmap config} for image decoding. Default value - {@link Bitmap.Config#ARGB_8888} */
  381 + public Builder bitmapConfig(Bitmap.Config bitmapConfig) {
  382 + if (bitmapConfig == null) throw new IllegalArgumentException("bitmapConfig can't be null");
  383 + decodingOptions.inPreferredConfig = bitmapConfig;
  384 + return this;
  385 + }
  386 +
  387 + /**
  388 + * Sets options for image decoding.<br />
  389 + * <b>NOTE:</b> {@link Options#inSampleSize} of incoming options will <b>NOT</b> be considered. Library
  390 + * calculate the most appropriate sample size itself according yo {@link #imageScaleType(ImageScaleType)}
  391 + * options.<br />
  392 + * <b>NOTE:</b> This option overlaps {@link #bitmapConfig(Bitmap.Config) bitmapConfig()}
  393 + * option.
  394 + */
  395 + public Builder decodingOptions(Options decodingOptions) {
  396 + if (decodingOptions == null) throw new IllegalArgumentException("decodingOptions can't be null");
  397 + this.decodingOptions = decodingOptions;
  398 + return this;
  399 + }
  400 +
  401 + /** Sets delay time before starting loading task. Default - no delay. */
  402 + public Builder delayBeforeLoading(int delayInMillis) {
  403 + this.delayBeforeLoading = delayInMillis;
  404 + return this;
  405 + }
  406 +
  407 + /** Sets auxiliary object which will be passed to {@link ImageDownloader#getStream(String, Object)} */
  408 + public Builder extraForDownloader(Object extra) {
  409 + this.extraForDownloader = extra;
  410 + return this;
  411 + }
  412 +
  413 + /** Sets whether ImageLoader will consider EXIF parameters of JPEG image (rotate, flip) */
  414 + public Builder considerExifParams(boolean considerExifParams) {
  415 + this.considerExifParams = considerExifParams;
  416 + return this;
  417 + }
  418 +
  419 + /**
  420 + * Sets bitmap processor which will be process bitmaps before they will be cached in memory. So memory cache
  421 + * will contain bitmap processed by incoming preProcessor.<br />
  422 + * Image will be pre-processed even if caching in memory is disabled.
  423 + */
  424 + public Builder preProcessor(BitmapProcessor preProcessor) {
  425 + this.preProcessor = preProcessor;
  426 + return this;
  427 + }
  428 +
  429 + /**
  430 + * Sets bitmap processor which will be process bitmaps before they will be displayed in
  431 + * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware image aware view} but
  432 + * after they'll have been saved in memory cache.
  433 + */
  434 + public Builder postProcessor(BitmapProcessor postProcessor) {
  435 + this.postProcessor = postProcessor;
  436 + return this;
  437 + }
  438 +
  439 + /**
  440 + * Sets custom {@link BitmapDisplayer displayer} for image loading task. Default value -
  441 + * {@link DefaultConfigurationFactory#createBitmapDisplayer()}
  442 + */
  443 + public Builder displayer(BitmapDisplayer displayer) {
  444 + if (displayer == null) throw new IllegalArgumentException("displayer can't be null");
  445 + this.displayer = displayer;
  446 + return this;
  447 + }
  448 +
  449 + Builder syncLoading(boolean isSyncLoading) {
  450 + this.isSyncLoading = isSyncLoading;
  451 + return this;
  452 + }
  453 +
  454 + /**
  455 + * Sets custom {@linkplain Handler handler} for displaying images and firing {@linkplain ImageLoadingListener
  456 + * listener} events.
  457 + */
  458 + public Builder handler(Handler handler) {
  459 + this.handler = handler;
  460 + return this;
  461 + }
  462 +
  463 + /** Sets all options equal to incoming options */
  464 + public Builder cloneFrom(DisplayImageOptions options) {
  465 + imageResOnLoading = options.imageResOnLoading;
  466 + imageResForEmptyUri = options.imageResForEmptyUri;
  467 + imageResOnFail = options.imageResOnFail;
  468 + imageOnLoading = options.imageOnLoading;
  469 + imageForEmptyUri = options.imageForEmptyUri;
  470 + imageOnFail = options.imageOnFail;
  471 + resetViewBeforeLoading = options.resetViewBeforeLoading;
  472 + cacheInMemory = options.cacheInMemory;
  473 + cacheOnDisk = options.cacheOnDisk;
  474 + imageScaleType = options.imageScaleType;
  475 + decodingOptions = options.decodingOptions;
  476 + delayBeforeLoading = options.delayBeforeLoading;
  477 + considerExifParams = options.considerExifParams;
  478 + extraForDownloader = options.extraForDownloader;
  479 + preProcessor = options.preProcessor;
  480 + postProcessor = options.postProcessor;
  481 + displayer = options.displayer;
  482 + handler = options.handler;
  483 + isSyncLoading = options.isSyncLoading;
  484 + return this;
  485 + }
  486 +
  487 + /** Builds configured {@link DisplayImageOptions} object */
  488 + public DisplayImageOptions build() {
  489 + return new DisplayImageOptions(this);
  490 + }
  491 + }
  492 +
  493 + /**
  494 + * Creates options appropriate for single displaying:
  495 + * <ul>
  496 + * <li>View will <b>not</b> be reset before loading</li>
  497 + * <li>Loaded image will <b>not</b> be cached in memory</li>
  498 + * <li>Loaded image will <b>not</b> be cached on disk</li>
  499 + * <li>{@link ImageScaleType#IN_SAMPLE_POWER_OF_2} decoding type will be used</li>
  500 + * <li>{@link Bitmap.Config#ARGB_8888} bitmap config will be used for image decoding</li>
  501 + * <li>{@link SimpleBitmapDisplayer} will be used for image displaying</li>
  502 + * </ul>
  503 + * <p/>
  504 + * These option are appropriate for simple single-use image (from drawables or from Internet) displaying.
  505 + */
  506 + public static DisplayImageOptions createSimple() {
  507 + return new Builder().build();
  508 + }
  509 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.os.Handler;
  20 +import android.os.Looper;
  21 +import android.text.TextUtils;
  22 +import android.view.View;
  23 +import android.widget.ImageView;
  24 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  25 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  26 +import com.nostra13.universalimageloader.core.assist.FailReason;
  27 +import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
  28 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  29 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  30 +import com.nostra13.universalimageloader.core.assist.ViewScaleType;
  31 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  32 +import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
  33 +import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
  34 +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  35 +import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
  36 +import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
  37 +import com.nostra13.universalimageloader.utils.ImageSizeUtils;
  38 +import com.nostra13.universalimageloader.utils.L;
  39 +import com.nostra13.universalimageloader.utils.MemoryCacheUtils;
  40 +
  41 +/**
  42 + * Singletone for image loading and displaying at {@link ImageView ImageViews}<br />
  43 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before any other method.
  44 + *
  45 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  46 + * @since 1.0.0
  47 + */
  48 +public class ImageLoader {
  49 +
  50 + public static final String TAG = ImageLoader.class.getSimpleName();
  51 +
  52 + static final String LOG_INIT_CONFIG = "Initialize ImageLoader with configuration";
  53 + static final String LOG_DESTROY = "Destroy ImageLoader";
  54 + static final String LOG_LOAD_IMAGE_FROM_MEMORY_CACHE = "Load image from memory cache [%s]";
  55 +
  56 + private static final String WARNING_RE_INIT_CONFIG = "Try to initialize ImageLoader which had already been initialized before. " + "To re-init ImageLoader with new configuration call ImageLoader.destroy() at first.";
  57 + private static final String ERROR_WRONG_ARGUMENTS = "Wrong arguments were passed to displayImage() method (ImageView reference must not be null)";
  58 + private static final String ERROR_NOT_INIT = "ImageLoader must be init with configuration before using";
  59 + private static final String ERROR_INIT_CONFIG_WITH_NULL = "ImageLoader configuration can not be initialized with null";
  60 +
  61 + private ImageLoaderConfiguration configuration;
  62 + private ImageLoaderEngine engine;
  63 +
  64 + private ImageLoadingListener defaultListener = new SimpleImageLoadingListener();
  65 +
  66 + private volatile static ImageLoader instance;
  67 +
  68 + /** Returns singleton class instance */
  69 + public static ImageLoader getInstance() {
  70 + if (instance == null) {
  71 + synchronized (ImageLoader.class) {
  72 + if (instance == null) {
  73 + instance = new ImageLoader();
  74 + }
  75 + }
  76 + }
  77 + return instance;
  78 + }
  79 +
  80 + protected ImageLoader() {
  81 + }
  82 +
  83 + /**
  84 + * Initializes ImageLoader instance with configuration.<br />
  85 + * If configurations was set before ( {@link #isInited()} == true) then this method does nothing.<br />
  86 + * To force initialization with new configuration you should {@linkplain #destroy() destroy ImageLoader} at first.
  87 + *
  88 + * @param configuration {@linkplain ImageLoaderConfiguration ImageLoader configuration}
  89 + * @throws IllegalArgumentException if <b>configuration</b> parameter is null
  90 + */
  91 + public synchronized void init(ImageLoaderConfiguration configuration) {
  92 + if (configuration == null) {
  93 + throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
  94 + }
  95 + if (this.configuration == null) {
  96 + L.d(LOG_INIT_CONFIG);
  97 + engine = new ImageLoaderEngine(configuration);
  98 + this.configuration = configuration;
  99 + } else {
  100 + L.w(WARNING_RE_INIT_CONFIG);
  101 + }
  102 + }
  103 +
  104 + /**
  105 + * Returns <b>true</b> - if ImageLoader {@linkplain #init(ImageLoaderConfiguration) is initialized with
  106 + * configuration}; <b>false</b> - otherwise
  107 + */
  108 + public boolean isInited() {
  109 + return configuration != null;
  110 + }
  111 +
  112 + /**
  113 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn. <br/>
  114 + * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
  115 + * configuration} will be used.<br />
  116 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  117 + *
  118 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  119 + * @param imageAware {@linkplain ImageAware Image aware view}
  120 + * which should display image
  121 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  122 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  123 + */
  124 + public void displayImage(String uri, ImageAware imageAware) {
  125 + displayImage(uri, imageAware, null, null, null);
  126 + }
  127 +
  128 + /**
  129 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.<br />
  130 + * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
  131 + * configuration} will be used.<br />
  132 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  133 + *
  134 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  135 + * @param imageAware {@linkplain ImageAware Image aware view}
  136 + * which should display image
  137 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
  138 + * UI thread if this method is called on UI thread.
  139 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  140 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  141 + */
  142 + public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener) {
  143 + displayImage(uri, imageAware, null, listener, null);
  144 + }
  145 +
  146 + /**
  147 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.<br />
  148 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  149 + *
  150 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  151 + * @param imageAware {@linkplain ImageAware Image aware view}
  152 + * which should display image
  153 + * @param options {@linkplain DisplayImageOptions Options} for image
  154 + * decoding and displaying. If <b>null</b> - default display image options
  155 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  156 + * from configuration} will be used.
  157 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  158 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  159 + */
  160 + public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options) {
  161 + displayImage(uri, imageAware, options, null, null);
  162 + }
  163 +
  164 + /**
  165 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.<br />
  166 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  167 + *
  168 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  169 + * @param imageAware {@linkplain ImageAware Image aware view}
  170 + * which should display image
  171 + * @param options {@linkplain DisplayImageOptions Options} for image
  172 + * decoding and displaying. If <b>null</b> - default display image options
  173 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  174 + * from configuration} will be used.
  175 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
  176 + * UI thread if this method is called on UI thread.
  177 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  178 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  179 + */
  180 + public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
  181 + ImageLoadingListener listener) {
  182 + displayImage(uri, imageAware, options, listener, null);
  183 + }
  184 +
  185 + /**
  186 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.<br />
  187 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  188 + *
  189 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  190 + * @param imageAware {@linkplain ImageAware Image aware view}
  191 + * which should display image
  192 + * @param options {@linkplain DisplayImageOptions Options} for image
  193 + * decoding and displaying. If <b>null</b> - default display image options
  194 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  195 + * from configuration} will be used.
  196 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  197 + * events on UI thread if this method is called on UI thread.
  198 + * @param progressListener {@linkplain ImageLoadingProgressListener
  199 + * Listener} for image loading progress. Listener fires events on UI thread if this method
  200 + * is called on UI thread. Caching on disk should be enabled in
  201 + * {@linkplain DisplayImageOptions options} to make
  202 + * this listener work.
  203 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  204 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  205 + */
  206 + public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
  207 + ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
  208 + checkConfiguration();
  209 + if (imageAware == null) {
  210 + throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
  211 + }
  212 + if (listener == null) {
  213 + listener = defaultListener;
  214 + }
  215 + if (options == null) {
  216 + options = configuration.defaultDisplayImageOptions;
  217 + }
  218 +
  219 + if (TextUtils.isEmpty(uri)) {
  220 + engine.cancelDisplayTaskFor(imageAware);
  221 + listener.onLoadingStarted(uri, imageAware.getWrappedView());
  222 + if (options.shouldShowImageForEmptyUri()) {
  223 + imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
  224 + } else {
  225 + imageAware.setImageDrawable(null);
  226 + }
  227 + listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
  228 + return;
  229 + }
  230 +
  231 + ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
  232 + String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
  233 + engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
  234 +
  235 + listener.onLoadingStarted(uri, imageAware.getWrappedView());
  236 +
  237 + Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
  238 + if (bmp != null && !bmp.isRecycled()) {
  239 + L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
  240 +
  241 + if (options.shouldPostProcess()) {
  242 + ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
  243 + options, listener, progressListener, engine.getLockForUri(uri));
  244 + ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
  245 + defineHandler(options));
  246 + if (options.isSyncLoading()) {
  247 + displayTask.run();
  248 + } else {
  249 + engine.submit(displayTask);
  250 + }
  251 + } else {
  252 + options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
  253 + listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
  254 + }
  255 + } else {
  256 + if (options.shouldShowImageOnLoading()) {
  257 + imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
  258 + } else if (options.isResetViewBeforeLoading()) {
  259 + imageAware.setImageDrawable(null);
  260 + }
  261 +
  262 + ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
  263 + options, listener, progressListener, engine.getLockForUri(uri));
  264 + LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
  265 + defineHandler(options));
  266 + if (options.isSyncLoading()) {
  267 + displayTask.run();
  268 + } else {
  269 + engine.submit(displayTask);
  270 + }
  271 + }
  272 + }
  273 +
  274 + /**
  275 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn. <br/>
  276 + * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
  277 + * configuration} will be used.<br />
  278 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  279 + *
  280 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  281 + * @param imageView {@link ImageView} which should display image
  282 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  283 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  284 + */
  285 + public void displayImage(String uri, ImageView imageView) {
  286 + displayImage(uri, new ImageViewAware(imageView), null, null, null);
  287 + }
  288 +
  289 + /**
  290 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn.<br />
  291 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  292 + *
  293 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  294 + * @param imageView {@link ImageView} which should display image
  295 + * @param options {@linkplain DisplayImageOptions Options} for image
  296 + * decoding and displaying. If <b>null</b> - default display image options
  297 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  298 + * from configuration} will be used.
  299 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  300 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  301 + */
  302 + public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
  303 + displayImage(uri, new ImageViewAware(imageView), options, null, null);
  304 + }
  305 +
  306 + /**
  307 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn.<br />
  308 + * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
  309 + * configuration} will be used.<br />
  310 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  311 + *
  312 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  313 + * @param imageView {@link ImageView} which should display image
  314 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
  315 + * UI thread if this method is called on UI thread.
  316 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  317 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  318 + */
  319 + public void displayImage(String uri, ImageView imageView, ImageLoadingListener listener) {
  320 + displayImage(uri, new ImageViewAware(imageView), null, listener, null);
  321 + }
  322 +
  323 + /**
  324 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn.<br />
  325 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  326 + *
  327 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  328 + * @param imageView {@link ImageView} which should display image
  329 + * @param options {@linkplain DisplayImageOptions Options} for image
  330 + * decoding and displaying. If <b>null</b> - default display image options
  331 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  332 + * from configuration} will be used.
  333 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
  334 + * UI thread if this method is called on UI thread.
  335 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  336 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  337 + */
  338 + public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
  339 + ImageLoadingListener listener) {
  340 + displayImage(uri, imageView, options, listener, null);
  341 + }
  342 +
  343 + /**
  344 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn.<br />
  345 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  346 + *
  347 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  348 + * @param imageView {@link ImageView} which should display image
  349 + * @param options {@linkplain DisplayImageOptions Options} for image
  350 + * decoding and displaying. If <b>null</b> - default display image options
  351 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  352 + * from configuration} will be used.
  353 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  354 + * events on UI thread if this method is called on UI thread.
  355 + * @param progressListener {@linkplain ImageLoadingProgressListener
  356 + * Listener} for image loading progress. Listener fires events on UI thread if this method
  357 + * is called on UI thread. Caching on disk should be enabled in
  358 + * {@linkplain DisplayImageOptions options} to make
  359 + * this listener work.
  360 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  361 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  362 + */
  363 + public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
  364 + ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
  365 + displayImage(uri, new ImageViewAware(imageView), options, listener, progressListener);
  366 + }
  367 +
  368 + /**
  369 + * Adds load image task to execution pool. Image will be returned with
  370 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  371 + * <br />
  372 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  373 + *
  374 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  375 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI
  376 + * thread if this method is called on UI thread.
  377 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  378 + */
  379 + public void loadImage(String uri, ImageLoadingListener listener) {
  380 + loadImage(uri, null, null, listener, null);
  381 + }
  382 +
  383 + /**
  384 + * Adds load image task to execution pool. Image will be returned with
  385 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  386 + * <br />
  387 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  388 + *
  389 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  390 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
  391 + * {@linkplain ImageLoadingListener#onLoadingComplete(String, View,
  392 + * Bitmap)} callback}. Downloaded image will be decoded
  393 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  394 + * larger) than incoming targetImageSize.
  395 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  396 + * events on UI thread if this method is called on UI thread.
  397 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  398 + */
  399 + public void loadImage(String uri, ImageSize targetImageSize, ImageLoadingListener listener) {
  400 + loadImage(uri, targetImageSize, null, listener, null);
  401 + }
  402 +
  403 + /**
  404 + * Adds load image task to execution pool. Image will be returned with
  405 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  406 + * <br />
  407 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  408 + *
  409 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  410 + * @param options {@linkplain DisplayImageOptions Options} for image
  411 + * decoding and displaying. If <b>null</b> - default display image options
  412 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
  413 + * configuration} will be used.<br />
  414 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI
  415 + * thread if this method is called on UI thread.
  416 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  417 + */
  418 + public void loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener) {
  419 + loadImage(uri, null, options, listener, null);
  420 + }
  421 +
  422 + /**
  423 + * Adds load image task to execution pool. Image will be returned with
  424 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  425 + * <br />
  426 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  427 + *
  428 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  429 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
  430 + * {@linkplain ImageLoadingListener#onLoadingComplete(String, View,
  431 + * Bitmap)} callback}. Downloaded image will be decoded
  432 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  433 + * larger) than incoming targetImageSize.
  434 + * @param options {@linkplain DisplayImageOptions Options} for image
  435 + * decoding and displaying. If <b>null</b> - default display image options
  436 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  437 + * from configuration} will be used.<br />
  438 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  439 + * events on UI thread if this method is called on UI thread.
  440 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  441 + */
  442 + public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
  443 + ImageLoadingListener listener) {
  444 + loadImage(uri, targetImageSize, options, listener, null);
  445 + }
  446 +
  447 + /**
  448 + * Adds load image task to execution pool. Image will be returned with
  449 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  450 + * <br />
  451 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  452 + *
  453 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  454 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
  455 + * {@linkplain ImageLoadingListener#onLoadingComplete(String, View,
  456 + * Bitmap)} callback}. Downloaded image will be decoded
  457 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  458 + * larger) than incoming targetImageSize.
  459 + * @param options {@linkplain DisplayImageOptions Options} for image
  460 + * decoding and displaying. If <b>null</b> - default display image options
  461 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  462 + * from configuration} will be used.<br />
  463 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  464 + * events on UI thread if this method is called on UI thread.
  465 + * @param progressListener {@linkplain ImageLoadingProgressListener
  466 + * Listener} for image loading progress. Listener fires events on UI thread if this method
  467 + * is called on UI thread. Caching on disk should be enabled in
  468 + * {@linkplain DisplayImageOptions options} to make
  469 + * this listener work.
  470 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  471 + */
  472 + public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
  473 + ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
  474 + checkConfiguration();
  475 + if (targetImageSize == null) {
  476 + targetImageSize = configuration.getMaxImageSize();
  477 + }
  478 + if (options == null) {
  479 + options = configuration.defaultDisplayImageOptions;
  480 + }
  481 +
  482 + NonViewAware imageAware = new NonViewAware(uri, targetImageSize, ViewScaleType.CROP);
  483 + displayImage(uri, imageAware, options, listener, progressListener);
  484 + }
  485 +
  486 + /**
  487 + * Loads and decodes image synchronously.<br />
  488 + * Default display image options
  489 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
  490 + * configuration} will be used.<br />
  491 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  492 + *
  493 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  494 + * @return Result image Bitmap. Can be <b>null</b> if image loading/decoding was failed or cancelled.
  495 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  496 + */
  497 + public Bitmap loadImageSync(String uri) {
  498 + return loadImageSync(uri, null, null);
  499 + }
  500 +
  501 + /**
  502 + * Loads and decodes image synchronously.<br />
  503 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  504 + *
  505 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  506 + * @param options {@linkplain DisplayImageOptions Options} for image
  507 + * decoding and scaling. If <b>null</b> - default display image options
  508 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
  509 + * configuration} will be used.
  510 + * @return Result image Bitmap. Can be <b>null</b> if image loading/decoding was failed or cancelled.
  511 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  512 + */
  513 + public Bitmap loadImageSync(String uri, DisplayImageOptions options) {
  514 + return loadImageSync(uri, null, options);
  515 + }
  516 +
  517 + /**
  518 + * Loads and decodes image synchronously.<br />
  519 + * Default display image options
  520 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
  521 + * configuration} will be used.<br />
  522 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  523 + *
  524 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  525 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded
  526 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  527 + * larger) than incoming targetImageSize.
  528 + * @return Result image Bitmap. Can be <b>null</b> if image loading/decoding was failed or cancelled.
  529 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  530 + */
  531 + public Bitmap loadImageSync(String uri, ImageSize targetImageSize) {
  532 + return loadImageSync(uri, targetImageSize, null);
  533 + }
  534 +
  535 + /**
  536 + * Loads and decodes image synchronously.<br />
  537 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  538 + *
  539 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  540 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded
  541 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  542 + * larger) than incoming targetImageSize.
  543 + * @param options {@linkplain DisplayImageOptions Options} for image
  544 + * decoding and scaling. If <b>null</b> - default display image options
  545 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  546 + * from configuration} will be used.
  547 + * @return Result image Bitmap. Can be <b>null</b> if image loading/decoding was failed or cancelled.
  548 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  549 + */
  550 + public Bitmap loadImageSync(String uri, ImageSize targetImageSize, DisplayImageOptions options) {
  551 + if (options == null) {
  552 + options = configuration.defaultDisplayImageOptions;
  553 + }
  554 + options = new DisplayImageOptions.Builder().cloneFrom(options).syncLoading(true).build();
  555 +
  556 + SyncImageLoadingListener listener = new SyncImageLoadingListener();
  557 + loadImage(uri, targetImageSize, options, listener);
  558 + return listener.getLoadedBitmap();
  559 + }
  560 +
  561 + /**
  562 + * Checks if ImageLoader's configuration was initialized
  563 + *
  564 + * @throws IllegalStateException if configuration wasn't initialized
  565 + */
  566 + private void checkConfiguration() {
  567 + if (configuration == null) {
  568 + throw new IllegalStateException(ERROR_NOT_INIT);
  569 + }
  570 + }
  571 +
  572 + /** Sets a default loading listener for all display and loading tasks. */
  573 + public void setDefaultLoadingListener(ImageLoadingListener listener) {
  574 + defaultListener = listener == null ? new SimpleImageLoadingListener() : listener;
  575 + }
  576 +
  577 + /**
  578 + * Returns memory cache
  579 + *
  580 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  581 + */
  582 + public MemoryCache getMemoryCache() {
  583 + checkConfiguration();
  584 + return configuration.memoryCache;
  585 + }
  586 +
  587 + /**
  588 + * Clears memory cache
  589 + *
  590 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  591 + */
  592 + public void clearMemoryCache() {
  593 + checkConfiguration();
  594 + configuration.memoryCache.clear();
  595 + }
  596 +
  597 + /**
  598 + * Returns disk cache
  599 + *
  600 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  601 + * @deprecated Use {@link #getDiskCache()} instead
  602 + */
  603 + @Deprecated
  604 + public DiskCache getDiscCache() {
  605 + return getDiskCache();
  606 + }
  607 +
  608 + /**
  609 + * Returns disk cache
  610 + *
  611 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  612 + */
  613 + public DiskCache getDiskCache() {
  614 + checkConfiguration();
  615 + return configuration.diskCache;
  616 + }
  617 +
  618 + /**
  619 + * Clears disk cache.
  620 + *
  621 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  622 + * @deprecated Use {@link #clearDiskCache()} instead
  623 + */
  624 + @Deprecated
  625 + public void clearDiscCache() {
  626 + clearDiskCache();
  627 + }
  628 +
  629 + /**
  630 + * Clears disk cache.
  631 + *
  632 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  633 + */
  634 + public void clearDiskCache() {
  635 + checkConfiguration();
  636 + configuration.diskCache.clear();
  637 + }
  638 +
  639 + /**
  640 + * Returns URI of image which is loading at this moment into passed
  641 + * {@link ImageAware ImageAware}
  642 + */
  643 + public String getLoadingUriForView(ImageAware imageAware) {
  644 + return engine.getLoadingUriForView(imageAware);
  645 + }
  646 +
  647 + /**
  648 + * Returns URI of image which is loading at this moment into passed
  649 + * {@link ImageView ImageView}
  650 + */
  651 + public String getLoadingUriForView(ImageView imageView) {
  652 + return engine.getLoadingUriForView(new ImageViewAware(imageView));
  653 + }
  654 +
  655 + /**
  656 + * Cancel the task of loading and displaying image for passed
  657 + * {@link ImageAware ImageAware}.
  658 + *
  659 + * @param imageAware {@link ImageAware ImageAware} for
  660 + * which display task will be cancelled
  661 + */
  662 + public void cancelDisplayTask(ImageAware imageAware) {
  663 + engine.cancelDisplayTaskFor(imageAware);
  664 + }
  665 +
  666 + /**
  667 + * Cancel the task of loading and displaying image for passed
  668 + * {@link ImageView ImageView}.
  669 + *
  670 + * @param imageView {@link ImageView ImageView} for which display task will be cancelled
  671 + */
  672 + public void cancelDisplayTask(ImageView imageView) {
  673 + engine.cancelDisplayTaskFor(new ImageViewAware(imageView));
  674 + }
  675 +
  676 + /**
  677 + * Denies or allows ImageLoader to download images from the network.<br />
  678 + * <br />
  679 + * If downloads are denied and if image isn't cached then
  680 + * {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired with
  681 + * {@link FailReason.FailType#NETWORK_DENIED}
  682 + *
  683 + * @param denyNetworkDownloads pass <b>true</b> - to deny engine to download images from the network; <b>false</b> -
  684 + * to allow engine to download images from network.
  685 + */
  686 + public void denyNetworkDownloads(boolean denyNetworkDownloads) {
  687 + engine.denyNetworkDownloads(denyNetworkDownloads);
  688 + }
  689 +
  690 + /**
  691 + * Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle <a
  692 + * href="http://code.google.com/p/android/issues/detail?id=6066">this known problem</a> or not.
  693 + *
  694 + * @param handleSlowNetwork pass <b>true</b> - to use {@link FlushedInputStream} for network downloads; <b>false</b>
  695 + * - otherwise.
  696 + */
  697 + public void handleSlowNetwork(boolean handleSlowNetwork) {
  698 + engine.handleSlowNetwork(handleSlowNetwork);
  699 + }
  700 +
  701 + /**
  702 + * Pause ImageLoader. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.
  703 + * <br />
  704 + * Already running tasks are not paused.
  705 + */
  706 + public void pause() {
  707 + engine.pause();
  708 + }
  709 +
  710 + /** Resumes waiting "load&display" tasks */
  711 + public void resume() {
  712 + engine.resume();
  713 + }
  714 +
  715 + /**
  716 + * Cancels all running and scheduled display image tasks.<br />
  717 + * <b>NOTE:</b> This method doesn't shutdown
  718 + * {@linkplain ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor)
  719 + * custom task executors} if you set them.<br />
  720 + * ImageLoader still can be used after calling this method.
  721 + */
  722 + public void stop() {
  723 + engine.stop();
  724 + }
  725 +
  726 + /**
  727 + * {@linkplain #stop() Stops ImageLoader} and clears current configuration. <br />
  728 + * You can {@linkplain #init(ImageLoaderConfiguration) init} ImageLoader with new configuration after calling this
  729 + * method.
  730 + */
  731 + public void destroy() {
  732 + if (configuration != null) L.d(LOG_DESTROY);
  733 + stop();
  734 + configuration.diskCache.close();
  735 + engine = null;
  736 + configuration = null;
  737 + }
  738 +
  739 + private static Handler defineHandler(DisplayImageOptions options) {
  740 + Handler handler = options.getHandler();
  741 + if (options.isSyncLoading()) {
  742 + handler = null;
  743 + } else if (handler == null && Looper.myLooper() == Looper.getMainLooper()) {
  744 + handler = new Handler();
  745 + }
  746 + return handler;
  747 + }
  748 +
  749 + /**
  750 + * Listener which is designed for synchronous image loading.
  751 + *
  752 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  753 + * @since 1.9.0
  754 + */
  755 + private static class SyncImageLoadingListener extends SimpleImageLoadingListener {
  756 +
  757 + private Bitmap loadedImage;
  758 +
  759 + @Override
  760 + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
  761 + this.loadedImage = loadedImage;
  762 + }
  763 +
  764 + public Bitmap getLoadedBitmap() {
  765 + return loadedImage;
  766 + }
  767 + }
  768 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.content.Context;
  19 +import android.content.res.Resources;
  20 +import android.util.DisplayMetrics;
  21 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  22 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  23 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  24 +import com.nostra13.universalimageloader.cache.memory.impl.FuzzyKeyMemoryCache;
  25 +import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
  26 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  27 +import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
  28 +import com.nostra13.universalimageloader.core.decode.ImageDecoder;
  29 +import com.nostra13.universalimageloader.core.download.ImageDownloader;
  30 +import com.nostra13.universalimageloader.core.process.BitmapProcessor;
  31 +import com.nostra13.universalimageloader.utils.L;
  32 +import com.nostra13.universalimageloader.utils.MemoryCacheUtils;
  33 +
  34 +import java.io.IOException;
  35 +import java.io.InputStream;
  36 +import java.util.concurrent.Executor;
  37 +
  38 +/**
  39 + * Presents configuration for {@link ImageLoader}
  40 + *
  41 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  42 + * @see ImageLoader
  43 + * @see MemoryCache
  44 + * @see DiskCache
  45 + * @see DisplayImageOptions
  46 + * @see ImageDownloader
  47 + * @see FileNameGenerator
  48 + * @since 1.0.0
  49 + */
  50 +public final class ImageLoaderConfiguration {
  51 +
  52 + final Resources resources;
  53 +
  54 + final int maxImageWidthForMemoryCache;
  55 + final int maxImageHeightForMemoryCache;
  56 + final int maxImageWidthForDiskCache;
  57 + final int maxImageHeightForDiskCache;
  58 + final BitmapProcessor processorForDiskCache;
  59 +
  60 + final Executor taskExecutor;
  61 + final Executor taskExecutorForCachedImages;
  62 + final boolean customExecutor;
  63 + final boolean customExecutorForCachedImages;
  64 +
  65 + final int threadPoolSize;
  66 + final int threadPriority;
  67 + final QueueProcessingType tasksProcessingType;
  68 +
  69 + final MemoryCache memoryCache;
  70 + final DiskCache diskCache;
  71 + final ImageDownloader downloader;
  72 + final ImageDecoder decoder;
  73 + final DisplayImageOptions defaultDisplayImageOptions;
  74 +
  75 + final ImageDownloader networkDeniedDownloader;
  76 + final ImageDownloader slowNetworkDownloader;
  77 +
  78 + private ImageLoaderConfiguration(final Builder builder) {
  79 + resources = builder.context.getResources();
  80 + maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache;
  81 + maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache;
  82 + maxImageWidthForDiskCache = builder.maxImageWidthForDiskCache;
  83 + maxImageHeightForDiskCache = builder.maxImageHeightForDiskCache;
  84 + processorForDiskCache = builder.processorForDiskCache;
  85 + taskExecutor = builder.taskExecutor;
  86 + taskExecutorForCachedImages = builder.taskExecutorForCachedImages;
  87 + threadPoolSize = builder.threadPoolSize;
  88 + threadPriority = builder.threadPriority;
  89 + tasksProcessingType = builder.tasksProcessingType;
  90 + diskCache = builder.diskCache;
  91 + memoryCache = builder.memoryCache;
  92 + defaultDisplayImageOptions = builder.defaultDisplayImageOptions;
  93 + downloader = builder.downloader;
  94 + decoder = builder.decoder;
  95 +
  96 + customExecutor = builder.customExecutor;
  97 + customExecutorForCachedImages = builder.customExecutorForCachedImages;
  98 +
  99 + networkDeniedDownloader = new NetworkDeniedImageDownloader(downloader);
  100 + slowNetworkDownloader = new SlowNetworkImageDownloader(downloader);
  101 +
  102 + L.writeDebugLogs(builder.writeLogs);
  103 + }
  104 +
  105 + /**
  106 + * Creates default configuration for {@link ImageLoader} <br />
  107 + * <b>Default values:</b>
  108 + * <ul>
  109 + * <li>maxImageWidthForMemoryCache = device's screen width</li>
  110 + * <li>maxImageHeightForMemoryCache = device's screen height</li>
  111 + * <li>maxImageWidthForDikcCache = unlimited</li>
  112 + * <li>maxImageHeightForDiskCache = unlimited</li>
  113 + * <li>threadPoolSize = {@link Builder#DEFAULT_THREAD_POOL_SIZE this}</li>
  114 + * <li>threadPriority = {@link Builder#DEFAULT_THREAD_PRIORITY this}</li>
  115 + * <li>allow to cache different sizes of image in memory</li>
  116 + * <li>memoryCache = {@link DefaultConfigurationFactory#createMemoryCache(Context, int)}</li>
  117 + * <li>diskCache = {@link com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache}</li>
  118 + * <li>imageDownloader = {@link DefaultConfigurationFactory#createImageDownloader(Context)}</li>
  119 + * <li>imageDecoder = {@link DefaultConfigurationFactory#createImageDecoder(boolean)}</li>
  120 + * <li>diskCacheFileNameGenerator = {@link DefaultConfigurationFactory#createFileNameGenerator()}</li>
  121 + * <li>defaultDisplayImageOptions = {@link DisplayImageOptions#createSimple() Simple options}</li>
  122 + * <li>tasksProcessingOrder = {@link QueueProcessingType#FIFO}</li>
  123 + * <li>detailed logging disabled</li>
  124 + * </ul>
  125 + */
  126 + public static ImageLoaderConfiguration createDefault(Context context) {
  127 + return new Builder(context).build();
  128 + }
  129 +
  130 + ImageSize getMaxImageSize() {
  131 + DisplayMetrics displayMetrics = resources.getDisplayMetrics();
  132 +
  133 + int width = maxImageWidthForMemoryCache;
  134 + if (width <= 0) {
  135 + width = displayMetrics.widthPixels;
  136 + }
  137 + int height = maxImageHeightForMemoryCache;
  138 + if (height <= 0) {
  139 + height = displayMetrics.heightPixels;
  140 + }
  141 + return new ImageSize(width, height);
  142 + }
  143 +
  144 + /**
  145 + * Builder for {@link ImageLoaderConfiguration}
  146 + *
  147 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  148 + */
  149 + public static class Builder {
  150 +
  151 + private static final String WARNING_OVERLAP_DISK_CACHE_PARAMS = "diskCache(), diskCacheSize() and diskCacheFileCount calls overlap each other";
  152 + private static final String WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR = "diskCache() and diskCacheFileNameGenerator() calls overlap each other";
  153 + private static final String WARNING_OVERLAP_MEMORY_CACHE = "memoryCache() and memoryCacheSize() calls overlap each other";
  154 + private static final String WARNING_OVERLAP_EXECUTOR = "threadPoolSize(), threadPriority() and tasksProcessingOrder() calls "
  155 + + "can overlap taskExecutor() and taskExecutorForCachedImages() calls.";
  156 +
  157 + /** {@value} */
  158 + public static final int DEFAULT_THREAD_POOL_SIZE = 3;
  159 + /** {@value} */
  160 + public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 2;
  161 + /** {@value} */
  162 + public static final QueueProcessingType DEFAULT_TASK_PROCESSING_TYPE = QueueProcessingType.FIFO;
  163 +
  164 + private Context context;
  165 +
  166 + private int maxImageWidthForMemoryCache = 0;
  167 + private int maxImageHeightForMemoryCache = 0;
  168 + private int maxImageWidthForDiskCache = 0;
  169 + private int maxImageHeightForDiskCache = 0;
  170 + private BitmapProcessor processorForDiskCache = null;
  171 +
  172 + private Executor taskExecutor = null;
  173 + private Executor taskExecutorForCachedImages = null;
  174 + private boolean customExecutor = false;
  175 + private boolean customExecutorForCachedImages = false;
  176 +
  177 + private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;
  178 + private int threadPriority = DEFAULT_THREAD_PRIORITY;
  179 + private boolean denyCacheImageMultipleSizesInMemory = false;
  180 + private QueueProcessingType tasksProcessingType = DEFAULT_TASK_PROCESSING_TYPE;
  181 +
  182 + private int memoryCacheSize = 0;
  183 + private long diskCacheSize = 0;
  184 + private int diskCacheFileCount = 0;
  185 +
  186 + private MemoryCache memoryCache = null;
  187 + private DiskCache diskCache = null;
  188 + private FileNameGenerator diskCacheFileNameGenerator = null;
  189 + private ImageDownloader downloader = null;
  190 + private ImageDecoder decoder;
  191 + private DisplayImageOptions defaultDisplayImageOptions = null;
  192 +
  193 + private boolean writeLogs = false;
  194 +
  195 + public Builder(Context context) {
  196 + this.context = context.getApplicationContext();
  197 + }
  198 +
  199 + /**
  200 + * Sets options for memory cache
  201 + *
  202 + * @param maxImageWidthForMemoryCache Maximum image width which will be used for memory saving during decoding
  203 + * an image to {@link android.graphics.Bitmap Bitmap}. <b>Default value - device's screen width</b>
  204 + * @param maxImageHeightForMemoryCache Maximum image height which will be used for memory saving during decoding
  205 + * an image to {@link android.graphics.Bitmap Bitmap}. <b>Default value</b> - device's screen height
  206 + */
  207 + public Builder memoryCacheExtraOptions(int maxImageWidthForMemoryCache, int maxImageHeightForMemoryCache) {
  208 + this.maxImageWidthForMemoryCache = maxImageWidthForMemoryCache;
  209 + this.maxImageHeightForMemoryCache = maxImageHeightForMemoryCache;
  210 + return this;
  211 + }
  212 +
  213 + /**
  214 + * @deprecated Use
  215 + * {@link #diskCacheExtraOptions(int, int, BitmapProcessor)}
  216 + * instead
  217 + */
  218 + @Deprecated
  219 + public Builder discCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
  220 + BitmapProcessor processorForDiskCache) {
  221 + return diskCacheExtraOptions(maxImageWidthForDiskCache, maxImageHeightForDiskCache, processorForDiskCache);
  222 + }
  223 +
  224 + /**
  225 + * Sets options for resizing/compressing of downloaded images before saving to disk cache.<br />
  226 + * <b>NOTE: Use this option only when you have appropriate needs. It can make ImageLoader slower.</b>
  227 + *
  228 + * @param maxImageWidthForDiskCache Maximum width of downloaded images for saving at disk cache
  229 + * @param maxImageHeightForDiskCache Maximum height of downloaded images for saving at disk cache
  230 + * @param processorForDiskCache null-ok; {@linkplain BitmapProcessor Bitmap processor} which process images before saving them in disc cache
  231 + */
  232 + public Builder diskCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
  233 + BitmapProcessor processorForDiskCache) {
  234 + this.maxImageWidthForDiskCache = maxImageWidthForDiskCache;
  235 + this.maxImageHeightForDiskCache = maxImageHeightForDiskCache;
  236 + this.processorForDiskCache = processorForDiskCache;
  237 + return this;
  238 + }
  239 +
  240 + /**
  241 + * Sets custom {@linkplain Executor executor} for tasks of loading and displaying images.<br />
  242 + * <br />
  243 + * <b>NOTE:</b> If you set custom executor then following configuration options will not be considered for this
  244 + * executor:
  245 + * <ul>
  246 + * <li>{@link #threadPoolSize(int)}</li>
  247 + * <li>{@link #threadPriority(int)}</li>
  248 + * <li>{@link #tasksProcessingOrder(QueueProcessingType)}</li>
  249 + * </ul>
  250 + *
  251 + * @see #taskExecutorForCachedImages(Executor)
  252 + */
  253 + public Builder taskExecutor(Executor executor) {
  254 + if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
  255 + L.w(WARNING_OVERLAP_EXECUTOR);
  256 + }
  257 +
  258 + this.taskExecutor = executor;
  259 + return this;
  260 + }
  261 +
  262 + /**
  263 + * Sets custom {@linkplain Executor executor} for tasks of displaying <b>cached on disk</b> images (these tasks
  264 + * are executed quickly so UIL prefer to use separate executor for them).<br />
  265 + * <br />
  266 + * If you set the same executor for {@linkplain #taskExecutor(Executor) general tasks} and
  267 + * tasks about cached images (this method) then these tasks will be in the
  268 + * same thread pool. So short-lived tasks can wait a long time for their turn.<br />
  269 + * <br />
  270 + * <b>NOTE:</b> If you set custom executor then following configuration options will not be considered for this
  271 + * executor:
  272 + * <ul>
  273 + * <li>{@link #threadPoolSize(int)}</li>
  274 + * <li>{@link #threadPriority(int)}</li>
  275 + * <li>{@link #tasksProcessingOrder(QueueProcessingType)}</li>
  276 + * </ul>
  277 + *
  278 + * @see #taskExecutor(Executor)
  279 + */
  280 + public Builder taskExecutorForCachedImages(Executor executorForCachedImages) {
  281 + if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
  282 + L.w(WARNING_OVERLAP_EXECUTOR);
  283 + }
  284 +
  285 + this.taskExecutorForCachedImages = executorForCachedImages;
  286 + return this;
  287 + }
  288 +
  289 + /**
  290 + * Sets thread pool size for image display tasks.<br />
  291 + * Default value - {@link #DEFAULT_THREAD_POOL_SIZE this}
  292 + */
  293 + public Builder threadPoolSize(int threadPoolSize) {
  294 + if (taskExecutor != null || taskExecutorForCachedImages != null) {
  295 + L.w(WARNING_OVERLAP_EXECUTOR);
  296 + }
  297 +
  298 + this.threadPoolSize = threadPoolSize;
  299 + return this;
  300 + }
  301 +
  302 + /**
  303 + * Sets the priority for image loading threads. Should be <b>NOT</b> greater than {@link Thread#MAX_PRIORITY} or
  304 + * less than {@link Thread#MIN_PRIORITY}<br />
  305 + * Default value - {@link #DEFAULT_THREAD_PRIORITY this}
  306 + */
  307 + public Builder threadPriority(int threadPriority) {
  308 + if (taskExecutor != null || taskExecutorForCachedImages != null) {
  309 + L.w(WARNING_OVERLAP_EXECUTOR);
  310 + }
  311 +
  312 + if (threadPriority < Thread.MIN_PRIORITY) {
  313 + this.threadPriority = Thread.MIN_PRIORITY;
  314 + } else {
  315 + if (threadPriority > Thread.MAX_PRIORITY) {
  316 + this.threadPriority = Thread.MAX_PRIORITY;
  317 + } else {
  318 + this.threadPriority = threadPriority;
  319 + }
  320 + }
  321 + return this;
  322 + }
  323 +
  324 + /**
  325 + * When you display an image in a small {@link android.widget.ImageView ImageView} and later you try to display
  326 + * this image (from identical URI) in a larger {@link android.widget.ImageView ImageView} so decoded image of
  327 + * bigger size will be cached in memory as a previous decoded image of smaller size.<br />
  328 + * So <b>the default behavior is to allow to cache multiple sizes of one image in memory</b>. You can
  329 + * <b>deny</b> it by calling <b>this</b> method: so when some image will be cached in memory then previous
  330 + * cached size of this image (if it exists) will be removed from memory cache before.
  331 + */
  332 + public Builder denyCacheImageMultipleSizesInMemory() {
  333 + this.denyCacheImageMultipleSizesInMemory = true;
  334 + return this;
  335 + }
  336 +
  337 + /**
  338 + * Sets type of queue processing for tasks for loading and displaying images.<br />
  339 + * Default value - {@link QueueProcessingType#FIFO}
  340 + */
  341 + public Builder tasksProcessingOrder(QueueProcessingType tasksProcessingType) {
  342 + if (taskExecutor != null || taskExecutorForCachedImages != null) {
  343 + L.w(WARNING_OVERLAP_EXECUTOR);
  344 + }
  345 +
  346 + this.tasksProcessingType = tasksProcessingType;
  347 + return this;
  348 + }
  349 +
  350 + /**
  351 + * Sets maximum memory cache size for {@link android.graphics.Bitmap bitmaps} (in bytes).<br />
  352 + * Default value - 1/8 of available app memory.<br />
  353 + * <b>NOTE:</b> If you use this method then
  354 + * {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache} will be used as
  355 + * memory cache. You can use {@link #memoryCache(MemoryCache)} method to set your own implementation of
  356 + * {@link MemoryCache}.
  357 + */
  358 + public Builder memoryCacheSize(int memoryCacheSize) {
  359 + if (memoryCacheSize <= 0) throw new IllegalArgumentException("memoryCacheSize must be a positive number");
  360 +
  361 + if (memoryCache != null) {
  362 + L.w(WARNING_OVERLAP_MEMORY_CACHE);
  363 + }
  364 +
  365 + this.memoryCacheSize = memoryCacheSize;
  366 + return this;
  367 + }
  368 +
  369 + /**
  370 + * Sets maximum memory cache size (in percent of available app memory) for {@link android.graphics.Bitmap
  371 + * bitmaps}.<br />
  372 + * Default value - 1/8 of available app memory.<br />
  373 + * <b>NOTE:</b> If you use this method then
  374 + * {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache} will be used as
  375 + * memory cache. You can use {@link #memoryCache(MemoryCache)} method to set your own implementation of
  376 + * {@link MemoryCache}.
  377 + */
  378 + public Builder memoryCacheSizePercentage(int availableMemoryPercent) {
  379 + if (availableMemoryPercent <= 0 || availableMemoryPercent >= 100) {
  380 + throw new IllegalArgumentException("availableMemoryPercent must be in range (0 < % < 100)");
  381 + }
  382 +
  383 + if (memoryCache != null) {
  384 + L.w(WARNING_OVERLAP_MEMORY_CACHE);
  385 + }
  386 +
  387 + long availableMemory = Runtime.getRuntime().maxMemory();
  388 + memoryCacheSize = (int) (availableMemory * (availableMemoryPercent / 100f));
  389 + return this;
  390 + }
  391 +
  392 + /**
  393 + * Sets memory cache for {@link android.graphics.Bitmap bitmaps}.<br />
  394 + * Default value - {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache}
  395 + * with limited memory cache size (size = 1/8 of available app memory)<br />
  396 + * <br />
  397 + * <b>NOTE:</b> If you set custom memory cache then following configuration option will not be considered:
  398 + * <ul>
  399 + * <li>{@link #memoryCacheSize(int)}</li>
  400 + * </ul>
  401 + */
  402 + public Builder memoryCache(MemoryCache memoryCache) {
  403 + if (memoryCacheSize != 0) {
  404 + L.w(WARNING_OVERLAP_MEMORY_CACHE);
  405 + }
  406 +
  407 + this.memoryCache = memoryCache;
  408 + return this;
  409 + }
  410 +
  411 + /** @deprecated Use {@link #diskCacheSize(int)} instead */
  412 + @Deprecated
  413 + public Builder discCacheSize(int maxCacheSize) {
  414 + return diskCacheSize(maxCacheSize);
  415 + }
  416 +
  417 + /**
  418 + * Sets maximum disk cache size for images (in bytes).<br />
  419 + * By default: disk cache is unlimited.<br />
  420 + * <b>NOTE:</b> If you use this method then
  421 + * {@link com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache LruDiskCache}
  422 + * will be used as disk cache. You can use {@link #diskCache(DiskCache)} method for introduction your own
  423 + * implementation of {@link DiskCache}
  424 + */
  425 + public Builder diskCacheSize(int maxCacheSize) {
  426 + if (maxCacheSize <= 0) throw new IllegalArgumentException("maxCacheSize must be a positive number");
  427 +
  428 + if (diskCache != null) {
  429 + L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS);
  430 + }
  431 +
  432 + this.diskCacheSize = maxCacheSize;
  433 + return this;
  434 + }
  435 +
  436 + /** @deprecated Use {@link #diskCacheFileCount(int)} instead */
  437 + @Deprecated
  438 + public Builder discCacheFileCount(int maxFileCount) {
  439 + return diskCacheFileCount(maxFileCount);
  440 + }
  441 +
  442 + /**
  443 + * Sets maximum file count in disk cache directory.<br />
  444 + * By default: disk cache is unlimited.<br />
  445 + * <b>NOTE:</b> If you use this method then
  446 + * {@link com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache LruDiskCache}
  447 + * will be used as disk cache. You can use {@link #diskCache(DiskCache)} method for introduction your own
  448 + * implementation of {@link DiskCache}
  449 + */
  450 + public Builder diskCacheFileCount(int maxFileCount) {
  451 + if (maxFileCount <= 0) throw new IllegalArgumentException("maxFileCount must be a positive number");
  452 +
  453 + if (diskCache != null) {
  454 + L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS);
  455 + }
  456 +
  457 + this.diskCacheFileCount = maxFileCount;
  458 + return this;
  459 + }
  460 +
  461 + /** @deprecated Use {@link #diskCacheFileNameGenerator(FileNameGenerator)} */
  462 + @Deprecated
  463 + public Builder discCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
  464 + return diskCacheFileNameGenerator(fileNameGenerator);
  465 + }
  466 +
  467 + /**
  468 + * Sets name generator for files cached in disk cache.<br />
  469 + * Default value -
  470 + * {@link DefaultConfigurationFactory#createFileNameGenerator()
  471 + * DefaultConfigurationFactory.createFileNameGenerator()}
  472 + */
  473 + public Builder diskCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
  474 + if (diskCache != null) {
  475 + L.w(WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR);
  476 + }
  477 +
  478 + this.diskCacheFileNameGenerator = fileNameGenerator;
  479 + return this;
  480 + }
  481 +
  482 + /** @deprecated Use {@link #diskCache(DiskCache)} */
  483 + @Deprecated
  484 + public Builder discCache(DiskCache diskCache) {
  485 + return diskCache(diskCache);
  486 + }
  487 +
  488 + /**
  489 + * Sets disk cache for images.<br />
  490 + * Default value - {@link com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache
  491 + * UnlimitedDiskCache}. Cache directory is defined by
  492 + * {@link com.nostra13.universalimageloader.utils.StorageUtils#getCacheDirectory(Context)
  493 + * StorageUtils.getCacheDirectory(Context)}.<br />
  494 + * <br />
  495 + * <b>NOTE:</b> If you set custom disk cache then following configuration option will not be considered:
  496 + * <ul>
  497 + * <li>{@link #diskCacheSize(int)}</li>
  498 + * <li>{@link #diskCacheFileCount(int)}</li>
  499 + * <li>{@link #diskCacheFileNameGenerator(FileNameGenerator)}</li>
  500 + * </ul>
  501 + */
  502 + public Builder diskCache(DiskCache diskCache) {
  503 + if (diskCacheSize > 0 || diskCacheFileCount > 0) {
  504 + L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS);
  505 + }
  506 + if (diskCacheFileNameGenerator != null) {
  507 + L.w(WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR);
  508 + }
  509 +
  510 + this.diskCache = diskCache;
  511 + return this;
  512 + }
  513 +
  514 + /**
  515 + * Sets utility which will be responsible for downloading of image.<br />
  516 + * Default value -
  517 + * {@link DefaultConfigurationFactory#createImageDownloader(Context)
  518 + * DefaultConfigurationFactory.createImageDownloader()}
  519 + */
  520 + public Builder imageDownloader(ImageDownloader imageDownloader) {
  521 + this.downloader = imageDownloader;
  522 + return this;
  523 + }
  524 +
  525 + /**
  526 + * Sets utility which will be responsible for decoding of image stream.<br />
  527 + * Default value -
  528 + * {@link DefaultConfigurationFactory#createImageDecoder(boolean)
  529 + * DefaultConfigurationFactory.createImageDecoder()}
  530 + */
  531 + public Builder imageDecoder(ImageDecoder imageDecoder) {
  532 + this.decoder = imageDecoder;
  533 + return this;
  534 + }
  535 +
  536 + /**
  537 + * Sets default {@linkplain DisplayImageOptions display image options} for image displaying. These options will
  538 + * be used for every {@linkplain ImageLoader#displayImage(String, android.widget.ImageView) image display call}
  539 + * without passing custom {@linkplain DisplayImageOptions options}<br />
  540 + * Default value - {@link DisplayImageOptions#createSimple() Simple options}
  541 + */
  542 + public Builder defaultDisplayImageOptions(DisplayImageOptions defaultDisplayImageOptions) {
  543 + this.defaultDisplayImageOptions = defaultDisplayImageOptions;
  544 + return this;
  545 + }
  546 +
  547 + /**
  548 + * Enables detail logging of {@link ImageLoader} work. To prevent detail logs don't call this method.
  549 + * Consider {@link L#disableLogging()} to disable
  550 + * ImageLoader logging completely (even error logs)
  551 + */
  552 + public Builder writeDebugLogs() {
  553 + this.writeLogs = true;
  554 + return this;
  555 + }
  556 +
  557 + /** Builds configured {@link ImageLoaderConfiguration} object */
  558 + public ImageLoaderConfiguration build() {
  559 + initEmptyFieldsWithDefaultValues();
  560 + return new ImageLoaderConfiguration(this);
  561 + }
  562 +
  563 + private void initEmptyFieldsWithDefaultValues() {
  564 + if (taskExecutor == null) {
  565 + taskExecutor = DefaultConfigurationFactory
  566 + .createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
  567 + } else {
  568 + customExecutor = true;
  569 + }
  570 + if (taskExecutorForCachedImages == null) {
  571 + taskExecutorForCachedImages = DefaultConfigurationFactory
  572 + .createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
  573 + } else {
  574 + customExecutorForCachedImages = true;
  575 + }
  576 + if (diskCache == null) {
  577 + if (diskCacheFileNameGenerator == null) {
  578 + diskCacheFileNameGenerator = DefaultConfigurationFactory.createFileNameGenerator();
  579 + }
  580 + diskCache = DefaultConfigurationFactory
  581 + .createDiskCache(context, diskCacheFileNameGenerator, diskCacheSize, diskCacheFileCount);
  582 + }
  583 + if (memoryCache == null) {
  584 + memoryCache = DefaultConfigurationFactory.createMemoryCache(context, memoryCacheSize);
  585 + }
  586 + if (denyCacheImageMultipleSizesInMemory) {
  587 + memoryCache = new FuzzyKeyMemoryCache(memoryCache, MemoryCacheUtils.createFuzzyKeyComparator());
  588 + }
  589 + if (downloader == null) {
  590 + downloader = DefaultConfigurationFactory.createImageDownloader(context);
  591 + }
  592 + if (decoder == null) {
  593 + decoder = DefaultConfigurationFactory.createImageDecoder(writeLogs);
  594 + }
  595 + if (defaultDisplayImageOptions == null) {
  596 + defaultDisplayImageOptions = DisplayImageOptions.createSimple();
  597 + }
  598 + }
  599 + }
  600 +
  601 + /**
  602 + * Decorator. Prevents downloads from network (throws {@link IllegalStateException exception}).<br />
  603 + * In most cases this downloader shouldn't be used directly.
  604 + *
  605 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  606 + * @since 1.8.0
  607 + */
  608 + private static class NetworkDeniedImageDownloader implements ImageDownloader {
  609 +
  610 + private final ImageDownloader wrappedDownloader;
  611 +
  612 + public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) {
  613 + this.wrappedDownloader = wrappedDownloader;
  614 + }
  615 +
  616 + @Override
  617 + public InputStream getStream(String imageUri, Object extra) throws IOException {
  618 + switch (Scheme.ofUri(imageUri)) {
  619 + case HTTP:
  620 + case HTTPS:
  621 + throw new IllegalStateException();
  622 + default:
  623 + return wrappedDownloader.getStream(imageUri, extra);
  624 + }
  625 + }
  626 + }
  627 +
  628 + /**
  629 + * Decorator. Handles <a href="http://code.google.com/p/android/issues/detail?id=6066">this problem</a> on slow networks
  630 + * using {@link FlushedInputStream}.
  631 + *
  632 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  633 + * @since 1.8.1
  634 + */
  635 + private static class SlowNetworkImageDownloader implements ImageDownloader {
  636 +
  637 + private final ImageDownloader wrappedDownloader;
  638 +
  639 + public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) {
  640 + this.wrappedDownloader = wrappedDownloader;
  641 + }
  642 +
  643 + @Override
  644 + public InputStream getStream(String imageUri, Object extra) throws IOException {
  645 + InputStream imageStream = wrappedDownloader.getStream(imageUri, extra);
  646 + switch (Scheme.ofUri(imageUri)) {
  647 + case HTTP:
  648 + case HTTPS:
  649 + return new FlushedInputStream(imageStream);
  650 + default:
  651 + return imageStream;
  652 + }
  653 + }
  654 + }
  655 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.view.View;
  19 +import com.nostra13.universalimageloader.core.assist.FailReason;
  20 +import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
  21 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  22 +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  23 +
  24 +import java.io.File;
  25 +import java.util.Collections;
  26 +import java.util.HashMap;
  27 +import java.util.Map;
  28 +import java.util.WeakHashMap;
  29 +import java.util.concurrent.Executor;
  30 +import java.util.concurrent.ExecutorService;
  31 +import java.util.concurrent.atomic.AtomicBoolean;
  32 +import java.util.concurrent.locks.ReentrantLock;
  33 +
  34 +/**
  35 + * {@link ImageLoader} engine which responsible for {@linkplain LoadAndDisplayImageTask display task} execution.
  36 + *
  37 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  38 + * @since 1.7.1
  39 + */
  40 +class ImageLoaderEngine {
  41 +
  42 + final ImageLoaderConfiguration configuration;
  43 +
  44 + private Executor taskExecutor;
  45 + private Executor taskExecutorForCachedImages;
  46 + private Executor taskDistributor;
  47 +
  48 + private final Map<Integer, String> cacheKeysForImageAwares = Collections
  49 + .synchronizedMap(new HashMap<Integer, String>());
  50 + private final Map<String, ReentrantLock> uriLocks = new WeakHashMap<String, ReentrantLock>();
  51 +
  52 + private final AtomicBoolean paused = new AtomicBoolean(false);
  53 + private final AtomicBoolean networkDenied = new AtomicBoolean(false);
  54 + private final AtomicBoolean slowNetwork = new AtomicBoolean(false);
  55 +
  56 + private final Object pauseLock = new Object();
  57 +
  58 + ImageLoaderEngine(ImageLoaderConfiguration configuration) {
  59 + this.configuration = configuration;
  60 +
  61 + taskExecutor = configuration.taskExecutor;
  62 + taskExecutorForCachedImages = configuration.taskExecutorForCachedImages;
  63 +
  64 + taskDistributor = DefaultConfigurationFactory.createTaskDistributor();
  65 + }
  66 +
  67 + /** Submits task to execution pool */
  68 + void submit(final LoadAndDisplayImageTask task) {
  69 + taskDistributor.execute(new Runnable() {
  70 + @Override
  71 + public void run() {
  72 + File image = configuration.diskCache.get(task.getLoadingUri());
  73 + boolean isImageCachedOnDisk = image != null && image.exists();
  74 + initExecutorsIfNeed();
  75 + if (isImageCachedOnDisk) {
  76 + taskExecutorForCachedImages.execute(task);
  77 + } else {
  78 + taskExecutor.execute(task);
  79 + }
  80 + }
  81 + });
  82 + }
  83 +
  84 + /** Submits task to execution pool */
  85 + void submit(ProcessAndDisplayImageTask task) {
  86 + initExecutorsIfNeed();
  87 + taskExecutorForCachedImages.execute(task);
  88 + }
  89 +
  90 + private void initExecutorsIfNeed() {
  91 + if (!configuration.customExecutor && ((ExecutorService) taskExecutor).isShutdown()) {
  92 + taskExecutor = createTaskExecutor();
  93 + }
  94 + if (!configuration.customExecutorForCachedImages && ((ExecutorService) taskExecutorForCachedImages)
  95 + .isShutdown()) {
  96 + taskExecutorForCachedImages = createTaskExecutor();
  97 + }
  98 + }
  99 +
  100 + private Executor createTaskExecutor() {
  101 + return DefaultConfigurationFactory
  102 + .createExecutor(configuration.threadPoolSize, configuration.threadPriority,
  103 + configuration.tasksProcessingType);
  104 + }
  105 +
  106 + /**
  107 + * Returns URI of image which is loading at this moment into passed {@link ImageAware}
  108 + */
  109 + String getLoadingUriForView(ImageAware imageAware) {
  110 + return cacheKeysForImageAwares.get(imageAware.getId());
  111 + }
  112 +
  113 + /**
  114 + * Associates <b>memoryCacheKey</b> with <b>imageAware</b>. Then it helps to define image URI is loaded into View at
  115 + * exact moment.
  116 + */
  117 + void prepareDisplayTaskFor(ImageAware imageAware, String memoryCacheKey) {
  118 + cacheKeysForImageAwares.put(imageAware.getId(), memoryCacheKey);
  119 + }
  120 +
  121 + /**
  122 + * Cancels the task of loading and displaying image for incoming <b>imageAware</b>.
  123 + *
  124 + * @param imageAware {@link ImageAware} for which display task
  125 + * will be cancelled
  126 + */
  127 + void cancelDisplayTaskFor(ImageAware imageAware) {
  128 + cacheKeysForImageAwares.remove(imageAware.getId());
  129 + }
  130 +
  131 + /**
  132 + * Denies or allows engine to download images from the network.<br /> <br /> If downloads are denied and if image
  133 + * isn't cached then {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired
  134 + * with {@link FailReason.FailType#NETWORK_DENIED}
  135 + *
  136 + * @param denyNetworkDownloads pass <b>true</b> - to deny engine to download images from the network; <b>false</b> -
  137 + * to allow engine to download images from network.
  138 + */
  139 + void denyNetworkDownloads(boolean denyNetworkDownloads) {
  140 + networkDenied.set(denyNetworkDownloads);
  141 + }
  142 +
  143 + /**
  144 + * Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle <a
  145 + * href="http://code.google.com/p/android/issues/detail?id=6066">this known problem</a> or not.
  146 + *
  147 + * @param handleSlowNetwork pass <b>true</b> - to use {@link FlushedInputStream} for network downloads; <b>false</b>
  148 + * - otherwise.
  149 + */
  150 + void handleSlowNetwork(boolean handleSlowNetwork) {
  151 + slowNetwork.set(handleSlowNetwork);
  152 + }
  153 +
  154 + /**
  155 + * Pauses engine. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.<br
  156 + * /> Already running tasks are not paused.
  157 + */
  158 + void pause() {
  159 + paused.set(true);
  160 + }
  161 +
  162 + /** Resumes engine work. Paused "load&display" tasks will continue its work. */
  163 + void resume() {
  164 + paused.set(false);
  165 + synchronized (pauseLock) {
  166 + pauseLock.notifyAll();
  167 + }
  168 + }
  169 +
  170 + /**
  171 + * Stops engine, cancels all running and scheduled display image tasks. Clears internal data.
  172 + * <br />
  173 + * <b>NOTE:</b> This method doesn't shutdown
  174 + * {@linkplain ImageLoaderConfiguration.Builder#taskExecutor(Executor)
  175 + * custom task executors} if you set them.
  176 + */
  177 + void stop() {
  178 + if (!configuration.customExecutor) {
  179 + ((ExecutorService) taskExecutor).shutdownNow();
  180 + }
  181 + if (!configuration.customExecutorForCachedImages) {
  182 + ((ExecutorService) taskExecutorForCachedImages).shutdownNow();
  183 + }
  184 +
  185 + cacheKeysForImageAwares.clear();
  186 + uriLocks.clear();
  187 + }
  188 +
  189 + void fireCallback(Runnable r) {
  190 + taskDistributor.execute(r);
  191 + }
  192 +
  193 + ReentrantLock getLockForUri(String uri) {
  194 + ReentrantLock lock = uriLocks.get(uri);
  195 + if (lock == null) {
  196 + lock = new ReentrantLock();
  197 + uriLocks.put(uri, lock);
  198 + }
  199 + return lock;
  200 + }
  201 +
  202 + AtomicBoolean getPause() {
  203 + return paused;
  204 + }
  205 +
  206 + Object getPauseLock() {
  207 + return pauseLock;
  208 + }
  209 +
  210 + boolean isNetworkDenied() {
  211 + return networkDenied.get();
  212 + }
  213 +
  214 + boolean isSlowNetwork() {
  215 + return slowNetwork.get();
  216 + }
  217 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  19 +import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
  20 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  21 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  22 +
  23 +import java.util.concurrent.locks.ReentrantLock;
  24 +
  25 +/**
  26 + * Information for load'n'display image task
  27 + *
  28 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  29 + * @see com.nostra13.universalimageloader.utils.MemoryCacheUtils
  30 + * @see DisplayImageOptions
  31 + * @see ImageLoadingListener
  32 + * @see ImageLoadingProgressListener
  33 + * @since 1.3.1
  34 + */
  35 +final class ImageLoadingInfo {
  36 +
  37 + final String uri;
  38 + final String memoryCacheKey;
  39 + final ImageAware imageAware;
  40 + final ImageSize targetSize;
  41 + final DisplayImageOptions options;
  42 + final ImageLoadingListener listener;
  43 + final ImageLoadingProgressListener progressListener;
  44 + final ReentrantLock loadFromUriLock;
  45 +
  46 + public ImageLoadingInfo(String uri, ImageAware imageAware, ImageSize targetSize, String memoryCacheKey,
  47 + DisplayImageOptions options, ImageLoadingListener listener,
  48 + ImageLoadingProgressListener progressListener, ReentrantLock loadFromUriLock) {
  49 + this.uri = uri;
  50 + this.imageAware = imageAware;
  51 + this.targetSize = targetSize;
  52 + this.options = options;
  53 + this.listener = listener;
  54 + this.progressListener = progressListener;
  55 + this.loadFromUriLock = loadFromUriLock;
  56 + this.memoryCacheKey = memoryCacheKey;
  57 + }
  58 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.os.Handler;
  20 +import com.nostra13.universalimageloader.core.assist.FailReason;
  21 +import com.nostra13.universalimageloader.core.assist.FailReason.FailType;
  22 +import com.nostra13.universalimageloader.core.assist.ImageScaleType;
  23 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  24 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  25 +import com.nostra13.universalimageloader.core.assist.ViewScaleType;
  26 +import com.nostra13.universalimageloader.core.decode.ImageDecoder;
  27 +import com.nostra13.universalimageloader.core.decode.ImageDecodingInfo;
  28 +import com.nostra13.universalimageloader.core.download.ImageDownloader;
  29 +import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme;
  30 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  31 +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  32 +import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
  33 +import com.nostra13.universalimageloader.utils.IoUtils;
  34 +import com.nostra13.universalimageloader.utils.L;
  35 +
  36 +import java.io.File;
  37 +import java.io.IOException;
  38 +import java.io.InputStream;
  39 +import java.util.concurrent.atomic.AtomicBoolean;
  40 +import java.util.concurrent.locks.ReentrantLock;
  41 +
  42 +/**
  43 + * Presents load'n'display image task. Used to load image from Internet or file system, decode it to {@link Bitmap}, and
  44 + * display it in {@link ImageAware} using {@link DisplayBitmapTask}.
  45 + *
  46 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  47 + * @see ImageLoaderConfiguration
  48 + * @see ImageLoadingInfo
  49 + * @since 1.3.1
  50 + */
  51 +final class LoadAndDisplayImageTask implements Runnable, IoUtils.CopyListener {
  52 +
  53 + private static final String LOG_WAITING_FOR_RESUME = "ImageLoader is paused. Waiting... [%s]";
  54 + private static final String LOG_RESUME_AFTER_PAUSE = ".. Resume loading [%s]";
  55 + private static final String LOG_DELAY_BEFORE_LOADING = "Delay %d ms before loading... [%s]";
  56 + private static final String LOG_START_DISPLAY_IMAGE_TASK = "Start display image task [%s]";
  57 + private static final String LOG_WAITING_FOR_IMAGE_LOADED = "Image already is loading. Waiting... [%s]";
  58 + private static final String LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING = "...Get cached bitmap from memory after waiting. [%s]";
  59 + private static final String LOG_LOAD_IMAGE_FROM_NETWORK = "Load image from network [%s]";
  60 + private static final String LOG_LOAD_IMAGE_FROM_DISK_CACHE = "Load image from disk cache [%s]";
  61 + private static final String LOG_RESIZE_CACHED_IMAGE_FILE = "Resize image in disk cache [%s]";
  62 + private static final String LOG_PREPROCESS_IMAGE = "PreProcess image before caching in memory [%s]";
  63 + private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
  64 + private static final String LOG_CACHE_IMAGE_IN_MEMORY = "Cache image in memory [%s]";
  65 + private static final String LOG_CACHE_IMAGE_ON_DISK = "Cache image on disk [%s]";
  66 + private static final String LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK = "Process image before cache on disk [%s]";
  67 + private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
  68 + private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";
  69 + private static final String LOG_TASK_INTERRUPTED = "Task was interrupted [%s]";
  70 +
  71 + private static final String ERROR_NO_IMAGE_STREAM = "No stream for image [%s]";
  72 + private static final String ERROR_PRE_PROCESSOR_NULL = "Pre-processor returned null [%s]";
  73 + private static final String ERROR_POST_PROCESSOR_NULL = "Post-processor returned null [%s]";
  74 + private static final String ERROR_PROCESSOR_FOR_DISK_CACHE_NULL = "Bitmap processor for disk cache returned null [%s]";
  75 +
  76 + private final ImageLoaderEngine engine;
  77 + private final ImageLoadingInfo imageLoadingInfo;
  78 + private final Handler handler;
  79 +
  80 + // Helper references
  81 + private final ImageLoaderConfiguration configuration;
  82 + private final ImageDownloader downloader;
  83 + private final ImageDownloader networkDeniedDownloader;
  84 + private final ImageDownloader slowNetworkDownloader;
  85 + private final ImageDecoder decoder;
  86 + final String uri;
  87 + private final String memoryCacheKey;
  88 + final ImageAware imageAware;
  89 + private final ImageSize targetSize;
  90 + final DisplayImageOptions options;
  91 + final ImageLoadingListener listener;
  92 + final ImageLoadingProgressListener progressListener;
  93 + private final boolean syncLoading;
  94 +
  95 + // State vars
  96 + private LoadedFrom loadedFrom = LoadedFrom.NETWORK;
  97 +
  98 + public LoadAndDisplayImageTask(ImageLoaderEngine engine, ImageLoadingInfo imageLoadingInfo, Handler handler) {
  99 + this.engine = engine;
  100 + this.imageLoadingInfo = imageLoadingInfo;
  101 + this.handler = handler;
  102 +
  103 + configuration = engine.configuration;
  104 + downloader = configuration.downloader;
  105 + networkDeniedDownloader = configuration.networkDeniedDownloader;
  106 + slowNetworkDownloader = configuration.slowNetworkDownloader;
  107 + decoder = configuration.decoder;
  108 + uri = imageLoadingInfo.uri;
  109 + memoryCacheKey = imageLoadingInfo.memoryCacheKey;
  110 + imageAware = imageLoadingInfo.imageAware;
  111 + targetSize = imageLoadingInfo.targetSize;
  112 + options = imageLoadingInfo.options;
  113 + listener = imageLoadingInfo.listener;
  114 + progressListener = imageLoadingInfo.progressListener;
  115 + syncLoading = options.isSyncLoading();
  116 + }
  117 +
  118 + @Override
  119 + public void run() {
  120 + if (waitIfPaused()) return;
  121 + if (delayIfNeed()) return;
  122 +
  123 + ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
  124 + L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
  125 + if (loadFromUriLock.isLocked()) {
  126 + L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
  127 + }
  128 +
  129 + loadFromUriLock.lock();
  130 + Bitmap bmp;
  131 + try {
  132 + checkTaskNotActual();
  133 +
  134 + bmp = configuration.memoryCache.get(memoryCacheKey);
  135 + if (bmp == null || bmp.isRecycled()) {
  136 + bmp = tryLoadBitmap();
  137 + if (bmp == null) return; // listener callback already was fired
  138 +
  139 + checkTaskNotActual();
  140 + checkTaskInterrupted();
  141 +
  142 + if (options.shouldPreProcess()) {
  143 + L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey);
  144 + bmp = options.getPreProcessor().process(bmp);
  145 + if (bmp == null) {
  146 + L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);
  147 + }
  148 + }
  149 +
  150 + if (bmp != null && options.isCacheInMemory()) {
  151 + L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
  152 + configuration.memoryCache.put(memoryCacheKey, bmp);
  153 + }
  154 + } else {
  155 + loadedFrom = LoadedFrom.MEMORY_CACHE;
  156 + L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);
  157 + }
  158 +
  159 + if (bmp != null && options.shouldPostProcess()) {
  160 + L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey);
  161 + bmp = options.getPostProcessor().process(bmp);
  162 + if (bmp == null) {
  163 + L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
  164 + }
  165 + }
  166 + checkTaskNotActual();
  167 + checkTaskInterrupted();
  168 + } catch (TaskCancelledException e) {
  169 + fireCancelEvent();
  170 + return;
  171 + } finally {
  172 + loadFromUriLock.unlock();
  173 + }
  174 +
  175 + DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
  176 + runTask(displayBitmapTask, syncLoading, handler, engine);
  177 + }
  178 +
  179 + /** @return <b>true</b> - if task should be interrupted; <b>false</b> - otherwise */
  180 + private boolean waitIfPaused() {
  181 + AtomicBoolean pause = engine.getPause();
  182 + if (pause.get()) {
  183 + synchronized (engine.getPauseLock()) {
  184 + if (pause.get()) {
  185 + L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey);
  186 + try {
  187 + engine.getPauseLock().wait();
  188 + } catch (InterruptedException e) {
  189 + L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
  190 + return true;
  191 + }
  192 + L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
  193 + }
  194 + }
  195 + }
  196 + return isTaskNotActual();
  197 + }
  198 +
  199 + /** @return <b>true</b> - if task should be interrupted; <b>false</b> - otherwise */
  200 + private boolean delayIfNeed() {
  201 + if (options.shouldDelayBeforeLoading()) {
  202 + L.d(LOG_DELAY_BEFORE_LOADING, options.getDelayBeforeLoading(), memoryCacheKey);
  203 + try {
  204 + Thread.sleep(options.getDelayBeforeLoading());
  205 + } catch (InterruptedException e) {
  206 + L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
  207 + return true;
  208 + }
  209 + return isTaskNotActual();
  210 + }
  211 + return false;
  212 + }
  213 +
  214 + private Bitmap tryLoadBitmap() throws TaskCancelledException {
  215 + Bitmap bitmap = null;
  216 + try {
  217 + File imageFile = configuration.diskCache.get(uri);
  218 + if (imageFile != null && imageFile.exists() && imageFile.length() > 0) {
  219 + L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);
  220 + loadedFrom = LoadedFrom.DISC_CACHE;
  221 +
  222 + checkTaskNotActual();
  223 + bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
  224 + }
  225 + if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
  226 + L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);
  227 + loadedFrom = LoadedFrom.NETWORK;
  228 +
  229 + String imageUriForDecoding = uri;
  230 + if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
  231 + imageFile = configuration.diskCache.get(uri);
  232 + if (imageFile != null) {
  233 + imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
  234 + }
  235 + }
  236 +
  237 + checkTaskNotActual();
  238 + bitmap = decodeImage(imageUriForDecoding);
  239 +
  240 + if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
  241 + fireFailEvent(FailType.DECODING_ERROR, null);
  242 + }
  243 + }
  244 + } catch (IllegalStateException e) {
  245 + fireFailEvent(FailType.NETWORK_DENIED, null);
  246 + } catch (TaskCancelledException e) {
  247 + throw e;
  248 + } catch (IOException e) {
  249 + L.e(e);
  250 + fireFailEvent(FailType.IO_ERROR, e);
  251 + } catch (OutOfMemoryError e) {
  252 + L.e(e);
  253 + fireFailEvent(FailType.OUT_OF_MEMORY, e);
  254 + } catch (Throwable e) {
  255 + L.e(e);
  256 + fireFailEvent(FailType.UNKNOWN, e);
  257 + }
  258 + return bitmap;
  259 + }
  260 +
  261 + private Bitmap decodeImage(String imageUri) throws IOException {
  262 + ViewScaleType viewScaleType = imageAware.getScaleType();
  263 + ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,
  264 + getDownloader(), options);
  265 + return decoder.decode(decodingInfo);
  266 + }
  267 +
  268 + /** @return <b>true</b> - if image was downloaded successfully; <b>false</b> - otherwise */
  269 + private boolean tryCacheImageOnDisk() throws TaskCancelledException {
  270 + L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey);
  271 +
  272 + boolean loaded;
  273 + try {
  274 + loaded = downloadImage();
  275 + if (loaded) {
  276 + int width = configuration.maxImageWidthForDiskCache;
  277 + int height = configuration.maxImageHeightForDiskCache;
  278 + if (width > 0 || height > 0) {
  279 + L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey);
  280 + resizeAndSaveImage(width, height); // TODO : process boolean result
  281 + }
  282 + }
  283 + } catch (IOException e) {
  284 + L.e(e);
  285 + loaded = false;
  286 + }
  287 + return loaded;
  288 + }
  289 +
  290 + private boolean downloadImage() throws IOException {
  291 + InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
  292 + if (is == null) {
  293 + L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey);
  294 + return false;
  295 + } else {
  296 + try {
  297 + return configuration.diskCache.save(uri, is, this);
  298 + } finally {
  299 + IoUtils.closeSilently(is);
  300 + }
  301 + }
  302 + }
  303 +
  304 + /** Decodes image file into Bitmap, resize it and save it back */
  305 + private boolean resizeAndSaveImage(int maxWidth, int maxHeight) throws IOException {
  306 + // Decode image file, compress and re-save it
  307 + boolean saved = false;
  308 + File targetFile = configuration.diskCache.get(uri);
  309 + if (targetFile != null && targetFile.exists()) {
  310 + ImageSize targetImageSize = new ImageSize(maxWidth, maxHeight);
  311 + DisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options)
  312 + .imageScaleType(ImageScaleType.IN_SAMPLE_INT).build();
  313 + ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey,
  314 + Scheme.FILE.wrap(targetFile.getAbsolutePath()), uri, targetImageSize, ViewScaleType.FIT_INSIDE,
  315 + getDownloader(), specialOptions);
  316 + Bitmap bmp = decoder.decode(decodingInfo);
  317 + if (bmp != null && configuration.processorForDiskCache != null) {
  318 + L.d(LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK, memoryCacheKey);
  319 + bmp = configuration.processorForDiskCache.process(bmp);
  320 + if (bmp == null) {
  321 + L.e(ERROR_PROCESSOR_FOR_DISK_CACHE_NULL, memoryCacheKey);
  322 + }
  323 + }
  324 + if (bmp != null) {
  325 + saved = configuration.diskCache.save(uri, bmp);
  326 + bmp.recycle();
  327 + }
  328 + }
  329 + return saved;
  330 + }
  331 +
  332 + @Override
  333 + public boolean onBytesCopied(int current, int total) {
  334 + return syncLoading || fireProgressEvent(current, total);
  335 + }
  336 +
  337 + /** @return <b>true</b> - if loading should be continued; <b>false</b> - if loading should be interrupted */
  338 + private boolean fireProgressEvent(final int current, final int total) {
  339 + if (isTaskInterrupted() || isTaskNotActual()) return false;
  340 + if (progressListener != null) {
  341 + Runnable r = new Runnable() {
  342 + @Override
  343 + public void run() {
  344 + progressListener.onProgressUpdate(uri, imageAware.getWrappedView(), current, total);
  345 + }
  346 + };
  347 + runTask(r, false, handler, engine);
  348 + }
  349 + return true;
  350 + }
  351 +
  352 + private void fireFailEvent(final FailType failType, final Throwable failCause) {
  353 + if (syncLoading || isTaskInterrupted() || isTaskNotActual()) return;
  354 + Runnable r = new Runnable() {
  355 + @Override
  356 + public void run() {
  357 + if (options.shouldShowImageOnFail()) {
  358 + imageAware.setImageDrawable(options.getImageOnFail(configuration.resources));
  359 + }
  360 + listener.onLoadingFailed(uri, imageAware.getWrappedView(), new FailReason(failType, failCause));
  361 + }
  362 + };
  363 + runTask(r, false, handler, engine);
  364 + }
  365 +
  366 + private void fireCancelEvent() {
  367 + if (syncLoading || isTaskInterrupted()) return;
  368 + Runnable r = new Runnable() {
  369 + @Override
  370 + public void run() {
  371 + listener.onLoadingCancelled(uri, imageAware.getWrappedView());
  372 + }
  373 + };
  374 + runTask(r, false, handler, engine);
  375 + }
  376 +
  377 + private ImageDownloader getDownloader() {
  378 + ImageDownloader d;
  379 + if (engine.isNetworkDenied()) {
  380 + d = networkDeniedDownloader;
  381 + } else if (engine.isSlowNetwork()) {
  382 + d = slowNetworkDownloader;
  383 + } else {
  384 + d = downloader;
  385 + }
  386 + return d;
  387 + }
  388 +
  389 + /**
  390 + * @throws TaskCancelledException if task is not actual (target ImageAware is collected by GC or the image URI of
  391 + * this task doesn't match to image URI which is actual for current ImageAware at
  392 + * this moment)
  393 + */
  394 + private void checkTaskNotActual() throws TaskCancelledException {
  395 + checkViewCollected();
  396 + checkViewReused();
  397 + }
  398 +
  399 + /**
  400 + * @return <b>true</b> - if task is not actual (target ImageAware is collected by GC or the image URI of this task
  401 + * doesn't match to image URI which is actual for current ImageAware at this moment)); <b>false</b> - otherwise
  402 + */
  403 + private boolean isTaskNotActual() {
  404 + return isViewCollected() || isViewReused();
  405 + }
  406 +
  407 + /** @throws TaskCancelledException if target ImageAware is collected */
  408 + private void checkViewCollected() throws TaskCancelledException {
  409 + if (isViewCollected()) {
  410 + throw new TaskCancelledException();
  411 + }
  412 + }
  413 +
  414 + /** @return <b>true</b> - if target ImageAware is collected by GC; <b>false</b> - otherwise */
  415 + private boolean isViewCollected() {
  416 + if (imageAware.isCollected()) {
  417 + L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
  418 + return true;
  419 + }
  420 + return false;
  421 + }
  422 +
  423 + /** @throws TaskCancelledException if target ImageAware is collected by GC */
  424 + private void checkViewReused() throws TaskCancelledException {
  425 + if (isViewReused()) {
  426 + throw new TaskCancelledException();
  427 + }
  428 + }
  429 +
  430 + /** @return <b>true</b> - if current ImageAware is reused for displaying another image; <b>false</b> - otherwise */
  431 + private boolean isViewReused() {
  432 + String currentCacheKey = engine.getLoadingUriForView(imageAware);
  433 + // Check whether memory cache key (image URI) for current ImageAware is actual.
  434 + // If ImageAware is reused for another task then current task should be cancelled.
  435 + boolean imageAwareWasReused = !memoryCacheKey.equals(currentCacheKey);
  436 + if (imageAwareWasReused) {
  437 + L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
  438 + return true;
  439 + }
  440 + return false;
  441 + }
  442 +
  443 + /** @throws TaskCancelledException if current task was interrupted */
  444 + private void checkTaskInterrupted() throws TaskCancelledException {
  445 + if (isTaskInterrupted()) {
  446 + throw new TaskCancelledException();
  447 + }
  448 + }
  449 +
  450 + /** @return <b>true</b> - if current task was interrupted; <b>false</b> - otherwise */
  451 + private boolean isTaskInterrupted() {
  452 + if (Thread.interrupted()) {
  453 + L.d(LOG_TASK_INTERRUPTED, memoryCacheKey);
  454 + return true;
  455 + }
  456 + return false;
  457 + }
  458 +
  459 + String getLoadingUri() {
  460 + return uri;
  461 + }
  462 +
  463 + static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) {
  464 + if (sync) {
  465 + r.run();
  466 + } else if (handler == null) {
  467 + engine.fireCallback(r);
  468 + } else {
  469 + handler.post(r);
  470 + }
  471 + }
  472 +
  473 + /**
  474 + * Exceptions for case when task is cancelled (thread is interrupted, image view is reused for another task, view is
  475 + * collected by GC).
  476 + *
  477 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  478 + * @since 1.9.1
  479 + */
  480 + class TaskCancelledException extends Exception {
  481 + }
  482 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.os.Handler;
  20 +import android.widget.ImageView;
  21 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  22 +import com.nostra13.universalimageloader.core.process.BitmapProcessor;
  23 +import com.nostra13.universalimageloader.utils.L;
  24 +
  25 +/**
  26 + * Presents process'n'display image task. Processes image {@linkplain Bitmap} and display it in {@link ImageView} using
  27 + * {@link DisplayBitmapTask}.
  28 + *
  29 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  30 + * @since 1.8.0
  31 + */
  32 +final class ProcessAndDisplayImageTask implements Runnable {
  33 +
  34 + private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
  35 +
  36 + private final ImageLoaderEngine engine;
  37 + private final Bitmap bitmap;
  38 + private final ImageLoadingInfo imageLoadingInfo;
  39 + private final Handler handler;
  40 +
  41 + public ProcessAndDisplayImageTask(ImageLoaderEngine engine, Bitmap bitmap, ImageLoadingInfo imageLoadingInfo,
  42 + Handler handler) {
  43 + this.engine = engine;
  44 + this.bitmap = bitmap;
  45 + this.imageLoadingInfo = imageLoadingInfo;
  46 + this.handler = handler;
  47 + }
  48 +
  49 + @Override
  50 + public void run() {
  51 + L.d(LOG_POSTPROCESS_IMAGE, imageLoadingInfo.memoryCacheKey);
  52 +
  53 + BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor();
  54 + Bitmap processedBitmap = processor.process(bitmap);
  55 + DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine,
  56 + LoadedFrom.MEMORY_CACHE);
  57 + LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine);
  58 + }
  59 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2013-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.assist;
  17 +
  18 +import java.io.IOException;
  19 +import java.io.InputStream;
  20 +
  21 +/**
  22 + * Decorator for {@link InputStream InputStream}. Provides possibility to return defined stream length by
  23 + * {@link #available()} method.
  24 + *
  25 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com), Mariotaku
  26 + * @since 1.9.1
  27 + */
  28 +public class ContentLengthInputStream extends InputStream {
  29 +
  30 + private final InputStream stream;
  31 + private final int length;
  32 +
  33 + public ContentLengthInputStream(InputStream stream, int length) {
  34 + this.stream = stream;
  35 + this.length = length;
  36 + }
  37 +
  38 + @Override
  39 + public int available() {
  40 + return length;
  41 + }
  42 +
  43 + @Override
  44 + public void close() throws IOException {
  45 + stream.close();
  46 + }
  47 +
  48 + @Override
  49 + public void mark(int readLimit) {
  50 + stream.mark(readLimit);
  51 + }
  52 +
  53 + @Override
  54 + public int read() throws IOException {
  55 + return stream.read();
  56 + }
  57 +
  58 + @Override
  59 + public int read(byte[] buffer) throws IOException {
  60 + return stream.read(buffer);
  61 + }
  62 +
  63 + @Override
  64 + public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
  65 + return stream.read(buffer, byteOffset, byteCount);
  66 + }
  67 +
  68 + @Override
  69 + public void reset() throws IOException {
  70 + stream.reset();
  71 + }
  72 +
  73 + @Override
  74 + public long skip(long byteCount) throws IOException {
  75 + return stream.skip(byteCount);
  76 + }
  77 +
  78 + @Override
  79 + public boolean markSupported() {
  80 + return stream.markSupported();
  81 + }
  82 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.assist;
  17 +
  18 +/**
  19 + * Presents the reason why image loading and displaying was failed
  20 + *
  21 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  22 + * @since 1.0.0
  23 + */
  24 +public class FailReason {
  25 +
  26 + private final FailType type;
  27 +
  28 + private final Throwable cause;
  29 +
  30 + public FailReason(FailType type, Throwable cause) {
  31 + this.type = type;
  32 + this.cause = cause;
  33 + }
  34 +
  35 + /** @return {@linkplain FailType Fail type} */
  36 + public FailType getType() {
  37 + return type;
  38 + }
  39 +
  40 + /** @return Thrown exception/error, can be <b>null</b> */
  41 + public Throwable getCause() {
  42 + return cause;
  43 + }
  44 +
  45 + /** Presents type of fail while image loading */
  46 + public static enum FailType {
  47 + /** Input/output error. Can be caused by network communication fail or error while caching image on file system. */
  48 + IO_ERROR,
  49 + /**
  50 + * Error while
  51 + * {@linkplain android.graphics.BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options)
  52 + * decode image to Bitmap}
  53 + */
  54 + DECODING_ERROR,
  55 + /**
  56 + * {@linkplain com.nostra13.universalimageloader.core.ImageLoader#denyNetworkDownloads(boolean) Network
  57 + * downloads are denied} and requested image wasn't cached in disk cache before.
  58 + */
  59 + NETWORK_DENIED,
  60 + /** Not enough memory to create needed Bitmap for image */
  61 + OUT_OF_MEMORY,
  62 + /** Unknown error was occurred while loading image */
  63 + UNKNOWN
  64 + }
  65 +}
\ No newline at end of file
... ...
  1 +package com.nostra13.universalimageloader.core.assist;
  2 +
  3 +import java.io.FilterInputStream;
  4 +import java.io.IOException;
  5 +import java.io.InputStream;
  6 +
  7 +/**
  8 + * Many streams obtained over slow connection show <a href="http://code.google.com/p/android/issues/detail?id=6066">this
  9 + * problem</a>.
  10 + */
  11 +public class FlushedInputStream extends FilterInputStream {
  12 +
  13 + public FlushedInputStream(InputStream inputStream) {
  14 + super(inputStream);
  15 + }
  16 +
  17 + @Override
  18 + public long skip(long n) throws IOException {
  19 + long totalBytesSkipped = 0L;
  20 + while (totalBytesSkipped < n) {
  21 + long bytesSkipped = in.skip(n - totalBytesSkipped);
  22 + if (bytesSkipped == 0L) {
  23 + int by_te = read();
  24 + if (by_te < 0) {
  25 + break; // we reached EOF
  26 + } else {
  27 + bytesSkipped = 1; // we read one byte
  28 + }
  29 + }
  30 + totalBytesSkipped += bytesSkipped;
  31 + }
  32 + return totalBytesSkipped;
  33 + }
  34 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.assist;
  17 +
  18 +/**
  19 + * Type of image scaling during decoding.
  20 + *
  21 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  22 + * @since 1.5.0
  23 + */
  24 +public enum ImageScaleType {
  25 + /** Image won't be scaled */
  26 + NONE,
  27 + /**
  28 + * Image will be scaled down only if image size is greater than
  29 + * {@linkplain javax.microedition.khronos.opengles.GL10#GL_MAX_TEXTURE_SIZE maximum acceptable texture size}.
  30 + * Usually it's 2048x2048.<br />
  31 + * If Bitmap is expected to display than it must not exceed this size (otherwise you'll get the exception
  32 + * "OpenGLRenderer: Bitmap too large to be uploaded into a texture".<br />
  33 + * Image will be subsampled in an integer number of times (1, 2, 3, ...) to maximum texture size of device.
  34 + */
  35 + NONE_SAFE,
  36 + /**
  37 + * Image will be reduces 2-fold until next reduce step make image smaller target size.<br />
  38 + * It's <b>fast</b> type and it's preferable for usage in lists/grids/galleries (and other
  39 + * {@linkplain android.widget.AdapterView adapter-views}) .<br />
  40 + * Relates to {@link android.graphics.BitmapFactory.Options#inSampleSize}<br />
  41 + * Note: If original image size is smaller than target size then original image <b>won't</b> be scaled.
  42 + */
  43 + IN_SAMPLE_POWER_OF_2,
  44 + /**
  45 + * Image will be subsampled in an integer number of times (1, 2, 3, ...). Use it if memory economy is quite
  46 + * important.<br />
  47 + * Relates to {@link android.graphics.BitmapFactory.Options#inSampleSize}<br />
  48 + * Note: If original image size is smaller than target size then original image <b>won't</b> be scaled.
  49 + */
  50 + IN_SAMPLE_INT,
  51 + /**
  52 + * Image will scaled-down exactly to target size (scaled width or height or both will be equal to target size;
  53 + * depends on {@linkplain android.widget.ImageView.ScaleType ImageView's scale type}). Use it if memory economy is
  54 + * critically important.<br />
  55 + * <b>Note:</b> If original image size is smaller than target size then original image <b>won't</b> be scaled.<br />
  56 + * <br />
  57 + * <b>NOTE:</b> For creating result Bitmap (of exact size) additional Bitmap will be created with
  58 + * {@link android.graphics.Bitmap#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean)
  59 + * Bitmap.createBitmap(...)}.<br />
  60 + * <b>Cons:</b> Saves memory by keeping smaller Bitmap in memory cache (comparing with IN_SAMPLE... scale types)<br />
  61 + * <b>Pros:</b> Requires more memory in one time for creation of result Bitmap.
  62 + */
  63 + EXACTLY,
  64 + /**
  65 + * Image will scaled exactly to target size (scaled width or height or both will be equal to target size; depends on
  66 + * {@linkplain android.widget.ImageView.ScaleType ImageView's scale type}). Use it if memory economy is critically
  67 + * important.<br />
  68 + * <b>Note:</b> If original image size is smaller than target size then original image <b>will be stretched</b> to
  69 + * target size.<br />
  70 + * <br />
  71 + * <b>NOTE:</b> For creating result Bitmap (of exact size) additional Bitmap will be created with
  72 + * {@link android.graphics.Bitmap#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean)
  73 + * Bitmap.createBitmap(...)}.<br />
  74 + * <b>Cons:</b> Saves memory by keeping smaller Bitmap in memory cache (comparing with IN_SAMPLE... scale types)<br />
  75 + * <b>Pros:</b> Requires more memory in one time for creation of result Bitmap.
  76 + */
  77 + EXACTLY_STRETCHED
  78 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.assist;
  17 +
  18 +/**
  19 + * Present width and height values
  20 + *
  21 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  22 + * @since 1.0.0
  23 + */
  24 +public class ImageSize {
  25 +
  26 + private static final int TO_STRING_MAX_LENGHT = 9; // "9999x9999".length()
  27 + private static final String SEPARATOR = "x";
  28 +
  29 + private final int width;
  30 + private final int height;
  31 +
  32 + public ImageSize(int width, int height) {
  33 + this.width = width;
  34 + this.height = height;
  35 + }
  36 +
  37 + public ImageSize(int width, int height, int rotation) {
  38 + if (rotation % 180 == 0) {
  39 + this.width = width;
  40 + this.height = height;
  41 + } else {
  42 + this.width = height;
  43 + this.height = width;
  44 + }
  45 + }
  46 +
  47 + public int getWidth() {
  48 + return width;
  49 + }
  50 +
  51 + public int getHeight() {
  52 + return height;
  53 + }
  54 +
  55 + /** Scales down dimensions in <b>sampleSize</b> times. Returns new object. */
  56 + public ImageSize scaleDown(int sampleSize) {
  57 + return new ImageSize(width / sampleSize, height / sampleSize);
  58 + }
  59 +
  60 + /** Scales dimensions according to incoming scale. Returns new object. */
  61 + public ImageSize scale(float scale) {
  62 + return new ImageSize((int) (width * scale), (int) (height * scale));
  63 + }
  64 +
  65 + @Override
  66 + public String toString() {
  67 + return new StringBuilder(TO_STRING_MAX_LENGHT).append(width).append(SEPARATOR).append(height).toString();
  68 + }
  69 +}
... ...
  1 +package com.nostra13.universalimageloader.core.assist;
  2 +
  3 +/**
  4 + * Source image loaded from.
  5 + *
  6 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  7 + */
  8 +public enum LoadedFrom {
  9 + NETWORK, DISC_CACHE, MEMORY_CACHE
  10 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.assist;
  17 +
  18 +/**
  19 + * Queue processing type which will be used for display task processing
  20 + *
  21 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  22 + * @since 1.6.3
  23 + */
  24 +public enum QueueProcessingType {
  25 + FIFO, LIFO
  26 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.assist;
  17 +
  18 +import android.widget.ImageView;
  19 +import android.widget.ImageView.ScaleType;
  20 +
  21 +/**
  22 + * Simplify {@linkplain ScaleType ImageView's scale type} to 2 types: {@link #FIT_INSIDE} and {@link #CROP}
  23 + *
  24 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  25 + * @since 1.6.1
  26 + */
  27 +public enum ViewScaleType {
  28 + /**
  29 + * Scale the image uniformly (maintain the image's aspect ratio) so that at least one dimension (width or height) of
  30 + * the image will be equal to or less the corresponding dimension of the view.
  31 + */
  32 + FIT_INSIDE,
  33 + /**
  34 + * Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the
  35 + * image will be equal to or larger than the corresponding dimension of the view.
  36 + */
  37 + CROP;
  38 +
  39 + /**
  40 + * Defines scale type of ImageView.
  41 + *
  42 + * @param imageView {@link ImageView}
  43 + * @return {@link #FIT_INSIDE} for
  44 + * <ul>
  45 + * <li>{@link ScaleType#FIT_CENTER}</li>
  46 + * <li>{@link ScaleType#FIT_XY}</li>
  47 + * <li>{@link ScaleType#FIT_START}</li>
  48 + * <li>{@link ScaleType#FIT_END}</li>
  49 + * <li>{@link ScaleType#CENTER_INSIDE}</li>
  50 + * </ul>
  51 + * {@link #CROP} for
  52 + * <ul>
  53 + * <li>{@link ScaleType#CENTER}</li>
  54 + * <li>{@link ScaleType#CENTER_CROP}</li>
  55 + * <li>{@link ScaleType#MATRIX}</li>
  56 + * </ul>
  57 + */
  58 + public static ViewScaleType fromImageView(ImageView imageView) {
  59 + switch (imageView.getScaleType()) {
  60 + case FIT_CENTER:
  61 + case FIT_XY:
  62 + case FIT_START:
  63 + case FIT_END:
  64 + case CENTER_INSIDE:
  65 + return FIT_INSIDE;
  66 + case MATRIX:
  67 + case CENTER:
  68 + case CENTER_CROP:
  69 + default:
  70 + return CROP;
  71 + }
  72 + }
  73 +}
... ...
  1 +/*
  2 + * Written by Doug Lea with assistance from members of JCP JSR-166
  3 + * Expert Group and released to the public domain, as explained at
  4 + * http://creativecommons.org/licenses/publicdomain
  5 + */
  6 +
  7 +package com.nostra13.universalimageloader.core.assist.deque;
  8 +import java.util.Iterator;
  9 +import java.util.NoSuchElementException;
  10 +import java.util.concurrent.BlockingQueue;
  11 +import java.util.concurrent.TimeUnit;
  12 +
  13 +/**
  14 + * A {@link Deque} that additionally supports blocking operations that wait
  15 + * for the deque to become non-empty when retrieving an element, and wait for
  16 + * space to become available in the deque when storing an element.
  17 + *
  18 + * <p><tt>BlockingDeque</tt> methods come in four forms, with different ways
  19 + * of handling operations that cannot be satisfied immediately, but may be
  20 + * satisfied at some point in the future:
  21 + * one throws an exception, the second returns a special value (either
  22 + * <tt>null</tt> or <tt>false</tt>, depending on the operation), the third
  23 + * blocks the current thread indefinitely until the operation can succeed,
  24 + * and the fourth blocks for only a given maximum time limit before giving
  25 + * up. These methods are summarized in the following table:
  26 + *
  27 + * <p>
  28 + * <table BORDER CELLPADDING=3 CELLSPACING=1>
  29 + * <tr>
  30 + * <td ALIGN=CENTER COLSPAN = 5> <b>First Element (Head)</b></td>
  31 + * </tr>
  32 + * <tr>
  33 + * <td></td>
  34 + * <td ALIGN=CENTER><em>Throws exception</em></td>
  35 + * <td ALIGN=CENTER><em>Special value</em></td>
  36 + * <td ALIGN=CENTER><em>Blocks</em></td>
  37 + * <td ALIGN=CENTER><em>Times out</em></td>
  38 + * </tr>
  39 + * <tr>
  40 + * <td><b>Insert</b></td>
  41 + * <td>{@link #addFirst addFirst(e)}</td>
  42 + * <td>{@link #offerFirst offerFirst(e)}</td>
  43 + * <td>{@link #putFirst putFirst(e)}</td>
  44 + * <td>{@link #offerFirst offerFirst(e, time, unit)}</td>
  45 + * </tr>
  46 + * <tr>
  47 + * <td><b>Remove</b></td>
  48 + * <td>{@link #removeFirst removeFirst()}</td>
  49 + * <td>{@link #pollFirst pollFirst()}</td>
  50 + * <td>{@link #takeFirst takeFirst()}</td>
  51 + * <td>{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}</td>
  52 + * </tr>
  53 + * <tr>
  54 + * <td><b>Examine</b></td>
  55 + * <td>{@link #getFirst getFirst()}</td>
  56 + * <td>{@link #peekFirst peekFirst()}</td>
  57 + * <td><em>not applicable</em></td>
  58 + * <td><em>not applicable</em></td>
  59 + * </tr>
  60 + * <tr>
  61 + * <td ALIGN=CENTER COLSPAN = 5> <b>Last Element (Tail)</b></td>
  62 + * </tr>
  63 + * <tr>
  64 + * <td></td>
  65 + * <td ALIGN=CENTER><em>Throws exception</em></td>
  66 + * <td ALIGN=CENTER><em>Special value</em></td>
  67 + * <td ALIGN=CENTER><em>Blocks</em></td>
  68 + * <td ALIGN=CENTER><em>Times out</em></td>
  69 + * </tr>
  70 + * <tr>
  71 + * <td><b>Insert</b></td>
  72 + * <td>{@link #addLast addLast(e)}</td>
  73 + * <td>{@link #offerLast offerLast(e)}</td>
  74 + * <td>{@link #putLast putLast(e)}</td>
  75 + * <td>{@link #offerLast offerLast(e, time, unit)}</td>
  76 + * </tr>
  77 + * <tr>
  78 + * <td><b>Remove</b></td>
  79 + * <td>{@link #removeLast() removeLast()}</td>
  80 + * <td>{@link #pollLast() pollLast()}</td>
  81 + * <td>{@link #takeLast takeLast()}</td>
  82 + * <td>{@link #pollLast(long, TimeUnit) pollLast(time, unit)}</td>
  83 + * </tr>
  84 + * <tr>
  85 + * <td><b>Examine</b></td>
  86 + * <td>{@link #getLast getLast()}</td>
  87 + * <td>{@link #peekLast peekLast()}</td>
  88 + * <td><em>not applicable</em></td>
  89 + * <td><em>not applicable</em></td>
  90 + * </tr>
  91 + * </table>
  92 + *
  93 + * <p>Like any {@link BlockingQueue}, a <tt>BlockingDeque</tt> is thread safe,
  94 + * does not permit null elements, and may (or may not) be
  95 + * capacity-constrained.
  96 + *
  97 + * <p>A <tt>BlockingDeque</tt> implementation may be used directly as a FIFO
  98 + * <tt>BlockingQueue</tt>. The methods inherited from the
  99 + * <tt>BlockingQueue</tt> interface are precisely equivalent to
  100 + * <tt>BlockingDeque</tt> methods as indicated in the following table:
  101 + *
  102 + * <p>
  103 + * <table BORDER CELLPADDING=3 CELLSPACING=1>
  104 + * <tr>
  105 + * <td ALIGN=CENTER> <b><tt>BlockingQueue</tt> Method</b></td>
  106 + * <td ALIGN=CENTER> <b>Equivalent <tt>BlockingDeque</tt> Method</b></td>
  107 + * </tr>
  108 + * <tr>
  109 + * <td ALIGN=CENTER COLSPAN = 2> <b>Insert</b></td>
  110 + * </tr>
  111 + * <tr>
  112 + * <td>{@link #add add(e)}</td>
  113 + * <td>{@link #addLast addLast(e)}</td>
  114 + * </tr>
  115 + * <tr>
  116 + * <td>{@link #offer offer(e)}</td>
  117 + * <td>{@link #offerLast offerLast(e)}</td>
  118 + * </tr>
  119 + * <tr>
  120 + * <td>{@link #put put(e)}</td>
  121 + * <td>{@link #putLast putLast(e)}</td>
  122 + * </tr>
  123 + * <tr>
  124 + * <td>{@link #offer offer(e, time, unit)}</td>
  125 + * <td>{@link #offerLast offerLast(e, time, unit)}</td>
  126 + * </tr>
  127 + * <tr>
  128 + * <td ALIGN=CENTER COLSPAN = 2> <b>Remove</b></td>
  129 + * </tr>
  130 + * <tr>
  131 + * <td>{@link #remove() remove()}</td>
  132 + * <td>{@link #removeFirst() removeFirst()}</td>
  133 + * </tr>
  134 + * <tr>
  135 + * <td>{@link #poll() poll()}</td>
  136 + * <td>{@link #pollFirst() pollFirst()}</td>
  137 + * </tr>
  138 + * <tr>
  139 + * <td>{@link #take() take()}</td>
  140 + * <td>{@link #takeFirst() takeFirst()}</td>
  141 + * </tr>
  142 + * <tr>
  143 + * <td>{@link #poll(long, TimeUnit) poll(time, unit)}</td>
  144 + * <td>{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}</td>
  145 + * </tr>
  146 + * <tr>
  147 + * <td ALIGN=CENTER COLSPAN = 2> <b>Examine</b></td>
  148 + * </tr>
  149 + * <tr>
  150 + * <td>{@link #element() element()}</td>
  151 + * <td>{@link #getFirst() getFirst()}</td>
  152 + * </tr>
  153 + * <tr>
  154 + * <td>{@link #peek() peek()}</td>
  155 + * <td>{@link #peekFirst() peekFirst()}</td>
  156 + * </tr>
  157 + * </table>
  158 + *
  159 + * <p>Memory consistency effects: As with other concurrent
  160 + * collections, actions in a thread prior to placing an object into a
  161 + * {@code BlockingDeque}
  162 + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
  163 + * actions subsequent to the access or removal of that element from
  164 + * the {@code BlockingDeque} in another thread.
  165 + *
  166 + * <p>This interface is a member of the
  167 + * <a href="{@docRoot}/../technotes/guides/collections/index.html">
  168 + * Java Collections Framework</a>.
  169 + *
  170 + * @since 1.6
  171 + * @author Doug Lea
  172 + * @param <E> the type of elements held in this collection
  173 + */
  174 +public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
  175 + /*
  176 + * We have "diamond" multiple interface inheritance here, and that
  177 + * introduces ambiguities. Methods might end up with different
  178 + * specs depending on the branch chosen by javadoc. Thus a lot of
  179 + * methods specs here are copied from superinterfaces.
  180 + */
  181 +
  182 + /**
  183 + * Inserts the specified element at the front of this deque if it is
  184 + * possible to do so immediately without violating capacity restrictions,
  185 + * throwing an <tt>IllegalStateException</tt> if no space is currently
  186 + * available. When using a capacity-restricted deque, it is generally
  187 + * preferable to use {@link #offerFirst offerFirst}.
  188 + *
  189 + * @param e the element to add
  190 + * @throws IllegalStateException {@inheritDoc}
  191 + * @throws ClassCastException {@inheritDoc}
  192 + * @throws NullPointerException if the specified element is null
  193 + * @throws IllegalArgumentException {@inheritDoc}
  194 + */
  195 + void addFirst(E e);
  196 +
  197 + /**
  198 + * Inserts the specified element at the end of this deque if it is
  199 + * possible to do so immediately without violating capacity restrictions,
  200 + * throwing an <tt>IllegalStateException</tt> if no space is currently
  201 + * available. When using a capacity-restricted deque, it is generally
  202 + * preferable to use {@link #offerLast offerLast}.
  203 + *
  204 + * @param e the element to add
  205 + * @throws IllegalStateException {@inheritDoc}
  206 + * @throws ClassCastException {@inheritDoc}
  207 + * @throws NullPointerException if the specified element is null
  208 + * @throws IllegalArgumentException {@inheritDoc}
  209 + */
  210 + void addLast(E e);
  211 +
  212 + /**
  213 + * Inserts the specified element at the front of this deque if it is
  214 + * possible to do so immediately without violating capacity restrictions,
  215 + * returning <tt>true</tt> upon success and <tt>false</tt> if no space is
  216 + * currently available.
  217 + * When using a capacity-restricted deque, this method is generally
  218 + * preferable to the {@link #addFirst addFirst} method, which can
  219 + * fail to insert an element only by throwing an exception.
  220 + *
  221 + * @param e the element to add
  222 + * @throws ClassCastException {@inheritDoc}
  223 + * @throws NullPointerException if the specified element is null
  224 + * @throws IllegalArgumentException {@inheritDoc}
  225 + */
  226 + boolean offerFirst(E e);
  227 +
  228 + /**
  229 + * Inserts the specified element at the end of this deque if it is
  230 + * possible to do so immediately without violating capacity restrictions,
  231 + * returning <tt>true</tt> upon success and <tt>false</tt> if no space is
  232 + * currently available.
  233 + * When using a capacity-restricted deque, this method is generally
  234 + * preferable to the {@link #addLast addLast} method, which can
  235 + * fail to insert an element only by throwing an exception.
  236 + *
  237 + * @param e the element to add
  238 + * @throws ClassCastException {@inheritDoc}
  239 + * @throws NullPointerException if the specified element is null
  240 + * @throws IllegalArgumentException {@inheritDoc}
  241 + */
  242 + boolean offerLast(E e);
  243 +
  244 + /**
  245 + * Inserts the specified element at the front of this deque,
  246 + * waiting if necessary for space to become available.
  247 + *
  248 + * @param e the element to add
  249 + * @throws InterruptedException if interrupted while waiting
  250 + * @throws ClassCastException if the class of the specified element
  251 + * prevents it from being added to this deque
  252 + * @throws NullPointerException if the specified element is null
  253 + * @throws IllegalArgumentException if some property of the specified
  254 + * element prevents it from being added to this deque
  255 + */
  256 + void putFirst(E e) throws InterruptedException;
  257 +
  258 + /**
  259 + * Inserts the specified element at the end of this deque,
  260 + * waiting if necessary for space to become available.
  261 + *
  262 + * @param e the element to add
  263 + * @throws InterruptedException if interrupted while waiting
  264 + * @throws ClassCastException if the class of the specified element
  265 + * prevents it from being added to this deque
  266 + * @throws NullPointerException if the specified element is null
  267 + * @throws IllegalArgumentException if some property of the specified
  268 + * element prevents it from being added to this deque
  269 + */
  270 + void putLast(E e) throws InterruptedException;
  271 +
  272 + /**
  273 + * Inserts the specified element at the front of this deque,
  274 + * waiting up to the specified wait time if necessary for space to
  275 + * become available.
  276 + *
  277 + * @param e the element to add
  278 + * @param timeout how long to wait before giving up, in units of
  279 + * <tt>unit</tt>
  280 + * @param unit a <tt>TimeUnit</tt> determining how to interpret the
  281 + * <tt>timeout</tt> parameter
  282 + * @return <tt>true</tt> if successful, or <tt>false</tt> if
  283 + * the specified waiting time elapses before space is available
  284 + * @throws InterruptedException if interrupted while waiting
  285 + * @throws ClassCastException if the class of the specified element
  286 + * prevents it from being added to this deque
  287 + * @throws NullPointerException if the specified element is null
  288 + * @throws IllegalArgumentException if some property of the specified
  289 + * element prevents it from being added to this deque
  290 + */
  291 + boolean offerFirst(E e, long timeout, TimeUnit unit)
  292 + throws InterruptedException;
  293 +
  294 + /**
  295 + * Inserts the specified element at the end of this deque,
  296 + * waiting up to the specified wait time if necessary for space to
  297 + * become available.
  298 + *
  299 + * @param e the element to add
  300 + * @param timeout how long to wait before giving up, in units of
  301 + * <tt>unit</tt>
  302 + * @param unit a <tt>TimeUnit</tt> determining how to interpret the
  303 + * <tt>timeout</tt> parameter
  304 + * @return <tt>true</tt> if successful, or <tt>false</tt> if
  305 + * the specified waiting time elapses before space is available
  306 + * @throws InterruptedException if interrupted while waiting
  307 + * @throws ClassCastException if the class of the specified element
  308 + * prevents it from being added to this deque
  309 + * @throws NullPointerException if the specified element is null
  310 + * @throws IllegalArgumentException if some property of the specified
  311 + * element prevents it from being added to this deque
  312 + */
  313 + boolean offerLast(E e, long timeout, TimeUnit unit)
  314 + throws InterruptedException;
  315 +
  316 + /**
  317 + * Retrieves and removes the first element of this deque, waiting
  318 + * if necessary until an element becomes available.
  319 + *
  320 + * @return the head of this deque
  321 + * @throws InterruptedException if interrupted while waiting
  322 + */
  323 + E takeFirst() throws InterruptedException;
  324 +
  325 + /**
  326 + * Retrieves and removes the last element of this deque, waiting
  327 + * if necessary until an element becomes available.
  328 + *
  329 + * @return the tail of this deque
  330 + * @throws InterruptedException if interrupted while waiting
  331 + */
  332 + E takeLast() throws InterruptedException;
  333 +
  334 + /**
  335 + * Retrieves and removes the first element of this deque, waiting
  336 + * up to the specified wait time if necessary for an element to
  337 + * become available.
  338 + *
  339 + * @param timeout how long to wait before giving up, in units of
  340 + * <tt>unit</tt>
  341 + * @param unit a <tt>TimeUnit</tt> determining how to interpret the
  342 + * <tt>timeout</tt> parameter
  343 + * @return the head of this deque, or <tt>null</tt> if the specified
  344 + * waiting time elapses before an element is available
  345 + * @throws InterruptedException if interrupted while waiting
  346 + */
  347 + E pollFirst(long timeout, TimeUnit unit)
  348 + throws InterruptedException;
  349 +
  350 + /**
  351 + * Retrieves and removes the last element of this deque, waiting
  352 + * up to the specified wait time if necessary for an element to
  353 + * become available.
  354 + *
  355 + * @param timeout how long to wait before giving up, in units of
  356 + * <tt>unit</tt>
  357 + * @param unit a <tt>TimeUnit</tt> determining how to interpret the
  358 + * <tt>timeout</tt> parameter
  359 + * @return the tail of this deque, or <tt>null</tt> if the specified
  360 + * waiting time elapses before an element is available
  361 + * @throws InterruptedException if interrupted while waiting
  362 + */
  363 + E pollLast(long timeout, TimeUnit unit)
  364 + throws InterruptedException;
  365 +
  366 + /**
  367 + * Removes the first occurrence of the specified element from this deque.
  368 + * If the deque does not contain the element, it is unchanged.
  369 + * More formally, removes the first element <tt>e</tt> such that
  370 + * <tt>o.equals(e)</tt> (if such an element exists).
  371 + * Returns <tt>true</tt> if this deque contained the specified element
  372 + * (or equivalently, if this deque changed as a result of the call).
  373 + *
  374 + * @param o element to be removed from this deque, if present
  375 + * @return <tt>true</tt> if an element was removed as a result of this call
  376 + * @throws ClassCastException if the class of the specified element
  377 + * is incompatible with this deque (optional)
  378 + * @throws NullPointerException if the specified element is null (optional)
  379 + */
  380 + boolean removeFirstOccurrence(Object o);
  381 +
  382 + /**
  383 + * Removes the last occurrence of the specified element from this deque.
  384 + * If the deque does not contain the element, it is unchanged.
  385 + * More formally, removes the last element <tt>e</tt> such that
  386 + * <tt>o.equals(e)</tt> (if such an element exists).
  387 + * Returns <tt>true</tt> if this deque contained the specified element
  388 + * (or equivalently, if this deque changed as a result of the call).
  389 + *
  390 + * @param o element to be removed from this deque, if present
  391 + * @return <tt>true</tt> if an element was removed as a result of this call
  392 + * @throws ClassCastException if the class of the specified element
  393 + * is incompatible with this deque (optional)
  394 + * @throws NullPointerException if the specified element is null (optional)
  395 + */
  396 + boolean removeLastOccurrence(Object o);
  397 +
  398 + // *** BlockingQueue methods ***
  399 +
  400 + /**
  401 + * Inserts the specified element into the queue represented by this deque
  402 + * (in other words, at the tail of this deque) if it is possible to do so
  403 + * immediately without violating capacity restrictions, returning
  404 + * <tt>true</tt> upon success and throwing an
  405 + * <tt>IllegalStateException</tt> if no space is currently available.
  406 + * When using a capacity-restricted deque, it is generally preferable to
  407 + * use {@link #offer offer}.
  408 + *
  409 + * <p>This method is equivalent to {@link #addLast addLast}.
  410 + *
  411 + * @param e the element to add
  412 + * @throws IllegalStateException {@inheritDoc}
  413 + * @throws ClassCastException if the class of the specified element
  414 + * prevents it from being added to this deque
  415 + * @throws NullPointerException if the specified element is null
  416 + * @throws IllegalArgumentException if some property of the specified
  417 + * element prevents it from being added to this deque
  418 + */
  419 + boolean add(E e);
  420 +
  421 + /**
  422 + * Inserts the specified element into the queue represented by this deque
  423 + * (in other words, at the tail of this deque) if it is possible to do so
  424 + * immediately without violating capacity restrictions, returning
  425 + * <tt>true</tt> upon success and <tt>false</tt> if no space is currently
  426 + * available. When using a capacity-restricted deque, this method is
  427 + * generally preferable to the {@link #add} method, which can fail to
  428 + * insert an element only by throwing an exception.
  429 + *
  430 + * <p>This method is equivalent to {@link #offerLast offerLast}.
  431 + *
  432 + * @param e the element to add
  433 + * @throws ClassCastException if the class of the specified element
  434 + * prevents it from being added to this deque
  435 + * @throws NullPointerException if the specified element is null
  436 + * @throws IllegalArgumentException if some property of the specified
  437 + * element prevents it from being added to this deque
  438 + */
  439 + boolean offer(E e);
  440 +
  441 + /**
  442 + * Inserts the specified element into the queue represented by this deque
  443 + * (in other words, at the tail of this deque), waiting if necessary for
  444 + * space to become available.
  445 + *
  446 + * <p>This method is equivalent to {@link #putLast putLast}.
  447 + *
  448 + * @param e the element to add
  449 + * @throws InterruptedException {@inheritDoc}
  450 + * @throws ClassCastException if the class of the specified element
  451 + * prevents it from being added to this deque
  452 + * @throws NullPointerException if the specified element is null
  453 + * @throws IllegalArgumentException if some property of the specified
  454 + * element prevents it from being added to this deque
  455 + */
  456 + void put(E e) throws InterruptedException;
  457 +
  458 + /**
  459 + * Inserts the specified element into the queue represented by this deque
  460 + * (in other words, at the tail of this deque), waiting up to the
  461 + * specified wait time if necessary for space to become available.
  462 + *
  463 + * <p>This method is equivalent to
  464 + * {@link #offerLast offerLast}.
  465 + *
  466 + * @param e the element to add
  467 + * @return <tt>true</tt> if the element was added to this deque, else
  468 + * <tt>false</tt>
  469 + * @throws InterruptedException {@inheritDoc}
  470 + * @throws ClassCastException if the class of the specified element
  471 + * prevents it from being added to this deque
  472 + * @throws NullPointerException if the specified element is null
  473 + * @throws IllegalArgumentException if some property of the specified
  474 + * element prevents it from being added to this deque
  475 + */
  476 + boolean offer(E e, long timeout, TimeUnit unit)
  477 + throws InterruptedException;
  478 +
  479 + /**
  480 + * Retrieves and removes the head of the queue represented by this deque
  481 + * (in other words, the first element of this deque).
  482 + * This method differs from {@link #poll poll} only in that it
  483 + * throws an exception if this deque is empty.
  484 + *
  485 + * <p>This method is equivalent to {@link #removeFirst() removeFirst}.
  486 + *
  487 + * @return the head of the queue represented by this deque
  488 + * @throws NoSuchElementException if this deque is empty
  489 + */
  490 + E remove();
  491 +
  492 + /**
  493 + * Retrieves and removes the head of the queue represented by this deque
  494 + * (in other words, the first element of this deque), or returns
  495 + * <tt>null</tt> if this deque is empty.
  496 + *
  497 + * <p>This method is equivalent to {@link #pollFirst()}.
  498 + *
  499 + * @return the head of this deque, or <tt>null</tt> if this deque is empty
  500 + */
  501 + E poll();
  502 +
  503 + /**
  504 + * Retrieves and removes the head of the queue represented by this deque
  505 + * (in other words, the first element of this deque), waiting if
  506 + * necessary until an element becomes available.
  507 + *
  508 + * <p>This method is equivalent to {@link #takeFirst() takeFirst}.
  509 + *
  510 + * @return the head of this deque
  511 + * @throws InterruptedException if interrupted while waiting
  512 + */
  513 + E take() throws InterruptedException;
  514 +
  515 + /**
  516 + * Retrieves and removes the head of the queue represented by this deque
  517 + * (in other words, the first element of this deque), waiting up to the
  518 + * specified wait time if necessary for an element to become available.
  519 + *
  520 + * <p>This method is equivalent to
  521 + * {@link #pollFirst(long,TimeUnit) pollFirst}.
  522 + *
  523 + * @return the head of this deque, or <tt>null</tt> if the
  524 + * specified waiting time elapses before an element is available
  525 + * @throws InterruptedException if interrupted while waiting
  526 + */
  527 + E poll(long timeout, TimeUnit unit)
  528 + throws InterruptedException;
  529 +
  530 + /**
  531 + * Retrieves, but does not remove, the head of the queue represented by
  532 + * this deque (in other words, the first element of this deque).
  533 + * This method differs from {@link #peek peek} only in that it throws an
  534 + * exception if this deque is empty.
  535 + *
  536 + * <p>This method is equivalent to {@link #getFirst() getFirst}.
  537 + *
  538 + * @return the head of this deque
  539 + * @throws NoSuchElementException if this deque is empty
  540 + */
  541 + E element();
  542 +
  543 + /**
  544 + * Retrieves, but does not remove, the head of the queue represented by
  545 + * this deque (in other words, the first element of this deque), or
  546 + * returns <tt>null</tt> if this deque is empty.
  547 + *
  548 + * <p>This method is equivalent to {@link #peekFirst() peekFirst}.
  549 + *
  550 + * @return the head of this deque, or <tt>null</tt> if this deque is empty
  551 + */
  552 + E peek();
  553 +
  554 + /**
  555 + * Removes the first occurrence of the specified element from this deque.
  556 + * If the deque does not contain the element, it is unchanged.
  557 + * More formally, removes the first element <tt>e</tt> such that
  558 + * <tt>o.equals(e)</tt> (if such an element exists).
  559 + * Returns <tt>true</tt> if this deque contained the specified element
  560 + * (or equivalently, if this deque changed as a result of the call).
  561 + *
  562 + * <p>This method is equivalent to
  563 + * {@link #removeFirstOccurrence removeFirstOccurrence}.
  564 + *
  565 + * @param o element to be removed from this deque, if present
  566 + * @return <tt>true</tt> if this deque changed as a result of the call
  567 + * @throws ClassCastException if the class of the specified element
  568 + * is incompatible with this deque (optional)
  569 + * @throws NullPointerException if the specified element is null (optional)
  570 + */
  571 + boolean remove(Object o);
  572 +
  573 + /**
  574 + * Returns <tt>true</tt> if this deque contains the specified element.
  575 + * More formally, returns <tt>true</tt> if and only if this deque contains
  576 + * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
  577 + *
  578 + * @param o object to be checked for containment in this deque
  579 + * @return <tt>true</tt> if this deque contains the specified element
  580 + * @throws ClassCastException if the class of the specified element
  581 + * is incompatible with this deque (optional)
  582 + * @throws NullPointerException if the specified element is null (optional)
  583 + */
  584 + public boolean contains(Object o);
  585 +
  586 + /**
  587 + * Returns the number of elements in this deque.
  588 + *
  589 + * @return the number of elements in this deque
  590 + */
  591 + public int size();
  592 +
  593 + /**
  594 + * Returns an iterator over the elements in this deque in proper sequence.
  595 + * The elements will be returned in order from first (head) to last (tail).
  596 + *
  597 + * @return an iterator over the elements in this deque in proper sequence
  598 + */
  599 + Iterator<E> iterator();
  600 +
  601 + // *** Stack methods ***
  602 +
  603 + /**
  604 + * Pushes an element onto the stack represented by this deque. In other
  605 + * words, inserts the element at the front of this deque unless it would
  606 + * violate capacity restrictions.
  607 + *
  608 + * <p>This method is equivalent to {@link #addFirst addFirst}.
  609 + *
  610 + * @throws IllegalStateException {@inheritDoc}
  611 + * @throws ClassCastException {@inheritDoc}
  612 + * @throws NullPointerException if the specified element is null
  613 + * @throws IllegalArgumentException {@inheritDoc}
  614 + */
  615 + void push(E e);
  616 +}
... ...
  1 +/*
  2 + * Written by Doug Lea and Josh Bloch with assistance from members of
  3 + * JCP JSR-166 Expert Group and released to the public domain, as explained
  4 + * at http://creativecommons.org/licenses/publicdomain
  5 + */
  6 +
  7 +package com.nostra13.universalimageloader.core.assist.deque;
  8 +
  9 +import java.util.Collection;
  10 +import java.util.Iterator;
  11 +import java.util.List;
  12 +import java.util.NoSuchElementException;
  13 +import java.util.Queue;
  14 +import java.util.Stack;
  15 +
  16 +/**
  17 + * A linear collection that supports element insertion and removal at
  18 + * both ends. The name <i>deque</i> is short for "double ended queue"
  19 + * and is usually pronounced "deck". Most <tt>Deque</tt>
  20 + * implementations place no fixed limits on the number of elements
  21 + * they may contain, but this interface supports capacity-restricted
  22 + * deques as well as those with no fixed size limit.
  23 + *
  24 + * <p>This interface defines methods to access the elements at both
  25 + * ends of the deque. Methods are provided to insert, remove, and
  26 + * examine the element. Each of these methods exists in two forms:
  27 + * one throws an exception if the operation fails, the other returns a
  28 + * special value (either <tt>null</tt> or <tt>false</tt>, depending on
  29 + * the operation). The latter form of the insert operation is
  30 + * designed specifically for use with capacity-restricted
  31 + * <tt>Deque</tt> implementations; in most implementations, insert
  32 + * operations cannot fail.
  33 + *
  34 + * <p>The twelve methods described above are summarized in the
  35 + * following table:
  36 + *
  37 + * <p>
  38 + * <table BORDER CELLPADDING=3 CELLSPACING=1>
  39 + * <tr>
  40 + * <td></td>
  41 + * <td ALIGN=CENTER COLSPAN = 2> <b>First Element (Head)</b></td>
  42 + * <td ALIGN=CENTER COLSPAN = 2> <b>Last Element (Tail)</b></td>
  43 + * </tr>
  44 + * <tr>
  45 + * <td></td>
  46 + * <td ALIGN=CENTER><em>Throws exception</em></td>
  47 + * <td ALIGN=CENTER><em>Special value</em></td>
  48 + * <td ALIGN=CENTER><em>Throws exception</em></td>
  49 + * <td ALIGN=CENTER><em>Special value</em></td>
  50 + * </tr>
  51 + * <tr>
  52 + * <td><b>Insert</b></td>
  53 + * <td>{@link #addFirst addFirst(e)}</td>
  54 + * <td>{@link #offerFirst offerFirst(e)}</td>
  55 + * <td>{@link #addLast addLast(e)}</td>
  56 + * <td>{@link #offerLast offerLast(e)}</td>
  57 + * </tr>
  58 + * <tr>
  59 + * <td><b>Remove</b></td>
  60 + * <td>{@link #removeFirst removeFirst()}</td>
  61 + * <td>{@link #pollFirst pollFirst()}</td>
  62 + * <td>{@link #removeLast removeLast()}</td>
  63 + * <td>{@link #pollLast pollLast()}</td>
  64 + * </tr>
  65 + * <tr>
  66 + * <td><b>Examine</b></td>
  67 + * <td>{@link #getFirst getFirst()}</td>
  68 + * <td>{@link #peekFirst peekFirst()}</td>
  69 + * <td>{@link #getLast getLast()}</td>
  70 + * <td>{@link #peekLast peekLast()}</td>
  71 + * </tr>
  72 + * </table>
  73 + *
  74 + * <p>This interface extends the {@link Queue} interface. When a deque is
  75 + * used as a queue, FIFO (First-In-First-Out) behavior results. Elements are
  76 + * added at the end of the deque and removed from the beginning. The methods
  77 + * inherited from the <tt>Queue</tt> interface are precisely equivalent to
  78 + * <tt>Deque</tt> methods as indicated in the following table:
  79 + *
  80 + * <p>
  81 + * <table BORDER CELLPADDING=3 CELLSPACING=1>
  82 + * <tr>
  83 + * <td ALIGN=CENTER> <b><tt>Queue</tt> Method</b></td>
  84 + * <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
  85 + * </tr>
  86 + * <tr>
  87 + * <td>{@link Queue#add add(e)}</td>
  88 + * <td>{@link #addLast addLast(e)}</td>
  89 + * </tr>
  90 + * <tr>
  91 + * <td>{@link Queue#offer offer(e)}</td>
  92 + * <td>{@link #offerLast offerLast(e)}</td>
  93 + * </tr>
  94 + * <tr>
  95 + * <td>{@link Queue#remove remove()}</td>
  96 + * <td>{@link #removeFirst removeFirst()}</td>
  97 + * </tr>
  98 + * <tr>
  99 + * <td>{@link Queue#poll poll()}</td>
  100 + * <td>{@link #pollFirst pollFirst()}</td>
  101 + * </tr>
  102 + * <tr>
  103 + * <td>{@link Queue#element element()}</td>
  104 + * <td>{@link #getFirst getFirst()}</td>
  105 + * </tr>
  106 + * <tr>
  107 + * <td>{@link Queue#peek peek()}</td>
  108 + * <td>{@link #peek peekFirst()}</td>
  109 + * </tr>
  110 + * </table>
  111 + *
  112 + * <p>Deques can also be used as LIFO (Last-In-First-Out) stacks. This
  113 + * interface should be used in preference to the legacy {@link Stack} class.
  114 + * When a deque is used as a stack, elements are pushed and popped from the
  115 + * beginning of the deque. Stack methods are precisely equivalent to
  116 + * <tt>Deque</tt> methods as indicated in the table below:
  117 + *
  118 + * <p>
  119 + * <table BORDER CELLPADDING=3 CELLSPACING=1>
  120 + * <tr>
  121 + * <td ALIGN=CENTER> <b>Stack Method</b></td>
  122 + * <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
  123 + * </tr>
  124 + * <tr>
  125 + * <td>{@link #push push(e)}</td>
  126 + * <td>{@link #addFirst addFirst(e)}</td>
  127 + * </tr>
  128 + * <tr>
  129 + * <td>{@link #pop pop()}</td>
  130 + * <td>{@link #removeFirst removeFirst()}</td>
  131 + * </tr>
  132 + * <tr>
  133 + * <td>{@link #peek peek()}</td>
  134 + * <td>{@link #peekFirst peekFirst()}</td>
  135 + * </tr>
  136 + * </table>
  137 + *
  138 + * <p>Note that the {@link #peek peek} method works equally well when
  139 + * a deque is used as a queue or a stack; in either case, elements are
  140 + * drawn from the beginning of the deque.
  141 + *
  142 + * <p>This interface provides two methods to remove interior
  143 + * elements, {@link #removeFirstOccurrence removeFirstOccurrence} and
  144 + * {@link #removeLastOccurrence removeLastOccurrence}.
  145 + *
  146 + * <p>Unlike the {@link List} interface, this interface does not
  147 + * provide support for indexed access to elements.
  148 + *
  149 + * <p>While <tt>Deque</tt> implementations are not strictly required
  150 + * to prohibit the insertion of null elements, they are strongly
  151 + * encouraged to do so. Users of any <tt>Deque</tt> implementations
  152 + * that do allow null elements are strongly encouraged <i>not</i> to
  153 + * take advantage of the ability to insert nulls. This is so because
  154 + * <tt>null</tt> is used as a special return value by various methods
  155 + * to indicated that the deque is empty.
  156 + *
  157 + * <p><tt>Deque</tt> implementations generally do not define
  158 + * element-based versions of the <tt>equals</tt> and <tt>hashCode</tt>
  159 + * methods, but instead inherit the identity-based versions from class
  160 + * <tt>Object</tt>.
  161 + *
  162 + * @author Doug Lea
  163 + * @author Josh Bloch
  164 + * @since 1.6
  165 + * @param <E> the type of elements held in this collection
  166 + */
  167 +
  168 +public interface Deque<E> extends Queue<E> {
  169 + /**
  170 + * Inserts the specified element at the front of this deque if it is
  171 + * possible to do so immediately without violating capacity restrictions.
  172 + * When using a capacity-restricted deque, it is generally preferable to
  173 + * use method {@link #offerFirst}.
  174 + *
  175 + * @param e the element to add
  176 + * @throws IllegalStateException if the element cannot be added at this
  177 + * time due to capacity restrictions
  178 + * @throws ClassCastException if the class of the specified element
  179 + * prevents it from being added to this deque
  180 + * @throws NullPointerException if the specified element is null and this
  181 + * deque does not permit null elements
  182 + * @throws IllegalArgumentException if some property of the specified
  183 + * element prevents it from being added to this deque
  184 + */
  185 + void addFirst(E e);
  186 +
  187 + /**
  188 + * Inserts the specified element at the end of this deque if it is
  189 + * possible to do so immediately without violating capacity restrictions.
  190 + * When using a capacity-restricted deque, it is generally preferable to
  191 + * use method {@link #offerLast}.
  192 + *
  193 + * <p>This method is equivalent to {@link #add}.
  194 + *
  195 + * @param e the element to add
  196 + * @throws IllegalStateException if the element cannot be added at this
  197 + * time due to capacity restrictions
  198 + * @throws ClassCastException if the class of the specified element
  199 + * prevents it from being added to this deque
  200 + * @throws NullPointerException if the specified element is null and this
  201 + * deque does not permit null elements
  202 + * @throws IllegalArgumentException if some property of the specified
  203 + * element prevents it from being added to this deque
  204 + */
  205 + void addLast(E e);
  206 +
  207 + /**
  208 + * Inserts the specified element at the front of this deque unless it would
  209 + * violate capacity restrictions. When using a capacity-restricted deque,
  210 + * this method is generally preferable to the {@link #addFirst} method,
  211 + * which can fail to insert an element only by throwing an exception.
  212 + *
  213 + * @param e the element to add
  214 + * @return <tt>true</tt> if the element was added to this deque, else
  215 + * <tt>false</tt>
  216 + * @throws ClassCastException if the class of the specified element
  217 + * prevents it from being added to this deque
  218 + * @throws NullPointerException if the specified element is null and this
  219 + * deque does not permit null elements
  220 + * @throws IllegalArgumentException if some property of the specified
  221 + * element prevents it from being added to this deque
  222 + */
  223 + boolean offerFirst(E e);
  224 +
  225 + /**
  226 + * Inserts the specified element at the end of this deque unless it would
  227 + * violate capacity restrictions. When using a capacity-restricted deque,
  228 + * this method is generally preferable to the {@link #addLast} method,
  229 + * which can fail to insert an element only by throwing an exception.
  230 + *
  231 + * @param e the element to add
  232 + * @return <tt>true</tt> if the element was added to this deque, else
  233 + * <tt>false</tt>
  234 + * @throws ClassCastException if the class of the specified element
  235 + * prevents it from being added to this deque
  236 + * @throws NullPointerException if the specified element is null and this
  237 + * deque does not permit null elements
  238 + * @throws IllegalArgumentException if some property of the specified
  239 + * element prevents it from being added to this deque
  240 + */
  241 + boolean offerLast(E e);
  242 +
  243 + /**
  244 + * Retrieves and removes the first element of this deque. This method
  245 + * differs from {@link #pollFirst pollFirst} only in that it throws an
  246 + * exception if this deque is empty.
  247 + *
  248 + * @return the head of this deque
  249 + * @throws NoSuchElementException if this deque is empty
  250 + */
  251 + E removeFirst();
  252 +
  253 + /**
  254 + * Retrieves and removes the last element of this deque. This method
  255 + * differs from {@link #pollLast pollLast} only in that it throws an
  256 + * exception if this deque is empty.
  257 + *
  258 + * @return the tail of this deque
  259 + * @throws NoSuchElementException if this deque is empty
  260 + */
  261 + E removeLast();
  262 +
  263 + /**
  264 + * Retrieves and removes the first element of this deque,
  265 + * or returns <tt>null</tt> if this deque is empty.
  266 + *
  267 + * @return the head of this deque, or <tt>null</tt> if this deque is empty
  268 + */
  269 + E pollFirst();
  270 +
  271 + /**
  272 + * Retrieves and removes the last element of this deque,
  273 + * or returns <tt>null</tt> if this deque is empty.
  274 + *
  275 + * @return the tail of this deque, or <tt>null</tt> if this deque is empty
  276 + */
  277 + E pollLast();
  278 +
  279 + /**
  280 + * Retrieves, but does not remove, the first element of this deque.
  281 + *
  282 + * This method differs from {@link #peekFirst peekFirst} only in that it
  283 + * throws an exception if this deque is empty.
  284 + *
  285 + * @return the head of this deque
  286 + * @throws NoSuchElementException if this deque is empty
  287 + */
  288 + E getFirst();
  289 +
  290 + /**
  291 + * Retrieves, but does not remove, the last element of this deque.
  292 + * This method differs from {@link #peekLast peekLast} only in that it
  293 + * throws an exception if this deque is empty.
  294 + *
  295 + * @return the tail of this deque
  296 + * @throws NoSuchElementException if this deque is empty
  297 + */
  298 + E getLast();
  299 +
  300 + /**
  301 + * Retrieves, but does not remove, the first element of this deque,
  302 + * or returns <tt>null</tt> if this deque is empty.
  303 + *
  304 + * @return the head of this deque, or <tt>null</tt> if this deque is empty
  305 + */
  306 + E peekFirst();
  307 +
  308 + /**
  309 + * Retrieves, but does not remove, the last element of this deque,
  310 + * or returns <tt>null</tt> if this deque is empty.
  311 + *
  312 + * @return the tail of this deque, or <tt>null</tt> if this deque is empty
  313 + */
  314 + E peekLast();
  315 +
  316 + /**
  317 + * Removes the first occurrence of the specified element from this deque.
  318 + * If the deque does not contain the element, it is unchanged.
  319 + * More formally, removes the first element <tt>e</tt> such that
  320 + * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
  321 + * (if such an element exists).
  322 + * Returns <tt>true</tt> if this deque contained the specified element
  323 + * (or equivalently, if this deque changed as a result of the call).
  324 + *
  325 + * @param o element to be removed from this deque, if present
  326 + * @return <tt>true</tt> if an element was removed as a result of this call
  327 + * @throws ClassCastException if the class of the specified element
  328 + * is incompatible with this deque (optional)
  329 + * @throws NullPointerException if the specified element is null and this
  330 + * deque does not permit null elements (optional)
  331 + */
  332 + boolean removeFirstOccurrence(Object o);
  333 +
  334 + /**
  335 + * Removes the last occurrence of the specified element from this deque.
  336 + * If the deque does not contain the element, it is unchanged.
  337 + * More formally, removes the last element <tt>e</tt> such that
  338 + * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
  339 + * (if such an element exists).
  340 + * Returns <tt>true</tt> if this deque contained the specified element
  341 + * (or equivalently, if this deque changed as a result of the call).
  342 + *
  343 + * @param o element to be removed from this deque, if present
  344 + * @return <tt>true</tt> if an element was removed as a result of this call
  345 + * @throws ClassCastException if the class of the specified element
  346 + * is incompatible with this deque (optional)
  347 + * @throws NullPointerException if the specified element is null and this
  348 + * deque does not permit null elements (optional)
  349 + */
  350 + boolean removeLastOccurrence(Object o);
  351 +
  352 + // *** Queue methods ***
  353 +
  354 + /**
  355 + * Inserts the specified element into the queue represented by this deque
  356 + * (in other words, at the tail of this deque) if it is possible to do so
  357 + * immediately without violating capacity restrictions, returning
  358 + * <tt>true</tt> upon success and throwing an
  359 + * <tt>IllegalStateException</tt> if no space is currently available.
  360 + * When using a capacity-restricted deque, it is generally preferable to
  361 + * use {@link #offer offer}.
  362 + *
  363 + * <p>This method is equivalent to {@link #addLast}.
  364 + *
  365 + * @param e the element to add
  366 + * @return <tt>true</tt> (as specified by {@link Collection#add})
  367 + * @throws IllegalStateException if the element cannot be added at this
  368 + * time due to capacity restrictions
  369 + * @throws ClassCastException if the class of the specified element
  370 + * prevents it from being added to this deque
  371 + * @throws NullPointerException if the specified element is null and this
  372 + * deque does not permit null elements
  373 + * @throws IllegalArgumentException if some property of the specified
  374 + * element prevents it from being added to this deque
  375 + */
  376 + boolean add(E e);
  377 +
  378 + /**
  379 + * Inserts the specified element into the queue represented by this deque
  380 + * (in other words, at the tail of this deque) if it is possible to do so
  381 + * immediately without violating capacity restrictions, returning
  382 + * <tt>true</tt> upon success and <tt>false</tt> if no space is currently
  383 + * available. When using a capacity-restricted deque, this method is
  384 + * generally preferable to the {@link #add} method, which can fail to
  385 + * insert an element only by throwing an exception.
  386 + *
  387 + * <p>This method is equivalent to {@link #offerLast}.
  388 + *
  389 + * @param e the element to add
  390 + * @return <tt>true</tt> if the element was added to this deque, else
  391 + * <tt>false</tt>
  392 + * @throws ClassCastException if the class of the specified element
  393 + * prevents it from being added to this deque
  394 + * @throws NullPointerException if the specified element is null and this
  395 + * deque does not permit null elements
  396 + * @throws IllegalArgumentException if some property of the specified
  397 + * element prevents it from being added to this deque
  398 + */
  399 + boolean offer(E e);
  400 +
  401 + /**
  402 + * Retrieves and removes the head of the queue represented by this deque
  403 + * (in other words, the first element of this deque).
  404 + * This method differs from {@link #poll poll} only in that it throws an
  405 + * exception if this deque is empty.
  406 + *
  407 + * <p>This method is equivalent to {@link #removeFirst()}.
  408 + *
  409 + * @return the head of the queue represented by this deque
  410 + * @throws NoSuchElementException if this deque is empty
  411 + */
  412 + E remove();
  413 +
  414 + /**
  415 + * Retrieves and removes the head of the queue represented by this deque
  416 + * (in other words, the first element of this deque), or returns
  417 + * <tt>null</tt> if this deque is empty.
  418 + *
  419 + * <p>This method is equivalent to {@link #pollFirst()}.
  420 + *
  421 + * @return the first element of this deque, or <tt>null</tt> if
  422 + * this deque is empty
  423 + */
  424 + E poll();
  425 +
  426 + /**
  427 + * Retrieves, but does not remove, the head of the queue represented by
  428 + * this deque (in other words, the first element of this deque).
  429 + * This method differs from {@link #peek peek} only in that it throws an
  430 + * exception if this deque is empty.
  431 + *
  432 + * <p>This method is equivalent to {@link #getFirst()}.
  433 + *
  434 + * @return the head of the queue represented by this deque
  435 + * @throws NoSuchElementException if this deque is empty
  436 + */
  437 + E element();
  438 +
  439 + /**
  440 + * Retrieves, but does not remove, the head of the queue represented by
  441 + * this deque (in other words, the first element of this deque), or
  442 + * returns <tt>null</tt> if this deque is empty.
  443 + *
  444 + * <p>This method is equivalent to {@link #peekFirst()}.
  445 + *
  446 + * @return the head of the queue represented by this deque, or
  447 + * <tt>null</tt> if this deque is empty
  448 + */
  449 + E peek();
  450 +
  451 +
  452 + // *** Stack methods ***
  453 +
  454 + /**
  455 + * Pushes an element onto the stack represented by this deque (in other
  456 + * words, at the head of this deque) if it is possible to do so
  457 + * immediately without violating capacity restrictions, returning
  458 + * <tt>true</tt> upon success and throwing an
  459 + * <tt>IllegalStateException</tt> if no space is currently available.
  460 + *
  461 + * <p>This method is equivalent to {@link #addFirst}.
  462 + *
  463 + * @param e the element to push
  464 + * @throws IllegalStateException if the element cannot be added at this
  465 + * time due to capacity restrictions
  466 + * @throws ClassCastException if the class of the specified element
  467 + * prevents it from being added to this deque
  468 + * @throws NullPointerException if the specified element is null and this
  469 + * deque does not permit null elements
  470 + * @throws IllegalArgumentException if some property of the specified
  471 + * element prevents it from being added to this deque
  472 + */
  473 + void push(E e);
  474 +
  475 + /**
  476 + * Pops an element from the stack represented by this deque. In other
  477 + * words, removes and returns the first element of this deque.
  478 + *
  479 + * <p>This method is equivalent to {@link #removeFirst()}.
  480 + *
  481 + * @return the element at the front of this deque (which is the top
  482 + * of the stack represented by this deque)
  483 + * @throws NoSuchElementException if this deque is empty
  484 + */
  485 + E pop();
  486 +
  487 +
  488 + // *** Collection methods ***
  489 +
  490 + /**
  491 + * Removes the first occurrence of the specified element from this deque.
  492 + * If the deque does not contain the element, it is unchanged.
  493 + * More formally, removes the first element <tt>e</tt> such that
  494 + * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
  495 + * (if such an element exists).
  496 + * Returns <tt>true</tt> if this deque contained the specified element
  497 + * (or equivalently, if this deque changed as a result of the call).
  498 + *
  499 + * <p>This method is equivalent to {@link #removeFirstOccurrence}.
  500 + *
  501 + * @param o element to be removed from this deque, if present
  502 + * @return <tt>true</tt> if an element was removed as a result of this call
  503 + * @throws ClassCastException if the class of the specified element
  504 + * is incompatible with this deque (optional)
  505 + * @throws NullPointerException if the specified element is null and this
  506 + * deque does not permit null elements (optional)
  507 + */
  508 + boolean remove(Object o);
  509 +
  510 + /**
  511 + * Returns <tt>true</tt> if this deque contains the specified element.
  512 + * More formally, returns <tt>true</tt> if and only if this deque contains
  513 + * at least one element <tt>e</tt> such that
  514 + * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
  515 + *
  516 + * @param o element whose presence in this deque is to be tested
  517 + * @return <tt>true</tt> if this deque contains the specified element
  518 + * @throws ClassCastException if the type of the specified element
  519 + * is incompatible with this deque (optional)
  520 + * @throws NullPointerException if the specified element is null and this
  521 + * deque does not permit null elements (optional)
  522 + */
  523 + boolean contains(Object o);
  524 +
  525 + /**
  526 + * Returns the number of elements in this deque.
  527 + *
  528 + * @return the number of elements in this deque
  529 + */
  530 + public int size();
  531 +
  532 + /**
  533 + * Returns an iterator over the elements in this deque in proper sequence.
  534 + * The elements will be returned in order from first (head) to last (tail).
  535 + *
  536 + * @return an iterator over the elements in this deque in proper sequence
  537 + */
  538 + Iterator<E> iterator();
  539 +
  540 + /**
  541 + * Returns an iterator over the elements in this deque in reverse
  542 + * sequential order. The elements will be returned in order from
  543 + * last (tail) to first (head).
  544 + *
  545 + * @return an iterator over the elements in this deque in reverse
  546 + * sequence
  547 + */
  548 + Iterator<E> descendingIterator();
  549 +
  550 +}
... ...
  1 +package com.nostra13.universalimageloader.core.assist.deque;
  2 +
  3 +import java.util.NoSuchElementException;
  4 +
  5 +/**
  6 + * {@link LinkedBlockingDeque} using LIFO algorithm
  7 + *
  8 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  9 + * @since 1.6.3
  10 + */
  11 +public class LIFOLinkedBlockingDeque<T> extends LinkedBlockingDeque<T> {
  12 +
  13 + private static final long serialVersionUID = -4114786347960826192L;
  14 +
  15 + /**
  16 + * Inserts the specified element at the front of this deque if it is possible to do so immediately without violating
  17 + * capacity restrictions, returning <tt>true</tt> upon success and <tt>false</tt> if no space is currently
  18 + * available. When using a capacity-restricted deque, this method is generally preferable to the {@link #addFirst
  19 + * addFirst} method, which can fail to insert an element only by throwing an exception.
  20 + *
  21 + * @param e
  22 + * the element to add
  23 + * @throws ClassCastException
  24 + * {@inheritDoc}
  25 + * @throws NullPointerException
  26 + * if the specified element is null
  27 + * @throws IllegalArgumentException
  28 + * {@inheritDoc}
  29 + */
  30 + @Override
  31 + public boolean offer(T e) {
  32 + return super.offerFirst(e);
  33 + }
  34 +
  35 + /**
  36 + * Retrieves and removes the first element of this deque. This method differs from {@link #pollFirst pollFirst} only
  37 + * in that it throws an exception if this deque is empty.
  38 + *
  39 + * @return the head of this deque
  40 + * @throws NoSuchElementException
  41 + * if this deque is empty
  42 + */
  43 + @Override
  44 + public T remove() {
  45 + return super.removeFirst();
  46 + }
  47 +}
\ No newline at end of file
... ...
  1 +/*
  2 + * Written by Doug Lea with assistance from members of JCP JSR-166
  3 + * Expert Group and released to the public domain, as explained at
  4 + * http://creativecommons.org/licenses/publicdomain
  5 + */
  6 +
  7 +package com.nostra13.universalimageloader.core.assist.deque;
  8 +
  9 +import java.util.AbstractQueue;
  10 +import java.util.Collection;
  11 +import java.util.Iterator;
  12 +import java.util.NoSuchElementException;
  13 +import java.util.concurrent.TimeUnit;
  14 +import java.util.concurrent.locks.Condition;
  15 +import java.util.concurrent.locks.ReentrantLock;
  16 +
  17 +/**
  18 + * An optionally-bounded {@linkplain BlockingDeque blocking deque} based on
  19 + * linked nodes.
  20 + *
  21 + * <p> The optional capacity bound constructor argument serves as a
  22 + * way to prevent excessive expansion. The capacity, if unspecified,
  23 + * is equal to {@link Integer#MAX_VALUE}. Linked nodes are
  24 + * dynamically created upon each insertion unless this would bring the
  25 + * deque above capacity.
  26 + *
  27 + * <p>Most operations run in constant time (ignoring time spent
  28 + * blocking). Exceptions include {@link #remove(Object) remove},
  29 + * {@link #removeFirstOccurrence removeFirstOccurrence}, {@link
  30 + * #removeLastOccurrence removeLastOccurrence}, {@link #contains
  31 + * contains}, {@link #iterator iterator.remove()}, and the bulk
  32 + * operations, all of which run in linear time.
  33 + *
  34 + * <p>This class and its iterator implement all of the
  35 + * <em>optional</em> methods of the {@link Collection} and {@link
  36 + * Iterator} interfaces.
  37 + *
  38 + * <p>This class is a member of the
  39 + * <a href="{@docRoot}/../technotes/guides/collections/index.html">
  40 + * Java Collections Framework</a>.
  41 + *
  42 + * @since 1.6
  43 + * @author Doug Lea
  44 + * @param <E> the type of elements held in this collection
  45 + */
  46 +public class LinkedBlockingDeque<E>
  47 + extends AbstractQueue<E>
  48 + implements BlockingDeque<E>, java.io.Serializable {
  49 +
  50 + /*
  51 + * Implemented as a simple doubly-linked list protected by a
  52 + * single lock and using conditions to manage blocking.
  53 + *
  54 + * To implement weakly consistent iterators, it appears we need to
  55 + * keep all Nodes GC-reachable from a predecessor dequeued Node.
  56 + * That would cause two problems:
  57 + * - allow a rogue Iterator to cause unbounded memory retention
  58 + * - cause cross-generational linking of old Nodes to new Nodes if
  59 + * a Node was tenured while live, which generational GCs have a
  60 + * hard time dealing with, causing repeated major collections.
  61 + * However, only non-deleted Nodes need to be reachable from
  62 + * dequeued Nodes, and reachability does not necessarily have to
  63 + * be of the kind understood by the GC. We use the trick of
  64 + * linking a Node that has just been dequeued to itself. Such a
  65 + * self-link implicitly means to jump to "first" (for next links)
  66 + * or "last" (for prev links).
  67 + */
  68 +
  69 + /*
  70 + * We have "diamond" multiple interface/abstract class inheritance
  71 + * here, and that introduces ambiguities. Often we want the
  72 + * BlockingDeque javadoc combined with the AbstractQueue
  73 + * implementation, so a lot of method specs are duplicated here.
  74 + */
  75 +
  76 + private static final long serialVersionUID = -387911632671998426L;
  77 +
  78 + /** Doubly-linked list node class */
  79 + static final class Node<E> {
  80 + /**
  81 + * The item, or null if this node has been removed.
  82 + */
  83 + E item;
  84 +
  85 + /**
  86 + * One of:
  87 + * - the real predecessor Node
  88 + * - this Node, meaning the predecessor is tail
  89 + * - null, meaning there is no predecessor
  90 + */
  91 + Node<E> prev;
  92 +
  93 + /**
  94 + * One of:
  95 + * - the real successor Node
  96 + * - this Node, meaning the successor is head
  97 + * - null, meaning there is no successor
  98 + */
  99 + Node<E> next;
  100 +
  101 + Node(E x) {
  102 + item = x;
  103 + }
  104 + }
  105 +
  106 + /**
  107 + * Pointer to first node.
  108 + * Invariant: (first == null && last == null) ||
  109 + * (first.prev == null && first.item != null)
  110 + */
  111 + transient Node<E> first;
  112 +
  113 + /**
  114 + * Pointer to last node.
  115 + * Invariant: (first == null && last == null) ||
  116 + * (last.next == null && last.item != null)
  117 + */
  118 + transient Node<E> last;
  119 +
  120 + /** Number of items in the deque */
  121 + private transient int count;
  122 +
  123 + /** Maximum number of items in the deque */
  124 + private final int capacity;
  125 +
  126 + /** Main lock guarding all access */
  127 + final ReentrantLock lock = new ReentrantLock();
  128 +
  129 + /** Condition for waiting takes */
  130 + private final Condition notEmpty = lock.newCondition();
  131 +
  132 + /** Condition for waiting puts */
  133 + private final Condition notFull = lock.newCondition();
  134 +
  135 + /**
  136 + * Creates a {@code LinkedBlockingDeque} with a capacity of
  137 + * {@link Integer#MAX_VALUE}.
  138 + */
  139 + public LinkedBlockingDeque() {
  140 + this(Integer.MAX_VALUE);
  141 + }
  142 +
  143 + /**
  144 + * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity.
  145 + *
  146 + * @param capacity the capacity of this deque
  147 + * @throws IllegalArgumentException if {@code capacity} is less than 1
  148 + */
  149 + public LinkedBlockingDeque(int capacity) {
  150 + if (capacity <= 0) throw new IllegalArgumentException();
  151 + this.capacity = capacity;
  152 + }
  153 +
  154 + /**
  155 + * Creates a {@code LinkedBlockingDeque} with a capacity of
  156 + * {@link Integer#MAX_VALUE}, initially containing the elements of
  157 + * the given collection, added in traversal order of the
  158 + * collection's iterator.
  159 + *
  160 + * @param c the collection of elements to initially contain
  161 + * @throws NullPointerException if the specified collection or any
  162 + * of its elements are null
  163 + */
  164 + public LinkedBlockingDeque(Collection<? extends E> c) {
  165 + this(Integer.MAX_VALUE);
  166 + final ReentrantLock lock = this.lock;
  167 + lock.lock(); // Never contended, but necessary for visibility
  168 + try {
  169 + for (E e : c) {
  170 + if (e == null)
  171 + throw new NullPointerException();
  172 + if (!linkLast(new Node<E>(e)))
  173 + throw new IllegalStateException("Deque full");
  174 + }
  175 + } finally {
  176 + lock.unlock();
  177 + }
  178 + }
  179 +
  180 +
  181 + // Basic linking and unlinking operations, called only while holding lock
  182 +
  183 + /**
  184 + * Links node as first element, or returns false if full.
  185 + */
  186 + private boolean linkFirst(Node<E> node) {
  187 + // assert lock.isHeldByCurrentThread();
  188 + if (count >= capacity)
  189 + return false;
  190 + Node<E> f = first;
  191 + node.next = f;
  192 + first = node;
  193 + if (last == null)
  194 + last = node;
  195 + else
  196 + f.prev = node;
  197 + ++count;
  198 + notEmpty.signal();
  199 + return true;
  200 + }
  201 +
  202 + /**
  203 + * Links node as last element, or returns false if full.
  204 + */
  205 + private boolean linkLast(Node<E> node) {
  206 + // assert lock.isHeldByCurrentThread();
  207 + if (count >= capacity)
  208 + return false;
  209 + Node<E> l = last;
  210 + node.prev = l;
  211 + last = node;
  212 + if (first == null)
  213 + first = node;
  214 + else
  215 + l.next = node;
  216 + ++count;
  217 + notEmpty.signal();
  218 + return true;
  219 + }
  220 +
  221 + /**
  222 + * Removes and returns first element, or null if empty.
  223 + */
  224 + private E unlinkFirst() {
  225 + // assert lock.isHeldByCurrentThread();
  226 + Node<E> f = first;
  227 + if (f == null)
  228 + return null;
  229 + Node<E> n = f.next;
  230 + E item = f.item;
  231 + f.item = null;
  232 + f.next = f; // help GC
  233 + first = n;
  234 + if (n == null)
  235 + last = null;
  236 + else
  237 + n.prev = null;
  238 + --count;
  239 + notFull.signal();
  240 + return item;
  241 + }
  242 +
  243 + /**
  244 + * Removes and returns last element, or null if empty.
  245 + */
  246 + private E unlinkLast() {
  247 + // assert lock.isHeldByCurrentThread();
  248 + Node<E> l = last;
  249 + if (l == null)
  250 + return null;
  251 + Node<E> p = l.prev;
  252 + E item = l.item;
  253 + l.item = null;
  254 + l.prev = l; // help GC
  255 + last = p;
  256 + if (p == null)
  257 + first = null;
  258 + else
  259 + p.next = null;
  260 + --count;
  261 + notFull.signal();
  262 + return item;
  263 + }
  264 +
  265 + /**
  266 + * Unlinks x.
  267 + */
  268 + void unlink(Node<E> x) {
  269 + // assert lock.isHeldByCurrentThread();
  270 + Node<E> p = x.prev;
  271 + Node<E> n = x.next;
  272 + if (p == null) {
  273 + unlinkFirst();
  274 + } else if (n == null) {
  275 + unlinkLast();
  276 + } else {
  277 + p.next = n;
  278 + n.prev = p;
  279 + x.item = null;
  280 + // Don't mess with x's links. They may still be in use by
  281 + // an iterator.
  282 + --count;
  283 + notFull.signal();
  284 + }
  285 + }
  286 +
  287 + // BlockingDeque methods
  288 +
  289 + /**
  290 + * @throws IllegalStateException {@inheritDoc}
  291 + * @throws NullPointerException {@inheritDoc}
  292 + */
  293 + public void addFirst(E e) {
  294 + if (!offerFirst(e))
  295 + throw new IllegalStateException("Deque full");
  296 + }
  297 +
  298 + /**
  299 + * @throws IllegalStateException {@inheritDoc}
  300 + * @throws NullPointerException {@inheritDoc}
  301 + */
  302 + public void addLast(E e) {
  303 + if (!offerLast(e))
  304 + throw new IllegalStateException("Deque full");
  305 + }
  306 +
  307 + /**
  308 + * @throws NullPointerException {@inheritDoc}
  309 + */
  310 + public boolean offerFirst(E e) {
  311 + if (e == null) throw new NullPointerException();
  312 + Node<E> node = new Node<E>(e);
  313 + final ReentrantLock lock = this.lock;
  314 + lock.lock();
  315 + try {
  316 + return linkFirst(node);
  317 + } finally {
  318 + lock.unlock();
  319 + }
  320 + }
  321 +
  322 + /**
  323 + * @throws NullPointerException {@inheritDoc}
  324 + */
  325 + public boolean offerLast(E e) {
  326 + if (e == null) throw new NullPointerException();
  327 + Node<E> node = new Node<E>(e);
  328 + final ReentrantLock lock = this.lock;
  329 + lock.lock();
  330 + try {
  331 + return linkLast(node);
  332 + } finally {
  333 + lock.unlock();
  334 + }
  335 + }
  336 +
  337 + /**
  338 + * @throws NullPointerException {@inheritDoc}
  339 + * @throws InterruptedException {@inheritDoc}
  340 + */
  341 + public void putFirst(E e) throws InterruptedException {
  342 + if (e == null) throw new NullPointerException();
  343 + Node<E> node = new Node<E>(e);
  344 + final ReentrantLock lock = this.lock;
  345 + lock.lock();
  346 + try {
  347 + while (!linkFirst(node))
  348 + notFull.await();
  349 + } finally {
  350 + lock.unlock();
  351 + }
  352 + }
  353 +
  354 + /**
  355 + * @throws NullPointerException {@inheritDoc}
  356 + * @throws InterruptedException {@inheritDoc}
  357 + */
  358 + public void putLast(E e) throws InterruptedException {
  359 + if (e == null) throw new NullPointerException();
  360 + Node<E> node = new Node<E>(e);
  361 + final ReentrantLock lock = this.lock;
  362 + lock.lock();
  363 + try {
  364 + while (!linkLast(node))
  365 + notFull.await();
  366 + } finally {
  367 + lock.unlock();
  368 + }
  369 + }
  370 +
  371 + /**
  372 + * @throws NullPointerException {@inheritDoc}
  373 + * @throws InterruptedException {@inheritDoc}
  374 + */
  375 + public boolean offerFirst(E e, long timeout, TimeUnit unit)
  376 + throws InterruptedException {
  377 + if (e == null) throw new NullPointerException();
  378 + Node<E> node = new Node<E>(e);
  379 + long nanos = unit.toNanos(timeout);
  380 + final ReentrantLock lock = this.lock;
  381 + lock.lockInterruptibly();
  382 + try {
  383 + while (!linkFirst(node)) {
  384 + if (nanos <= 0)
  385 + return false;
  386 + nanos = notFull.awaitNanos(nanos);
  387 + }
  388 + return true;
  389 + } finally {
  390 + lock.unlock();
  391 + }
  392 + }
  393 +
  394 + /**
  395 + * @throws NullPointerException {@inheritDoc}
  396 + * @throws InterruptedException {@inheritDoc}
  397 + */
  398 + public boolean offerLast(E e, long timeout, TimeUnit unit)
  399 + throws InterruptedException {
  400 + if (e == null) throw new NullPointerException();
  401 + Node<E> node = new Node<E>(e);
  402 + long nanos = unit.toNanos(timeout);
  403 + final ReentrantLock lock = this.lock;
  404 + lock.lockInterruptibly();
  405 + try {
  406 + while (!linkLast(node)) {
  407 + if (nanos <= 0)
  408 + return false;
  409 + nanos = notFull.awaitNanos(nanos);
  410 + }
  411 + return true;
  412 + } finally {
  413 + lock.unlock();
  414 + }
  415 + }
  416 +
  417 + /**
  418 + * @throws NoSuchElementException {@inheritDoc}
  419 + */
  420 + public E removeFirst() {
  421 + E x = pollFirst();
  422 + if (x == null) throw new NoSuchElementException();
  423 + return x;
  424 + }
  425 +
  426 + /**
  427 + * @throws NoSuchElementException {@inheritDoc}
  428 + */
  429 + public E removeLast() {
  430 + E x = pollLast();
  431 + if (x == null) throw new NoSuchElementException();
  432 + return x;
  433 + }
  434 +
  435 + public E pollFirst() {
  436 + final ReentrantLock lock = this.lock;
  437 + lock.lock();
  438 + try {
  439 + return unlinkFirst();
  440 + } finally {
  441 + lock.unlock();
  442 + }
  443 + }
  444 +
  445 + public E pollLast() {
  446 + final ReentrantLock lock = this.lock;
  447 + lock.lock();
  448 + try {
  449 + return unlinkLast();
  450 + } finally {
  451 + lock.unlock();
  452 + }
  453 + }
  454 +
  455 + public E takeFirst() throws InterruptedException {
  456 + final ReentrantLock lock = this.lock;
  457 + lock.lock();
  458 + try {
  459 + E x;
  460 + while ( (x = unlinkFirst()) == null)
  461 + notEmpty.await();
  462 + return x;
  463 + } finally {
  464 + lock.unlock();
  465 + }
  466 + }
  467 +
  468 + public E takeLast() throws InterruptedException {
  469 + final ReentrantLock lock = this.lock;
  470 + lock.lock();
  471 + try {
  472 + E x;
  473 + while ( (x = unlinkLast()) == null)
  474 + notEmpty.await();
  475 + return x;
  476 + } finally {
  477 + lock.unlock();
  478 + }
  479 + }
  480 +
  481 + public E pollFirst(long timeout, TimeUnit unit)
  482 + throws InterruptedException {
  483 + long nanos = unit.toNanos(timeout);
  484 + final ReentrantLock lock = this.lock;
  485 + lock.lockInterruptibly();
  486 + try {
  487 + E x;
  488 + while ( (x = unlinkFirst()) == null) {
  489 + if (nanos <= 0)
  490 + return null;
  491 + nanos = notEmpty.awaitNanos(nanos);
  492 + }
  493 + return x;
  494 + } finally {
  495 + lock.unlock();
  496 + }
  497 + }
  498 +
  499 + public E pollLast(long timeout, TimeUnit unit)
  500 + throws InterruptedException {
  501 + long nanos = unit.toNanos(timeout);
  502 + final ReentrantLock lock = this.lock;
  503 + lock.lockInterruptibly();
  504 + try {
  505 + E x;
  506 + while ( (x = unlinkLast()) == null) {
  507 + if (nanos <= 0)
  508 + return null;
  509 + nanos = notEmpty.awaitNanos(nanos);
  510 + }
  511 + return x;
  512 + } finally {
  513 + lock.unlock();
  514 + }
  515 + }
  516 +
  517 + /**
  518 + * @throws NoSuchElementException {@inheritDoc}
  519 + */
  520 + public E getFirst() {
  521 + E x = peekFirst();
  522 + if (x == null) throw new NoSuchElementException();
  523 + return x;
  524 + }
  525 +
  526 + /**
  527 + * @throws NoSuchElementException {@inheritDoc}
  528 + */
  529 + public E getLast() {
  530 + E x = peekLast();
  531 + if (x == null) throw new NoSuchElementException();
  532 + return x;
  533 + }
  534 +
  535 + public E peekFirst() {
  536 + final ReentrantLock lock = this.lock;
  537 + lock.lock();
  538 + try {
  539 + return (first == null) ? null : first.item;
  540 + } finally {
  541 + lock.unlock();
  542 + }
  543 + }
  544 +
  545 + public E peekLast() {
  546 + final ReentrantLock lock = this.lock;
  547 + lock.lock();
  548 + try {
  549 + return (last == null) ? null : last.item;
  550 + } finally {
  551 + lock.unlock();
  552 + }
  553 + }
  554 +
  555 + public boolean removeFirstOccurrence(Object o) {
  556 + if (o == null) return false;
  557 + final ReentrantLock lock = this.lock;
  558 + lock.lock();
  559 + try {
  560 + for (Node<E> p = first; p != null; p = p.next) {
  561 + if (o.equals(p.item)) {
  562 + unlink(p);
  563 + return true;
  564 + }
  565 + }
  566 + return false;
  567 + } finally {
  568 + lock.unlock();
  569 + }
  570 + }
  571 +
  572 + public boolean removeLastOccurrence(Object o) {
  573 + if (o == null) return false;
  574 + final ReentrantLock lock = this.lock;
  575 + lock.lock();
  576 + try {
  577 + for (Node<E> p = last; p != null; p = p.prev) {
  578 + if (o.equals(p.item)) {
  579 + unlink(p);
  580 + return true;
  581 + }
  582 + }
  583 + return false;
  584 + } finally {
  585 + lock.unlock();
  586 + }
  587 + }
  588 +
  589 + // BlockingQueue methods
  590 +
  591 + /**
  592 + * Inserts the specified element at the end of this deque unless it would
  593 + * violate capacity restrictions. When using a capacity-restricted deque,
  594 + * it is generally preferable to use method {@link #offer offer}.
  595 + *
  596 + * <p>This method is equivalent to {@link #addLast}.
  597 + *
  598 + * @throws IllegalStateException if the element cannot be added at this
  599 + * time due to capacity restrictions
  600 + * @throws NullPointerException if the specified element is null
  601 + */
  602 + public boolean add(E e) {
  603 + addLast(e);
  604 + return true;
  605 + }
  606 +
  607 + /**
  608 + * @throws NullPointerException if the specified element is null
  609 + */
  610 + public boolean offer(E e) {
  611 + return offerLast(e);
  612 + }
  613 +
  614 + /**
  615 + * @throws NullPointerException {@inheritDoc}
  616 + * @throws InterruptedException {@inheritDoc}
  617 + */
  618 + public void put(E e) throws InterruptedException {
  619 + putLast(e);
  620 + }
  621 +
  622 + /**
  623 + * @throws NullPointerException {@inheritDoc}
  624 + * @throws InterruptedException {@inheritDoc}
  625 + */
  626 + public boolean offer(E e, long timeout, TimeUnit unit)
  627 + throws InterruptedException {
  628 + return offerLast(e, timeout, unit);
  629 + }
  630 +
  631 + /**
  632 + * Retrieves and removes the head of the queue represented by this deque.
  633 + * This method differs from {@link #poll poll} only in that it throws an
  634 + * exception if this deque is empty.
  635 + *
  636 + * <p>This method is equivalent to {@link #removeFirst() removeFirst}.
  637 + *
  638 + * @return the head of the queue represented by this deque
  639 + * @throws NoSuchElementException if this deque is empty
  640 + */
  641 + public E remove() {
  642 + return removeFirst();
  643 + }
  644 +
  645 + public E poll() {
  646 + return pollFirst();
  647 + }
  648 +
  649 + public E take() throws InterruptedException {
  650 + return takeFirst();
  651 + }
  652 +
  653 + public E poll(long timeout, TimeUnit unit) throws InterruptedException {
  654 + return pollFirst(timeout, unit);
  655 + }
  656 +
  657 + /**
  658 + * Retrieves, but does not remove, the head of the queue represented by
  659 + * this deque. This method differs from {@link #peek peek} only in that
  660 + * it throws an exception if this deque is empty.
  661 + *
  662 + * <p>This method is equivalent to {@link #getFirst() getFirst}.
  663 + *
  664 + * @return the head of the queue represented by this deque
  665 + * @throws NoSuchElementException if this deque is empty
  666 + */
  667 + public E element() {
  668 + return getFirst();
  669 + }
  670 +
  671 + public E peek() {
  672 + return peekFirst();
  673 + }
  674 +
  675 + /**
  676 + * Returns the number of additional elements that this deque can ideally
  677 + * (in the absence of memory or resource constraints) accept without
  678 + * blocking. This is always equal to the initial capacity of this deque
  679 + * less the current {@code size} of this deque.
  680 + *
  681 + * <p>Note that you <em>cannot</em> always tell if an attempt to insert
  682 + * an element will succeed by inspecting {@code remainingCapacity}
  683 + * because it may be the case that another thread is about to
  684 + * insert or remove an element.
  685 + */
  686 + public int remainingCapacity() {
  687 + final ReentrantLock lock = this.lock;
  688 + lock.lock();
  689 + try {
  690 + return capacity - count;
  691 + } finally {
  692 + lock.unlock();
  693 + }
  694 + }
  695 +
  696 + /**
  697 + * @throws UnsupportedOperationException {@inheritDoc}
  698 + * @throws ClassCastException {@inheritDoc}
  699 + * @throws NullPointerException {@inheritDoc}
  700 + * @throws IllegalArgumentException {@inheritDoc}
  701 + */
  702 + public int drainTo(Collection<? super E> c) {
  703 + return drainTo(c, Integer.MAX_VALUE);
  704 + }
  705 +
  706 + /**
  707 + * @throws UnsupportedOperationException {@inheritDoc}
  708 + * @throws ClassCastException {@inheritDoc}
  709 + * @throws NullPointerException {@inheritDoc}
  710 + * @throws IllegalArgumentException {@inheritDoc}
  711 + */
  712 + public int drainTo(Collection<? super E> c, int maxElements) {
  713 + if (c == null)
  714 + throw new NullPointerException();
  715 + if (c == this)
  716 + throw new IllegalArgumentException();
  717 + final ReentrantLock lock = this.lock;
  718 + lock.lock();
  719 + try {
  720 + int n = Math.min(maxElements, count);
  721 + for (int i = 0; i < n; i++) {
  722 + c.add(first.item); // In this order, in case add() throws.
  723 + unlinkFirst();
  724 + }
  725 + return n;
  726 + } finally {
  727 + lock.unlock();
  728 + }
  729 + }
  730 +
  731 + // Stack methods
  732 +
  733 + /**
  734 + * @throws IllegalStateException {@inheritDoc}
  735 + * @throws NullPointerException {@inheritDoc}
  736 + */
  737 + public void push(E e) {
  738 + addFirst(e);
  739 + }
  740 +
  741 + /**
  742 + * @throws NoSuchElementException {@inheritDoc}
  743 + */
  744 + public E pop() {
  745 + return removeFirst();
  746 + }
  747 +
  748 + // Collection methods
  749 +
  750 + /**
  751 + * Removes the first occurrence of the specified element from this deque.
  752 + * If the deque does not contain the element, it is unchanged.
  753 + * More formally, removes the first element {@code e} such that
  754 + * {@code o.equals(e)} (if such an element exists).
  755 + * Returns {@code true} if this deque contained the specified element
  756 + * (or equivalently, if this deque changed as a result of the call).
  757 + *
  758 + * <p>This method is equivalent to
  759 + * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}.
  760 + *
  761 + * @param o element to be removed from this deque, if present
  762 + * @return {@code true} if this deque changed as a result of the call
  763 + */
  764 + public boolean remove(Object o) {
  765 + return removeFirstOccurrence(o);
  766 + }
  767 +
  768 + /**
  769 + * Returns the number of elements in this deque.
  770 + *
  771 + * @return the number of elements in this deque
  772 + */
  773 + public int size() {
  774 + final ReentrantLock lock = this.lock;
  775 + lock.lock();
  776 + try {
  777 + return count;
  778 + } finally {
  779 + lock.unlock();
  780 + }
  781 + }
  782 +
  783 + /**
  784 + * Returns {@code true} if this deque contains the specified element.
  785 + * More formally, returns {@code true} if and only if this deque contains
  786 + * at least one element {@code e} such that {@code o.equals(e)}.
  787 + *
  788 + * @param o object to be checked for containment in this deque
  789 + * @return {@code true} if this deque contains the specified element
  790 + */
  791 + public boolean contains(Object o) {
  792 + if (o == null) return false;
  793 + final ReentrantLock lock = this.lock;
  794 + lock.lock();
  795 + try {
  796 + for (Node<E> p = first; p != null; p = p.next)
  797 + if (o.equals(p.item))
  798 + return true;
  799 + return false;
  800 + } finally {
  801 + lock.unlock();
  802 + }
  803 + }
  804 +
  805 + /*
  806 + * TODO: Add support for more efficient bulk operations.
  807 + *
  808 + * We don't want to acquire the lock for every iteration, but we
  809 + * also want other threads a chance to interact with the
  810 + * collection, especially when count is close to capacity.
  811 + */
  812 +
  813 +// /**
  814 +// * Adds all of the elements in the specified collection to this
  815 +// * queue. Attempts to addAll of a queue to itself result in
  816 +// * {@code IllegalArgumentException}. Further, the behavior of
  817 +// * this operation is undefined if the specified collection is
  818 +// * modified while the operation is in progress.
  819 +// *
  820 +// * @param c collection containing elements to be added to this queue
  821 +// * @return {@code true} if this queue changed as a result of the call
  822 +// * @throws ClassCastException {@inheritDoc}
  823 +// * @throws NullPointerException {@inheritDoc}
  824 +// * @throws IllegalArgumentException {@inheritDoc}
  825 +// * @throws IllegalStateException {@inheritDoc}
  826 +// * @see #add(Object)
  827 +// */
  828 +// public boolean addAll(Collection<? extends E> c) {
  829 +// if (c == null)
  830 +// throw new NullPointerException();
  831 +// if (c == this)
  832 +// throw new IllegalArgumentException();
  833 +// final ReentrantLock lock = this.lock;
  834 +// lock.lock();
  835 +// try {
  836 +// boolean modified = false;
  837 +// for (E e : c)
  838 +// if (linkLast(e))
  839 +// modified = true;
  840 +// return modified;
  841 +// } finally {
  842 +// lock.unlock();
  843 +// }
  844 +// }
  845 +
  846 + /**
  847 + * Returns an array containing all of the elements in this deque, in
  848 + * proper sequence (from first to last element).
  849 + *
  850 + * <p>The returned array will be "safe" in that no references to it are
  851 + * maintained by this deque. (In other words, this method must allocate
  852 + * a new array). The caller is thus free to modify the returned array.
  853 + *
  854 + * <p>This method acts as bridge between array-based and collection-based
  855 + * APIs.
  856 + *
  857 + * @return an array containing all of the elements in this deque
  858 + */
  859 + public Object[] toArray() {
  860 + final ReentrantLock lock = this.lock;
  861 + lock.lock();
  862 + try {
  863 + Object[] a = new Object[count];
  864 + int k = 0;
  865 + for (Node<E> p = first; p != null; p = p.next)
  866 + a[k++] = p.item;
  867 + return a;
  868 + } finally {
  869 + lock.unlock();
  870 + }
  871 + }
  872 +
  873 + /**
  874 + * Returns an array containing all of the elements in this deque, in
  875 + * proper sequence; the runtime type of the returned array is that of
  876 + * the specified array. If the deque fits in the specified array, it
  877 + * is returned therein. Otherwise, a new array is allocated with the
  878 + * runtime type of the specified array and the size of this deque.
  879 + *
  880 + * <p>If this deque fits in the specified array with room to spare
  881 + * (i.e., the array has more elements than this deque), the element in
  882 + * the array immediately following the end of the deque is set to
  883 + * {@code null}.
  884 + *
  885 + * <p>Like the {@link #toArray()} method, this method acts as bridge between
  886 + * array-based and collection-based APIs. Further, this method allows
  887 + * precise control over the runtime type of the output array, and may,
  888 + * under certain circumstances, be used to save allocation costs.
  889 + *
  890 + * <p>Suppose {@code x} is a deque known to contain only strings.
  891 + * The following code can be used to dump the deque into a newly
  892 + * allocated array of {@code String}:
  893 + *
  894 + * <pre>
  895 + * String[] y = x.toArray(new String[0]);</pre>
  896 + *
  897 + * Note that {@code toArray(new Object[0])} is identical in function to
  898 + * {@code toArray()}.
  899 + *
  900 + * @param a the array into which the elements of the deque are to
  901 + * be stored, if it is big enough; otherwise, a new array of the
  902 + * same runtime type is allocated for this purpose
  903 + * @return an array containing all of the elements in this deque
  904 + * @throws ArrayStoreException if the runtime type of the specified array
  905 + * is not a supertype of the runtime type of every element in
  906 + * this deque
  907 + * @throws NullPointerException if the specified array is null
  908 + */
  909 + @SuppressWarnings("unchecked")
  910 + public <T> T[] toArray(T[] a) {
  911 + final ReentrantLock lock = this.lock;
  912 + lock.lock();
  913 + try {
  914 + if (a.length < count)
  915 + a = (T[])java.lang.reflect.Array.newInstance
  916 + (a.getClass().getComponentType(), count);
  917 +
  918 + int k = 0;
  919 + for (Node<E> p = first; p != null; p = p.next)
  920 + a[k++] = (T)p.item;
  921 + if (a.length > k)
  922 + a[k] = null;
  923 + return a;
  924 + } finally {
  925 + lock.unlock();
  926 + }
  927 + }
  928 +
  929 + public String toString() {
  930 + final ReentrantLock lock = this.lock;
  931 + lock.lock();
  932 + try {
  933 + Node<E> p = first;
  934 + if (p == null)
  935 + return "[]";
  936 +
  937 + StringBuilder sb = new StringBuilder();
  938 + sb.append('[');
  939 + for (;;) {
  940 + E e = p.item;
  941 + sb.append(e == this ? "(this Collection)" : e);
  942 + p = p.next;
  943 + if (p == null)
  944 + return sb.append(']').toString();
  945 + sb.append(',').append(' ');
  946 + }
  947 + } finally {
  948 + lock.unlock();
  949 + }
  950 + }
  951 +
  952 + /**
  953 + * Atomically removes all of the elements from this deque.
  954 + * The deque will be empty after this call returns.
  955 + */
  956 + public void clear() {
  957 + final ReentrantLock lock = this.lock;
  958 + lock.lock();
  959 + try {
  960 + for (Node<E> f = first; f != null; ) {
  961 + f.item = null;
  962 + Node<E> n = f.next;
  963 + f.prev = null;
  964 + f.next = null;
  965 + f = n;
  966 + }
  967 + first = last = null;
  968 + count = 0;
  969 + notFull.signalAll();
  970 + } finally {
  971 + lock.unlock();
  972 + }
  973 + }
  974 +
  975 + /**
  976 + * Returns an iterator over the elements in this deque in proper sequence.
  977 + * The elements will be returned in order from first (head) to last (tail).
  978 + *
  979 + * <p>The returned iterator is a "weakly consistent" iterator that
  980 + * will never throw {@link java.util.ConcurrentModificationException
  981 + * ConcurrentModificationException}, and guarantees to traverse
  982 + * elements as they existed upon construction of the iterator, and
  983 + * may (but is not guaranteed to) reflect any modifications
  984 + * subsequent to construction.
  985 + *
  986 + * @return an iterator over the elements in this deque in proper sequence
  987 + */
  988 + public Iterator<E> iterator() {
  989 + return new Itr();
  990 + }
  991 +
  992 + /**
  993 + * Returns an iterator over the elements in this deque in reverse
  994 + * sequential order. The elements will be returned in order from
  995 + * last (tail) to first (head).
  996 + *
  997 + * <p>The returned iterator is a "weakly consistent" iterator that
  998 + * will never throw {@link java.util.ConcurrentModificationException
  999 + * ConcurrentModificationException}, and guarantees to traverse
  1000 + * elements as they existed upon construction of the iterator, and
  1001 + * may (but is not guaranteed to) reflect any modifications
  1002 + * subsequent to construction.
  1003 + *
  1004 + * @return an iterator over the elements in this deque in reverse order
  1005 + */
  1006 + public Iterator<E> descendingIterator() {
  1007 + return new DescendingItr();
  1008 + }
  1009 +
  1010 + /**
  1011 + * Base class for Iterators for LinkedBlockingDeque
  1012 + */
  1013 + private abstract class AbstractItr implements Iterator<E> {
  1014 + /**
  1015 + * The next node to return in next()
  1016 + */
  1017 + Node<E> next;
  1018 +
  1019 + /**
  1020 + * nextItem holds on to item fields because once we claim that
  1021 + * an element exists in hasNext(), we must return item read
  1022 + * under lock (in advance()) even if it was in the process of
  1023 + * being removed when hasNext() was called.
  1024 + */
  1025 + E nextItem;
  1026 +
  1027 + /**
  1028 + * Node returned by most recent call to next. Needed by remove.
  1029 + * Reset to null if this element is deleted by a call to remove.
  1030 + */
  1031 + private Node<E> lastRet;
  1032 +
  1033 + abstract Node<E> firstNode();
  1034 + abstract Node<E> nextNode(Node<E> n);
  1035 +
  1036 + AbstractItr() {
  1037 + // set to initial position
  1038 + final ReentrantLock lock = LinkedBlockingDeque.this.lock;
  1039 + lock.lock();
  1040 + try {
  1041 + next = firstNode();
  1042 + nextItem = (next == null) ? null : next.item;
  1043 + } finally {
  1044 + lock.unlock();
  1045 + }
  1046 + }
  1047 +
  1048 + /**
  1049 + * Returns the successor node of the given non-null, but
  1050 + * possibly previously deleted, node.
  1051 + */
  1052 + private Node<E> succ(Node<E> n) {
  1053 + // Chains of deleted nodes ending in null or self-links
  1054 + // are possible if multiple interior nodes are removed.
  1055 + for (;;) {
  1056 + Node<E> s = nextNode(n);
  1057 + if (s == null)
  1058 + return null;
  1059 + else if (s.item != null)
  1060 + return s;
  1061 + else if (s == n)
  1062 + return firstNode();
  1063 + else
  1064 + n = s;
  1065 + }
  1066 + }
  1067 +
  1068 + /**
  1069 + * Advances next.
  1070 + */
  1071 + void advance() {
  1072 + final ReentrantLock lock = LinkedBlockingDeque.this.lock;
  1073 + lock.lock();
  1074 + try {
  1075 + // assert next != null;
  1076 + next = succ(next);
  1077 + nextItem = (next == null) ? null : next.item;
  1078 + } finally {
  1079 + lock.unlock();
  1080 + }
  1081 + }
  1082 +
  1083 + public boolean hasNext() {
  1084 + return next != null;
  1085 + }
  1086 +
  1087 + public E next() {
  1088 + if (next == null)
  1089 + throw new NoSuchElementException();
  1090 + lastRet = next;
  1091 + E x = nextItem;
  1092 + advance();
  1093 + return x;
  1094 + }
  1095 +
  1096 + public void remove() {
  1097 + Node<E> n = lastRet;
  1098 + if (n == null)
  1099 + throw new IllegalStateException();
  1100 + lastRet = null;
  1101 + final ReentrantLock lock = LinkedBlockingDeque.this.lock;
  1102 + lock.lock();
  1103 + try {
  1104 + if (n.item != null)
  1105 + unlink(n);
  1106 + } finally {
  1107 + lock.unlock();
  1108 + }
  1109 + }
  1110 + }
  1111 +
  1112 + /** Forward iterator */
  1113 + private class Itr extends AbstractItr {
  1114 + Node<E> firstNode() { return first; }
  1115 + Node<E> nextNode(Node<E> n) { return n.next; }
  1116 + }
  1117 +
  1118 + /** Descending iterator */
  1119 + private class DescendingItr extends AbstractItr {
  1120 + Node<E> firstNode() { return last; }
  1121 + Node<E> nextNode(Node<E> n) { return n.prev; }
  1122 + }
  1123 +
  1124 + /**
  1125 + * Save the state of this deque to a stream (that is, serialize it).
  1126 + *
  1127 + * @serialData The capacity (int), followed by elements (each an
  1128 + * {@code Object}) in the proper order, followed by a null
  1129 + * @param s the stream
  1130 + */
  1131 + private void writeObject(java.io.ObjectOutputStream s)
  1132 + throws java.io.IOException {
  1133 + final ReentrantLock lock = this.lock;
  1134 + lock.lock();
  1135 + try {
  1136 + // Write out capacity and any hidden stuff
  1137 + s.defaultWriteObject();
  1138 + // Write out all elements in the proper order.
  1139 + for (Node<E> p = first; p != null; p = p.next)
  1140 + s.writeObject(p.item);
  1141 + // Use trailing null as sentinel
  1142 + s.writeObject(null);
  1143 + } finally {
  1144 + lock.unlock();
  1145 + }
  1146 + }
  1147 +
  1148 + /**
  1149 + * Reconstitute this deque from a stream (that is,
  1150 + * deserialize it).
  1151 + * @param s the stream
  1152 + */
  1153 + private void readObject(java.io.ObjectInputStream s)
  1154 + throws java.io.IOException, ClassNotFoundException {
  1155 + s.defaultReadObject();
  1156 + count = 0;
  1157 + first = null;
  1158 + last = null;
  1159 + // Read in all elements and place in queue
  1160 + for (;;) {
  1161 + @SuppressWarnings("unchecked")
  1162 + E item = (E)s.readObject();
  1163 + if (item == null)
  1164 + break;
  1165 + add(item);
  1166 + }
  1167 + }
  1168 +
  1169 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.decode;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.graphics.BitmapFactory;
  20 +import android.graphics.BitmapFactory.Options;
  21 +import android.graphics.Matrix;
  22 +import android.media.ExifInterface;
  23 +import com.nostra13.universalimageloader.core.assist.ImageScaleType;
  24 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  25 +import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme;
  26 +import com.nostra13.universalimageloader.utils.ImageSizeUtils;
  27 +import com.nostra13.universalimageloader.utils.IoUtils;
  28 +import com.nostra13.universalimageloader.utils.L;
  29 +
  30 +import java.io.IOException;
  31 +import java.io.InputStream;
  32 +
  33 +/**
  34 + * Decodes images to {@link Bitmap}, scales them to needed size
  35 + *
  36 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  37 + * @see ImageDecodingInfo
  38 + * @since 1.8.3
  39 + */
  40 +public class BaseImageDecoder implements ImageDecoder {
  41 +
  42 + protected static final String LOG_SUBSAMPLE_IMAGE = "Subsample original image (%1$s) to %2$s (scale = %3$d) [%4$s]";
  43 + protected static final String LOG_SCALE_IMAGE = "Scale subsampled image (%1$s) to %2$s (scale = %3$.5f) [%4$s]";
  44 + protected static final String LOG_ROTATE_IMAGE = "Rotate image on %1$d\u00B0 [%2$s]";
  45 + protected static final String LOG_FLIP_IMAGE = "Flip image horizontally [%s]";
  46 + protected static final String ERROR_NO_IMAGE_STREAM = "No stream for image [%s]";
  47 + protected static final String ERROR_CANT_DECODE_IMAGE = "Image can't be decoded [%s]";
  48 +
  49 + protected final boolean loggingEnabled;
  50 +
  51 + /**
  52 + * @param loggingEnabled Whether debug logs will be written to LogCat. Usually should match {@link
  53 + * com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#writeDebugLogs()
  54 + * ImageLoaderConfiguration.writeDebugLogs()}
  55 + */
  56 + public BaseImageDecoder(boolean loggingEnabled) {
  57 + this.loggingEnabled = loggingEnabled;
  58 + }
  59 +
  60 + /**
  61 + * Decodes image from URI into {@link Bitmap}. Image is scaled close to incoming {@linkplain ImageSize target size}
  62 + * during decoding (depend on incoming parameters).
  63 + *
  64 + * @param decodingInfo Needed data for decoding image
  65 + * @return Decoded bitmap
  66 + * @throws IOException if some I/O exception occurs during image reading
  67 + * @throws UnsupportedOperationException if image URI has unsupported scheme(protocol)
  68 + */
  69 + @Override
  70 + public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {
  71 + Bitmap decodedBitmap;
  72 + ImageFileInfo imageInfo;
  73 +
  74 + InputStream imageStream = getImageStream(decodingInfo);
  75 + if (imageStream == null) {
  76 + L.e(ERROR_NO_IMAGE_STREAM, decodingInfo.getImageKey());
  77 + return null;
  78 + }
  79 + try {
  80 + imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo);
  81 + imageStream = resetStream(imageStream, decodingInfo);
  82 + Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo);
  83 + decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);
  84 + } finally {
  85 + IoUtils.closeSilently(imageStream);
  86 + }
  87 +
  88 + if (decodedBitmap == null) {
  89 + L.e(ERROR_CANT_DECODE_IMAGE, decodingInfo.getImageKey());
  90 + } else {
  91 + decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation,
  92 + imageInfo.exif.flipHorizontal);
  93 + }
  94 + return decodedBitmap;
  95 + }
  96 +
  97 + protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException {
  98 + return decodingInfo.getDownloader().getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader());
  99 + }
  100 +
  101 + protected ImageFileInfo defineImageSizeAndRotation(InputStream imageStream, ImageDecodingInfo decodingInfo)
  102 + throws IOException {
  103 + Options options = new Options();
  104 + options.inJustDecodeBounds = true;
  105 + BitmapFactory.decodeStream(imageStream, null, options);
  106 +
  107 + ExifInfo exif;
  108 + String imageUri = decodingInfo.getImageUri();
  109 + if (decodingInfo.shouldConsiderExifParams() && canDefineExifParams(imageUri, options.outMimeType)) {
  110 + exif = defineExifOrientation(imageUri);
  111 + } else {
  112 + exif = new ExifInfo();
  113 + }
  114 + return new ImageFileInfo(new ImageSize(options.outWidth, options.outHeight, exif.rotation), exif);
  115 + }
  116 +
  117 + private boolean canDefineExifParams(String imageUri, String mimeType) {
  118 + return "image/jpeg".equalsIgnoreCase(mimeType) && (Scheme.ofUri(imageUri) == Scheme.FILE);
  119 + }
  120 +
  121 + protected ExifInfo defineExifOrientation(String imageUri) {
  122 + int rotation = 0;
  123 + boolean flip = false;
  124 + try {
  125 + ExifInterface exif = new ExifInterface(Scheme.FILE.crop(imageUri));
  126 + int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
  127 + switch (exifOrientation) {
  128 + case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
  129 + flip = true;
  130 + case ExifInterface.ORIENTATION_NORMAL:
  131 + rotation = 0;
  132 + break;
  133 + case ExifInterface.ORIENTATION_TRANSVERSE:
  134 + flip = true;
  135 + case ExifInterface.ORIENTATION_ROTATE_90:
  136 + rotation = 90;
  137 + break;
  138 + case ExifInterface.ORIENTATION_FLIP_VERTICAL:
  139 + flip = true;
  140 + case ExifInterface.ORIENTATION_ROTATE_180:
  141 + rotation = 180;
  142 + break;
  143 + case ExifInterface.ORIENTATION_TRANSPOSE:
  144 + flip = true;
  145 + case ExifInterface.ORIENTATION_ROTATE_270:
  146 + rotation = 270;
  147 + break;
  148 + }
  149 + } catch (IOException e) {
  150 + L.w("Can't read EXIF tags from file [%s]", imageUri);
  151 + }
  152 + return new ExifInfo(rotation, flip);
  153 + }
  154 +
  155 + protected Options prepareDecodingOptions(ImageSize imageSize, ImageDecodingInfo decodingInfo) {
  156 + ImageScaleType scaleType = decodingInfo.getImageScaleType();
  157 + int scale;
  158 + if (scaleType == ImageScaleType.NONE) {
  159 + scale = 1;
  160 + } else if (scaleType == ImageScaleType.NONE_SAFE) {
  161 + scale = ImageSizeUtils.computeMinImageSampleSize(imageSize);
  162 + } else {
  163 + ImageSize targetSize = decodingInfo.getTargetSize();
  164 + boolean powerOf2 = scaleType == ImageScaleType.IN_SAMPLE_POWER_OF_2;
  165 + scale = ImageSizeUtils.computeImageSampleSize(imageSize, targetSize, decodingInfo.getViewScaleType(), powerOf2);
  166 + }
  167 + if (scale > 1 && loggingEnabled) {
  168 + L.d(LOG_SUBSAMPLE_IMAGE, imageSize, imageSize.scaleDown(scale), scale, decodingInfo.getImageKey());
  169 + }
  170 +
  171 + Options decodingOptions = decodingInfo.getDecodingOptions();
  172 + decodingOptions.inSampleSize = scale;
  173 + return decodingOptions;
  174 + }
  175 +
  176 + protected InputStream resetStream(InputStream imageStream, ImageDecodingInfo decodingInfo) throws IOException {
  177 + try {
  178 + imageStream.reset();
  179 + } catch (IOException e) {
  180 + IoUtils.closeSilently(imageStream);
  181 + imageStream = getImageStream(decodingInfo);
  182 + }
  183 + return imageStream;
  184 + }
  185 +
  186 + protected Bitmap considerExactScaleAndOrientatiton(Bitmap subsampledBitmap, ImageDecodingInfo decodingInfo,
  187 + int rotation, boolean flipHorizontal) {
  188 + Matrix m = new Matrix();
  189 + // Scale to exact size if need
  190 + ImageScaleType scaleType = decodingInfo.getImageScaleType();
  191 + if (scaleType == ImageScaleType.EXACTLY || scaleType == ImageScaleType.EXACTLY_STRETCHED) {
  192 + ImageSize srcSize = new ImageSize(subsampledBitmap.getWidth(), subsampledBitmap.getHeight(), rotation);
  193 + float scale = ImageSizeUtils.computeImageScale(srcSize, decodingInfo.getTargetSize(), decodingInfo
  194 + .getViewScaleType(), scaleType == ImageScaleType.EXACTLY_STRETCHED);
  195 + if (Float.compare(scale, 1f) != 0) {
  196 + m.setScale(scale, scale);
  197 +
  198 + if (loggingEnabled) {
  199 + L.d(LOG_SCALE_IMAGE, srcSize, srcSize.scale(scale), scale, decodingInfo.getImageKey());
  200 + }
  201 + }
  202 + }
  203 + // Flip bitmap if need
  204 + if (flipHorizontal) {
  205 + m.postScale(-1, 1);
  206 +
  207 + if (loggingEnabled) L.d(LOG_FLIP_IMAGE, decodingInfo.getImageKey());
  208 + }
  209 + // Rotate bitmap if need
  210 + if (rotation != 0) {
  211 + m.postRotate(rotation);
  212 +
  213 + if (loggingEnabled) L.d(LOG_ROTATE_IMAGE, rotation, decodingInfo.getImageKey());
  214 + }
  215 +
  216 + Bitmap finalBitmap = Bitmap.createBitmap(subsampledBitmap, 0, 0, subsampledBitmap.getWidth(), subsampledBitmap
  217 + .getHeight(), m, true);
  218 + if (finalBitmap != subsampledBitmap) {
  219 + subsampledBitmap.recycle();
  220 + }
  221 + return finalBitmap;
  222 + }
  223 +
  224 + protected static class ExifInfo {
  225 +
  226 + public final int rotation;
  227 + public final boolean flipHorizontal;
  228 +
  229 + protected ExifInfo() {
  230 + this.rotation = 0;
  231 + this.flipHorizontal = false;
  232 + }
  233 +
  234 + protected ExifInfo(int rotation, boolean flipHorizontal) {
  235 + this.rotation = rotation;
  236 + this.flipHorizontal = flipHorizontal;
  237 + }
  238 + }
  239 +
  240 + protected static class ImageFileInfo {
  241 +
  242 + public final ImageSize imageSize;
  243 + public final ExifInfo exif;
  244 +
  245 + protected ImageFileInfo(ImageSize imageSize, ExifInfo exif) {
  246 + this.imageSize = imageSize;
  247 + this.exif = exif;
  248 + }
  249 + }
  250 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.decode;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import java.io.IOException;
  21 +
  22 +/**
  23 + * Provide decoding image to result {@link Bitmap}.
  24 + *
  25 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  26 + * @see ImageDecodingInfo
  27 + * @since 1.8.3
  28 + */
  29 +public interface ImageDecoder {
  30 +
  31 + /**
  32 + * Decodes image to {@link Bitmap} according target size and other parameters.
  33 + *
  34 + * @param imageDecodingInfo
  35 + * @return
  36 + * @throws IOException
  37 + */
  38 + Bitmap decode(ImageDecodingInfo imageDecodingInfo) throws IOException;
  39 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2013-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.decode;
  17 +
  18 +import android.annotation.TargetApi;
  19 +import android.graphics.BitmapFactory.Options;
  20 +import android.os.Build;
  21 +
  22 +import com.nostra13.universalimageloader.core.DisplayImageOptions;
  23 +import com.nostra13.universalimageloader.core.assist.ImageScaleType;
  24 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  25 +import com.nostra13.universalimageloader.core.assist.ViewScaleType;
  26 +import com.nostra13.universalimageloader.core.download.ImageDownloader;
  27 +
  28 +/**
  29 + * Contains needed information for decoding image to Bitmap
  30 + *
  31 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  32 + * @since 1.8.3
  33 + */
  34 +public class ImageDecodingInfo {
  35 +
  36 + private final String imageKey;
  37 + private final String imageUri;
  38 + private final String originalImageUri;
  39 + private final ImageSize targetSize;
  40 +
  41 + private final ImageScaleType imageScaleType;
  42 + private final ViewScaleType viewScaleType;
  43 +
  44 + private final ImageDownloader downloader;
  45 + private final Object extraForDownloader;
  46 +
  47 + private final boolean considerExifParams;
  48 + private final Options decodingOptions;
  49 +
  50 + public ImageDecodingInfo(String imageKey, String imageUri, String originalImageUri, ImageSize targetSize, ViewScaleType viewScaleType,
  51 + ImageDownloader downloader, DisplayImageOptions displayOptions) {
  52 + this.imageKey = imageKey;
  53 + this.imageUri = imageUri;
  54 + this.originalImageUri = originalImageUri;
  55 + this.targetSize = targetSize;
  56 +
  57 + this.imageScaleType = displayOptions.getImageScaleType();
  58 + this.viewScaleType = viewScaleType;
  59 +
  60 + this.downloader = downloader;
  61 + this.extraForDownloader = displayOptions.getExtraForDownloader();
  62 +
  63 + considerExifParams = displayOptions.isConsiderExifParams();
  64 + decodingOptions = new Options();
  65 + copyOptions(displayOptions.getDecodingOptions(), decodingOptions);
  66 + }
  67 +
  68 + private void copyOptions(Options srcOptions, Options destOptions) {
  69 + destOptions.inDensity = srcOptions.inDensity;
  70 + destOptions.inDither = srcOptions.inDither;
  71 + destOptions.inInputShareable = srcOptions.inInputShareable;
  72 + destOptions.inJustDecodeBounds = srcOptions.inJustDecodeBounds;
  73 + destOptions.inPreferredConfig = srcOptions.inPreferredConfig;
  74 + destOptions.inPurgeable = srcOptions.inPurgeable;
  75 + destOptions.inSampleSize = srcOptions.inSampleSize;
  76 + destOptions.inScaled = srcOptions.inScaled;
  77 + destOptions.inScreenDensity = srcOptions.inScreenDensity;
  78 + destOptions.inTargetDensity = srcOptions.inTargetDensity;
  79 + destOptions.inTempStorage = srcOptions.inTempStorage;
  80 + if (Build.VERSION.SDK_INT >= 10) copyOptions10(srcOptions, destOptions);
  81 + if (Build.VERSION.SDK_INT >= 11) copyOptions11(srcOptions, destOptions);
  82 + }
  83 +
  84 + @TargetApi(10)
  85 + private void copyOptions10(Options srcOptions, Options destOptions) {
  86 + destOptions.inPreferQualityOverSpeed = srcOptions.inPreferQualityOverSpeed;
  87 + }
  88 +
  89 + @TargetApi(11)
  90 + private void copyOptions11(Options srcOptions, Options destOptions) {
  91 + destOptions.inBitmap = srcOptions.inBitmap;
  92 + destOptions.inMutable = srcOptions.inMutable;
  93 + }
  94 +
  95 + /** @return Original {@linkplain com.nostra13.universalimageloader.utils.MemoryCacheUtils#generateKey(String, ImageSize) image key} (used in memory cache). */
  96 + public String getImageKey() {
  97 + return imageKey;
  98 + }
  99 +
  100 + /** @return Image URI for decoding (usually image from disk cache) */
  101 + public String getImageUri() {
  102 + return imageUri;
  103 + }
  104 +
  105 + /** @return The original image URI which was passed to ImageLoader */
  106 + public String getOriginalImageUri() {
  107 + return originalImageUri;
  108 + }
  109 +
  110 + /**
  111 + * @return Target size for image. Decoded bitmap should close to this size according to {@linkplain ImageScaleType
  112 + * image scale type} and {@linkplain ViewScaleType view scale type}.
  113 + */
  114 + public ImageSize getTargetSize() {
  115 + return targetSize;
  116 + }
  117 +
  118 + /**
  119 + * @return {@linkplain ImageScaleType Scale type for image sampling and scaling}. This parameter affects result size
  120 + * of decoded bitmap.
  121 + */
  122 + public ImageScaleType getImageScaleType() {
  123 + return imageScaleType;
  124 + }
  125 +
  126 + /** @return {@linkplain ViewScaleType View scale type}. This parameter affects result size of decoded bitmap. */
  127 + public ViewScaleType getViewScaleType() {
  128 + return viewScaleType;
  129 + }
  130 +
  131 + /** @return Downloader for image loading */
  132 + public ImageDownloader getDownloader() {
  133 + return downloader;
  134 + }
  135 +
  136 + /** @return Auxiliary object for downloader */
  137 + public Object getExtraForDownloader() {
  138 + return extraForDownloader;
  139 + }
  140 +
  141 + /** @return <b>true</b> - if EXIF params of image should be considered; <b>false</b> - otherwise */
  142 + public boolean shouldConsiderExifParams() {
  143 + return considerExifParams;
  144 + }
  145 +
  146 + /** @return Decoding options */
  147 + public Options getDecodingOptions() {
  148 + return decodingOptions;
  149 + }
  150 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.display;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  20 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  21 +
  22 +/**
  23 + * Displays {@link Bitmap} in {@link ImageAware}. Implementations can
  24 + * apply some changes to Bitmap or any animation for displaying Bitmap.<br />
  25 + * Implementations have to be thread-safe.
  26 + *
  27 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  28 + * @see ImageAware
  29 + * @see LoadedFrom
  30 + * @since 1.5.6
  31 + */
  32 +public interface BitmapDisplayer {
  33 + /**
  34 + * Displays bitmap in {@link ImageAware}.
  35 + * <b>NOTE:</b> This method is called on UI thread so it's strongly recommended not to do any heavy work in it.
  36 + *
  37 + * @param bitmap Source bitmap
  38 + * @param imageAware {@linkplain ImageAware Image aware view} to
  39 + * display Bitmap
  40 + * @param loadedFrom Source of loaded image
  41 + */
  42 + void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom);
  43 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich, Daniel Martí
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.display;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.view.View;
  20 +import android.view.animation.AlphaAnimation;
  21 +import android.view.animation.DecelerateInterpolator;
  22 +import android.widget.ImageView;
  23 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  24 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  25 +
  26 +/**
  27 + * Displays image with "fade in" animation
  28 + *
  29 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com), Daniel Martí
  30 + * @since 1.6.4
  31 + */
  32 +public class FadeInBitmapDisplayer implements BitmapDisplayer {
  33 +
  34 + private final int durationMillis;
  35 +
  36 + private final boolean animateFromNetwork;
  37 + private final boolean animateFromDisk;
  38 + private final boolean animateFromMemory;
  39 +
  40 + /**
  41 + * @param durationMillis Duration of "fade-in" animation (in milliseconds)
  42 + */
  43 + public FadeInBitmapDisplayer(int durationMillis) {
  44 + this(durationMillis, true, true, true);
  45 + }
  46 +
  47 + /**
  48 + * @param durationMillis Duration of "fade-in" animation (in milliseconds)
  49 + * @param animateFromNetwork Whether animation should be played if image is loaded from network
  50 + * @param animateFromDisk Whether animation should be played if image is loaded from disk cache
  51 + * @param animateFromMemory Whether animation should be played if image is loaded from memory cache
  52 + */
  53 + public FadeInBitmapDisplayer(int durationMillis, boolean animateFromNetwork, boolean animateFromDisk,
  54 + boolean animateFromMemory) {
  55 + this.durationMillis = durationMillis;
  56 + this.animateFromNetwork = animateFromNetwork;
  57 + this.animateFromDisk = animateFromDisk;
  58 + this.animateFromMemory = animateFromMemory;
  59 + }
  60 +
  61 + @Override
  62 + public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
  63 + imageAware.setImageBitmap(bitmap);
  64 +
  65 + if ((animateFromNetwork && loadedFrom == LoadedFrom.NETWORK) ||
  66 + (animateFromDisk && loadedFrom == LoadedFrom.DISC_CACHE) ||
  67 + (animateFromMemory && loadedFrom == LoadedFrom.MEMORY_CACHE)) {
  68 + animate(imageAware.getWrappedView(), durationMillis);
  69 + }
  70 + }
  71 +
  72 + /**
  73 + * Animates {@link ImageView} with "fade-in" effect
  74 + *
  75 + * @param imageView {@link ImageView} which display image in
  76 + * @param durationMillis The length of the animation in milliseconds
  77 + */
  78 + public static void animate(View imageView, int durationMillis) {
  79 + if (imageView != null) {
  80 + AlphaAnimation fadeImage = new AlphaAnimation(0, 1);
  81 + fadeImage.setDuration(durationMillis);
  82 + fadeImage.setInterpolator(new DecelerateInterpolator());
  83 + imageView.startAnimation(fadeImage);
  84 + }
  85 + }
  86 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.display;
  17 +
  18 +import android.graphics.*;
  19 +import android.graphics.drawable.Drawable;
  20 +
  21 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  22 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  23 +import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
  24 +
  25 +/**
  26 + * Can display bitmap with rounded corners. This implementation works only with ImageViews wrapped
  27 + * in ImageViewAware.
  28 + * <br />
  29 + * This implementation is inspired by
  30 + * <a href="http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/">
  31 + * Romain Guy's article</a>. It rounds images using custom drawable drawing. Original bitmap isn't changed.
  32 + * <br />
  33 + * <br />
  34 + * If this implementation doesn't meet your needs then consider
  35 + * <a href="https://github.com/vinc3m1/RoundedImageView">RoundedImageView</a> or
  36 + * <a href="https://github.com/Pkmmte/CircularImageView">CircularImageView</a> projects for usage.
  37 + *
  38 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  39 + * @since 1.5.6
  40 + */
  41 +public class RoundedBitmapDisplayer implements BitmapDisplayer {
  42 +
  43 + protected final int cornerRadius;
  44 + protected final int margin;
  45 +
  46 + public RoundedBitmapDisplayer(int cornerRadiusPixels) {
  47 + this(cornerRadiusPixels, 0);
  48 + }
  49 +
  50 + public RoundedBitmapDisplayer(int cornerRadiusPixels, int marginPixels) {
  51 + this.cornerRadius = cornerRadiusPixels;
  52 + this.margin = marginPixels;
  53 + }
  54 +
  55 + @Override
  56 + public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
  57 + if (!(imageAware instanceof ImageViewAware)) {
  58 + throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
  59 + }
  60 +
  61 + imageAware.setImageDrawable(new RoundedDrawable(bitmap, cornerRadius, margin));
  62 + }
  63 +
  64 + public static class RoundedDrawable extends Drawable {
  65 +
  66 + protected final float cornerRadius;
  67 + protected final int margin;
  68 +
  69 + protected final RectF mRect = new RectF(),
  70 + mBitmapRect;
  71 + protected final BitmapShader bitmapShader;
  72 + protected final Paint paint;
  73 +
  74 + public RoundedDrawable(Bitmap bitmap, int cornerRadius, int margin) {
  75 + this.cornerRadius = cornerRadius;
  76 + this.margin = margin;
  77 +
  78 + bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
  79 + mBitmapRect = new RectF (margin, margin, bitmap.getWidth() - margin, bitmap.getHeight() - margin);
  80 +
  81 + paint = new Paint();
  82 + paint.setAntiAlias(true);
  83 + paint.setShader(bitmapShader);
  84 + }
  85 +
  86 + @Override
  87 + protected void onBoundsChange(Rect bounds) {
  88 + super.onBoundsChange(bounds);
  89 + mRect.set(margin, margin, bounds.width() - margin, bounds.height() - margin);
  90 +
  91 + // Resize the original bitmap to fit the new bound
  92 + Matrix shaderMatrix = new Matrix();
  93 + shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL);
  94 + bitmapShader.setLocalMatrix(shaderMatrix);
  95 +
  96 + }
  97 +
  98 + @Override
  99 + public void draw(Canvas canvas) {
  100 + canvas.drawRoundRect(mRect, cornerRadius, cornerRadius, paint);
  101 + }
  102 +
  103 + @Override
  104 + public int getOpacity() {
  105 + return PixelFormat.TRANSLUCENT;
  106 + }
  107 +
  108 + @Override
  109 + public void setAlpha(int alpha) {
  110 + paint.setAlpha(alpha);
  111 + }
  112 +
  113 + @Override
  114 + public void setColorFilter(ColorFilter cf) {
  115 + paint.setColorFilter(cf);
  116 + }
  117 + }
  118 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.display;
  17 +
  18 +import android.graphics.*;
  19 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  20 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  21 +import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
  22 +
  23 +/**
  24 + * Can display bitmap with rounded corners and vignette effect. This implementation works only with ImageViews wrapped
  25 + * in ImageViewAware.
  26 + * <br />
  27 + * This implementation is inspired by
  28 + * <a href="http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/">
  29 + * Romain Guy's article</a>. It rounds images using custom drawable drawing. Original bitmap isn't changed.
  30 + * <br />
  31 + * <br />
  32 + * If this implementation doesn't meet your needs then consider
  33 + * <a href="https://github.com/vinc3m1/RoundedImageView">this project</a> for usage.
  34 + *
  35 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  36 + * @since 1.9.1
  37 + */
  38 +public class RoundedVignetteBitmapDisplayer extends RoundedBitmapDisplayer {
  39 +
  40 + public RoundedVignetteBitmapDisplayer(int cornerRadiusPixels, int marginPixels) {
  41 + super(cornerRadiusPixels, marginPixels);
  42 + }
  43 +
  44 + @Override
  45 + public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
  46 + if (!(imageAware instanceof ImageViewAware)) {
  47 + throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
  48 + }
  49 +
  50 + imageAware.setImageDrawable(new RoundedVignetteDrawable(bitmap, cornerRadius, margin));
  51 + }
  52 +
  53 + protected static class RoundedVignetteDrawable extends RoundedDrawable {
  54 +
  55 + RoundedVignetteDrawable(Bitmap bitmap, int cornerRadius, int margin) {
  56 + super(bitmap, cornerRadius, margin);
  57 + }
  58 +
  59 + @Override
  60 + protected void onBoundsChange(Rect bounds) {
  61 + super.onBoundsChange(bounds);
  62 + RadialGradient vignette = new RadialGradient(
  63 + mRect.centerX(), mRect.centerY() * 1.0f / 0.7f, mRect.centerX() * 1.3f,
  64 + new int[]{0, 0, 0x7f000000}, new float[]{0.0f, 0.7f, 1.0f},
  65 + Shader.TileMode.CLAMP);
  66 +
  67 + Matrix oval = new Matrix();
  68 + oval.setScale(1.0f, 0.7f);
  69 + vignette.setLocalMatrix(oval);
  70 +
  71 + paint.setShader(new ComposeShader(bitmapShader, vignette, PorterDuff.Mode.SRC_OVER));
  72 + }
  73 + }
  74 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.display;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  20 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  21 +
  22 +/**
  23 + * Just displays {@link Bitmap} in {@link ImageAware}
  24 + *
  25 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  26 + * @since 1.5.6
  27 + */
  28 +public final class SimpleBitmapDisplayer implements BitmapDisplayer {
  29 + @Override
  30 + public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
  31 + imageAware.setImageBitmap(bitmap);
  32 + }
  33 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + * <p>
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.download;
  17 +
  18 +import android.annotation.TargetApi;
  19 +import android.content.ContentResolver;
  20 +import android.content.Context;
  21 +import android.graphics.Bitmap;
  22 +import android.graphics.Bitmap.CompressFormat;
  23 +import android.media.ThumbnailUtils;
  24 +import android.net.Uri;
  25 +import android.os.Build;
  26 +import android.provider.ContactsContract;
  27 +import android.provider.MediaStore;
  28 +import android.webkit.MimeTypeMap;
  29 +import com.nostra13.universalimageloader.core.DisplayImageOptions;
  30 +import com.nostra13.universalimageloader.core.assist.ContentLengthInputStream;
  31 +import com.nostra13.universalimageloader.utils.IoUtils;
  32 +
  33 +import java.io.*;
  34 +import java.net.HttpURLConnection;
  35 +import java.net.URL;
  36 +import java.net.URLConnection;
  37 +
  38 +/**
  39 + * Provides retrieving of {@link InputStream} of image by URI from network or file system or app resources.<br />
  40 + * {@link URLConnection} is used to retrieve image stream from network.
  41 + *
  42 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  43 + * @since 1.8.0
  44 + */
  45 +public class BaseImageDownloader implements ImageDownloader {
  46 + /**
  47 + * {@value}
  48 + */
  49 + public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = 5 * 1000; // milliseconds
  50 + /**
  51 + * {@value}
  52 + */
  53 + public static final int DEFAULT_HTTP_READ_TIMEOUT = 20 * 1000; // milliseconds
  54 +
  55 + /**
  56 + * {@value}
  57 + */
  58 + protected static final int BUFFER_SIZE = 32 * 1024; // 32 Kb
  59 + /**
  60 + * {@value}
  61 + */
  62 + protected static final String ALLOWED_URI_CHARS = "@#&=*+-_.,:!?()/~'%";
  63 +
  64 + protected static final int MAX_REDIRECT_COUNT = 5;
  65 +
  66 + protected static final String CONTENT_CONTACTS_URI_PREFIX = "content://com.android.contacts/";
  67 +
  68 + private static final String ERROR_UNSUPPORTED_SCHEME = "UIL doesn't support scheme(protocol) by default [%s]. " + "You should implement this support yourself (BaseImageDownloader.getStreamFromOtherSource(...))";
  69 +
  70 + protected final Context context;
  71 + protected final int connectTimeout;
  72 + protected final int readTimeout;
  73 +
  74 + public BaseImageDownloader(Context context) {
  75 + this(context, DEFAULT_HTTP_CONNECT_TIMEOUT, DEFAULT_HTTP_READ_TIMEOUT);
  76 + }
  77 +
  78 + public BaseImageDownloader(Context context, int connectTimeout, int readTimeout) {
  79 + this.context = context.getApplicationContext();
  80 + this.connectTimeout = connectTimeout;
  81 + this.readTimeout = readTimeout;
  82 + }
  83 +
  84 + @Override
  85 + public InputStream getStream(String imageUri, Object extra) throws IOException {
  86 + switch (Scheme.ofUri(imageUri)) {
  87 + case HTTP:
  88 + case HTTPS:
  89 + return getStreamFromNetwork(imageUri, extra);
  90 + case FILE:
  91 + return getStreamFromFile(imageUri, extra);
  92 + case CONTENT:
  93 + return getStreamFromContent(imageUri, extra);
  94 + case ASSETS:
  95 + return getStreamFromAssets(imageUri, extra);
  96 + case DRAWABLE:
  97 + return getStreamFromDrawable(imageUri, extra);
  98 + case UNKNOWN:
  99 + default:
  100 + return getStreamFromOtherSource(imageUri, extra);
  101 + }
  102 + }
  103 +
  104 + /**
  105 + * Retrieves {@link InputStream} of image by URI (image is located in the network).
  106 + *
  107 + * @param imageUri Image URI
  108 + * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
  109 + * DisplayImageOptions.extraForDownloader(Object)}; can be null
  110 + * @return {@link InputStream} of image
  111 + * @throws IOException if some I/O error occurs during network request or if no InputStream could be created for
  112 + * URL.
  113 + */
  114 + protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
  115 + HttpURLConnection conn = createConnection(imageUri, extra);
  116 +
  117 + int redirectCount = 0;
  118 + while (conn.getResponseCode() / 100 == 3 && redirectCount < MAX_REDIRECT_COUNT) {
  119 + conn = createConnection(conn.getHeaderField("Location"), extra);
  120 + redirectCount++;
  121 + }
  122 +
  123 + InputStream imageStream;
  124 + try {
  125 + imageStream = conn.getInputStream();
  126 + } catch (IOException e) {
  127 + // Read all data to allow reuse connection (http://bit.ly/1ad35PY)
  128 + IoUtils.readAndCloseStream(conn.getErrorStream());
  129 + throw e;
  130 + }
  131 + if (!shouldBeProcessed(conn)) {
  132 + IoUtils.closeSilently(imageStream);
  133 + throw new IOException("Image request failed with response code " + conn.getResponseCode());
  134 + }
  135 +
  136 + return new ContentLengthInputStream(new BufferedInputStream(imageStream, BUFFER_SIZE), conn.getContentLength());
  137 + }
  138 +
  139 + /**
  140 + * @param conn Opened request connection (response code is available)
  141 + * @return <b>true</b> - if data from connection is correct and should be read and processed;
  142 + * <b>false</b> - if response contains irrelevant data and shouldn't be processed
  143 + * @throws IOException
  144 + */
  145 + protected boolean shouldBeProcessed(HttpURLConnection conn) throws IOException {
  146 + return conn.getResponseCode() == 200;
  147 + }
  148 +
  149 + /**
  150 + * Create {@linkplain HttpURLConnection HTTP connection} for incoming URL
  151 + *
  152 + * @param url URL to connect to
  153 + * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
  154 + * DisplayImageOptions.extraForDownloader(Object)}; can be null
  155 + * @return {@linkplain HttpURLConnection Connection} for incoming URL. Connection isn't established so it still configurable.
  156 + * @throws IOException if some I/O error occurs during network request or if no InputStream could be created for
  157 + * URL.
  158 + */
  159 + protected HttpURLConnection createConnection(String url, Object extra) throws IOException {
  160 + String encodedUrl = Uri.encode(url, ALLOWED_URI_CHARS);
  161 + HttpURLConnection conn = (HttpURLConnection) new URL(encodedUrl).openConnection();
  162 + conn.setConnectTimeout(connectTimeout);
  163 + conn.setReadTimeout(readTimeout);
  164 + return conn;
  165 + }
  166 +
  167 + public static final int XOR_CONST = 0X99; //密钥
  168 +
  169 + /**
  170 + * Retrieves {@link InputStream} of image by URI (image is located on the local file system or SD card).
  171 + *
  172 + * @param imageUri Image URI
  173 + * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
  174 + * DisplayImageOptions.extraForDownloader(Object)}; can be null
  175 + * @return {@link InputStream} of image
  176 + * @throws IOException if some I/O error occurs reading from file system
  177 + */
  178 + protected InputStream getStreamFromFile(String imageUri, Object extra) throws IOException {
  179 + String filePath = Scheme.FILE.crop(imageUri);
  180 + if (isVideoFileUri(imageUri)) {
  181 + return getVideoThumbnailStream(filePath);
  182 + } else {
  183 + InputStream in = new FileInputStream(filePath);
  184 + if (filePath.endsWith("qnj")) {
  185 + in = getInputStream(in);
  186 + } else {
  187 + in = new FileInputStream(filePath);
  188 + }
  189 +
  190 + assert in != null;
  191 + BufferedInputStream imageStream = new BufferedInputStream(in, BUFFER_SIZE);
  192 + return new ContentLengthInputStream(imageStream, (int) new File(filePath).length());
  193 + }
  194 + }
  195 +
  196 + public static InputStream getInputStream(InputStream fis) {
  197 + ByteArrayOutputStream baos = new ByteArrayOutputStream();
  198 + InputStream inputStream = null;
  199 + try {
  200 + int read;
  201 + int bytesWritten = 0;
  202 + byte[] buffer = new byte[4096];
  203 + while ((read = fis.read(buffer)) > -1) {
  204 + byte[] otherBuffer = new byte[read - 1];
  205 + baos.write(buffer[0] ^ XOR_CONST);
  206 + for (int i = 1; i < read; i++) {
  207 + otherBuffer[i - 1] = buffer[i];
  208 + }
  209 + baos.write(otherBuffer, bytesWritten, read - 1);
  210 + }
  211 + byte[] byteArray = baos.toByteArray();
  212 + inputStream = new ByteArrayInputStream(byteArray);
  213 + return inputStream;
  214 + } catch (FileNotFoundException e) {
  215 + e.printStackTrace();
  216 + return null;
  217 + } catch (IOException e) {
  218 + e.printStackTrace();
  219 + return null;
  220 + } finally {
  221 + if (inputStream != null) {
  222 + try {
  223 + inputStream.close();
  224 + } catch (IOException e) {
  225 + e.printStackTrace();
  226 + }
  227 + }
  228 + }
  229 + }
  230 +
  231 +
  232 + @TargetApi(Build.VERSION_CODES.FROYO)
  233 + private InputStream getVideoThumbnailStream(String filePath) {
  234 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
  235 + Bitmap bitmap = ThumbnailUtils
  236 + .createVideoThumbnail(filePath, MediaStore.Images.Thumbnails.FULL_SCREEN_KIND);
  237 + if (bitmap != null) {
  238 + ByteArrayOutputStream bos = new ByteArrayOutputStream();
  239 + bitmap.compress(CompressFormat.PNG, 0, bos);
  240 + return new ByteArrayInputStream(bos.toByteArray());
  241 + }
  242 + }
  243 + return null;
  244 + }
  245 +
  246 + /**
  247 + * Retrieves {@link InputStream} of image by URI (image is accessed using {@link ContentResolver}).
  248 + *
  249 + * @param imageUri Image URI
  250 + * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
  251 + * DisplayImageOptions.extraForDownloader(Object)}; can be null
  252 + * @return {@link InputStream} of image
  253 + * @throws FileNotFoundException if the provided URI could not be opened
  254 + */
  255 + protected InputStream getStreamFromContent(String imageUri, Object extra) throws FileNotFoundException {
  256 + ContentResolver res = context.getContentResolver();
  257 +
  258 + Uri uri = Uri.parse(imageUri);
  259 + if (isVideoContentUri(uri)) { // video thumbnail
  260 + Long origId = Long.valueOf(uri.getLastPathSegment());
  261 + Bitmap bitmap = MediaStore.Video.Thumbnails
  262 + .getThumbnail(res, origId, MediaStore.Images.Thumbnails.MINI_KIND, null);
  263 + if (bitmap != null) {
  264 + ByteArrayOutputStream bos = new ByteArrayOutputStream();
  265 + bitmap.compress(CompressFormat.PNG, 0, bos);
  266 + return new ByteArrayInputStream(bos.toByteArray());
  267 + }
  268 + } else if (imageUri.startsWith(CONTENT_CONTACTS_URI_PREFIX)) { // contacts photo
  269 + return getContactPhotoStream(uri);
  270 + }
  271 +
  272 + return res.openInputStream(uri);
  273 + }
  274 +
  275 + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  276 + protected InputStream getContactPhotoStream(Uri uri) {
  277 + ContentResolver res = context.getContentResolver();
  278 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  279 + return ContactsContract.Contacts.openContactPhotoInputStream(res, uri, true);
  280 + } else {
  281 + return ContactsContract.Contacts.openContactPhotoInputStream(res, uri);
  282 + }
  283 + }
  284 +
  285 + /**
  286 + * Retrieves {@link InputStream} of image by URI (image is located in assets of application).
  287 + *
  288 + * @param imageUri Image URI
  289 + * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
  290 + * DisplayImageOptions.extraForDownloader(Object)}; can be null
  291 + * @return {@link InputStream} of image
  292 + * @throws IOException if some I/O error occurs file reading
  293 + */
  294 + protected InputStream getStreamFromAssets(String imageUri, Object extra) throws IOException {
  295 + String filePath = Scheme.ASSETS.crop(imageUri);
  296 + return context.getAssets().open(filePath);
  297 + }
  298 +
  299 + /**
  300 + * Retrieves {@link InputStream} of image by URI (image is located in drawable resources of application).
  301 + *
  302 + * @param imageUri Image URI
  303 + * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
  304 + * DisplayImageOptions.extraForDownloader(Object)}; can be null
  305 + * @return {@link InputStream} of image
  306 + */
  307 + protected InputStream getStreamFromDrawable(String imageUri, Object extra) {
  308 + String drawableIdString = Scheme.DRAWABLE.crop(imageUri);
  309 + int drawableId = Integer.parseInt(drawableIdString);
  310 + return context.getResources().openRawResource(drawableId);
  311 + }
  312 +
  313 + /**
  314 + * Retrieves {@link InputStream} of image by URI from other source with unsupported scheme. Should be overriden by
  315 + * successors to implement image downloading from special sources.<br />
  316 + * This method is called only if image URI has unsupported scheme. Throws {@link UnsupportedOperationException} by
  317 + * default.
  318 + *
  319 + * @param imageUri Image URI
  320 + * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
  321 + * DisplayImageOptions.extraForDownloader(Object)}; can be null
  322 + * @return {@link InputStream} of image
  323 + * @throws IOException if some I/O error occurs
  324 + * @throws UnsupportedOperationException if image URI has unsupported scheme(protocol)
  325 + */
  326 + protected InputStream getStreamFromOtherSource(String imageUri, Object extra) throws IOException {
  327 + throw new UnsupportedOperationException(String.format(ERROR_UNSUPPORTED_SCHEME, imageUri));
  328 + }
  329 +
  330 + private boolean isVideoContentUri(Uri uri) {
  331 + String mimeType = context.getContentResolver().getType(uri);
  332 + return mimeType != null && mimeType.startsWith("video/");
  333 + }
  334 +
  335 + private boolean isVideoFileUri(String uri) {
  336 + String extension = MimeTypeMap.getFileExtensionFromUrl(uri);
  337 + String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
  338 + return mimeType != null && mimeType.startsWith("video/");
  339 + }
  340 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.download;
  17 +
  18 +import com.nostra13.universalimageloader.core.DisplayImageOptions;
  19 +
  20 +import java.io.IOException;
  21 +import java.io.InputStream;
  22 +import java.util.Locale;
  23 +
  24 +/**
  25 + * Provides retrieving of {@link InputStream} of image by URI.<br />
  26 + * Implementations have to be thread-safe.
  27 + *
  28 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  29 + * @since 1.4.0
  30 + */
  31 +public interface ImageDownloader {
  32 + /**
  33 + * Retrieves {@link InputStream} of image by URI.
  34 + *
  35 + * @param imageUri Image URI
  36 + * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
  37 + * DisplayImageOptions.extraForDownloader(Object)}; can be null
  38 + * @return {@link InputStream} of image
  39 + * @throws IOException if some I/O error occurs during getting image stream
  40 + * @throws UnsupportedOperationException if image URI has unsupported scheme(protocol)
  41 + */
  42 + InputStream getStream(String imageUri, Object extra) throws IOException;
  43 +
  44 + /** Represents supported schemes(protocols) of URI. Provides convenient methods for work with schemes and URIs. */
  45 + public enum Scheme {
  46 + HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"), ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN("");
  47 +
  48 + private String scheme;
  49 + private String uriPrefix;
  50 +
  51 + Scheme(String scheme) {
  52 + this.scheme = scheme;
  53 + uriPrefix = scheme + "://";
  54 + }
  55 +
  56 + /**
  57 + * Defines scheme of incoming URI
  58 + *
  59 + * @param uri URI for scheme detection
  60 + * @return Scheme of incoming URI
  61 + */
  62 + public static Scheme ofUri(String uri) {
  63 + if (uri != null) {
  64 + for (Scheme s : values()) {
  65 + if (s.belongsTo(uri)) {
  66 + return s;
  67 + }
  68 + }
  69 + }
  70 + return UNKNOWN;
  71 + }
  72 +
  73 + private boolean belongsTo(String uri) {
  74 + return uri.toLowerCase(Locale.US).startsWith(uriPrefix);
  75 + }
  76 +
  77 + /** Appends scheme to incoming path */
  78 + public String wrap(String path) {
  79 + return uriPrefix + path;
  80 + }
  81 +
  82 + /** Removed scheme part ("scheme://") from incoming URI */
  83 + public String crop(String uri) {
  84 + if (!belongsTo(uri)) {
  85 + throw new IllegalArgumentException(String.format("URI [%1$s] doesn't have expected scheme [%2$s]", uri, scheme));
  86 + }
  87 + return uri.substring(uriPrefix.length());
  88 + }
  89 + }
  90 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2013-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.imageaware;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.graphics.drawable.Drawable;
  20 +import android.view.View;
  21 +import com.nostra13.universalimageloader.core.assist.ViewScaleType;
  22 +
  23 +/**
  24 + * Represents image aware view which provides all needed properties and behavior for image processing and displaying
  25 + * through {@link com.nostra13.universalimageloader.core.ImageLoader ImageLoader}.
  26 + * It can wrap any Android {@link View View} which can be accessed by {@link #getWrappedView()}. Wrapped
  27 + * view is returned in {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener ImageLoadingListener}'s
  28 + * callbacks.
  29 + *
  30 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  31 + * @see ViewAware
  32 + * @see ImageViewAware
  33 + * @see NonViewAware
  34 + * @since 1.9.0
  35 + */
  36 +public interface ImageAware {
  37 + /**
  38 + * Returns width of image aware view. This value is used to define scale size for original image.
  39 + * Can return 0 if width is undefined.<br />
  40 + * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
  41 + */
  42 + int getWidth();
  43 +
  44 + /**
  45 + * Returns height of image aware view. This value is used to define scale size for original image.
  46 + * Can return 0 if height is undefined.<br />
  47 + * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
  48 + */
  49 + int getHeight();
  50 +
  51 + /**
  52 + * Returns {@linkplain ViewScaleType scale type} which is used for
  53 + * scaling image for this image aware view. Must <b>NOT</b> return <b>null</b>.
  54 + */
  55 + ViewScaleType getScaleType();
  56 +
  57 + /**
  58 + * Returns wrapped Android {@link View View}. Can return <b>null</b> if no view is wrapped or view was
  59 + * collected by GC.<br />
  60 + * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
  61 + */
  62 + View getWrappedView();
  63 +
  64 + /**
  65 + * Returns a flag whether image aware view is collected by GC or whatsoever. If so then ImageLoader stop processing
  66 + * of task for this image aware view and fires
  67 + * {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener#onLoadingCancelled(String,
  68 + * View) ImageLoadingListener#onLoadingCancelled(String, View)} callback.<br />
  69 + * Mey be called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
  70 + *
  71 + * @return <b>true</b> - if view is collected by GC and ImageLoader should stop processing this image aware view;
  72 + * <b>false</b> - otherwise
  73 + */
  74 + boolean isCollected();
  75 +
  76 + /**
  77 + * Returns ID of image aware view. Point of ID is similar to Object's hashCode. This ID should be unique for every
  78 + * image view instance and should be the same for same instances. This ID identifies processing task in ImageLoader
  79 + * so ImageLoader won't process two image aware views with the same ID in one time. When ImageLoader get new task
  80 + * it cancels old task with this ID (if any) and starts new task.
  81 + * <p/>
  82 + * It's reasonable to return hash code of wrapped view (if any) to prevent displaying non-actual images in view
  83 + * because of view re-using.
  84 + */
  85 + int getId();
  86 +
  87 + /**
  88 + * Sets image drawable into this image aware view.<br />
  89 + * Displays drawable in this image aware view
  90 + * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageForEmptyUri(
  91 + *Drawable) for empty Uri},
  92 + * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageOnLoading(
  93 + *Drawable) on loading} or
  94 + * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageOnFail(
  95 + *Drawable) on loading fail}. These drawables can be specified in
  96 + * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions display options}.<br />
  97 + * Also can be called in {@link com.nostra13.universalimageloader.core.display.BitmapDisplayer BitmapDisplayer}.< br />
  98 + * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
  99 + *
  100 + * @return <b>true</b> if drawable was set successfully; <b>false</b> - otherwise
  101 + */
  102 + boolean setImageDrawable(Drawable drawable);
  103 +
  104 + /**
  105 + * Sets image bitmap into this image aware view.<br />
  106 + * Displays loaded and decoded image {@link Bitmap} in this image view aware.
  107 + * Actually it's used only in
  108 + * {@link com.nostra13.universalimageloader.core.display.BitmapDisplayer BitmapDisplayer}.< br />
  109 + * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
  110 + *
  111 + * @return <b>true</b> if bitmap was set successfully; <b>false</b> - otherwise
  112 + */
  113 + boolean setImageBitmap(Bitmap bitmap);
  114 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2013-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.imageaware;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.graphics.drawable.AnimationDrawable;
  20 +import android.graphics.drawable.Drawable;
  21 +import android.view.View;
  22 +import android.widget.ImageView;
  23 +import com.nostra13.universalimageloader.core.assist.ViewScaleType;
  24 +import com.nostra13.universalimageloader.utils.L;
  25 +
  26 +import java.lang.reflect.Field;
  27 +
  28 +/**
  29 + * Wrapper for Android {@link ImageView ImageView}. Keeps weak reference of ImageView to prevent memory
  30 + * leaks.
  31 + *
  32 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  33 + * @since 1.9.0
  34 + */
  35 +public class ImageViewAware extends ViewAware {
  36 +
  37 + /**
  38 + * Constructor. <br />
  39 + * References {@link #ImageViewAware(ImageView, boolean) ImageViewAware(imageView, true)}.
  40 + *
  41 + * @param imageView {@link ImageView ImageView} to work with
  42 + */
  43 + public ImageViewAware(ImageView imageView) {
  44 + super(imageView);
  45 + }
  46 +
  47 + /**
  48 + * Constructor
  49 + *
  50 + * @param imageView {@link ImageView ImageView} to work with
  51 + * @param checkActualViewSize <b>true</b> - then {@link #getWidth()} and {@link #getHeight()} will check actual
  52 + * size of ImageView. It can cause known issues like
  53 + * <a href="https://github.com/nostra13/Android-Universal-Image-Loader/issues/376">this</a>.
  54 + * But it helps to save memory because memory cache keeps bitmaps of actual (less in
  55 + * general) size.
  56 + * <p/>
  57 + * <b>false</b> - then {@link #getWidth()} and {@link #getHeight()} will <b>NOT</b>
  58 + * consider actual size of ImageView, just layout parameters. <br /> If you set 'false'
  59 + * it's recommended 'android:layout_width' and 'android:layout_height' (or
  60 + * 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to
  61 + * save memory.
  62 + * <p/>
  63 + */
  64 + public ImageViewAware(ImageView imageView, boolean checkActualViewSize) {
  65 + super(imageView, checkActualViewSize);
  66 + }
  67 +
  68 + /**
  69 + * {@inheritDoc}
  70 + * <br />
  71 + * 3) Get <b>maxWidth</b>.
  72 + */
  73 + @Override
  74 + public int getWidth() {
  75 + int width = super.getWidth();
  76 + if (width <= 0) {
  77 + ImageView imageView = (ImageView) viewRef.get();
  78 + if (imageView != null) {
  79 + width = getImageViewFieldValue(imageView, "mMaxWidth"); // Check maxWidth parameter
  80 + }
  81 + }
  82 + return width;
  83 + }
  84 +
  85 + /**
  86 + * {@inheritDoc}
  87 + * <br />
  88 + * 3) Get <b>maxHeight</b>
  89 + */
  90 + @Override
  91 + public int getHeight() {
  92 + int height = super.getHeight();
  93 + if (height <= 0) {
  94 + ImageView imageView = (ImageView) viewRef.get();
  95 + if (imageView != null) {
  96 + height = getImageViewFieldValue(imageView, "mMaxHeight"); // Check maxHeight parameter
  97 + }
  98 + }
  99 + return height;
  100 + }
  101 +
  102 + @Override
  103 + public ViewScaleType getScaleType() {
  104 + ImageView imageView = (ImageView) viewRef.get();
  105 + if (imageView != null) {
  106 + return ViewScaleType.fromImageView(imageView);
  107 + }
  108 + return super.getScaleType();
  109 + }
  110 +
  111 + @Override
  112 + public ImageView getWrappedView() {
  113 + return (ImageView) super.getWrappedView();
  114 + }
  115 +
  116 + @Override
  117 + protected void setImageDrawableInto(Drawable drawable, View view) {
  118 + ((ImageView) view).setImageDrawable(drawable);
  119 + if (drawable instanceof AnimationDrawable) {
  120 + ((AnimationDrawable)drawable).start();
  121 + }
  122 + }
  123 +
  124 + @Override
  125 + protected void setImageBitmapInto(Bitmap bitmap, View view) {
  126 + ((ImageView) view).setImageBitmap(bitmap);
  127 + }
  128 +
  129 + private static int getImageViewFieldValue(Object object, String fieldName) {
  130 + int value = 0;
  131 + try {
  132 + Field field = ImageView.class.getDeclaredField(fieldName);
  133 + field.setAccessible(true);
  134 + int fieldValue = (Integer) field.get(object);
  135 + if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
  136 + value = fieldValue;
  137 + }
  138 + } catch (Exception e) {
  139 + L.e(e);
  140 + }
  141 + return value;
  142 + }
  143 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2013-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.imageaware;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.graphics.drawable.Drawable;
  20 +import android.text.TextUtils;
  21 +import android.view.View;
  22 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  23 +import com.nostra13.universalimageloader.core.assist.ViewScaleType;
  24 +
  25 +/**
  26 + * ImageAware which provides needed info for processing of original image but do nothing for displaying image. It's
  27 + * used when user need just load and decode image and get it in {@linkplain
  28 + * com.nostra13.universalimageloader.core.listener.ImageLoadingListener#onLoadingComplete(String, View,
  29 + * Bitmap) callback}.
  30 + *
  31 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  32 + * @since 1.9.0
  33 + */
  34 +public class NonViewAware implements ImageAware {
  35 +
  36 + protected final String imageUri;
  37 + protected final ImageSize imageSize;
  38 + protected final ViewScaleType scaleType;
  39 +
  40 + public NonViewAware(ImageSize imageSize, ViewScaleType scaleType) {
  41 + this(null, imageSize, scaleType);
  42 + }
  43 +
  44 + public NonViewAware(String imageUri, ImageSize imageSize, ViewScaleType scaleType) {
  45 + if (imageSize == null) throw new IllegalArgumentException("imageSize must not be null");
  46 + if (scaleType == null) throw new IllegalArgumentException("scaleType must not be null");
  47 +
  48 + this.imageUri = imageUri;
  49 + this.imageSize = imageSize;
  50 + this.scaleType = scaleType;
  51 + }
  52 +
  53 + @Override
  54 + public int getWidth() {
  55 + return imageSize.getWidth();
  56 + }
  57 +
  58 + @Override
  59 + public int getHeight() {
  60 + return imageSize.getHeight();
  61 + }
  62 +
  63 + @Override
  64 + public ViewScaleType getScaleType() {
  65 + return scaleType;
  66 + }
  67 +
  68 + @Override
  69 + public View getWrappedView() {
  70 + return null;
  71 + }
  72 +
  73 + @Override
  74 + public boolean isCollected() {
  75 + return false;
  76 + }
  77 +
  78 + @Override
  79 + public int getId() {
  80 + return TextUtils.isEmpty(imageUri) ? super.hashCode() : imageUri.hashCode();
  81 + }
  82 +
  83 + @Override
  84 + public boolean setImageDrawable(Drawable drawable) { // Do nothing
  85 + return true;
  86 + }
  87 +
  88 + @Override
  89 + public boolean setImageBitmap(Bitmap bitmap) { // Do nothing
  90 + return true;
  91 + }
  92 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.imageaware;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.graphics.drawable.Drawable;
  20 +import android.os.Looper;
  21 +import android.view.View;
  22 +import android.view.ViewGroup;
  23 +import com.nostra13.universalimageloader.core.assist.ViewScaleType;
  24 +import com.nostra13.universalimageloader.utils.L;
  25 +
  26 +import java.lang.ref.Reference;
  27 +import java.lang.ref.WeakReference;
  28 +
  29 +/**
  30 + * Wrapper for Android {@link View View}. Keeps weak reference of View to prevent memory leaks.
  31 + *
  32 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  33 + * @since 1.9.2
  34 + */
  35 +public abstract class ViewAware implements ImageAware {
  36 +
  37 + public static final String WARN_CANT_SET_DRAWABLE = "Can't set a drawable into view. You should call ImageLoader on UI thread for it.";
  38 + public static final String WARN_CANT_SET_BITMAP = "Can't set a bitmap into view. You should call ImageLoader on UI thread for it.";
  39 +
  40 + protected Reference<View> viewRef;
  41 + protected boolean checkActualViewSize;
  42 +
  43 + /**
  44 + * Constructor. <br />
  45 + * References {@link #ViewAware(View, boolean) ImageViewAware(imageView, true)}.
  46 + *
  47 + * @param view {@link View View} to work with
  48 + */
  49 + public ViewAware(View view) {
  50 + this(view, true);
  51 + }
  52 +
  53 + /**
  54 + * Constructor
  55 + *
  56 + * @param view {@link View View} to work with
  57 + * @param checkActualViewSize <b>true</b> - then {@link #getWidth()} and {@link #getHeight()} will check actual
  58 + * size of View. It can cause known issues like
  59 + * <a href="https://github.com/nostra13/Android-Universal-Image-Loader/issues/376">this</a>.
  60 + * But it helps to save memory because memory cache keeps bitmaps of actual (less in
  61 + * general) size.
  62 + * <p/>
  63 + * <b>false</b> - then {@link #getWidth()} and {@link #getHeight()} will <b>NOT</b>
  64 + * consider actual size of View, just layout parameters. <br /> If you set 'false'
  65 + * it's recommended 'android:layout_width' and 'android:layout_height' (or
  66 + * 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to
  67 + * save memory.
  68 + */
  69 + public ViewAware(View view, boolean checkActualViewSize) {
  70 + if (view == null) throw new IllegalArgumentException("view must not be null");
  71 +
  72 + this.viewRef = new WeakReference<View>(view);
  73 + this.checkActualViewSize = checkActualViewSize;
  74 + }
  75 +
  76 + /**
  77 + * {@inheritDoc}
  78 + * <p/>
  79 + * Width is defined by target {@link View view} parameters, configuration
  80 + * parameters or device display dimensions.<br />
  81 + * Size computing algorithm (go by steps until get non-zero value):<br />
  82 + * 1) Get the actual drawn <b>getWidth()</b> of the View<br />
  83 + * 2) Get <b>layout_width</b>
  84 + */
  85 + @Override
  86 + public int getWidth() {
  87 + View view = viewRef.get();
  88 + if (view != null) {
  89 + final ViewGroup.LayoutParams params = view.getLayoutParams();
  90 + int width = 0;
  91 + if (checkActualViewSize && params != null && params.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
  92 + width = view.getWidth(); // Get actual image width
  93 + }
  94 + if (width <= 0 && params != null) width = params.width; // Get layout width parameter
  95 + return width;
  96 + }
  97 + return 0;
  98 + }
  99 +
  100 + /**
  101 + * {@inheritDoc}
  102 + * <p/>
  103 + * Height is defined by target {@link View view} parameters, configuration
  104 + * parameters or device display dimensions.<br />
  105 + * Size computing algorithm (go by steps until get non-zero value):<br />
  106 + * 1) Get the actual drawn <b>getHeight()</b> of the View<br />
  107 + * 2) Get <b>layout_height</b>
  108 + */
  109 + @Override
  110 + public int getHeight() {
  111 + View view = viewRef.get();
  112 + if (view != null) {
  113 + final ViewGroup.LayoutParams params = view.getLayoutParams();
  114 + int height = 0;
  115 + if (checkActualViewSize && params != null && params.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
  116 + height = view.getHeight(); // Get actual image height
  117 + }
  118 + if (height <= 0 && params != null) height = params.height; // Get layout height parameter
  119 + return height;
  120 + }
  121 + return 0;
  122 + }
  123 +
  124 + @Override
  125 + public ViewScaleType getScaleType() {
  126 + return ViewScaleType.CROP;
  127 + }
  128 +
  129 + @Override
  130 + public View getWrappedView() {
  131 + return viewRef.get();
  132 + }
  133 +
  134 + @Override
  135 + public boolean isCollected() {
  136 + return viewRef.get() == null;
  137 + }
  138 +
  139 + @Override
  140 + public int getId() {
  141 + View view = viewRef.get();
  142 + return view == null ? super.hashCode() : view.hashCode();
  143 + }
  144 +
  145 + @Override
  146 + public boolean setImageDrawable(Drawable drawable) {
  147 + if (Looper.myLooper() == Looper.getMainLooper()) {
  148 + View view = viewRef.get();
  149 + if (view != null) {
  150 + setImageDrawableInto(drawable, view);
  151 + return true;
  152 + }
  153 + } else {
  154 + L.w(WARN_CANT_SET_DRAWABLE);
  155 + }
  156 + return false;
  157 + }
  158 +
  159 + @Override
  160 + public boolean setImageBitmap(Bitmap bitmap) {
  161 + if (Looper.myLooper() == Looper.getMainLooper()) {
  162 + View view = viewRef.get();
  163 + if (view != null) {
  164 + setImageBitmapInto(bitmap, view);
  165 + return true;
  166 + }
  167 + } else {
  168 + L.w(WARN_CANT_SET_BITMAP);
  169 + }
  170 + return false;
  171 + }
  172 +
  173 + /**
  174 + * Should set drawable into incoming view. Incoming view is guaranteed not null.<br />
  175 + * This method is called on UI thread.
  176 + */
  177 + protected abstract void setImageDrawableInto(Drawable drawable, View view);
  178 +
  179 + /**
  180 + * Should set Bitmap into incoming view. Incoming view is guaranteed not null.< br />
  181 + * This method is called on UI thread.
  182 + */
  183 + protected abstract void setImageBitmapInto(Bitmap bitmap, View view);
  184 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.listener;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.view.View;
  20 +import com.nostra13.universalimageloader.core.assist.FailReason;
  21 +
  22 +/**
  23 + * Listener for image loading process.<br />
  24 + * You can use {@link SimpleImageLoadingListener} for implementing only needed methods.
  25 + *
  26 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  27 + * @see SimpleImageLoadingListener
  28 + * @see FailReason
  29 + * @since 1.0.0
  30 + */
  31 +public interface ImageLoadingListener {
  32 +
  33 + /**
  34 + * Is called when image loading task was started
  35 + *
  36 + * @param imageUri Loading image URI
  37 + * @param view View for image
  38 + */
  39 + void onLoadingStarted(String imageUri, View view);
  40 +
  41 + /**
  42 + * Is called when an error was occurred during image loading
  43 + *
  44 + * @param imageUri Loading image URI
  45 + * @param view View for image. Can be <b>null</b>.
  46 + * @param failReason {@linkplain FailReason The reason} why image
  47 + * loading was failed
  48 + */
  49 + void onLoadingFailed(String imageUri, View view, FailReason failReason);
  50 +
  51 + /**
  52 + * Is called when image is loaded successfully (and displayed in View if one was specified)
  53 + *
  54 + * @param imageUri Loaded image URI
  55 + * @param view View for image. Can be <b>null</b>.
  56 + * @param loadedImage Bitmap of loaded and decoded image
  57 + */
  58 + void onLoadingComplete(String imageUri, View view, Bitmap loadedImage);
  59 +
  60 + /**
  61 + * Is called when image loading task was cancelled because View for image was reused in newer task
  62 + *
  63 + * @param imageUri Loading image URI
  64 + * @param view View for image. Can be <b>null</b>.
  65 + */
  66 + void onLoadingCancelled(String imageUri, View view);
  67 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.listener;
  17 +
  18 +import android.view.View;
  19 +
  20 +/**
  21 + * Listener for image loading progress.
  22 + *
  23 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  24 + * @since 1.9.1
  25 + */
  26 +public interface ImageLoadingProgressListener {
  27 +
  28 + /**
  29 + * Is called when image loading progress changed.
  30 + *
  31 + * @param imageUri Image URI
  32 + * @param view View for image. Can be <b>null</b>.
  33 + * @param current Downloaded size in bytes
  34 + * @param total Total size in bytes
  35 + */
  36 + void onProgressUpdate(String imageUri, View view, int current, int total);
  37 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.listener;
  17 +
  18 +import android.widget.AbsListView;
  19 +import android.widget.AbsListView.OnScrollListener;
  20 +import android.widget.GridView;
  21 +import android.widget.ListView;
  22 +import com.nostra13.universalimageloader.core.ImageLoader;
  23 +
  24 +/**
  25 + * Listener-helper for {@linkplain AbsListView list views} ({@link ListView}, {@link GridView}) which can
  26 + * {@linkplain ImageLoader#pause() pause ImageLoader's tasks} while list view is scrolling (touch scrolling and/or
  27 + * fling). It prevents redundant loadings.<br />
  28 + * Set it to your list view's {@link AbsListView#setOnScrollListener(OnScrollListener) setOnScrollListener(...)}.<br />
  29 + * This listener can wrap your custom {@linkplain OnScrollListener listener}.
  30 + *
  31 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  32 + * @since 1.7.0
  33 + */
  34 +public class PauseOnScrollListener implements OnScrollListener {
  35 +
  36 + private ImageLoader imageLoader;
  37 +
  38 + private final boolean pauseOnScroll;
  39 + private final boolean pauseOnFling;
  40 + private final OnScrollListener externalListener;
  41 +
  42 + /**
  43 + * Constructor
  44 + *
  45 + * @param imageLoader {@linkplain ImageLoader} instance for controlling
  46 + * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling
  47 + * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling
  48 + */
  49 + public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling) {
  50 + this(imageLoader, pauseOnScroll, pauseOnFling, null);
  51 + }
  52 +
  53 + /**
  54 + * Constructor
  55 + *
  56 + * @param imageLoader {@linkplain ImageLoader} instance for controlling
  57 + * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling
  58 + * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling
  59 + * @param customListener Your custom {@link OnScrollListener} for {@linkplain AbsListView list view} which also
  60 + * will be get scroll events
  61 + */
  62 + public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling,
  63 + OnScrollListener customListener) {
  64 + this.imageLoader = imageLoader;
  65 + this.pauseOnScroll = pauseOnScroll;
  66 + this.pauseOnFling = pauseOnFling;
  67 + externalListener = customListener;
  68 + }
  69 +
  70 + @Override
  71 + public void onScrollStateChanged(AbsListView view, int scrollState) {
  72 + switch (scrollState) {
  73 + case OnScrollListener.SCROLL_STATE_IDLE:
  74 + imageLoader.resume();
  75 + break;
  76 + case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
  77 + if (pauseOnScroll) {
  78 + imageLoader.pause();
  79 + }
  80 + break;
  81 + case OnScrollListener.SCROLL_STATE_FLING:
  82 + if (pauseOnFling) {
  83 + imageLoader.pause();
  84 + }
  85 + break;
  86 + }
  87 + if (externalListener != null) {
  88 + externalListener.onScrollStateChanged(view, scrollState);
  89 + }
  90 + }
  91 +
  92 + @Override
  93 + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
  94 + if (externalListener != null) {
  95 + externalListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
  96 + }
  97 + }
  98 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.listener;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.view.View;
  20 +import com.nostra13.universalimageloader.core.assist.FailReason;
  21 +
  22 +/**
  23 + * A convenient class to extend when you only want to listen for a subset of all the image loading events. This
  24 + * implements all methods in the {@link ImageLoadingListener} but does
  25 + * nothing.
  26 + *
  27 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  28 + * @since 1.4.0
  29 + */
  30 +public class SimpleImageLoadingListener implements ImageLoadingListener {
  31 + @Override
  32 + public void onLoadingStarted(String imageUri, View view) {
  33 + // Empty implementation
  34 + }
  35 +
  36 + @Override
  37 + public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
  38 + // Empty implementation
  39 + }
  40 +
  41 + @Override
  42 + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
  43 + // Empty implementation
  44 + }
  45 +
  46 + @Override
  47 + public void onLoadingCancelled(String imageUri, View view) {
  48 + // Empty implementation
  49 + }
  50 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core.process;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.core.DisplayImageOptions;
  20 +
  21 +/**
  22 + * Makes some processing on {@link Bitmap}. Implementations can apply any changes to original {@link Bitmap}.<br />
  23 + * Implementations have to be thread-safe.
  24 + *
  25 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  26 + * @since 1.8.0
  27 + */
  28 +public interface BitmapProcessor {
  29 + /**
  30 + * Makes some processing of incoming bitmap.<br />
  31 + * This method is executing on additional thread (not on UI thread).<br />
  32 + * <b>Note:</b> If this processor is used as {@linkplain DisplayImageOptions.Builder#preProcessor(BitmapProcessor)
  33 + * pre-processor} then don't forget {@linkplain Bitmap#recycle() to recycle} incoming bitmap if you return a new
  34 + * created one.
  35 + *
  36 + * @param bitmap Original {@linkplain Bitmap bitmap}
  37 + * @return Processed {@linkplain Bitmap bitmap}
  38 + */
  39 + Bitmap process(Bitmap bitmap);
  40 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.utils;
  17 +
  18 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  19 +
  20 +import java.io.File;
  21 +
  22 +/**
  23 + * Utility for convenient work with disk cache.<br />
  24 + * <b>NOTE:</b> This utility works with file system so avoid using it on application main thread.
  25 + *
  26 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  27 + * @since 1.8.0
  28 + */
  29 +public final class DiskCacheUtils {
  30 +
  31 + private DiskCacheUtils() {
  32 + }
  33 +
  34 + /** Returns {@link File} of cached image or <b>null</b> if image was not cached in disk cache */
  35 + public static File findInCache(String imageUri, DiskCache diskCache) {
  36 + File image = diskCache.get(imageUri);
  37 + return image != null && image.exists() ? image : null;
  38 + }
  39 +
  40 + /**
  41 + * Removed cached image file from disk cache (if image was cached in disk cache before)
  42 + *
  43 + * @return <b>true</b> - if cached image file existed and was deleted; <b>false</b> - otherwise.
  44 + */
  45 + public static boolean removeFromCache(String imageUri, DiskCache diskCache) {
  46 + File image = diskCache.get(imageUri);
  47 + return image != null && image.exists() && image.delete();
  48 + }
  49 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2013-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.utils;
  17 +
  18 +import android.graphics.BitmapFactory;
  19 +import android.opengl.GLES10;
  20 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  21 +import com.nostra13.universalimageloader.core.assist.ViewScaleType;
  22 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  23 +
  24 +import javax.microedition.khronos.opengles.GL10;
  25 +
  26 +/**
  27 + * Provides calculations with image sizes, scales
  28 + *
  29 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  30 + * @since 1.8.3
  31 + */
  32 +public final class ImageSizeUtils {
  33 +
  34 + private static final int DEFAULT_MAX_BITMAP_DIMENSION = 2048;
  35 +
  36 + private static ImageSize maxBitmapSize;
  37 +
  38 + static {
  39 + int[] maxTextureSize = new int[1];
  40 + GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
  41 + int maxBitmapDimension = Math.max(maxTextureSize[0], DEFAULT_MAX_BITMAP_DIMENSION);
  42 + maxBitmapSize = new ImageSize(maxBitmapDimension, maxBitmapDimension);
  43 + }
  44 +
  45 + private ImageSizeUtils() {
  46 + }
  47 +
  48 + /**
  49 + * Defines target size for image aware view. Size is defined by target
  50 + * {@link ImageAware view} parameters, configuration
  51 + * parameters or device display dimensions.<br />
  52 + */
  53 + public static ImageSize defineTargetSizeForView(ImageAware imageAware, ImageSize maxImageSize) {
  54 + int width = imageAware.getWidth();
  55 + if (width <= 0) width = maxImageSize.getWidth();
  56 +
  57 + int height = imageAware.getHeight();
  58 + if (height <= 0) height = maxImageSize.getHeight();
  59 +
  60 + return new ImageSize(width, height);
  61 + }
  62 +
  63 + /**
  64 + * Computes sample size for downscaling image size (<b>srcSize</b>) to view size (<b>targetSize</b>). This sample
  65 + * size is used during
  66 + * {@linkplain BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, BitmapFactory.Options)
  67 + * decoding image} to bitmap.<br />
  68 + * <br />
  69 + * <b>Examples:</b><br />
  70 + * <p/>
  71 + * <pre>
  72 + * srcSize(100x100), targetSize(10x10), powerOf2Scale = true -> sampleSize = 8
  73 + * srcSize(100x100), targetSize(10x10), powerOf2Scale = false -> sampleSize = 10
  74 + *
  75 + * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> sampleSize = 5
  76 + * srcSize(100x100), targetSize(20x40), viewScaleType = CROP -> sampleSize = 2
  77 + * </pre>
  78 + * <p/>
  79 + * <br />
  80 + * The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded
  81 + * bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16
  82 + * the number of pixels. Any value <= 1 is treated the same as 1.
  83 + *
  84 + * @param srcSize Original (image) size
  85 + * @param targetSize Target (view) size
  86 + * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view
  87 + * @param powerOf2Scale <i>true</i> - if sample size be a power of 2 (1, 2, 4, 8, ...)
  88 + * @return Computed sample size
  89 + */
  90 + public static int computeImageSampleSize(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
  91 + boolean powerOf2Scale) {
  92 + final int srcWidth = srcSize.getWidth();
  93 + final int srcHeight = srcSize.getHeight();
  94 + final int targetWidth = targetSize.getWidth();
  95 + final int targetHeight = targetSize.getHeight();
  96 +
  97 + int scale = 1;
  98 +
  99 + switch (viewScaleType) {
  100 + case FIT_INSIDE:
  101 + if (powerOf2Scale) {
  102 + final int halfWidth = srcWidth / 2;
  103 + final int halfHeight = srcHeight / 2;
  104 + while ((halfWidth / scale) > targetWidth || (halfHeight / scale) > targetHeight) { // ||
  105 + scale *= 2;
  106 + }
  107 + } else {
  108 + scale = Math.max(srcWidth / targetWidth, srcHeight / targetHeight); // max
  109 + }
  110 + break;
  111 + case CROP:
  112 + if (powerOf2Scale) {
  113 + final int halfWidth = srcWidth / 2;
  114 + final int halfHeight = srcHeight / 2;
  115 + while ((halfWidth / scale) > targetWidth && (halfHeight / scale) > targetHeight) { // &&
  116 + scale *= 2;
  117 + }
  118 + } else {
  119 + scale = Math.min(srcWidth / targetWidth, srcHeight / targetHeight); // min
  120 + }
  121 + break;
  122 + }
  123 +
  124 + if (scale < 1) {
  125 + scale = 1;
  126 + }
  127 + scale = considerMaxTextureSize(srcWidth, srcHeight, scale, powerOf2Scale);
  128 +
  129 + return scale;
  130 + }
  131 +
  132 + private static int considerMaxTextureSize(int srcWidth, int srcHeight, int scale, boolean powerOf2) {
  133 + final int maxWidth = maxBitmapSize.getWidth();
  134 + final int maxHeight = maxBitmapSize.getHeight();
  135 + while ((srcWidth / scale) > maxWidth || (srcHeight / scale) > maxHeight) {
  136 + if (powerOf2) {
  137 + scale *= 2;
  138 + } else {
  139 + scale++;
  140 + }
  141 + }
  142 + return scale;
  143 + }
  144 +
  145 + /**
  146 + * Computes minimal sample size for downscaling image so result image size won't exceed max acceptable OpenGL
  147 + * texture size.<br />
  148 + * We can't create Bitmap in memory with size exceed max texture size (usually this is 2048x2048) so this method
  149 + * calculate minimal sample size which should be applied to image to fit into these limits.
  150 + *
  151 + * @param srcSize Original image size
  152 + * @return Minimal sample size
  153 + */
  154 + public static int computeMinImageSampleSize(ImageSize srcSize) {
  155 + final int srcWidth = srcSize.getWidth();
  156 + final int srcHeight = srcSize.getHeight();
  157 + final int targetWidth = maxBitmapSize.getWidth();
  158 + final int targetHeight = maxBitmapSize.getHeight();
  159 +
  160 + final int widthScale = (int) Math.ceil((float) srcWidth / targetWidth);
  161 + final int heightScale = (int) Math.ceil((float) srcHeight / targetHeight);
  162 +
  163 + return Math.max(widthScale, heightScale); // max
  164 + }
  165 +
  166 + /**
  167 + * Computes scale of target size (<b>targetSize</b>) to source size (<b>srcSize</b>).<br />
  168 + * <br />
  169 + * <b>Examples:</b><br />
  170 + * <p/>
  171 + * <pre>
  172 + * srcSize(40x40), targetSize(10x10) -> scale = 0.25
  173 + *
  174 + * srcSize(10x10), targetSize(20x20), stretch = false -> scale = 1
  175 + * srcSize(10x10), targetSize(20x20), stretch = true -> scale = 2
  176 + *
  177 + * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> scale = 0.2
  178 + * srcSize(100x100), targetSize(20x40), viewScaleType = CROP -> scale = 0.4
  179 + * </pre>
  180 + *
  181 + * @param srcSize Source (image) size
  182 + * @param targetSize Target (view) size
  183 + * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view
  184 + * @param stretch Whether source size should be stretched if target size is larger than source size. If <b>false</b>
  185 + * then result scale value can't be greater than 1.
  186 + * @return Computed scale
  187 + */
  188 + public static float computeImageScale(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
  189 + boolean stretch) {
  190 + final int srcWidth = srcSize.getWidth();
  191 + final int srcHeight = srcSize.getHeight();
  192 + final int targetWidth = targetSize.getWidth();
  193 + final int targetHeight = targetSize.getHeight();
  194 +
  195 + final float widthScale = (float) srcWidth / targetWidth;
  196 + final float heightScale = (float) srcHeight / targetHeight;
  197 +
  198 + final int destWidth;
  199 + final int destHeight;
  200 + if ((viewScaleType == ViewScaleType.FIT_INSIDE && widthScale >= heightScale) || (viewScaleType == ViewScaleType.CROP && widthScale < heightScale)) {
  201 + destWidth = targetWidth;
  202 + destHeight = (int) (srcHeight / widthScale);
  203 + } else {
  204 + destWidth = (int) (srcWidth / heightScale);
  205 + destHeight = targetHeight;
  206 + }
  207 +
  208 + float scale = 1;
  209 + if ((!stretch && destWidth < srcWidth && destHeight < srcHeight) || (stretch && destWidth != srcWidth && destHeight != srcHeight)) {
  210 + scale = (float) destWidth / srcWidth;
  211 + }
  212 +
  213 + return scale;
  214 + }
  215 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.utils;
  17 +
  18 +import java.io.Closeable;
  19 +import java.io.IOException;
  20 +import java.io.InputStream;
  21 +import java.io.OutputStream;
  22 +
  23 +/**
  24 + * Provides I/O operations
  25 + *
  26 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  27 + * @since 1.0.0
  28 + */
  29 +public final class IoUtils {
  30 +
  31 + /** {@value} */
  32 + public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 KB
  33 + /** {@value} */
  34 + public static final int DEFAULT_IMAGE_TOTAL_SIZE = 500 * 1024; // 500 Kb
  35 + /** {@value} */
  36 + public static final int CONTINUE_LOADING_PERCENTAGE = 75;
  37 +
  38 + private IoUtils() {
  39 + }
  40 +
  41 + /**
  42 + * Copies stream, fires progress events by listener, can be interrupted by listener. Uses buffer size =
  43 + * {@value #DEFAULT_BUFFER_SIZE} bytes.
  44 + *
  45 + * @param is Input stream
  46 + * @param os Output stream
  47 + * @param listener null-ok; Listener of copying progress and controller of copying interrupting
  48 + * @return <b>true</b> - if stream copied successfully; <b>false</b> - if copying was interrupted by listener
  49 + * @throws IOException
  50 + */
  51 + public static boolean copyStream(InputStream is, OutputStream os, CopyListener listener) throws IOException {
  52 + return copyStream(is, os, listener, DEFAULT_BUFFER_SIZE);
  53 + }
  54 +
  55 + /**
  56 + * Copies stream, fires progress events by listener, can be interrupted by listener.
  57 + *
  58 + * @param is Input stream
  59 + * @param os Output stream
  60 + * @param listener null-ok; Listener of copying progress and controller of copying interrupting
  61 + * @param bufferSize Buffer size for copying, also represents a step for firing progress listener callback, i.e.
  62 + * progress event will be fired after every copied <b>bufferSize</b> bytes
  63 + * @return <b>true</b> - if stream copied successfully; <b>false</b> - if copying was interrupted by listener
  64 + * @throws IOException
  65 + */
  66 + public static boolean copyStream(InputStream is, OutputStream os, CopyListener listener, int bufferSize)
  67 + throws IOException {
  68 + int current = 0;
  69 + int total = is.available();
  70 + if (total <= 0) {
  71 + total = DEFAULT_IMAGE_TOTAL_SIZE;
  72 + }
  73 +
  74 + final byte[] bytes = new byte[bufferSize];
  75 + int count;
  76 + if (shouldStopLoading(listener, current, total)) return false;
  77 + while ((count = is.read(bytes, 0, bufferSize)) != -1) {
  78 + os.write(bytes, 0, count);
  79 + current += count;
  80 + if (shouldStopLoading(listener, current, total)) return false;
  81 + }
  82 + os.flush();
  83 + return true;
  84 + }
  85 +
  86 + private static boolean shouldStopLoading(CopyListener listener, int current, int total) {
  87 + if (listener != null) {
  88 + boolean shouldContinue = listener.onBytesCopied(current, total);
  89 + if (!shouldContinue) {
  90 + if (100 * current / total < CONTINUE_LOADING_PERCENTAGE) {
  91 + return true; // if loaded more than 75% then continue loading anyway
  92 + }
  93 + }
  94 + }
  95 + return false;
  96 + }
  97 +
  98 + /**
  99 + * Reads all data from stream and close it silently
  100 + *
  101 + * @param is Input stream
  102 + */
  103 + public static void readAndCloseStream(InputStream is) {
  104 + final byte[] bytes = new byte[DEFAULT_BUFFER_SIZE];
  105 + try {
  106 + while (is.read(bytes, 0, DEFAULT_BUFFER_SIZE) != -1);
  107 + } catch (IOException ignored) {
  108 + } finally {
  109 + closeSilently(is);
  110 + }
  111 + }
  112 +
  113 + public static void closeSilently(Closeable closeable) {
  114 + if (closeable != null) {
  115 + try {
  116 + closeable.close();
  117 + } catch (Exception ignored) {
  118 + }
  119 + }
  120 + }
  121 +
  122 + /** Listener and controller for copy process */
  123 + public static interface CopyListener {
  124 + /**
  125 + * @param current Loaded bytes
  126 + * @param total Total bytes for loading
  127 + * @return <b>true</b> - if copying should be continued; <b>false</b> - if copying should be interrupted
  128 + */
  129 + boolean onBytesCopied(int current, int total);
  130 + }
  131 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.utils;
  17 +
  18 +import android.util.Log;
  19 +import com.nostra13.universalimageloader.core.ImageLoader;
  20 +
  21 +/**
  22 + * "Less-word" analog of Android {@link Log logger}
  23 + *
  24 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  25 + * @since 1.6.4
  26 + */
  27 +public final class L {
  28 +
  29 + private static final String LOG_FORMAT = "%1$s\n%2$s";
  30 + private static volatile boolean writeDebugLogs = false;
  31 + private static volatile boolean writeLogs = true;
  32 +
  33 + private L() {
  34 + }
  35 +
  36 + /**
  37 + * Enables logger (if {@link #disableLogging()} was called before)
  38 + *
  39 + * @deprecated Use {@link #writeLogs(boolean) writeLogs(true)} instead
  40 + */
  41 + @Deprecated
  42 + public static void enableLogging() {
  43 + writeLogs(true);
  44 + }
  45 +
  46 + /**
  47 + * Disables logger, no logs will be passed to LogCat, all log methods will do nothing
  48 + *
  49 + * @deprecated Use {@link #writeLogs(boolean) writeLogs(false)} instead
  50 + */
  51 + @Deprecated
  52 + public static void disableLogging() {
  53 + writeLogs(false);
  54 + }
  55 +
  56 + /**
  57 + * Enables/disables detail logging of {@link ImageLoader} work.
  58 + * Consider {@link L#disableLogging()} to disable
  59 + * ImageLoader logging completely (even error logs)<br />
  60 + * Debug logs are disabled by default.
  61 + */
  62 + public static void writeDebugLogs(boolean writeDebugLogs) {
  63 + L.writeDebugLogs = writeDebugLogs;
  64 + }
  65 +
  66 + /** Enables/disables logging of {@link ImageLoader} completely (even error logs). */
  67 + public static void writeLogs(boolean writeLogs) {
  68 + L.writeLogs = writeLogs;
  69 + }
  70 +
  71 + public static void d(String message, Object... args) {
  72 + if (writeDebugLogs) {
  73 + log(Log.DEBUG, null, message, args);
  74 + }
  75 + }
  76 +
  77 + public static void i(String message, Object... args) {
  78 + log(Log.INFO, null, message, args);
  79 + }
  80 +
  81 + public static void w(String message, Object... args) {
  82 + log(Log.WARN, null, message, args);
  83 + }
  84 +
  85 + public static void e(Throwable ex) {
  86 + log(Log.ERROR, ex, null);
  87 + }
  88 +
  89 + public static void e(String message, Object... args) {
  90 + log(Log.ERROR, null, message, args);
  91 + }
  92 +
  93 + public static void e(Throwable ex, String message, Object... args) {
  94 + log(Log.ERROR, ex, message, args);
  95 + }
  96 +
  97 + private static void log(int priority, Throwable ex, String message, Object... args) {
  98 + if (!writeLogs) return;
  99 + if (args.length > 0) {
  100 + message = String.format(message, args);
  101 + }
  102 +
  103 + String log;
  104 + if (ex == null) {
  105 + log = message;
  106 + } else {
  107 + String logMessage = message == null ? ex.getMessage() : message;
  108 + String logBody = Log.getStackTraceString(ex);
  109 + log = String.format(LOG_FORMAT, logMessage, logBody);
  110 + }
  111 + Log.println(priority, ImageLoader.TAG, log);
  112 + }
  113 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.utils;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  21 +import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
  22 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  23 +
  24 +import java.util.ArrayList;
  25 +import java.util.Comparator;
  26 +import java.util.List;
  27 +
  28 +/**
  29 + * Utility for generating of keys for memory cache, key comparing and other work with memory cache
  30 + *
  31 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  32 + * @since 1.6.3
  33 + */
  34 +public final class MemoryCacheUtils {
  35 +
  36 + private static final String URI_AND_SIZE_SEPARATOR = "_";
  37 + private static final String WIDTH_AND_HEIGHT_SEPARATOR = "x";
  38 +
  39 + private MemoryCacheUtils() {
  40 + }
  41 +
  42 + /**
  43 + * Generates key for memory cache for incoming image (URI + size).<br />
  44 + * Pattern for cache key - <b>[imageUri]_[width]x[height]</b>.
  45 + */
  46 + public static String generateKey(String imageUri, ImageSize targetSize) {
  47 + return new StringBuilder(imageUri).append(URI_AND_SIZE_SEPARATOR).append(targetSize.getWidth()).append(WIDTH_AND_HEIGHT_SEPARATOR).append(targetSize.getHeight()).toString();
  48 + }
  49 +
  50 + public static Comparator<String> createFuzzyKeyComparator() {
  51 + return new Comparator<String>() {
  52 + @Override
  53 + public int compare(String key1, String key2) {
  54 + String imageUri1 = key1.substring(0, key1.lastIndexOf(URI_AND_SIZE_SEPARATOR));
  55 + String imageUri2 = key2.substring(0, key2.lastIndexOf(URI_AND_SIZE_SEPARATOR));
  56 + return imageUri1.compareTo(imageUri2);
  57 + }
  58 + };
  59 + }
  60 +
  61 + /**
  62 + * Searches all bitmaps in memory cache which are corresponded to incoming URI.<br />
  63 + * <b>Note:</b> Memory cache can contain multiple sizes of the same image if only you didn't set
  64 + * {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory()
  65 + * denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration}
  66 + */
  67 + public static List<Bitmap> findCachedBitmapsForImageUri(String imageUri, MemoryCache memoryCache) {
  68 + List<Bitmap> values = new ArrayList<Bitmap>();
  69 + for (String key : memoryCache.keys()) {
  70 + if (key.startsWith(imageUri)) {
  71 + values.add(memoryCache.get(key));
  72 + }
  73 + }
  74 + return values;
  75 + }
  76 +
  77 + /**
  78 + * Searches all keys in memory cache which are corresponded to incoming URI.<br />
  79 + * <b>Note:</b> Memory cache can contain multiple sizes of the same image if only you didn't set
  80 + * {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory()
  81 + * denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration}
  82 + */
  83 + public static List<String> findCacheKeysForImageUri(String imageUri, MemoryCache memoryCache) {
  84 + List<String> values = new ArrayList<String>();
  85 + for (String key : memoryCache.keys()) {
  86 + if (key.startsWith(imageUri)) {
  87 + values.add(key);
  88 + }
  89 + }
  90 + return values;
  91 + }
  92 +
  93 + /**
  94 + * Removes from memory cache all images for incoming URI.<br />
  95 + * <b>Note:</b> Memory cache can contain multiple sizes of the same image if only you didn't set
  96 + * {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory()
  97 + * denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration}
  98 + */
  99 + public static void removeFromCache(String imageUri, MemoryCache memoryCache) {
  100 + List<String> keysToRemove = new ArrayList<String>();
  101 + for (String key : memoryCache.keys()) {
  102 + if (key.startsWith(imageUri)) {
  103 + keysToRemove.add(key);
  104 + }
  105 + }
  106 + for (String keyToRemove : keysToRemove) {
  107 + memoryCache.remove(keyToRemove);
  108 + }
  109 + }
  110 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.utils;
  17 +
  18 +import android.content.Context;
  19 +import android.content.pm.PackageManager;
  20 +import android.os.Environment;
  21 +
  22 +import java.io.File;
  23 +import java.io.IOException;
  24 +
  25 +import static android.os.Environment.MEDIA_MOUNTED;
  26 +
  27 +/**
  28 + * Provides application storage paths
  29 + *
  30 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  31 + * @since 1.0.0
  32 + */
  33 +public final class StorageUtils {
  34 +
  35 + private static final String EXTERNAL_STORAGE_PERMISSION = "android.permission.WRITE_EXTERNAL_STORAGE";
  36 + private static final String INDIVIDUAL_DIR_NAME = "uil-images";
  37 +
  38 + private StorageUtils() {
  39 + }
  40 +
  41 + /**
  42 + * Returns application cache directory. Cache directory will be created on SD card
  43 + * <i>("/Android/data/[app_package_name]/cache")</i> if card is mounted and app has appropriate permission. Else -
  44 + * Android defines cache directory on device's file system.
  45 + *
  46 + * @param context Application context
  47 + * @return Cache {@link File directory}.<br />
  48 + * <b>NOTE:</b> Can be null in some unpredictable cases (if SD card is unmounted and
  49 + * {@link Context#getCacheDir() Context.getCacheDir()} returns null).
  50 + */
  51 + public static File getCacheDirectory(Context context) {
  52 + return getCacheDirectory(context, true);
  53 + }
  54 +
  55 + /**
  56 + * Returns application cache directory. Cache directory will be created on SD card
  57 + * <i>("/Android/data/[app_package_name]/cache")</i> (if card is mounted and app has appropriate permission) or
  58 + * on device's file system depending incoming parameters.
  59 + *
  60 + * @param context Application context
  61 + * @param preferExternal Whether prefer external location for cache
  62 + * @return Cache {@link File directory}.<br />
  63 + * <b>NOTE:</b> Can be null in some unpredictable cases (if SD card is unmounted and
  64 + * {@link Context#getCacheDir() Context.getCacheDir()} returns null).
  65 + */
  66 + public static File getCacheDirectory(Context context, boolean preferExternal) {
  67 + File appCacheDir = null;
  68 + String externalStorageState;
  69 + try {
  70 + externalStorageState = Environment.getExternalStorageState();
  71 + } catch (NullPointerException e) { // (sh)it happens (Issue #660)
  72 + externalStorageState = "";
  73 + } catch (IncompatibleClassChangeError e) { // (sh)it happens too (Issue #989)
  74 + externalStorageState = "";
  75 + }
  76 + if (preferExternal && MEDIA_MOUNTED.equals(externalStorageState) && hasExternalStoragePermission(context)) {
  77 + appCacheDir = getExternalCacheDir(context);
  78 + }
  79 + if (appCacheDir == null) {
  80 + appCacheDir = context.getCacheDir();
  81 + }
  82 + if (appCacheDir == null) {
  83 + String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache/";
  84 + L.w("Can't define system cache directory! '%s' will be used.", cacheDirPath);
  85 + appCacheDir = new File(cacheDirPath);
  86 + }
  87 + return appCacheDir;
  88 + }
  89 +
  90 + /**
  91 + * Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be
  92 + * created on SD card <i>("/Android/data/[app_package_name]/cache/uil-images")</i> if card is mounted and app has
  93 + * appropriate permission. Else - Android defines cache directory on device's file system.
  94 + *
  95 + * @param context Application context
  96 + * @return Cache {@link File directory}
  97 + */
  98 + public static File getIndividualCacheDirectory(Context context) {
  99 + return getIndividualCacheDirectory(context, INDIVIDUAL_DIR_NAME);
  100 + }
  101 +
  102 + /**
  103 + * Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be
  104 + * created on SD card <i>("/Android/data/[app_package_name]/cache/uil-images")</i> if card is mounted and app has
  105 + * appropriate permission. Else - Android defines cache directory on device's file system.
  106 + *
  107 + * @param context Application context
  108 + * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
  109 + * @return Cache {@link File directory}
  110 + */
  111 + public static File getIndividualCacheDirectory(Context context, String cacheDir) {
  112 + File appCacheDir = getCacheDirectory(context);
  113 + File individualCacheDir = new File(appCacheDir, cacheDir);
  114 + if (!individualCacheDir.exists()) {
  115 + if (!individualCacheDir.mkdir()) {
  116 + individualCacheDir = appCacheDir;
  117 + }
  118 + }
  119 + return individualCacheDir;
  120 + }
  121 +
  122 + /**
  123 + * Returns specified application cache directory. Cache directory will be created on SD card by defined path if card
  124 + * is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system.
  125 + *
  126 + * @param context Application context
  127 + * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
  128 + * @return Cache {@link File directory}
  129 + */
  130 + public static File getOwnCacheDirectory(Context context, String cacheDir) {
  131 + File appCacheDir = null;
  132 + if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) {
  133 + appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
  134 + }
  135 + if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) {
  136 + appCacheDir = context.getCacheDir();
  137 + }
  138 + return appCacheDir;
  139 + }
  140 +
  141 + /**
  142 + * Returns specified application cache directory. Cache directory will be created on SD card by defined path if card
  143 + * is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system.
  144 + *
  145 + * @param context Application context
  146 + * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
  147 + * @return Cache {@link File directory}
  148 + */
  149 + public static File getOwnCacheDirectory(Context context, String cacheDir, boolean preferExternal) {
  150 + File appCacheDir = null;
  151 + if (preferExternal && MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) {
  152 + appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
  153 + }
  154 + if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) {
  155 + appCacheDir = context.getCacheDir();
  156 + }
  157 + return appCacheDir;
  158 + }
  159 +
  160 + private static File getExternalCacheDir(Context context) {
  161 + File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data");
  162 + File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache");
  163 + if (!appCacheDir.exists()) {
  164 + if (!appCacheDir.mkdirs()) {
  165 + L.w("Unable to create external cache directory");
  166 + return null;
  167 + }
  168 + try {
  169 + new File(appCacheDir, ".nomedia").createNewFile();
  170 + } catch (IOException e) {
  171 + L.i("Can't create \".nomedia\" file in application external cache directory");
  172 + }
  173 + }
  174 + return appCacheDir;
  175 + }
  176 +
  177 + private static boolean hasExternalStoragePermission(Context context) {
  178 + int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION);
  179 + return perm == PackageManager.PERMISSION_GRANTED;
  180 + }
  181 +}
... ...
  1 +<!--
  2 + ~ Copyright (c) 2016. wugian
  3 + ~ Licensed under the Apache License, Version 2.0 (the "License");
  4 + ~ you may not use this file except in compliance with the License.
  5 + ~ You may obtain a copy of the License at
  6 + ~
  7 + ~ http://www.apache.org/licenses/LICENSE-2.0
  8 + ~
  9 + ~ Unless required by applicable law or agreed to in writing, software
  10 + ~ distributed under the License is distributed on an "AS IS" BASIS,
  11 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 + ~ See the License for the specific language governing permissions and
  13 + ~ limitations under the License.
  14 + ~
  15 + -->
  16 +
  17 +<resources>
  18 +</resources>
... ...
Please register or login to post a comment