The cache stores its data in a directory on the filesystem. This + * directory must be exclusive to the cache; the cache may delete or overwrite + * files from its directory. It is an error for multiple processes to use the + * same cache directory at the same time. + * + *
This cache limits the number of bytes that it will store on the + * filesystem. When the number of stored bytes exceeds the limit, the cache will + * remove entries in the background until the limit is satisfied. The limit is + * not strict: the cache may temporarily exceed it while waiting for files to be + * deleted. The limit does not include filesystem overhead or the cache + * journal so space-sensitive applications should set a conservative limit. + * + *
Clients call {@link #edit} to create or update the values of an entry. An + * entry may have only one editor at one time; if a value is not available to be + * edited then {@link #edit} will return null. + *
Clients call {@link #get} to read a snapshot of an entry. The read will + * observe the value at the time that {@link #get} was called. Updates and + * removals after the call do not impact ongoing reads. + * + *
This class is tolerant of some I/O errors. If files are missing from the
+ * filesystem, the corresponding entries will be dropped from the cache. If
+ * an error occurs while writing a cache value, the edit will fail silently.
+ * Callers should handle other problems by catching {@code IOException} and
+ * responding appropriately.
+ */
+final class DiskLruCache implements Closeable {
+ static final String JOURNAL_FILE = "journal";
+ static final String JOURNAL_FILE_TEMP = "journal.tmp";
+ static final String JOURNAL_FILE_BACKUP = "journal.bkp";
+ static final String MAGIC = "libcore.io.DiskLruCache";
+ static final String VERSION_1 = "1";
+ static final long ANY_SEQUENCE_NUMBER = -1;
+ static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,64}");
+ private static final String CLEAN = "CLEAN";
+ private static final String DIRTY = "DIRTY";
+ private static final String REMOVE = "REMOVE";
+ private static final String READ = "READ";
+
+ /*
+ * This cache uses a journal file named "journal". A typical journal file
+ * looks like this:
+ * libcore.io.DiskLruCache
+ * 1
+ * 100
+ * 2
+ *
+ * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
+ * DIRTY 335c4c6028171cfddfbaae1a9c313c52
+ * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
+ * REMOVE 335c4c6028171cfddfbaae1a9c313c52
+ * DIRTY 1ab96a171faeeee38496d8b330771a7a
+ * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
+ * READ 335c4c6028171cfddfbaae1a9c313c52
+ * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
+ *
+ * The first five lines of the journal form its header. They are the
+ * constant string "libcore.io.DiskLruCache", the disk cache's version,
+ * the application's version, the value count, and a blank line.
+ *
+ * Each of the subsequent lines in the file is a record of the state of a
+ * cache entry. Each line contains space-separated values: a state, a key,
+ * and optional state-specific values.
+ * o DIRTY lines track that an entry is actively being created or updated.
+ * Every successful DIRTY action should be followed by a CLEAN or REMOVE
+ * action. DIRTY lines without a matching CLEAN or REMOVE indicate that
+ * temporary files may need to be deleted.
+ * o CLEAN lines track a cache entry that has been successfully published
+ * and may be read. A publish line is followed by the lengths of each of
+ * its values.
+ * o READ lines track accesses for LRU.
+ * o REMOVE lines track entries that have been deleted.
+ *
+ * The journal file is appended to as cache operations occur. The journal may
+ * occasionally be compacted by dropping redundant lines. A temporary file named
+ * "journal.tmp" will be used during compaction; that file should be deleted if
+ * it exists when the cache is opened.
+ */
+
+ private final File directory;
+ private final File journalFile;
+ private final File journalFileTmp;
+ private final File journalFileBackup;
+ private final int appVersion;
+ private long maxSize;
+ private int maxFileCount;
+ private final int valueCount;
+ private long size = 0;
+ private int fileCount = 0;
+ private Writer journalWriter;
+ private final LinkedHashMap This class is used for buffered reading of lines. For purposes of this class, a line ends
+ * with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
+ * line at end of input is invalid and will be ignored, the caller may use {@code
+ * hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
+ *
+ * This class is intended for reading input that strictly consists of lines, such as line-based
+ * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
+ * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
+ * end-of-input reporting and a more restrictive definition of a line.
+ *
+ * This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
+ * and 10, respectively, and the representation of no other character contains these values.
+ * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
+ * The default charset is US_ASCII.
+ */
+class StrictLineReader implements Closeable {
+ private static final byte CR = (byte) '\r';
+ private static final byte LF = (byte) '\n';
+
+ private final InputStream in;
+ private final Charset charset;
+
+ /*
+ * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
+ * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
+ * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
+ * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
+ */
+ private byte[] buf;
+ private int pos;
+ private int end;
+
+ /**
+ * Constructs a new {@code LineReader} with the specified charset and the default capacity.
+ *
+ * @param in the {@code InputStream} to read data from.
+ * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
+ * supported.
+ * @throws NullPointerException if {@code in} or {@code charset} is null.
+ * @throws IllegalArgumentException if the specified charset is not supported.
+ */
+ public StrictLineReader(InputStream in, Charset charset) {
+ this(in, 8192, charset);
+ }
+
+ /**
+ * Constructs a new {@code LineReader} with the specified capacity and charset.
+ *
+ * @param in the {@code InputStream} to read data from.
+ * @param capacity the capacity of the buffer.
+ * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
+ * supported.
+ * @throws NullPointerException if {@code in} or {@code charset} is null.
+ * @throws IllegalArgumentException if {@code capacity} is negative or zero
+ * or the specified charset is not supported.
+ */
+ public StrictLineReader(InputStream in, int capacity, Charset charset) {
+ if (in == null || charset == null) {
+ throw new NullPointerException();
+ }
+ if (capacity < 0) {
+ throw new IllegalArgumentException("capacity <= 0");
+ }
+ if (!(charset.equals(Util.US_ASCII))) {
+ throw new IllegalArgumentException("Unsupported encoding");
+ }
+
+ this.in = in;
+ this.charset = charset;
+ buf = new byte[capacity];
+ }
+
+ /**
+ * Closes the reader by closing the underlying {@code InputStream} and
+ * marking this reader as closed.
+ *
+ * @throws IOException for errors when closing the underlying {@code InputStream}.
+ */
+ public void close() throws IOException {
+ synchronized (in) {
+ if (buf != null) {
+ buf = null;
+ in.close();
+ }
+ }
+ }
+
+ /**
+ * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
+ * this end of line marker is not included in the result.
+ *
+ * @return the next line from the input.
+ * @throws IOException for underlying {@code InputStream} errors.
+ * @throws EOFException for the end of source stream.
+ */
+ public String readLine() throws IOException {
+ synchronized (in) {
+ if (buf == null) {
+ throw new IOException("LineReader is closed");
+ }
+
+ // Read more data if we are at the end of the buffered data.
+ // Though it's an error to read after an exception, we will let {@code fillBuf()}
+ // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
+ if (pos >= end) {
+ fillBuf();
+ }
+ // Try to find LF in the buffered data and return the line if successful.
+ for (int i = pos; i != end; ++i) {
+ if (buf[i] == LF) {
+ int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
+ String res = new String(buf, pos, lineEnd - pos, charset.name());
+ pos = i + 1;
+ return res;
+ }
+ }
+
+ // Let's anticipate up to 80 characters on top of those already read.
+ ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
+ @Override
+ public String toString() {
+ int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
+ try {
+ return new String(buf, 0, length, charset.name());
+ } catch (UnsupportedEncodingException e) {
+ throw new AssertionError(e); // Since we control the charset this will never happen.
+ }
+ }
+ };
+
+ while (true) {
+ out.write(buf, pos, end - pos);
+ // Mark unterminated line in case fillBuf throws EOFException or IOException.
+ end = -1;
+ fillBuf();
+ // Try to find LF in the buffered data and return the line if successful.
+ for (int i = pos; i != end; ++i) {
+ if (buf[i] == LF) {
+ if (i != pos) {
+ out.write(buf, pos, i - pos);
+ }
+ pos = i + 1;
+ return out.toString();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Reads new input data into the buffer. Call only with pos == end or end == -1,
+ * depending on the desired outcome if the function throws.
+ */
+ private void fillBuf() throws IOException {
+ int result = in.read(buf, 0, buf.length);
+ if (result == -1) {
+ throw new EOFException();
+ }
+ pos = 0;
+ end = result;
+ }
+}
+
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/Util.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/Util.java
new file mode 100644
index 000000000..fd7a4ba17
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/impl/ext/Util.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.nostra13.universalimageloader.cache.disc.impl.ext;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+
+/** Junk drawer of utility methods. */
+final class Util {
+ static final Charset US_ASCII = Charset.forName("US-ASCII");
+ static final Charset UTF_8 = Charset.forName("UTF-8");
+
+ private Util() {
+ }
+
+ static String readFully(Reader reader) throws IOException {
+ try {
+ StringWriter writer = new StringWriter();
+ char[] buffer = new char[1024];
+ int count;
+ while ((count = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, count);
+ }
+ return writer.toString();
+ } finally {
+ reader.close();
+ }
+ }
+
+ /**
+ * Deletes the contents of {@code dir}. Throws an IOException if any file
+ * could not be deleted, or if {@code dir} is not a readable directory.
+ */
+ static void deleteContents(File dir) throws IOException {
+ File[] files = dir.listFiles();
+ if (files == null) {
+ throw new IOException("not a readable directory: " + dir);
+ }
+ for (File file : files) {
+ if (file.isDirectory()) {
+ deleteContents(file);
+ }
+ if (!file.delete()) {
+ throw new IOException("failed to delete file: " + file);
+ }
+ }
+ }
+
+ static void closeQuietly(/*Auto*/Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/FileNameGenerator.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/FileNameGenerator.java
new file mode 100644
index 000000000..20e1d664b
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/FileNameGenerator.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.cache.disc.naming;
+
+/**
+ * Generates names for files at disk cache
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.3.1
+ */
+public interface FileNameGenerator {
+
+ /** Generates unique file name for image defined by URI */
+ String generate(String imageUri);
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/HashCodeFileNameGenerator.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/HashCodeFileNameGenerator.java
new file mode 100644
index 000000000..a284b53b0
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/HashCodeFileNameGenerator.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.cache.disc.naming;
+
+/**
+ * Names image file as image URI {@linkplain String#hashCode() hashcode}
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.3.1
+ */
+public class HashCodeFileNameGenerator implements FileNameGenerator {
+ @Override
+ public String generate(String imageUri) {
+ return String.valueOf(imageUri.hashCode());
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/Md5FileNameGenerator.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/Md5FileNameGenerator.java
new file mode 100644
index 000000000..9d50bc6cd
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/disc/naming/Md5FileNameGenerator.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.cache.disc.naming;
+
+import com.nostra13.universalimageloader.utils.L;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Names image file as MD5 hash of image URI
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.4.0
+ */
+public class Md5FileNameGenerator implements FileNameGenerator {
+
+ private static final String HASH_ALGORITHM = "MD5";
+ private static final int RADIX = 10 + 26; // 10 digits + 26 letters
+
+ @Override
+ public String generate(String imageUri) {
+ byte[] md5 = getMD5(imageUri.getBytes());
+ BigInteger bi = new BigInteger(md5).abs();
+ return bi.toString(RADIX);
+ }
+
+ private byte[] getMD5(byte[] data) {
+ byte[] hash = null;
+ try {
+ MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
+ digest.update(data);
+ hash = digest.digest();
+ } catch (NoSuchAlgorithmException e) {
+ L.e(e);
+ }
+ return hash;
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/BaseMemoryCache.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/BaseMemoryCache.java
new file mode 100644
index 000000000..a1cc0b779
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/cache/memory/BaseMemoryCache.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.cache.memory;
+
+import android.graphics.Bitmap;
+
+import java.lang.ref.Reference;
+import java.util.*;
+
+/**
+ * Base memory cache. Implements common functionality for memory cache. Provides object references (
+ * {@linkplain Reference not strong}) storing.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public abstract class BaseMemoryCache implements MemoryCache {
+
+ /** Stores not strong references to objects */
+ private final Map BlockingDeque methods come in four forms, with different ways
+ * of handling operations that cannot be satisfied immediately, but may be
+ * satisfied at some point in the future:
+ * one throws an exception, the second returns a special value (either
+ * null or false, depending on the operation), the third
+ * blocks the current thread indefinitely until the operation can succeed,
+ * and the fourth blocks for only a given maximum time limit before giving
+ * up. These methods are summarized in the following table:
+ *
+ *
+ * Like any {@link BlockingQueue}, a BlockingDeque is thread safe,
+ * does not permit null elements, and may (or may not) be
+ * capacity-constrained.
+ *
+ * A BlockingDeque implementation may be used directly as a FIFO
+ * BlockingQueue. The methods inherited from the
+ * BlockingQueue interface are precisely equivalent to
+ * BlockingDeque methods as indicated in the following table:
+ *
+ *
+ * Memory consistency effects: As with other concurrent
+ * collections, actions in a thread prior to placing an object into a
+ * {@code BlockingDeque}
+ * happen-before
+ * actions subsequent to the access or removal of that element from
+ * the {@code BlockingDeque} in another thread.
+ *
+ * This interface is a member of the
+ *
+ * Java Collections Framework.
+ *
+ * @since 1.6
+ * @author Doug Lea
+ * @param This method is equivalent to {@link #addLast addLast}.
+ *
+ * @param e the element to add
+ * @throws IllegalStateException {@inheritDoc}
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean add(E e);
+
+ /**
+ * Inserts the specified element into the queue represented by this deque
+ * (in other words, at the tail of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * true upon success and false if no space is currently
+ * available. When using a capacity-restricted deque, this method is
+ * generally preferable to the {@link #add} method, which can fail to
+ * insert an element only by throwing an exception.
+ *
+ * This method is equivalent to {@link #offerLast offerLast}.
+ *
+ * @param e the element to add
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean offer(E e);
+
+ /**
+ * Inserts the specified element into the queue represented by this deque
+ * (in other words, at the tail of this deque), waiting if necessary for
+ * space to become available.
+ *
+ * This method is equivalent to {@link #putLast putLast}.
+ *
+ * @param e the element to add
+ * @throws InterruptedException {@inheritDoc}
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ void put(E e) throws InterruptedException;
+
+ /**
+ * Inserts the specified element into the queue represented by this deque
+ * (in other words, at the tail of this deque), waiting up to the
+ * specified wait time if necessary for space to become available.
+ *
+ * This method is equivalent to
+ * {@link #offerLast offerLast}.
+ *
+ * @param e the element to add
+ * @return true if the element was added to this deque, else
+ * false
+ * @throws InterruptedException {@inheritDoc}
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean offer(E e, long timeout, TimeUnit unit)
+ throws InterruptedException;
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque).
+ * This method differs from {@link #poll poll} only in that it
+ * throws an exception if this deque is empty.
+ *
+ * This method is equivalent to {@link #removeFirst() removeFirst}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E remove();
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque), or returns
+ * null if this deque is empty.
+ *
+ * This method is equivalent to {@link #pollFirst()}.
+ *
+ * @return the head of this deque, or null if this deque is empty
+ */
+ E poll();
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque), waiting if
+ * necessary until an element becomes available.
+ *
+ * This method is equivalent to {@link #takeFirst() takeFirst}.
+ *
+ * @return the head of this deque
+ * @throws InterruptedException if interrupted while waiting
+ */
+ E take() throws InterruptedException;
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque), waiting up to the
+ * specified wait time if necessary for an element to become available.
+ *
+ * This method is equivalent to
+ * {@link #pollFirst(long,TimeUnit) pollFirst}.
+ *
+ * @return the head of this deque, or null if the
+ * specified waiting time elapses before an element is available
+ * @throws InterruptedException if interrupted while waiting
+ */
+ E poll(long timeout, TimeUnit unit)
+ throws InterruptedException;
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque (in other words, the first element of this deque).
+ * This method differs from {@link #peek peek} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * This method is equivalent to {@link #getFirst() getFirst}.
+ *
+ * @return the head of this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E element();
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque (in other words, the first element of this deque), or
+ * returns null if this deque is empty.
+ *
+ * This method is equivalent to {@link #peekFirst() peekFirst}.
+ *
+ * @return the head of this deque, or null if this deque is empty
+ */
+ E peek();
+
+ /**
+ * Removes the first occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element e such that
+ * o.equals(e) (if such an element exists).
+ * Returns true if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * This method is equivalent to
+ * {@link #removeFirstOccurrence removeFirstOccurrence}.
+ *
+ * @param o element to be removed from this deque, if present
+ * @return true if this deque changed as a result of the call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null (optional)
+ */
+ boolean remove(Object o);
+
+ /**
+ * Returns true if this deque contains the specified element.
+ * More formally, returns true if and only if this deque contains
+ * at least one element e such that o.equals(e).
+ *
+ * @param o object to be checked for containment in this deque
+ * @return true if this deque contains the specified element
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null (optional)
+ */
+ public boolean contains(Object o);
+
+ /**
+ * Returns the number of elements in this deque.
+ *
+ * @return the number of elements in this deque
+ */
+ public int size();
+
+ /**
+ * Returns an iterator over the elements in this deque in proper sequence.
+ * The elements will be returned in order from first (head) to last (tail).
+ *
+ * @return an iterator over the elements in this deque in proper sequence
+ */
+ Iterator This method is equivalent to {@link #addFirst addFirst}.
+ *
+ * @throws IllegalStateException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException {@inheritDoc}
+ */
+ void push(E e);
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/Deque.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/Deque.java
new file mode 100644
index 000000000..1b359ffdf
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/Deque.java
@@ -0,0 +1,550 @@
+/*
+ * Written by Doug Lea and Josh Bloch with assistance from members of
+ * JCP JSR-166 Expert Group and released to the public domain, as explained
+ * at http://creativecommons.org/licenses/publicdomain
+ */
+
+package com.nostra13.universalimageloader.core.assist.deque;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Stack;
+
+/**
+ * A linear collection that supports element insertion and removal at
+ * both ends. The name deque is short for "double ended queue"
+ * and is usually pronounced "deck". Most Deque
+ * implementations place no fixed limits on the number of elements
+ * they may contain, but this interface supports capacity-restricted
+ * deques as well as those with no fixed size limit.
+ *
+ * This interface defines methods to access the elements at both
+ * ends of the deque. Methods are provided to insert, remove, and
+ * examine the element. Each of these methods exists in two forms:
+ * one throws an exception if the operation fails, the other returns a
+ * special value (either null or false, depending on
+ * the operation). The latter form of the insert operation is
+ * designed specifically for use with capacity-restricted
+ * Deque implementations; in most implementations, insert
+ * operations cannot fail.
+ *
+ * The twelve methods described above are summarized in the
+ * following table:
+ *
+ *
+ * This interface extends the {@link Queue} interface. When a deque is
+ * used as a queue, FIFO (First-In-First-Out) behavior results. Elements are
+ * added at the end of the deque and removed from the beginning. The methods
+ * inherited from the Queue interface are precisely equivalent to
+ * Deque methods as indicated in the following table:
+ *
+ *
+ * Deques can also be used as LIFO (Last-In-First-Out) stacks. This
+ * interface should be used in preference to the legacy {@link Stack} class.
+ * When a deque is used as a stack, elements are pushed and popped from the
+ * beginning of the deque. Stack methods are precisely equivalent to
+ * Deque methods as indicated in the table below:
+ *
+ *
+ * Note that the {@link #peek peek} method works equally well when
+ * a deque is used as a queue or a stack; in either case, elements are
+ * drawn from the beginning of the deque.
+ *
+ * This interface provides two methods to remove interior
+ * elements, {@link #removeFirstOccurrence removeFirstOccurrence} and
+ * {@link #removeLastOccurrence removeLastOccurrence}.
+ *
+ * Unlike the {@link List} interface, this interface does not
+ * provide support for indexed access to elements.
+ *
+ * While Deque implementations are not strictly required
+ * to prohibit the insertion of null elements, they are strongly
+ * encouraged to do so. Users of any Deque implementations
+ * that do allow null elements are strongly encouraged not to
+ * take advantage of the ability to insert nulls. This is so because
+ * null is used as a special return value by various methods
+ * to indicated that the deque is empty.
+ *
+ * Deque implementations generally do not define
+ * element-based versions of the equals and hashCode
+ * methods, but instead inherit the identity-based versions from class
+ * Object.
+ *
+ * @author Doug Lea
+ * @author Josh Bloch
+ * @since 1.6
+ * @param This method is equivalent to {@link #add}.
+ *
+ * @param e the element to add
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ void addLast(E e);
+
+ /**
+ * Inserts the specified element at the front of this deque unless it would
+ * violate capacity restrictions. When using a capacity-restricted deque,
+ * this method is generally preferable to the {@link #addFirst} method,
+ * which can fail to insert an element only by throwing an exception.
+ *
+ * @param e the element to add
+ * @return true if the element was added to this deque, else
+ * false
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean offerFirst(E e);
+
+ /**
+ * Inserts the specified element at the end of this deque unless it would
+ * violate capacity restrictions. When using a capacity-restricted deque,
+ * this method is generally preferable to the {@link #addLast} method,
+ * which can fail to insert an element only by throwing an exception.
+ *
+ * @param e the element to add
+ * @return true if the element was added to this deque, else
+ * false
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean offerLast(E e);
+
+ /**
+ * Retrieves and removes the first element of this deque. This method
+ * differs from {@link #pollFirst pollFirst} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * @return the head of this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E removeFirst();
+
+ /**
+ * Retrieves and removes the last element of this deque. This method
+ * differs from {@link #pollLast pollLast} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * @return the tail of this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E removeLast();
+
+ /**
+ * Retrieves and removes the first element of this deque,
+ * or returns null if this deque is empty.
+ *
+ * @return the head of this deque, or null if this deque is empty
+ */
+ E pollFirst();
+
+ /**
+ * Retrieves and removes the last element of this deque,
+ * or returns null if this deque is empty.
+ *
+ * @return the tail of this deque, or null if this deque is empty
+ */
+ E pollLast();
+
+ /**
+ * Retrieves, but does not remove, the first element of this deque.
+ *
+ * This method differs from {@link #peekFirst peekFirst} only in that it
+ * throws an exception if this deque is empty.
+ *
+ * @return the head of this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E getFirst();
+
+ /**
+ * Retrieves, but does not remove, the last element of this deque.
+ * This method differs from {@link #peekLast peekLast} only in that it
+ * throws an exception if this deque is empty.
+ *
+ * @return the tail of this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E getLast();
+
+ /**
+ * Retrieves, but does not remove, the first element of this deque,
+ * or returns null if this deque is empty.
+ *
+ * @return the head of this deque, or null if this deque is empty
+ */
+ E peekFirst();
+
+ /**
+ * Retrieves, but does not remove, the last element of this deque,
+ * or returns null if this deque is empty.
+ *
+ * @return the tail of this deque, or null if this deque is empty
+ */
+ E peekLast();
+
+ /**
+ * Removes the first occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element e such that
+ * (o==null ? e==null : o.equals(e))
+ * (if such an element exists).
+ * Returns true if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * @param o element to be removed from this deque, if present
+ * @return true if an element was removed as a result of this call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean removeFirstOccurrence(Object o);
+
+ /**
+ * Removes the last occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the last element e such that
+ * (o==null ? e==null : o.equals(e))
+ * (if such an element exists).
+ * Returns true if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * @param o element to be removed from this deque, if present
+ * @return true if an element was removed as a result of this call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean removeLastOccurrence(Object o);
+
+ // *** Queue methods ***
+
+ /**
+ * Inserts the specified element into the queue represented by this deque
+ * (in other words, at the tail of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * true upon success and throwing an
+ * IllegalStateException if no space is currently available.
+ * When using a capacity-restricted deque, it is generally preferable to
+ * use {@link #offer offer}.
+ *
+ * This method is equivalent to {@link #addLast}.
+ *
+ * @param e the element to add
+ * @return true (as specified by {@link Collection#add})
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean add(E e);
+
+ /**
+ * Inserts the specified element into the queue represented by this deque
+ * (in other words, at the tail of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * true upon success and false if no space is currently
+ * available. When using a capacity-restricted deque, this method is
+ * generally preferable to the {@link #add} method, which can fail to
+ * insert an element only by throwing an exception.
+ *
+ * This method is equivalent to {@link #offerLast}.
+ *
+ * @param e the element to add
+ * @return true if the element was added to this deque, else
+ * false
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean offer(E e);
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque).
+ * This method differs from {@link #poll poll} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * This method is equivalent to {@link #removeFirst()}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E remove();
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque), or returns
+ * null if this deque is empty.
+ *
+ * This method is equivalent to {@link #pollFirst()}.
+ *
+ * @return the first element of this deque, or null if
+ * this deque is empty
+ */
+ E poll();
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque (in other words, the first element of this deque).
+ * This method differs from {@link #peek peek} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * This method is equivalent to {@link #getFirst()}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E element();
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque (in other words, the first element of this deque), or
+ * returns null if this deque is empty.
+ *
+ * This method is equivalent to {@link #peekFirst()}.
+ *
+ * @return the head of the queue represented by this deque, or
+ * null if this deque is empty
+ */
+ E peek();
+
+
+ // *** Stack methods ***
+
+ /**
+ * Pushes an element onto the stack represented by this deque (in other
+ * words, at the head of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * true upon success and throwing an
+ * IllegalStateException if no space is currently available.
+ *
+ * This method is equivalent to {@link #addFirst}.
+ *
+ * @param e the element to push
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ void push(E e);
+
+ /**
+ * Pops an element from the stack represented by this deque. In other
+ * words, removes and returns the first element of this deque.
+ *
+ * This method is equivalent to {@link #removeFirst()}.
+ *
+ * @return the element at the front of this deque (which is the top
+ * of the stack represented by this deque)
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E pop();
+
+
+ // *** Collection methods ***
+
+ /**
+ * Removes the first occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element e such that
+ * (o==null ? e==null : o.equals(e))
+ * (if such an element exists).
+ * Returns true if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * This method is equivalent to {@link #removeFirstOccurrence}.
+ *
+ * @param o element to be removed from this deque, if present
+ * @return true if an element was removed as a result of this call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean remove(Object o);
+
+ /**
+ * Returns true if this deque contains the specified element.
+ * More formally, returns true if and only if this deque contains
+ * at least one element e such that
+ * (o==null ? e==null : o.equals(e)).
+ *
+ * @param o element whose presence in this deque is to be tested
+ * @return true if this deque contains the specified element
+ * @throws ClassCastException if the type of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean contains(Object o);
+
+ /**
+ * Returns the number of elements in this deque.
+ *
+ * @return the number of elements in this deque
+ */
+ public int size();
+
+ /**
+ * Returns an iterator over the elements in this deque in proper sequence.
+ * The elements will be returned in order from first (head) to last (tail).
+ *
+ * @return an iterator over the elements in this deque in proper sequence
+ */
+ Iterator The optional capacity bound constructor argument serves as a
+ * way to prevent excessive expansion. The capacity, if unspecified,
+ * is equal to {@link Integer#MAX_VALUE}. Linked nodes are
+ * dynamically created upon each insertion unless this would bring the
+ * deque above capacity.
+ *
+ * Most operations run in constant time (ignoring time spent
+ * blocking). Exceptions include {@link #remove(Object) remove},
+ * {@link #removeFirstOccurrence removeFirstOccurrence}, {@link
+ * #removeLastOccurrence removeLastOccurrence}, {@link #contains
+ * contains}, {@link #iterator iterator.remove()}, and the bulk
+ * operations, all of which run in linear time.
+ *
+ * This class and its iterator implement all of the
+ * optional methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
+ *
+ * This class is a member of the
+ *
+ * Java Collections Framework.
+ *
+ * @since 1.6
+ * @author Doug Lea
+ * @param This method is equivalent to {@link #addLast}.
+ *
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean add(E e) {
+ addLast(e);
+ return true;
+ }
+
+ /**
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean offer(E e) {
+ return offerLast(e);
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ * @throws InterruptedException {@inheritDoc}
+ */
+ public void put(E e) throws InterruptedException {
+ putLast(e);
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ * @throws InterruptedException {@inheritDoc}
+ */
+ public boolean offer(E e, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return offerLast(e, timeout, unit);
+ }
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque.
+ * This method differs from {@link #poll poll} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * This method is equivalent to {@link #removeFirst() removeFirst}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ public E remove() {
+ return removeFirst();
+ }
+
+ public E poll() {
+ return pollFirst();
+ }
+
+ public E take() throws InterruptedException {
+ return takeFirst();
+ }
+
+ public E poll(long timeout, TimeUnit unit) throws InterruptedException {
+ return pollFirst(timeout, unit);
+ }
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque. This method differs from {@link #peek peek} only in that
+ * it throws an exception if this deque is empty.
+ *
+ * This method is equivalent to {@link #getFirst() getFirst}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ public E element() {
+ return getFirst();
+ }
+
+ public E peek() {
+ return peekFirst();
+ }
+
+ /**
+ * Returns the number of additional elements that this deque can ideally
+ * (in the absence of memory or resource constraints) accept without
+ * blocking. This is always equal to the initial capacity of this deque
+ * less the current {@code size} of this deque.
+ *
+ * Note that you cannot always tell if an attempt to insert
+ * an element will succeed by inspecting {@code remainingCapacity}
+ * because it may be the case that another thread is about to
+ * insert or remove an element.
+ */
+ public int remainingCapacity() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ return capacity - count;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ */
+ public int drainTo(Collection super E> c) {
+ return drainTo(c, Integer.MAX_VALUE);
+ }
+
+ /**
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ */
+ public int drainTo(Collection super E> c, int maxElements) {
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ int n = Math.min(maxElements, count);
+ for (int i = 0; i < n; i++) {
+ c.add(first.item); // In this order, in case add() throws.
+ unlinkFirst();
+ }
+ return n;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ // Stack methods
+
+ /**
+ * @throws IllegalStateException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ */
+ public void push(E e) {
+ addFirst(e);
+ }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E pop() {
+ return removeFirst();
+ }
+
+ // Collection methods
+
+ /**
+ * Removes the first occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element {@code e} such that
+ * {@code o.equals(e)} (if such an element exists).
+ * Returns {@code true} if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * This method is equivalent to
+ * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}.
+ *
+ * @param o element to be removed from this deque, if present
+ * @return {@code true} if this deque changed as a result of the call
+ */
+ public boolean remove(Object o) {
+ return removeFirstOccurrence(o);
+ }
+
+ /**
+ * Returns the number of elements in this deque.
+ *
+ * @return the number of elements in this deque
+ */
+ public int size() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ return count;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns {@code true} if this deque contains the specified element.
+ * More formally, returns {@code true} if and only if this deque contains
+ * at least one element {@code e} such that {@code o.equals(e)}.
+ *
+ * @param o object to be checked for containment in this deque
+ * @return {@code true} if this deque contains the specified element
+ */
+ public boolean contains(Object o) {
+ if (o == null) return false;
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ for (Node The returned array will be "safe" in that no references to it are
+ * maintained by this deque. (In other words, this method must allocate
+ * a new array). The caller is thus free to modify the returned array.
+ *
+ * This method acts as bridge between array-based and collection-based
+ * APIs.
+ *
+ * @return an array containing all of the elements in this deque
+ */
+ public Object[] toArray() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ Object[] a = new Object[count];
+ int k = 0;
+ for (Node If this deque fits in the specified array with room to spare
+ * (i.e., the array has more elements than this deque), the element in
+ * the array immediately following the end of the deque is set to
+ * {@code null}.
+ *
+ * Like the {@link #toArray()} method, this method acts as bridge between
+ * array-based and collection-based APIs. Further, this method allows
+ * precise control over the runtime type of the output array, and may,
+ * under certain circumstances, be used to save allocation costs.
+ *
+ * Suppose {@code x} is a deque known to contain only strings.
+ * The following code can be used to dump the deque into a newly
+ * allocated array of {@code String}:
+ *
+ * The returned iterator is a "weakly consistent" iterator that
+ * will never throw {@link java.util.ConcurrentModificationException
+ * ConcurrentModificationException}, and guarantees to traverse
+ * elements as they existed upon construction of the iterator, and
+ * may (but is not guaranteed to) reflect any modifications
+ * subsequent to construction.
+ *
+ * @return an iterator over the elements in this deque in proper sequence
+ */
+ public Iterator The returned iterator is a "weakly consistent" iterator that
+ * will never throw {@link java.util.ConcurrentModificationException
+ * ConcurrentModificationException}, and guarantees to traverse
+ * elements as they existed upon construction of the iterator, and
+ * may (but is not guaranteed to) reflect any modifications
+ * subsequent to construction.
+ *
+ * @return an iterator over the elements in this deque in reverse order
+ */
+ public Iterator
+ *
+ * NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
+ * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @see BaseMemoryCache
+ * @since 1.0.0
+ */
+public abstract class LimitedMemoryCache extends BaseMemoryCache {
+
+ private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16;
+ private static final int MAX_NORMAL_CACHE_SIZE = MAX_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024;
+
+ private final int sizeLimit;
+
+ private final AtomicInteger cacheSize;
+
+ /**
+ * Contains strong references to stored objects. Each next object is added last. If hard cache size will exceed
+ * limit then first object is deleted (but it continue exist at {@link #softMap} and can be collected by GC at any
+ * time)
+ */
+ private final List
+ *
+ * NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
+ * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public class FIFOLimitedMemoryCache extends LimitedMemoryCache {
+
+ private final List
+ * NOTE: Used for internal needs. Normally you don't need to use this class.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public class FuzzyKeyMemoryCache implements MemoryCache {
+
+ private final MemoryCache cache;
+ private final Comparator
+ *
+ * NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
+ * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.3.0
+ */
+public class LRULimitedMemoryCache extends LimitedMemoryCache {
+
+ private static final int INITIAL_CAPACITY = 10;
+ private static final float LOAD_FACTOR = 1.1f;
+
+ /** Cache providing Least-Recently-Used logic */
+ private final Map
+ *
+ * NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
+ * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public class LargestLimitedMemoryCache extends LimitedMemoryCache {
+ /**
+ * Contains strong references to stored objects (keys) and sizes of the objects. If hard cache
+ * size will exceed limit then object with the largest size is deleted (but it continue exist at
+ * {@link #softMap} and can be collected by GC at any time)
+ */
+ private final Map
+ *
+ * NOTE: This cache uses only strong references for stored Bitmaps.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.8.1
+ */
+public class LruMemoryCache implements MemoryCache {
+
+ private final LinkedHashMap
+ *
+ * NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
+ * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache {
+ /**
+ * Contains strong references to stored objects (keys) and last object usage date (in milliseconds). If hard cache
+ * size will exceed limit then object with the least frequently usage is deleted (but it continue exist at
+ * {@link #softMap} and can be collected by GC at any time)
+ */
+ private final Map
+ *
+ * NOTE: This cache uses only weak references for stored Bitmaps.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.5.3
+ */
+public class WeakMemoryCache extends BaseMemoryCache {
+ @Override
+ protected Reference
+ * Default cache size = 1/8 of available app memory.
+ */
+ public static MemoryCache createMemoryCache(int memoryCacheSize) {
+ if (memoryCacheSize == 0) {
+ memoryCacheSize = (int) (Runtime.getRuntime().maxMemory() / 8);
+ }
+ return new LruMemoryCache(memoryCacheSize);
+ }
+
+ /** Creates default implementation of {@link ImageDownloader} - {@link BaseImageDownloader} */
+ public static ImageDownloader createImageDownloader(Context context) {
+ return new BaseImageDownloader(context);
+ }
+
+ /** Creates default implementation of {@link ImageDecoder} - {@link BaseImageDecoder} */
+ public static ImageDecoder createImageDecoder(boolean loggingEnabled) {
+ return new BaseImageDecoder(loggingEnabled);
+ }
+
+ /** Creates default implementation of {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer} */
+ public static BitmapDisplayer createBitmapDisplayer() {
+ return new SimpleBitmapDisplayer();
+ }
+
+ /** Creates default implementation of {@linkplain ThreadFactory thread factory} for task executor */
+ private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) {
+ return new DefaultThreadFactory(threadPriority, threadNamePrefix);
+ }
+
+ private static class DefaultThreadFactory implements ThreadFactory {
+
+ private static final AtomicInteger poolNumber = new AtomicInteger(1);
+
+ private final ThreadGroup group;
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+ private final String namePrefix;
+ private final int threadPriority;
+
+ DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
+ this.threadPriority = threadPriority;
+ group = Thread.currentThread().getThreadGroup();
+ namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
+ }
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
+ if (t.isDaemon()) t.setDaemon(false);
+ t.setPriority(threadPriority);
+ return t;
+ }
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java
new file mode 100644
index 000000000..341321e13
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core;
+
+import android.graphics.Bitmap;
+import com.nostra13.universalimageloader.core.assist.LoadedFrom;
+import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
+import com.nostra13.universalimageloader.utils.L;
+
+/**
+ * Displays bitmap in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}. Must be called on UI thread.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @see ImageLoadingListener
+ * @see BitmapDisplayer
+ * @since 1.3.1
+ */
+final class DisplayBitmapTask implements Runnable {
+
+ private static final String LOG_DISPLAY_IMAGE_IN_IMAGEAWARE = "Display image in ImageAware (loaded from %1$s) [%2$s]";
+ private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
+ private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";
+
+ private final Bitmap bitmap;
+ private final String imageUri;
+ private final ImageAware imageAware;
+ private final String memoryCacheKey;
+ private final BitmapDisplayer displayer;
+ private final ImageLoadingListener listener;
+ private final ImageLoaderEngine engine;
+ private final LoadedFrom loadedFrom;
+
+ public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, ImageLoaderEngine engine,
+ LoadedFrom loadedFrom) {
+ this.bitmap = bitmap;
+ imageUri = imageLoadingInfo.uri;
+ imageAware = imageLoadingInfo.imageAware;
+ memoryCacheKey = imageLoadingInfo.memoryCacheKey;
+ displayer = imageLoadingInfo.options.getDisplayer();
+ listener = imageLoadingInfo.listener;
+ this.engine = engine;
+ this.loadedFrom = loadedFrom;
+ }
+
+ @Override
+ public void run() {
+ if (imageAware.isCollected()) {
+ L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
+ listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
+ } else if (isViewWasReused()) {
+ L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
+ listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
+ } else {
+ L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
+ displayer.display(bitmap, imageAware, loadedFrom);
+ engine.cancelDisplayTaskFor(imageAware);
+ listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
+ }
+ }
+
+ /** Checks whether memory cache key (image URI) for current ImageAware is actual */
+ private boolean isViewWasReused() {
+ String currentCacheKey = engine.getLoadingUriForView(imageAware);
+ return !memoryCacheKey.equals(currentCacheKey);
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayImageOptions.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayImageOptions.java
new file mode 100644
index 000000000..b8ff9fd1c
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/DisplayImageOptions.java
@@ -0,0 +1,509 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
+import com.nostra13.universalimageloader.core.assist.ImageScaleType;
+import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
+import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
+import com.nostra13.universalimageloader.core.download.ImageDownloader;
+import com.nostra13.universalimageloader.core.process.BitmapProcessor;
+
+/**
+ * Contains options for image display. Defines:
+ *
+ *
+ *
+ * You can create instance:
+ *
+ *
+ * i.e. :
+ * new {@link DisplayImageOptions}.{@link Builder#Builder() Builder()}.{@link Builder#cacheInMemory() cacheInMemory()}.
+ * {@link Builder#showImageOnLoading(int) showImageOnLoading()}.{@link Builder#build() build()}
+ *
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public final class DisplayImageOptions {
+
+ private final int imageResOnLoading;
+ private final int imageResForEmptyUri;
+ private final int imageResOnFail;
+ private final Drawable imageOnLoading;
+ private final Drawable imageForEmptyUri;
+ private final Drawable imageOnFail;
+ private final boolean resetViewBeforeLoading;
+ private final boolean cacheInMemory;
+ private final boolean cacheOnDisk;
+ private final ImageScaleType imageScaleType;
+ private final Options decodingOptions;
+ private final int delayBeforeLoading;
+ private final boolean considerExifParams;
+ private final Object extraForDownloader;
+ private final BitmapProcessor preProcessor;
+ private final BitmapProcessor postProcessor;
+ private final BitmapDisplayer displayer;
+ private final Handler handler;
+ private final boolean isSyncLoading;
+
+ private DisplayImageOptions(Builder builder) {
+ imageResOnLoading = builder.imageResOnLoading;
+ imageResForEmptyUri = builder.imageResForEmptyUri;
+ imageResOnFail = builder.imageResOnFail;
+ imageOnLoading = builder.imageOnLoading;
+ imageForEmptyUri = builder.imageForEmptyUri;
+ imageOnFail = builder.imageOnFail;
+ resetViewBeforeLoading = builder.resetViewBeforeLoading;
+ cacheInMemory = builder.cacheInMemory;
+ cacheOnDisk = builder.cacheOnDisk;
+ imageScaleType = builder.imageScaleType;
+ decodingOptions = builder.decodingOptions;
+ delayBeforeLoading = builder.delayBeforeLoading;
+ considerExifParams = builder.considerExifParams;
+ extraForDownloader = builder.extraForDownloader;
+ preProcessor = builder.preProcessor;
+ postProcessor = builder.postProcessor;
+ displayer = builder.displayer;
+ handler = builder.handler;
+ isSyncLoading = builder.isSyncLoading;
+ }
+
+ public boolean shouldShowImageOnLoading() {
+ return imageOnLoading != null || imageResOnLoading != 0;
+ }
+
+ public boolean shouldShowImageForEmptyUri() {
+ return imageForEmptyUri != null || imageResForEmptyUri != 0;
+ }
+
+ public boolean shouldShowImageOnFail() {
+ return imageOnFail != null || imageResOnFail != 0;
+ }
+
+ public boolean shouldPreProcess() {
+ return preProcessor != null;
+ }
+
+ public boolean shouldPostProcess() {
+ return postProcessor != null;
+ }
+
+ public boolean shouldDelayBeforeLoading() {
+ return delayBeforeLoading > 0;
+ }
+
+ public Drawable getImageOnLoading(Resources res) {
+ return imageResOnLoading != 0 ? res.getDrawable(imageResOnLoading) : imageOnLoading;
+ }
+
+ public Drawable getImageForEmptyUri(Resources res) {
+ return imageResForEmptyUri != 0 ? res.getDrawable(imageResForEmptyUri) : imageForEmptyUri;
+ }
+
+ public Drawable getImageOnFail(Resources res) {
+ return imageResOnFail != 0 ? res.getDrawable(imageResOnFail) : imageOnFail;
+ }
+
+ public boolean isResetViewBeforeLoading() {
+ return resetViewBeforeLoading;
+ }
+
+ public boolean isCacheInMemory() {
+ return cacheInMemory;
+ }
+
+ public boolean isCacheOnDisk() {
+ return cacheOnDisk;
+ }
+
+ public ImageScaleType getImageScaleType() {
+ return imageScaleType;
+ }
+
+ public Options getDecodingOptions() {
+ return decodingOptions;
+ }
+
+ public int getDelayBeforeLoading() {
+ return delayBeforeLoading;
+ }
+
+ public boolean isConsiderExifParams() {
+ return considerExifParams;
+ }
+
+ public Object getExtraForDownloader() {
+ return extraForDownloader;
+ }
+
+ public BitmapProcessor getPreProcessor() {
+ return preProcessor;
+ }
+
+ public BitmapProcessor getPostProcessor() {
+ return postProcessor;
+ }
+
+ public BitmapDisplayer getDisplayer() {
+ return displayer;
+ }
+
+ public Handler getHandler() {
+ return handler;
+ }
+
+ boolean isSyncLoading() {
+ return isSyncLoading;
+ }
+
+ /**
+ * Builder for {@link DisplayImageOptions}
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ */
+ public static class Builder {
+ private int imageResOnLoading = 0;
+ private int imageResForEmptyUri = 0;
+ private int imageResOnFail = 0;
+ private Drawable imageOnLoading = null;
+ private Drawable imageForEmptyUri = null;
+ private Drawable imageOnFail = null;
+ private boolean resetViewBeforeLoading = false;
+ private boolean cacheInMemory = false;
+ private boolean cacheOnDisk = false;
+ private ImageScaleType imageScaleType = ImageScaleType.IN_SAMPLE_POWER_OF_2;
+ private Options decodingOptions = new Options();
+ private int delayBeforeLoading = 0;
+ private boolean considerExifParams = false;
+ private Object extraForDownloader = null;
+ private BitmapProcessor preProcessor = null;
+ private BitmapProcessor postProcessor = null;
+ private BitmapDisplayer displayer = DefaultConfigurationFactory.createBitmapDisplayer();
+ private Handler handler = null;
+ private boolean isSyncLoading = false;
+
+ public Builder() {
+ decodingOptions.inPurgeable = true;
+ decodingOptions.inInputShareable = true;
+ }
+
+ /**
+ * Stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * image aware view} during image loading
+ *
+ * @param imageRes Stub image resource
+ * @deprecated Use {@link #showImageOnLoading(int)} instead
+ */
+ @Deprecated
+ public Builder showStubImage(int imageRes) {
+ imageResOnLoading = imageRes;
+ return this;
+ }
+
+ /**
+ * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * image aware view} during image loading
+ *
+ * @param imageRes Image resource
+ */
+ public Builder showImageOnLoading(int imageRes) {
+ imageResOnLoading = imageRes;
+ return this;
+ }
+
+ /**
+ * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * image aware view} during image loading.
+ * This option will be ignored if {@link DisplayImageOptions.Builder#showImageOnLoading(int)} is set.
+ */
+ public Builder showImageOnLoading(Drawable drawable) {
+ imageOnLoading = drawable;
+ return this;
+ }
+
+ /**
+ * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * image aware view} if empty URI (null or empty
+ * string) will be passed to ImageLoader.displayImage(...) method.
+ *
+ * @param imageRes Image resource
+ */
+ public Builder showImageForEmptyUri(int imageRes) {
+ imageResForEmptyUri = imageRes;
+ return this;
+ }
+
+ /**
+ * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * image aware view} if empty URI (null or empty
+ * string) will be passed to ImageLoader.displayImage(...) method.
+ * This option will be ignored if {@link DisplayImageOptions.Builder#showImageForEmptyUri(int)} is set.
+ */
+ public Builder showImageForEmptyUri(Drawable drawable) {
+ imageForEmptyUri = drawable;
+ return this;
+ }
+
+ /**
+ * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * image aware view} if some error occurs during
+ * requested image loading/decoding.
+ *
+ * @param imageRes Image resource
+ */
+ public Builder showImageOnFail(int imageRes) {
+ imageResOnFail = imageRes;
+ return this;
+ }
+
+ /**
+ * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * image aware view} if some error occurs during
+ * requested image loading/decoding.
+ * This option will be ignored if {@link DisplayImageOptions.Builder#showImageOnFail(int)} is set.
+ */
+ public Builder showImageOnFail(Drawable drawable) {
+ imageOnFail = drawable;
+ return this;
+ }
+
+ /**
+ * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * image aware view} will be reset (set null) before image loading start
+ *
+ * @deprecated Use {@link #resetViewBeforeLoading(boolean) resetViewBeforeLoading(true)} instead
+ */
+ public Builder resetViewBeforeLoading() {
+ resetViewBeforeLoading = true;
+ return this;
+ }
+
+ /**
+ * Sets whether {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * image aware view} will be reset (set null) before image loading start
+ */
+ public Builder resetViewBeforeLoading(boolean resetViewBeforeLoading) {
+ this.resetViewBeforeLoading = resetViewBeforeLoading;
+ return this;
+ }
+
+ /**
+ * Loaded image will be cached in memory
+ *
+ * @deprecated Use {@link #cacheInMemory(boolean) cacheInMemory(true)} instead
+ */
+ @Deprecated
+ public Builder cacheInMemory() {
+ cacheInMemory = true;
+ return this;
+ }
+
+ /** Sets whether loaded image will be cached in memory */
+ public Builder cacheInMemory(boolean cacheInMemory) {
+ this.cacheInMemory = cacheInMemory;
+ return this;
+ }
+
+ /**
+ * Loaded image will be cached on disk
+ *
+ * @deprecated Use {@link #cacheOnDisk(boolean) cacheOnDisk(true)} instead
+ */
+ @Deprecated
+ public Builder cacheOnDisc() {
+ return cacheOnDisk(true);
+ }
+
+ /**
+ * Sets whether loaded image will be cached on disk
+ *
+ * @deprecated Use {@link #cacheOnDisk(boolean)} instead
+ */
+ @Deprecated
+ public Builder cacheOnDisc(boolean cacheOnDisk) {
+ return cacheOnDisk(cacheOnDisk);
+ }
+
+ /** Sets whether loaded image will be cached on disk */
+ public Builder cacheOnDisk(boolean cacheOnDisk) {
+ this.cacheOnDisk = cacheOnDisk;
+ return this;
+ }
+
+ /**
+ * Sets {@linkplain ImageScaleType scale type} for decoding image. This parameter is used while define scale
+ * size for decoding image to Bitmap. Default value - {@link ImageScaleType#IN_SAMPLE_POWER_OF_2}
+ */
+ public Builder imageScaleType(ImageScaleType imageScaleType) {
+ this.imageScaleType = imageScaleType;
+ return this;
+ }
+
+ /** Sets {@link Bitmap.Config bitmap config} for image decoding. Default value - {@link Bitmap.Config#ARGB_8888} */
+ public Builder bitmapConfig(Bitmap.Config bitmapConfig) {
+ if (bitmapConfig == null) throw new IllegalArgumentException("bitmapConfig can't be null");
+ decodingOptions.inPreferredConfig = bitmapConfig;
+ return this;
+ }
+
+ /**
+ * Sets options for image decoding.
+ * NOTE: {@link Options#inSampleSize} of incoming options will NOT be considered. Library
+ * calculate the most appropriate sample size itself according yo {@link #imageScaleType(ImageScaleType)}
+ * options.
+ * NOTE: This option overlaps {@link #bitmapConfig(android.graphics.Bitmap.Config) bitmapConfig()}
+ * option.
+ */
+ public Builder decodingOptions(Options decodingOptions) {
+ if (decodingOptions == null) throw new IllegalArgumentException("decodingOptions can't be null");
+ this.decodingOptions = decodingOptions;
+ return this;
+ }
+
+ /** Sets delay time before starting loading task. Default - no delay. */
+ public Builder delayBeforeLoading(int delayInMillis) {
+ this.delayBeforeLoading = delayInMillis;
+ return this;
+ }
+
+ /** Sets auxiliary object which will be passed to {@link ImageDownloader#getStream(String, Object)} */
+ public Builder extraForDownloader(Object extra) {
+ this.extraForDownloader = extra;
+ return this;
+ }
+
+ /** Sets whether ImageLoader will consider EXIF parameters of JPEG image (rotate, flip) */
+ public Builder considerExifParams(boolean considerExifParams) {
+ this.considerExifParams = considerExifParams;
+ return this;
+ }
+
+ /**
+ * Sets bitmap processor which will be process bitmaps before they will be cached in memory. So memory cache
+ * will contain bitmap processed by incoming preProcessor.
+ * Image will be pre-processed even if caching in memory is disabled.
+ */
+ public Builder preProcessor(BitmapProcessor preProcessor) {
+ this.preProcessor = preProcessor;
+ return this;
+ }
+
+ /**
+ * Sets bitmap processor which will be process bitmaps before they will be displayed in
+ * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware image aware view} but
+ * after they'll have been saved in memory cache.
+ */
+ public Builder postProcessor(BitmapProcessor postProcessor) {
+ this.postProcessor = postProcessor;
+ return this;
+ }
+
+ /**
+ * Sets custom {@link BitmapDisplayer displayer} for image loading task. Default value -
+ * {@link DefaultConfigurationFactory#createBitmapDisplayer()}
+ */
+ public Builder displayer(BitmapDisplayer displayer) {
+ if (displayer == null) throw new IllegalArgumentException("displayer can't be null");
+ this.displayer = displayer;
+ return this;
+ }
+
+ Builder syncLoading(boolean isSyncLoading) {
+ this.isSyncLoading = isSyncLoading;
+ return this;
+ }
+
+ /**
+ * Sets custom {@linkplain Handler handler} for displaying images and firing {@linkplain ImageLoadingListener
+ * listener} events.
+ */
+ public Builder handler(Handler handler) {
+ this.handler = handler;
+ return this;
+ }
+
+ /** Sets all options equal to incoming options */
+ public Builder cloneFrom(DisplayImageOptions options) {
+ imageResOnLoading = options.imageResOnLoading;
+ imageResForEmptyUri = options.imageResForEmptyUri;
+ imageResOnFail = options.imageResOnFail;
+ imageOnLoading = options.imageOnLoading;
+ imageForEmptyUri = options.imageForEmptyUri;
+ imageOnFail = options.imageOnFail;
+ resetViewBeforeLoading = options.resetViewBeforeLoading;
+ cacheInMemory = options.cacheInMemory;
+ cacheOnDisk = options.cacheOnDisk;
+ imageScaleType = options.imageScaleType;
+ decodingOptions = options.decodingOptions;
+ delayBeforeLoading = options.delayBeforeLoading;
+ considerExifParams = options.considerExifParams;
+ extraForDownloader = options.extraForDownloader;
+ preProcessor = options.preProcessor;
+ postProcessor = options.postProcessor;
+ displayer = options.displayer;
+ handler = options.handler;
+ isSyncLoading = options.isSyncLoading;
+ return this;
+ }
+
+ /** Builds configured {@link DisplayImageOptions} object */
+ public DisplayImageOptions build() {
+ return new DisplayImageOptions(this);
+ }
+ }
+
+ /**
+ * Creates options appropriate for single displaying:
+ *
+ *
+ *
+ * These option are appropriate for simple single-use image (from drawables or from Internet) displaying.
+ */
+ public static DisplayImageOptions createSimple() {
+ return new Builder().build();
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoader.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoader.java
new file mode 100644
index 000000000..6a745933d
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoader.java
@@ -0,0 +1,763 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core;
+
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.ImageView;
+import com.nostra13.universalimageloader.cache.disc.DiskCache;
+import com.nostra13.universalimageloader.cache.memory.MemoryCache;
+import com.nostra13.universalimageloader.core.assist.FailReason;
+import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
+import com.nostra13.universalimageloader.core.assist.ImageSize;
+import com.nostra13.universalimageloader.core.assist.LoadedFrom;
+import com.nostra13.universalimageloader.core.assist.ViewScaleType;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
+import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
+import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
+import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
+import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
+import com.nostra13.universalimageloader.utils.ImageSizeUtils;
+import com.nostra13.universalimageloader.utils.L;
+import com.nostra13.universalimageloader.utils.MemoryCacheUtils;
+
+/**
+ * Singletone for image loading and displaying at {@link ImageView ImageViews}
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before any other method.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public class ImageLoader {
+
+ public static final String TAG = ImageLoader.class.getSimpleName();
+
+ static final String LOG_INIT_CONFIG = "Initialize ImageLoader with configuration";
+ static final String LOG_DESTROY = "Destroy ImageLoader";
+ static final String LOG_LOAD_IMAGE_FROM_MEMORY_CACHE = "Load image from memory cache [%s]";
+
+ private static final String WARNING_RE_INIT_CONFIG = "Try to initialize ImageLoader which had already been initialized before. " + "To re-init ImageLoader with new configuration call ImageLoader.destroy() at first.";
+ private static final String ERROR_WRONG_ARGUMENTS = "Wrong arguments were passed to displayImage() method (ImageView reference must not be null)";
+ private static final String ERROR_NOT_INIT = "ImageLoader must be init with configuration before using";
+ private static final String ERROR_INIT_CONFIG_WITH_NULL = "ImageLoader configuration can not be initialized with null";
+
+ private ImageLoaderConfiguration configuration;
+ private ImageLoaderEngine engine;
+
+ private final ImageLoadingListener emptyListener = new SimpleImageLoadingListener();
+
+ private volatile static ImageLoader instance;
+
+ /** Returns singleton class instance */
+ public static ImageLoader getInstance() {
+ if (instance == null) {
+ synchronized (ImageLoader.class) {
+ if (instance == null) {
+ instance = new ImageLoader();
+ }
+ }
+ }
+ return instance;
+ }
+
+ protected ImageLoader() {
+ }
+
+ /**
+ * Initializes ImageLoader instance with configuration.
+ * If configurations was set before ( {@link #isInited()} == true) then this method does nothing.
+ * To force initialization with new configuration you should {@linkplain #destroy() destroy ImageLoader} at first.
+ *
+ * @param configuration {@linkplain ImageLoaderConfiguration ImageLoader configuration}
+ * @throws IllegalArgumentException if configuration parameter is null
+ */
+ public synchronized void init(ImageLoaderConfiguration configuration) {
+ if (configuration == null) {
+ throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
+ }
+ if (this.configuration == null) {
+ L.d(LOG_INIT_CONFIG);
+ engine = new ImageLoaderEngine(configuration);
+ this.configuration = configuration;
+ } else {
+ L.w(WARNING_RE_INIT_CONFIG);
+ }
+ }
+
+ /**
+ * Returns true - if ImageLoader {@linkplain #init(ImageLoaderConfiguration) is initialized with
+ * configuration}; false - otherwise
+ */
+ public boolean isInited() {
+ return configuration != null;
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
+ * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
+ * configuration} will be used.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
+ * which should display image
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageAware is null
+ */
+ public void displayImage(String uri, ImageAware imageAware) {
+ displayImage(uri, imageAware, null, null, null);
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
+ * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
+ * configuration} will be used.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
+ * which should display image
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
+ * UI thread if this method is called on UI thread.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageAware is null
+ */
+ public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener) {
+ displayImage(uri, imageAware, null, listener, null);
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
+ * which should display image
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and displaying. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
+ * from configuration} will be used.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageAware is null
+ */
+ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options) {
+ displayImage(uri, imageAware, options, null, null);
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
+ * which should display image
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and displaying. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
+ * from configuration} will be used.
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
+ * UI thread if this method is called on UI thread.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageAware is null
+ */
+ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
+ ImageLoadingListener listener) {
+ displayImage(uri, imageAware, options, listener, null);
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
+ * which should display image
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and displaying. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
+ * from configuration} will be used.
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
+ * events on UI thread if this method is called on UI thread.
+ * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
+ * Listener} for image loading progress. Listener fires events on UI thread if this method
+ * is called on UI thread. Caching on disk should be enabled in
+ * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
+ * this listener work.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageAware is null
+ */
+ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
+ ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
+ checkConfiguration();
+ if (imageAware == null) {
+ throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
+ }
+ if (listener == null) {
+ listener = emptyListener;
+ }
+ if (options == null) {
+ options = configuration.defaultDisplayImageOptions;
+ }
+
+ if (TextUtils.isEmpty(uri)) {
+ engine.cancelDisplayTaskFor(imageAware);
+ listener.onLoadingStarted(uri, imageAware.getWrappedView());
+ if (options.shouldShowImageForEmptyUri()) {
+ imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
+ } else {
+ imageAware.setImageDrawable(null);
+ }
+ listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
+ return;
+ }
+
+ ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
+ String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
+ engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
+
+ listener.onLoadingStarted(uri, imageAware.getWrappedView());
+
+ Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
+ if (bmp != null && !bmp.isRecycled()) {
+ L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
+
+ if (options.shouldPostProcess()) {
+ ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
+ options, listener, progressListener, engine.getLockForUri(uri));
+ ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
+ defineHandler(options));
+ if (options.isSyncLoading()) {
+ displayTask.run();
+ } else {
+ engine.submit(displayTask);
+ }
+ } else {
+ options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
+ listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
+ }
+ } else {
+ if (options.shouldShowImageOnLoading()) {
+ imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
+ } else if (options.isResetViewBeforeLoading()) {
+ imageAware.setImageDrawable(null);
+ }
+
+ ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
+ options, listener, progressListener, engine.getLockForUri(uri));
+ LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
+ defineHandler(options));
+ if (options.isSyncLoading()) {
+ displayTask.run();
+ } else {
+ engine.submit(displayTask);
+ }
+ }
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
+ * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
+ * configuration} will be used.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageView {@link ImageView} which should display image
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageView is null
+ */
+ public void displayImage(String uri, ImageView imageView) {
+ displayImage(uri, new ImageViewAware(imageView), null, null, null);
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageView {@link ImageView} which should display image
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and displaying. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
+ * from configuration} will be used.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageView is null
+ */
+ public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
+ displayImage(uri, new ImageViewAware(imageView), options, null, null);
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
+ * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
+ * configuration} will be used.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageView {@link ImageView} which should display image
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
+ * UI thread if this method is called on UI thread.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageView is null
+ */
+ public void displayImage(String uri, ImageView imageView, ImageLoadingListener listener) {
+ displayImage(uri, new ImageViewAware(imageView), null, listener, null);
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageView {@link ImageView} which should display image
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and displaying. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
+ * from configuration} will be used.
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
+ * UI thread if this method is called on UI thread.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageView is null
+ */
+ public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
+ ImageLoadingListener listener) {
+ displayImage(uri, imageView, options, listener, null);
+ }
+
+ /**
+ * Adds display image task to execution pool. Image will be set to ImageView when it's turn.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param imageView {@link ImageView} which should display image
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and displaying. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
+ * from configuration} will be used.
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
+ * events on UI thread if this method is called on UI thread.
+ * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
+ * Listener} for image loading progress. Listener fires events on UI thread if this method
+ * is called on UI thread. Caching on disk should be enabled in
+ * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
+ * this listener work.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @throws IllegalArgumentException if passed imageView is null
+ */
+ public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
+ ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
+ displayImage(uri, new ImageViewAware(imageView), options, listener, progressListener);
+ }
+
+ /**
+ * Adds load image task to execution pool. Image will be returned with
+ * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
+ *
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI
+ * thread if this method is called on UI thread.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public void loadImage(String uri, ImageLoadingListener listener) {
+ loadImage(uri, null, null, listener, null);
+ }
+
+ /**
+ * Adds load image task to execution pool. Image will be returned with
+ * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
+ *
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
+ * {@linkplain ImageLoadingListener#onLoadingComplete(String, android.view.View,
+ * android.graphics.Bitmap)} callback}. Downloaded image will be decoded
+ * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
+ * larger) than incoming targetImageSize.
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
+ * events on UI thread if this method is called on UI thread.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public void loadImage(String uri, ImageSize targetImageSize, ImageLoadingListener listener) {
+ loadImage(uri, targetImageSize, null, listener, null);
+ }
+
+ /**
+ * Adds load image task to execution pool. Image will be returned with
+ * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
+ *
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and displaying. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
+ * configuration} will be used.
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI
+ * thread if this method is called on UI thread.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public void loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener) {
+ loadImage(uri, null, options, listener, null);
+ }
+
+ /**
+ * Adds load image task to execution pool. Image will be returned with
+ * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
+ *
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
+ * {@linkplain ImageLoadingListener#onLoadingComplete(String, android.view.View,
+ * android.graphics.Bitmap)} callback}. Downloaded image will be decoded
+ * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
+ * larger) than incoming targetImageSize.
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and displaying. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
+ * from configuration} will be used.
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
+ * events on UI thread if this method is called on UI thread.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
+ ImageLoadingListener listener) {
+ loadImage(uri, targetImageSize, options, listener, null);
+ }
+
+ /**
+ * Adds load image task to execution pool. Image will be returned with
+ * {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
+ *
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
+ * {@linkplain ImageLoadingListener#onLoadingComplete(String, android.view.View,
+ * android.graphics.Bitmap)} callback}. Downloaded image will be decoded
+ * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
+ * larger) than incoming targetImageSize.
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and displaying. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
+ * from configuration} will be used.
+ * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
+ * events on UI thread if this method is called on UI thread.
+ * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
+ * Listener} for image loading progress. Listener fires events on UI thread if this method
+ * is called on UI thread. Caching on disk should be enabled in
+ * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
+ * this listener work.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
+ ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
+ checkConfiguration();
+ if (targetImageSize == null) {
+ targetImageSize = configuration.getMaxImageSize();
+ }
+ if (options == null) {
+ options = configuration.defaultDisplayImageOptions;
+ }
+
+ NonViewAware imageAware = new NonViewAware(uri, targetImageSize, ViewScaleType.CROP);
+ displayImage(uri, imageAware, options, listener, progressListener);
+ }
+
+ /**
+ * Loads and decodes image synchronously.
+ * Default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
+ * configuration} will be used.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public Bitmap loadImageSync(String uri) {
+ return loadImageSync(uri, null, null);
+ }
+
+ /**
+ * Loads and decodes image synchronously.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and scaling. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
+ * configuration} will be used.
+ * @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public Bitmap loadImageSync(String uri, DisplayImageOptions options) {
+ return loadImageSync(uri, null, options);
+ }
+
+ /**
+ * Loads and decodes image synchronously.
+ * Default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
+ * configuration} will be used.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded
+ * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
+ * larger) than incoming targetImageSize.
+ * @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public Bitmap loadImageSync(String uri, ImageSize targetImageSize) {
+ return loadImageSync(uri, targetImageSize, null);
+ }
+
+ /**
+ * Loads and decodes image synchronously.
+ * NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
+ *
+ * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
+ * @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded
+ * and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
+ * larger) than incoming targetImageSize.
+ * @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
+ * decoding and scaling. If null - default display image options
+ * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
+ * from configuration} will be used.
+ * @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled.
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public Bitmap loadImageSync(String uri, ImageSize targetImageSize, DisplayImageOptions options) {
+ if (options == null) {
+ options = configuration.defaultDisplayImageOptions;
+ }
+ options = new DisplayImageOptions.Builder().cloneFrom(options).syncLoading(true).build();
+
+ SyncImageLoadingListener listener = new SyncImageLoadingListener();
+ loadImage(uri, targetImageSize, options, listener);
+ return listener.getLoadedBitmap();
+ }
+
+ /**
+ * Checks if ImageLoader's configuration was initialized
+ *
+ * @throws IllegalStateException if configuration wasn't initialized
+ */
+ private void checkConfiguration() {
+ if (configuration == null) {
+ throw new IllegalStateException(ERROR_NOT_INIT);
+ }
+ }
+
+ /**
+ * Returns memory cache
+ *
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public MemoryCache getMemoryCache() {
+ checkConfiguration();
+ return configuration.memoryCache;
+ }
+
+ /**
+ * Clears memory cache
+ *
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public void clearMemoryCache() {
+ checkConfiguration();
+ configuration.memoryCache.clear();
+ }
+
+ /**
+ * Returns disk cache
+ *
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @deprecated Use {@link #getDiskCache()} instead
+ */
+ @Deprecated
+ public DiskCache getDiscCache() {
+ return getDiskCache();
+ }
+
+ /**
+ * Returns disk cache
+ *
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public DiskCache getDiskCache() {
+ checkConfiguration();
+ return configuration.diskCache;
+ }
+
+ /**
+ * Clears disk cache.
+ *
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ * @deprecated Use {@link #clearDiskCache()} instead
+ */
+ @Deprecated
+ public void clearDiscCache() {
+ clearDiskCache();
+ }
+
+ /**
+ * Clears disk cache.
+ *
+ * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
+ */
+ public void clearDiskCache() {
+ checkConfiguration();
+ configuration.diskCache.clear();
+ }
+
+ /**
+ * Returns URI of image which is loading at this moment into passed
+ * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware}
+ */
+ public String getLoadingUriForView(ImageAware imageAware) {
+ return engine.getLoadingUriForView(imageAware);
+ }
+
+ /**
+ * Returns URI of image which is loading at this moment into passed
+ * {@link android.widget.ImageView ImageView}
+ */
+ public String getLoadingUriForView(ImageView imageView) {
+ return engine.getLoadingUriForView(new ImageViewAware(imageView));
+ }
+
+ /**
+ * Cancel the task of loading and displaying image for passed
+ * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware}.
+ *
+ * @param imageAware {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware} for
+ * which display task will be cancelled
+ */
+ public void cancelDisplayTask(ImageAware imageAware) {
+ engine.cancelDisplayTaskFor(imageAware);
+ }
+
+ /**
+ * Cancel the task of loading and displaying image for passed
+ * {@link android.widget.ImageView ImageView}.
+ *
+ * @param imageView {@link android.widget.ImageView ImageView} for which display task will be cancelled
+ */
+ public void cancelDisplayTask(ImageView imageView) {
+ engine.cancelDisplayTaskFor(new ImageViewAware(imageView));
+ }
+
+ /**
+ * Denies or allows ImageLoader to download images from the network.
+ *
+ * If downloads are denied and if image isn't cached then
+ * {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired with
+ * {@link FailReason.FailType#NETWORK_DENIED}
+ *
+ * @param denyNetworkDownloads pass true - to deny engine to download images from the network; false -
+ * to allow engine to download images from network.
+ */
+ public void denyNetworkDownloads(boolean denyNetworkDownloads) {
+ engine.denyNetworkDownloads(denyNetworkDownloads);
+ }
+
+ /**
+ * Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle this known problem or not.
+ *
+ * @param handleSlowNetwork pass true - to use {@link FlushedInputStream} for network downloads; false
+ * - otherwise.
+ */
+ public void handleSlowNetwork(boolean handleSlowNetwork) {
+ engine.handleSlowNetwork(handleSlowNetwork);
+ }
+
+ /**
+ * Pause ImageLoader. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.
+ *
+ * Already running tasks are not paused.
+ */
+ public void pause() {
+ engine.pause();
+ }
+
+ /** Resumes waiting "load&display" tasks */
+ public void resume() {
+ engine.resume();
+ }
+
+ /**
+ * Cancels all running and scheduled display image tasks.
+ * NOTE: This method doesn't shutdown
+ * {@linkplain com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor)
+ * custom task executors} if you set them.
+ * ImageLoader still can be used after calling this method.
+ */
+ public void stop() {
+ engine.stop();
+ }
+
+ /**
+ * {@linkplain #stop() Stops ImageLoader} and clears current configuration.
+ * You can {@linkplain #init(ImageLoaderConfiguration) init} ImageLoader with new configuration after calling this
+ * method.
+ */
+ public void destroy() {
+ if (configuration != null) L.d(LOG_DESTROY);
+ stop();
+ configuration.diskCache.close();
+ engine = null;
+ configuration = null;
+ }
+
+ private static Handler defineHandler(DisplayImageOptions options) {
+ Handler handler = options.getHandler();
+ if (options.isSyncLoading()) {
+ handler = null;
+ } else if (handler == null && Looper.myLooper() == Looper.getMainLooper()) {
+ handler = new Handler();
+ }
+ return handler;
+ }
+
+ /**
+ * Listener which is designed for synchronous image loading.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.9.0
+ */
+ private static class SyncImageLoadingListener extends SimpleImageLoadingListener {
+
+ private Bitmap loadedImage;
+
+ @Override
+ public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
+ this.loadedImage = loadedImage;
+ }
+
+ public Bitmap getLoadedBitmap() {
+ return loadedImage;
+ }
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java
new file mode 100644
index 000000000..1fa2e8a00
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java
@@ -0,0 +1,655 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import com.nostra13.universalimageloader.cache.disc.DiskCache;
+import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
+import com.nostra13.universalimageloader.cache.memory.MemoryCache;
+import com.nostra13.universalimageloader.cache.memory.impl.FuzzyKeyMemoryCache;
+import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
+import com.nostra13.universalimageloader.core.assist.ImageSize;
+import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
+import com.nostra13.universalimageloader.core.decode.ImageDecoder;
+import com.nostra13.universalimageloader.core.download.ImageDownloader;
+import com.nostra13.universalimageloader.core.process.BitmapProcessor;
+import com.nostra13.universalimageloader.utils.L;
+import com.nostra13.universalimageloader.utils.MemoryCacheUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.Executor;
+
+/**
+ * Presents configuration for {@link ImageLoader}
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @see ImageLoader
+ * @see MemoryCache
+ * @see DiskCache
+ * @see DisplayImageOptions
+ * @see ImageDownloader
+ * @see FileNameGenerator
+ * @since 1.0.0
+ */
+public final class ImageLoaderConfiguration {
+
+ final Resources resources;
+
+ final int maxImageWidthForMemoryCache;
+ final int maxImageHeightForMemoryCache;
+ final int maxImageWidthForDiskCache;
+ final int maxImageHeightForDiskCache;
+ final BitmapProcessor processorForDiskCache;
+
+ final Executor taskExecutor;
+ final Executor taskExecutorForCachedImages;
+ final boolean customExecutor;
+ final boolean customExecutorForCachedImages;
+
+ final int threadPoolSize;
+ final int threadPriority;
+ final QueueProcessingType tasksProcessingType;
+
+ final MemoryCache memoryCache;
+ final DiskCache diskCache;
+ final ImageDownloader downloader;
+ final ImageDecoder decoder;
+ final DisplayImageOptions defaultDisplayImageOptions;
+
+ final ImageDownloader networkDeniedDownloader;
+ final ImageDownloader slowNetworkDownloader;
+
+ private ImageLoaderConfiguration(final Builder builder) {
+ resources = builder.context.getResources();
+ maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache;
+ maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache;
+ maxImageWidthForDiskCache = builder.maxImageWidthForDiskCache;
+ maxImageHeightForDiskCache = builder.maxImageHeightForDiskCache;
+ processorForDiskCache = builder.processorForDiskCache;
+ taskExecutor = builder.taskExecutor;
+ taskExecutorForCachedImages = builder.taskExecutorForCachedImages;
+ threadPoolSize = builder.threadPoolSize;
+ threadPriority = builder.threadPriority;
+ tasksProcessingType = builder.tasksProcessingType;
+ diskCache = builder.diskCache;
+ memoryCache = builder.memoryCache;
+ defaultDisplayImageOptions = builder.defaultDisplayImageOptions;
+ downloader = builder.downloader;
+ decoder = builder.decoder;
+
+ customExecutor = builder.customExecutor;
+ customExecutorForCachedImages = builder.customExecutorForCachedImages;
+
+ networkDeniedDownloader = new NetworkDeniedImageDownloader(downloader);
+ slowNetworkDownloader = new SlowNetworkImageDownloader(downloader);
+
+ L.writeDebugLogs(builder.writeLogs);
+ }
+
+ /**
+ * Creates default configuration for {@link ImageLoader}
+ * Default values:
+ *
+ *
+ */
+ public static ImageLoaderConfiguration createDefault(Context context) {
+ return new Builder(context).build();
+ }
+
+ ImageSize getMaxImageSize() {
+ DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+
+ int width = maxImageWidthForMemoryCache;
+ if (width <= 0) {
+ width = displayMetrics.widthPixels;
+ }
+ int height = maxImageHeightForMemoryCache;
+ if (height <= 0) {
+ height = displayMetrics.heightPixels;
+ }
+ return new ImageSize(width, height);
+ }
+
+ /**
+ * Builder for {@link ImageLoaderConfiguration}
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ */
+ public static class Builder {
+
+ private static final String WARNING_OVERLAP_DISK_CACHE_PARAMS = "diskCache(), diskCacheSize() and diskCacheFileCount calls overlap each other";
+ private static final String WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR = "diskCache() and diskCacheFileNameGenerator() calls overlap each other";
+ private static final String WARNING_OVERLAP_MEMORY_CACHE = "memoryCache() and memoryCacheSize() calls overlap each other";
+ private static final String WARNING_OVERLAP_EXECUTOR = "threadPoolSize(), threadPriority() and tasksProcessingOrder() calls "
+ + "can overlap taskExecutor() and taskExecutorForCachedImages() calls.";
+
+ /** {@value} */
+ public static final int DEFAULT_THREAD_POOL_SIZE = 3;
+ /** {@value} */
+ public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 1;
+ /** {@value} */
+ public static final QueueProcessingType DEFAULT_TASK_PROCESSING_TYPE = QueueProcessingType.FIFO;
+
+ private Context context;
+
+ private int maxImageWidthForMemoryCache = 0;
+ private int maxImageHeightForMemoryCache = 0;
+ private int maxImageWidthForDiskCache = 0;
+ private int maxImageHeightForDiskCache = 0;
+ private BitmapProcessor processorForDiskCache = null;
+
+ private Executor taskExecutor = null;
+ private Executor taskExecutorForCachedImages = null;
+ private boolean customExecutor = false;
+ private boolean customExecutorForCachedImages = false;
+
+ private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;
+ private int threadPriority = DEFAULT_THREAD_PRIORITY;
+ private boolean denyCacheImageMultipleSizesInMemory = false;
+ private QueueProcessingType tasksProcessingType = DEFAULT_TASK_PROCESSING_TYPE;
+
+ private int memoryCacheSize = 0;
+ private long diskCacheSize = 0;
+ private int diskCacheFileCount = 0;
+
+ private MemoryCache memoryCache = null;
+ private DiskCache diskCache = null;
+ private FileNameGenerator diskCacheFileNameGenerator = null;
+ private ImageDownloader downloader = null;
+ private ImageDecoder decoder;
+ private DisplayImageOptions defaultDisplayImageOptions = null;
+
+ private boolean writeLogs = false;
+
+ public Builder(Context context) {
+ this.context = context.getApplicationContext();
+ }
+
+ /**
+ * Sets options for memory cache
+ *
+ * @param maxImageWidthForMemoryCache Maximum image width which will be used for memory saving during decoding
+ * an image to {@link android.graphics.Bitmap Bitmap}. Default value - device's screen width
+ * @param maxImageHeightForMemoryCache Maximum image height which will be used for memory saving during decoding
+ * an image to {@link android.graphics.Bitmap Bitmap}. Default value - device's screen height
+ */
+ public Builder memoryCacheExtraOptions(int maxImageWidthForMemoryCache, int maxImageHeightForMemoryCache) {
+ this.maxImageWidthForMemoryCache = maxImageWidthForMemoryCache;
+ this.maxImageHeightForMemoryCache = maxImageHeightForMemoryCache;
+ return this;
+ }
+
+ /**
+ * @deprecated Use
+ * {@link #diskCacheExtraOptions(int, int, com.nostra13.universalimageloader.core.process.BitmapProcessor)}
+ * instead
+ */
+ @Deprecated
+ public Builder discCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
+ BitmapProcessor processorForDiskCache) {
+ return diskCacheExtraOptions(maxImageWidthForDiskCache, maxImageHeightForDiskCache, processorForDiskCache);
+ }
+
+ /**
+ * Sets options for resizing/compressing of downloaded images before saving to disk cache.
+ * NOTE: Use this option only when you have appropriate needs. It can make ImageLoader slower.
+ *
+ * @param maxImageWidthForDiskCache Maximum width of downloaded images for saving at disk cache
+ * @param maxImageHeightForDiskCache Maximum height of downloaded images for saving at disk cache
+ * @param processorForDiskCache null-ok; {@linkplain BitmapProcessor Bitmap processor} which process images before saving them in disc cache
+ */
+ public Builder diskCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
+ BitmapProcessor processorForDiskCache) {
+ this.maxImageWidthForDiskCache = maxImageWidthForDiskCache;
+ this.maxImageHeightForDiskCache = maxImageHeightForDiskCache;
+ this.processorForDiskCache = processorForDiskCache;
+ return this;
+ }
+
+ /**
+ * Sets custom {@linkplain Executor executor} for tasks of loading and displaying images.
+ *
+ * NOTE: If you set custom executor then following configuration options will not be considered for this
+ * executor:
+ *
+ *
+ *
+ * @see #taskExecutorForCachedImages(Executor)
+ */
+ public Builder taskExecutor(Executor executor) {
+ if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
+ L.w(WARNING_OVERLAP_EXECUTOR);
+ }
+
+ this.taskExecutor = executor;
+ return this;
+ }
+
+ /**
+ * Sets custom {@linkplain Executor executor} for tasks of displaying cached on disk images (these tasks
+ * are executed quickly so UIL prefer to use separate executor for them).
+ *
+ * If you set the same executor for {@linkplain #taskExecutor(Executor) general tasks} and
+ * tasks about cached images (this method) then these tasks will be in the
+ * same thread pool. So short-lived tasks can wait a long time for their turn.
+ *
+ * NOTE: If you set custom executor then following configuration options will not be considered for this
+ * executor:
+ *
+ *
+ *
+ * @see #taskExecutor(Executor)
+ */
+ public Builder taskExecutorForCachedImages(Executor executorForCachedImages) {
+ if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
+ L.w(WARNING_OVERLAP_EXECUTOR);
+ }
+
+ this.taskExecutorForCachedImages = executorForCachedImages;
+ return this;
+ }
+
+ /**
+ * Sets thread pool size for image display tasks.
+ * Default value - {@link #DEFAULT_THREAD_POOL_SIZE this}
+ */
+ public Builder threadPoolSize(int threadPoolSize) {
+ if (taskExecutor != null || taskExecutorForCachedImages != null) {
+ L.w(WARNING_OVERLAP_EXECUTOR);
+ }
+
+ this.threadPoolSize = threadPoolSize;
+ return this;
+ }
+
+ /**
+ * Sets the priority for image loading threads. Should be NOT greater than {@link Thread#MAX_PRIORITY} or
+ * less than {@link Thread#MIN_PRIORITY}
+ * Default value - {@link #DEFAULT_THREAD_PRIORITY this}
+ */
+ public Builder threadPriority(int threadPriority) {
+ if (taskExecutor != null || taskExecutorForCachedImages != null) {
+ L.w(WARNING_OVERLAP_EXECUTOR);
+ }
+
+ if (threadPriority < Thread.MIN_PRIORITY) {
+ this.threadPriority = Thread.MIN_PRIORITY;
+ } else {
+ if (threadPriority > Thread.MAX_PRIORITY) {
+ this.threadPriority = Thread.MAX_PRIORITY;
+ } else {
+ this.threadPriority = threadPriority;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * When you display an image in a small {@link android.widget.ImageView ImageView} and later you try to display
+ * this image (from identical URI) in a larger {@link android.widget.ImageView ImageView} so decoded image of
+ * bigger size will be cached in memory as a previous decoded image of smaller size.
+ * So the default behavior is to allow to cache multiple sizes of one image in memory. You can
+ * deny it by calling this method: so when some image will be cached in memory then previous
+ * cached size of this image (if it exists) will be removed from memory cache before.
+ */
+ public Builder denyCacheImageMultipleSizesInMemory() {
+ this.denyCacheImageMultipleSizesInMemory = true;
+ return this;
+ }
+
+ /**
+ * Sets type of queue processing for tasks for loading and displaying images.
+ * Default value - {@link QueueProcessingType#FIFO}
+ */
+ public Builder tasksProcessingOrder(QueueProcessingType tasksProcessingType) {
+ if (taskExecutor != null || taskExecutorForCachedImages != null) {
+ L.w(WARNING_OVERLAP_EXECUTOR);
+ }
+
+ this.tasksProcessingType = tasksProcessingType;
+ return this;
+ }
+
+ /**
+ * Sets maximum memory cache size for {@link android.graphics.Bitmap bitmaps} (in bytes).
+ * Default value - 1/8 of available app memory.
+ * NOTE: If you use this method then
+ * {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache} will be used as
+ * memory cache. You can use {@link #memoryCache(MemoryCache)} method to set your own implementation of
+ * {@link MemoryCache}.
+ */
+ public Builder memoryCacheSize(int memoryCacheSize) {
+ if (memoryCacheSize <= 0) throw new IllegalArgumentException("memoryCacheSize must be a positive number");
+
+ if (memoryCache != null) {
+ L.w(WARNING_OVERLAP_MEMORY_CACHE);
+ }
+
+ this.memoryCacheSize = memoryCacheSize;
+ return this;
+ }
+
+ /**
+ * Sets maximum memory cache size (in percent of available app memory) for {@link android.graphics.Bitmap
+ * bitmaps}.
+ * Default value - 1/8 of available app memory.
+ * NOTE: If you use this method then
+ * {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache} will be used as
+ * memory cache. You can use {@link #memoryCache(MemoryCache)} method to set your own implementation of
+ * {@link MemoryCache}.
+ */
+ public Builder memoryCacheSizePercentage(int availableMemoryPercent) {
+ if (availableMemoryPercent <= 0 || availableMemoryPercent >= 100) {
+ throw new IllegalArgumentException("availableMemoryPercent must be in range (0 < % < 100)");
+ }
+
+ if (memoryCache != null) {
+ L.w(WARNING_OVERLAP_MEMORY_CACHE);
+ }
+
+ long availableMemory = Runtime.getRuntime().maxMemory();
+ memoryCacheSize = (int) (availableMemory * (availableMemoryPercent / 100f));
+ return this;
+ }
+
+ /**
+ * Sets memory cache for {@link android.graphics.Bitmap bitmaps}.
+ * Default value - {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache}
+ * with limited memory cache size (size = 1/8 of available app memory)
+ *
+ * NOTE: If you set custom memory cache then following configuration option will not be considered:
+ *
+ *
+ */
+ public Builder memoryCache(MemoryCache memoryCache) {
+ if (memoryCacheSize != 0) {
+ L.w(WARNING_OVERLAP_MEMORY_CACHE);
+ }
+
+ this.memoryCache = memoryCache;
+ return this;
+ }
+
+ /** @deprecated Use {@link #diskCacheSize(int)} instead */
+ @Deprecated
+ public Builder discCacheSize(int maxCacheSize) {
+ return diskCacheSize(maxCacheSize);
+ }
+
+ /**
+ * Sets maximum disk cache size for images (in bytes).
+ * By default: disk cache is unlimited.
+ * NOTE: If you use this method then
+ * {@link com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiscCache LruDiscCache}
+ * will be used as disk cache. You can use {@link #diskCache(DiskCache)} method for introduction your own
+ * implementation of {@link DiskCache}
+ */
+ public Builder diskCacheSize(int maxCacheSize) {
+ if (maxCacheSize <= 0) throw new IllegalArgumentException("maxCacheSize must be a positive number");
+
+ if (diskCache != null) {
+ L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS);
+ }
+
+ this.diskCacheSize = maxCacheSize;
+ return this;
+ }
+
+ /** @deprecated Use {@link #diskCacheFileCount(int)} instead */
+ @Deprecated
+ public Builder discCacheFileCount(int maxFileCount) {
+ return diskCacheFileCount(maxFileCount);
+ }
+
+ /**
+ * Sets maximum file count in disk cache directory.
+ * By default: disk cache is unlimited.
+ * NOTE: If you use this method then
+ * {@link com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiscCache LruDiscCache}
+ * will be used as disk cache. You can use {@link #diskCache(DiskCache)} method for introduction your own
+ * implementation of {@link DiskCache}
+ */
+ public Builder diskCacheFileCount(int maxFileCount) {
+ if (maxFileCount <= 0) throw new IllegalArgumentException("maxFileCount must be a positive number");
+
+ if (diskCache != null) {
+ L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS);
+ }
+
+ this.diskCacheFileCount = maxFileCount;
+ return this;
+ }
+
+ /** @deprecated Use {@link #diskCacheFileNameGenerator(com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator)} */
+ @Deprecated
+ public Builder discCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
+ return diskCacheFileNameGenerator(fileNameGenerator);
+ }
+
+ /**
+ * Sets name generator for files cached in disk cache.
+ * Default value -
+ * {@link com.nostra13.universalimageloader.core.DefaultConfigurationFactory#createFileNameGenerator()
+ * DefaultConfigurationFactory.createFileNameGenerator()}
+ */
+ public Builder diskCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
+ if (diskCache != null) {
+ L.w(WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR);
+ }
+
+ this.diskCacheFileNameGenerator = fileNameGenerator;
+ return this;
+ }
+
+ /** @deprecated Use {@link #diskCache(com.nostra13.universalimageloader.cache.disc.DiskCache)} */
+ @Deprecated
+ public Builder discCache(DiskCache diskCache) {
+ return diskCache(diskCache);
+ }
+
+ /**
+ * Sets disk cache for images.
+ * Default value - {@link com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache
+ * BaseDiscCache}. Cache directory is defined by
+ * {@link com.nostra13.universalimageloader.utils.StorageUtils#getCacheDirectory(Context)
+ * StorageUtils.getCacheDirectory(Context)}.
+ *
+ * NOTE: If you set custom disk cache then following configuration option will not be considered:
+ *
+ *
+ */
+ public Builder diskCache(DiskCache diskCache) {
+ if (diskCacheSize > 0 || diskCacheFileCount > 0) {
+ L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS);
+ }
+ if (diskCacheFileNameGenerator != null) {
+ L.w(WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR);
+ }
+
+ this.diskCache = diskCache;
+ return this;
+ }
+
+ /**
+ * Sets utility which will be responsible for downloading of image.
+ * Default value -
+ * {@link com.nostra13.universalimageloader.core.DefaultConfigurationFactory#createImageDownloader(Context)
+ * DefaultConfigurationFactory.createImageDownloader()}
+ */
+ public Builder imageDownloader(ImageDownloader imageDownloader) {
+ this.downloader = imageDownloader;
+ return this;
+ }
+
+ /**
+ * Sets utility which will be responsible for decoding of image stream.
+ * Default value -
+ * {@link com.nostra13.universalimageloader.core.DefaultConfigurationFactory#createImageDecoder(boolean)
+ * DefaultConfigurationFactory.createImageDecoder()}
+ */
+ public Builder imageDecoder(ImageDecoder imageDecoder) {
+ this.decoder = imageDecoder;
+ return this;
+ }
+
+ /**
+ * Sets default {@linkplain DisplayImageOptions display image options} for image displaying. These options will
+ * be used for every {@linkplain ImageLoader#displayImage(String, android.widget.ImageView) image display call}
+ * without passing custom {@linkplain DisplayImageOptions options}
+ * Default value - {@link DisplayImageOptions#createSimple() Simple options}
+ */
+ public Builder defaultDisplayImageOptions(DisplayImageOptions defaultDisplayImageOptions) {
+ this.defaultDisplayImageOptions = defaultDisplayImageOptions;
+ return this;
+ }
+
+ /**
+ * Enables detail logging of {@link ImageLoader} work. To prevent detail logs don't call this method.
+ * Consider {@link com.nostra13.universalimageloader.utils.L#disableLogging()} to disable
+ * ImageLoader logging completely (even error logs)
+ */
+ public Builder writeDebugLogs() {
+ this.writeLogs = true;
+ return this;
+ }
+
+ /** Builds configured {@link ImageLoaderConfiguration} object */
+ public ImageLoaderConfiguration build() {
+ initEmptyFieldsWithDefaultValues();
+ return new ImageLoaderConfiguration(this);
+ }
+
+ private void initEmptyFieldsWithDefaultValues() {
+ if (taskExecutor == null) {
+ taskExecutor = DefaultConfigurationFactory
+ .createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
+ } else {
+ customExecutor = true;
+ }
+ if (taskExecutorForCachedImages == null) {
+ taskExecutorForCachedImages = DefaultConfigurationFactory
+ .createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
+ } else {
+ customExecutorForCachedImages = true;
+ }
+ if (diskCache == null) {
+ if (diskCacheFileNameGenerator == null) {
+ diskCacheFileNameGenerator = DefaultConfigurationFactory.createFileNameGenerator();
+ }
+ diskCache = DefaultConfigurationFactory
+ .createDiskCache(context, diskCacheFileNameGenerator, diskCacheSize, diskCacheFileCount);
+ }
+ if (memoryCache == null) {
+ memoryCache = DefaultConfigurationFactory.createMemoryCache(memoryCacheSize);
+ }
+ if (denyCacheImageMultipleSizesInMemory) {
+ memoryCache = new FuzzyKeyMemoryCache(memoryCache, MemoryCacheUtils.createFuzzyKeyComparator());
+ }
+ if (downloader == null) {
+ downloader = DefaultConfigurationFactory.createImageDownloader(context);
+ }
+ if (decoder == null) {
+ decoder = DefaultConfigurationFactory.createImageDecoder(writeLogs);
+ }
+ if (defaultDisplayImageOptions == null) {
+ defaultDisplayImageOptions = DisplayImageOptions.createSimple();
+ }
+ }
+ }
+
+ /**
+ * Decorator. Prevents downloads from network (throws {@link IllegalStateException exception}).
+ * In most cases this downloader shouldn't be used directly.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.8.0
+ */
+ private static class NetworkDeniedImageDownloader implements ImageDownloader {
+
+ private final ImageDownloader wrappedDownloader;
+
+ public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) {
+ this.wrappedDownloader = wrappedDownloader;
+ }
+
+ @Override
+ public InputStream getStream(String imageUri, Object extra) throws IOException {
+ switch (Scheme.ofUri(imageUri)) {
+ case HTTP:
+ case HTTPS:
+ throw new IllegalStateException();
+ default:
+ return wrappedDownloader.getStream(imageUri, extra);
+ }
+ }
+ }
+
+ /**
+ * Decorator. Handles this problem on slow networks
+ * using {@link com.nostra13.universalimageloader.core.assist.FlushedInputStream}.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.8.1
+ */
+ private static class SlowNetworkImageDownloader implements ImageDownloader {
+
+ private final ImageDownloader wrappedDownloader;
+
+ public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) {
+ this.wrappedDownloader = wrappedDownloader;
+ }
+
+ @Override
+ public InputStream getStream(String imageUri, Object extra) throws IOException {
+ InputStream imageStream = wrappedDownloader.getStream(imageUri, extra);
+ switch (Scheme.ofUri(imageUri)) {
+ case HTTP:
+ case HTTPS:
+ return new FlushedInputStream(imageStream);
+ default:
+ return imageStream;
+ }
+ }
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java
new file mode 100644
index 000000000..b25f6c9d7
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core;
+
+import android.view.View;
+import com.nostra13.universalimageloader.core.assist.FailReason;
+import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * {@link ImageLoader} engine which responsible for {@linkplain LoadAndDisplayImageTask display task} execution.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.7.1
+ */
+class ImageLoaderEngine {
+
+ final ImageLoaderConfiguration configuration;
+
+ private Executor taskExecutor;
+ private Executor taskExecutorForCachedImages;
+ private Executor taskDistributor;
+
+ private final Map
If downloads are denied and if image
+ * isn't cached then {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired
+ * with {@link FailReason.FailType#NETWORK_DENIED}
+ *
+ * @param denyNetworkDownloads pass true - to deny engine to download images from the network; false -
+ * to allow engine to download images from network.
+ */
+ void denyNetworkDownloads(boolean denyNetworkDownloads) {
+ networkDenied.set(denyNetworkDownloads);
+ }
+
+ /**
+ * Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle this known problem or not.
+ *
+ * @param handleSlowNetwork pass true - to use {@link FlushedInputStream} for network downloads; false
+ * - otherwise.
+ */
+ void handleSlowNetwork(boolean handleSlowNetwork) {
+ slowNetwork.set(handleSlowNetwork);
+ }
+
+ /**
+ * Pauses engine. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.
Already running tasks are not paused.
+ */
+ void pause() {
+ paused.set(true);
+ }
+
+ /** Resumes engine work. Paused "load&display" tasks will continue its work. */
+ void resume() {
+ paused.set(false);
+ synchronized (pauseLock) {
+ pauseLock.notifyAll();
+ }
+ }
+
+ /**
+ * Stops engine, cancels all running and scheduled display image tasks. Clears internal data.
+ *
+ * NOTE: This method doesn't shutdown
+ * {@linkplain com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor)
+ * custom task executors} if you set them.
+ */
+ void stop() {
+ if (!configuration.customExecutor) {
+ ((ExecutorService) taskExecutor).shutdownNow();
+ }
+ if (!configuration.customExecutorForCachedImages) {
+ ((ExecutorService) taskExecutorForCachedImages).shutdownNow();
+ }
+
+ cacheKeysForImageAwares.clear();
+ uriLocks.clear();
+ }
+
+ void fireCallback(Runnable r) {
+ taskDistributor.execute(r);
+ }
+
+ ReentrantLock getLockForUri(String uri) {
+ ReentrantLock lock = uriLocks.get(uri);
+ if (lock == null) {
+ lock = new ReentrantLock();
+ uriLocks.put(uri, lock);
+ }
+ return lock;
+ }
+
+ AtomicBoolean getPause() {
+ return paused;
+ }
+
+ Object getPauseLock() {
+ return pauseLock;
+ }
+
+ boolean isNetworkDenied() {
+ return networkDenied.get();
+ }
+
+ boolean isSlowNetwork() {
+ return slowNetwork.get();
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoadingInfo.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoadingInfo.java
new file mode 100644
index 000000000..2950fc771
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ImageLoadingInfo.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core;
+
+import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
+import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
+import com.nostra13.universalimageloader.core.assist.ImageSize;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Information for load'n'display image task
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @see com.nostra13.universalimageloader.utils.MemoryCacheUtils
+ * @see DisplayImageOptions
+ * @see ImageLoadingListener
+ * @see com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
+ * @since 1.3.1
+ */
+final class ImageLoadingInfo {
+
+ final String uri;
+ final String memoryCacheKey;
+ final ImageAware imageAware;
+ final ImageSize targetSize;
+ final DisplayImageOptions options;
+ final ImageLoadingListener listener;
+ final ImageLoadingProgressListener progressListener;
+ final ReentrantLock loadFromUriLock;
+
+ public ImageLoadingInfo(String uri, ImageAware imageAware, ImageSize targetSize, String memoryCacheKey,
+ DisplayImageOptions options, ImageLoadingListener listener,
+ ImageLoadingProgressListener progressListener, ReentrantLock loadFromUriLock) {
+ this.uri = uri;
+ this.imageAware = imageAware;
+ this.targetSize = targetSize;
+ this.options = options;
+ this.listener = listener;
+ this.progressListener = progressListener;
+ this.loadFromUriLock = loadFromUriLock;
+ this.memoryCacheKey = memoryCacheKey;
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java
new file mode 100644
index 000000000..24cbc497f
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java
@@ -0,0 +1,472 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core;
+
+import android.graphics.Bitmap;
+import android.os.Handler;
+import com.nostra13.universalimageloader.core.assist.FailReason;
+import com.nostra13.universalimageloader.core.assist.FailReason.FailType;
+import com.nostra13.universalimageloader.core.assist.ImageScaleType;
+import com.nostra13.universalimageloader.core.assist.ImageSize;
+import com.nostra13.universalimageloader.core.assist.LoadedFrom;
+import com.nostra13.universalimageloader.core.assist.ViewScaleType;
+import com.nostra13.universalimageloader.core.decode.ImageDecoder;
+import com.nostra13.universalimageloader.core.decode.ImageDecodingInfo;
+import com.nostra13.universalimageloader.core.download.ImageDownloader;
+import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
+import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
+import com.nostra13.universalimageloader.utils.IoUtils;
+import com.nostra13.universalimageloader.utils.L;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Presents load'n'display image task. Used to load image from Internet or file system, decode it to {@link Bitmap}, and
+ * display it in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} using {@link DisplayBitmapTask}.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @see ImageLoaderConfiguration
+ * @see ImageLoadingInfo
+ * @since 1.3.1
+ */
+final class LoadAndDisplayImageTask implements Runnable, IoUtils.CopyListener {
+
+ private static final String LOG_WAITING_FOR_RESUME = "ImageLoader is paused. Waiting... [%s]";
+ private static final String LOG_RESUME_AFTER_PAUSE = ".. Resume loading [%s]";
+ private static final String LOG_DELAY_BEFORE_LOADING = "Delay %d ms before loading... [%s]";
+ private static final String LOG_START_DISPLAY_IMAGE_TASK = "Start display image task [%s]";
+ private static final String LOG_WAITING_FOR_IMAGE_LOADED = "Image already is loading. Waiting... [%s]";
+ private static final String LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING = "...Get cached bitmap from memory after waiting. [%s]";
+ private static final String LOG_LOAD_IMAGE_FROM_NETWORK = "Load image from network [%s]";
+ private static final String LOG_LOAD_IMAGE_FROM_DISK_CACHE = "Load image from disk cache [%s]";
+ private static final String LOG_RESIZE_CACHED_IMAGE_FILE = "Resize image in disk cache [%s]";
+ private static final String LOG_PREPROCESS_IMAGE = "PreProcess image before caching in memory [%s]";
+ private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
+ private static final String LOG_CACHE_IMAGE_IN_MEMORY = "Cache image in memory [%s]";
+ private static final String LOG_CACHE_IMAGE_ON_DISK = "Cache image on disk [%s]";
+ private static final String LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK = "Process image before cache on disk [%s]";
+ private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
+ private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";
+ private static final String LOG_TASK_INTERRUPTED = "Task was interrupted [%s]";
+
+ private static final String ERROR_PRE_PROCESSOR_NULL = "Pre-processor returned null [%s]";
+ private static final String ERROR_POST_PROCESSOR_NULL = "Post-processor returned null [%s]";
+ private static final String ERROR_PROCESSOR_FOR_DISK_CACHE_NULL = "Bitmap processor for disk cache returned null [%s]";
+
+ private final ImageLoaderEngine engine;
+ private final ImageLoadingInfo imageLoadingInfo;
+ private final Handler handler;
+
+ // Helper references
+ private final ImageLoaderConfiguration configuration;
+ private final ImageDownloader downloader;
+ private final ImageDownloader networkDeniedDownloader;
+ private final ImageDownloader slowNetworkDownloader;
+ private final ImageDecoder decoder;
+ final String uri;
+ private final String memoryCacheKey;
+ final ImageAware imageAware;
+ private final ImageSize targetSize;
+ final DisplayImageOptions options;
+ final ImageLoadingListener listener;
+ final ImageLoadingProgressListener progressListener;
+ private final boolean syncLoading;
+
+ // State vars
+ private LoadedFrom loadedFrom = LoadedFrom.NETWORK;
+
+ public LoadAndDisplayImageTask(ImageLoaderEngine engine, ImageLoadingInfo imageLoadingInfo, Handler handler) {
+ this.engine = engine;
+ this.imageLoadingInfo = imageLoadingInfo;
+ this.handler = handler;
+
+ configuration = engine.configuration;
+ downloader = configuration.downloader;
+ networkDeniedDownloader = configuration.networkDeniedDownloader;
+ slowNetworkDownloader = configuration.slowNetworkDownloader;
+ decoder = configuration.decoder;
+ uri = imageLoadingInfo.uri;
+ memoryCacheKey = imageLoadingInfo.memoryCacheKey;
+ imageAware = imageLoadingInfo.imageAware;
+ targetSize = imageLoadingInfo.targetSize;
+ options = imageLoadingInfo.options;
+ listener = imageLoadingInfo.listener;
+ progressListener = imageLoadingInfo.progressListener;
+ syncLoading = options.isSyncLoading();
+ }
+
+ @Override
+ public void run() {
+ if (waitIfPaused()) return;
+ if (delayIfNeed()) return;
+
+ ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
+ L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
+ if (loadFromUriLock.isLocked()) {
+ L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
+ }
+
+ loadFromUriLock.lock();
+ Bitmap bmp;
+ try {
+ checkTaskNotActual();
+
+ bmp = configuration.memoryCache.get(memoryCacheKey);
+ if (bmp == null || bmp.isRecycled()) {
+ bmp = tryLoadBitmap();
+ if (bmp == null) return; // listener callback already was fired
+
+ checkTaskNotActual();
+ checkTaskInterrupted();
+
+ if (options.shouldPreProcess()) {
+ L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey);
+ bmp = options.getPreProcessor().process(bmp);
+ if (bmp == null) {
+ L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);
+ }
+ }
+
+ if (bmp != null && options.isCacheInMemory()) {
+ L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
+ configuration.memoryCache.put(memoryCacheKey, bmp);
+ }
+ } else {
+ loadedFrom = LoadedFrom.MEMORY_CACHE;
+ L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);
+ }
+
+ if (bmp != null && options.shouldPostProcess()) {
+ L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey);
+ bmp = options.getPostProcessor().process(bmp);
+ if (bmp == null) {
+ L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
+ }
+ }
+ checkTaskNotActual();
+ checkTaskInterrupted();
+ } catch (TaskCancelledException e) {
+ fireCancelEvent();
+ return;
+ } finally {
+ loadFromUriLock.unlock();
+ }
+
+ DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
+ runTask(displayBitmapTask, syncLoading, handler, engine);
+ }
+
+ /** @return true - if task should be interrupted; false - otherwise */
+ private boolean waitIfPaused() {
+ AtomicBoolean pause = engine.getPause();
+ if (pause.get()) {
+ synchronized (engine.getPauseLock()) {
+ if (pause.get()) {
+ L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey);
+ try {
+ engine.getPauseLock().wait();
+ } catch (InterruptedException e) {
+ L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
+ return true;
+ }
+ L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
+ }
+ }
+ }
+ return isTaskNotActual();
+ }
+
+ /** @return true - if task should be interrupted; false - otherwise */
+ private boolean delayIfNeed() {
+ if (options.shouldDelayBeforeLoading()) {
+ L.d(LOG_DELAY_BEFORE_LOADING, options.getDelayBeforeLoading(), memoryCacheKey);
+ try {
+ Thread.sleep(options.getDelayBeforeLoading());
+ } catch (InterruptedException e) {
+ L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
+ return true;
+ }
+ return isTaskNotActual();
+ }
+ return false;
+ }
+
+ private Bitmap tryLoadBitmap() throws TaskCancelledException {
+ Bitmap bitmap = null;
+ try {
+ File imageFile = configuration.diskCache.get(uri);
+ if (imageFile != null && imageFile.exists()) {
+ L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);
+ loadedFrom = LoadedFrom.DISC_CACHE;
+
+ checkTaskNotActual();
+ bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
+ }
+ if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
+ L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);
+ loadedFrom = LoadedFrom.NETWORK;
+
+ String imageUriForDecoding = uri;
+ if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
+ imageFile = configuration.diskCache.get(uri);
+ if (imageFile != null) {
+ imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
+ }
+ }
+
+ checkTaskNotActual();
+ bitmap = decodeImage(imageUriForDecoding);
+
+ if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
+ fireFailEvent(FailType.DECODING_ERROR, null);
+ }
+ }
+ } catch (IllegalStateException e) {
+ fireFailEvent(FailType.NETWORK_DENIED, null);
+ } catch (TaskCancelledException e) {
+ throw e;
+ } catch (IOException e) {
+ L.e(e);
+ fireFailEvent(FailType.IO_ERROR, e);
+ } catch (OutOfMemoryError e) {
+ L.e(e);
+ fireFailEvent(FailType.OUT_OF_MEMORY, e);
+ } catch (Throwable e) {
+ L.e(e);
+ fireFailEvent(FailType.UNKNOWN, e);
+ }
+ return bitmap;
+ }
+
+ private Bitmap decodeImage(String imageUri) throws IOException {
+ ViewScaleType viewScaleType = imageAware.getScaleType();
+ ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,
+ getDownloader(), options);
+ return decoder.decode(decodingInfo);
+ }
+
+ /** @return true - if image was downloaded successfully; false - otherwise */
+ private boolean tryCacheImageOnDisk() throws TaskCancelledException {
+ L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey);
+
+ boolean loaded;
+ try {
+ loaded = downloadImage();
+ if (loaded) {
+ int width = configuration.maxImageWidthForDiskCache;
+ int height = configuration.maxImageHeightForDiskCache;
+ if (width > 0 || height > 0) {
+ L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey);
+ resizeAndSaveImage(width, height); // TODO : process boolean result
+ }
+ }
+ } catch (IOException e) {
+ L.e(e);
+ loaded = false;
+ }
+ return loaded;
+ }
+
+ private boolean downloadImage() throws IOException {
+ InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
+ return configuration.diskCache.save(uri, is, this);
+ }
+
+ /** Decodes image file into Bitmap, resize it and save it back */
+ private boolean resizeAndSaveImage(int maxWidth, int maxHeight) throws IOException {
+ // Decode image file, compress and re-save it
+ boolean saved = false;
+ File targetFile = configuration.diskCache.get(uri);
+ if (targetFile != null && targetFile.exists()) {
+ ImageSize targetImageSize = new ImageSize(maxWidth, maxHeight);
+ DisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options)
+ .imageScaleType(ImageScaleType.IN_SAMPLE_INT).build();
+ ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey,
+ Scheme.FILE.wrap(targetFile.getAbsolutePath()), uri, targetImageSize, ViewScaleType.FIT_INSIDE,
+ getDownloader(), specialOptions);
+ Bitmap bmp = decoder.decode(decodingInfo);
+ if (bmp != null && configuration.processorForDiskCache != null) {
+ L.d(LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK, memoryCacheKey);
+ bmp = configuration.processorForDiskCache.process(bmp);
+ if (bmp == null) {
+ L.e(ERROR_PROCESSOR_FOR_DISK_CACHE_NULL, memoryCacheKey);
+ }
+ }
+ if (bmp != null) {
+ saved = configuration.diskCache.save(uri, bmp);
+ bmp.recycle();
+ }
+ }
+ return saved;
+ }
+
+ @Override
+ public boolean onBytesCopied(int current, int total) {
+ return syncLoading || fireProgressEvent(current, total);
+ }
+
+ /** @return true - if loading should be continued; false - if loading should be interrupted */
+ private boolean fireProgressEvent(final int current, final int total) {
+ if (isTaskInterrupted() || isTaskNotActual()) return false;
+ if (progressListener != null) {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ progressListener.onProgressUpdate(uri, imageAware.getWrappedView(), current, total);
+ }
+ };
+ runTask(r, false, handler, engine);
+ }
+ return true;
+ }
+
+ private void fireFailEvent(final FailType failType, final Throwable failCause) {
+ if (syncLoading || isTaskInterrupted() || isTaskNotActual()) return;
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ if (options.shouldShowImageOnFail()) {
+ imageAware.setImageDrawable(options.getImageOnFail(configuration.resources));
+ }
+ listener.onLoadingFailed(uri, imageAware.getWrappedView(), new FailReason(failType, failCause));
+ }
+ };
+ runTask(r, false, handler, engine);
+ }
+
+ private void fireCancelEvent() {
+ if (syncLoading || isTaskInterrupted()) return;
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ listener.onLoadingCancelled(uri, imageAware.getWrappedView());
+ }
+ };
+ runTask(r, false, handler, engine);
+ }
+
+ private ImageDownloader getDownloader() {
+ ImageDownloader d;
+ if (engine.isNetworkDenied()) {
+ d = networkDeniedDownloader;
+ } else if (engine.isSlowNetwork()) {
+ d = slowNetworkDownloader;
+ } else {
+ d = downloader;
+ }
+ return d;
+ }
+
+ /**
+ * @throws TaskCancelledException if task is not actual (target ImageAware is collected by GC or the image URI of
+ * this task doesn't match to image URI which is actual for current ImageAware at
+ * this moment)
+ */
+ private void checkTaskNotActual() throws TaskCancelledException {
+ checkViewCollected();
+ checkViewReused();
+ }
+
+ /**
+ * @return true - if task is not actual (target ImageAware is collected by GC or the image URI of this task
+ * doesn't match to image URI which is actual for current ImageAware at this moment)); false - otherwise
+ */
+ private boolean isTaskNotActual() {
+ return isViewCollected() || isViewReused();
+ }
+
+ /** @throws TaskCancelledException if target ImageAware is collected */
+ private void checkViewCollected() throws TaskCancelledException {
+ if (isViewCollected()) {
+ throw new TaskCancelledException();
+ }
+ }
+
+ /** @return true - if target ImageAware is collected by GC; false - otherwise */
+ private boolean isViewCollected() {
+ if (imageAware.isCollected()) {
+ L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
+ return true;
+ }
+ return false;
+ }
+
+ /** @throws TaskCancelledException if target ImageAware is collected by GC */
+ private void checkViewReused() throws TaskCancelledException {
+ if (isViewReused()) {
+ throw new TaskCancelledException();
+ }
+ }
+
+ /** @return true - if current ImageAware is reused for displaying another image; false - otherwise */
+ private boolean isViewReused() {
+ String currentCacheKey = engine.getLoadingUriForView(imageAware);
+ // Check whether memory cache key (image URI) for current ImageAware is actual.
+ // If ImageAware is reused for another task then current task should be cancelled.
+ boolean imageAwareWasReused = !memoryCacheKey.equals(currentCacheKey);
+ if (imageAwareWasReused) {
+ L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
+ return true;
+ }
+ return false;
+ }
+
+ /** @throws TaskCancelledException if current task was interrupted */
+ private void checkTaskInterrupted() throws TaskCancelledException {
+ if (isTaskInterrupted()) {
+ throw new TaskCancelledException();
+ }
+ }
+
+ /** @return true - if current task was interrupted; false - otherwise */
+ private boolean isTaskInterrupted() {
+ if (Thread.interrupted()) {
+ L.d(LOG_TASK_INTERRUPTED, memoryCacheKey);
+ return true;
+ }
+ return false;
+ }
+
+ String getLoadingUri() {
+ return uri;
+ }
+
+ static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) {
+ if (sync) {
+ r.run();
+ } else if (handler == null) {
+ engine.fireCallback(r);
+ } else {
+ handler.post(r);
+ }
+ }
+
+ /**
+ * Exceptions for case when task is cancelled (thread is interrupted, image view is reused for another task, view is
+ * collected by GC).
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.9.1
+ */
+ class TaskCancelledException extends Exception {
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ProcessAndDisplayImageTask.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ProcessAndDisplayImageTask.java
new file mode 100644
index 000000000..d7d759d8a
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/ProcessAndDisplayImageTask.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core;
+
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.widget.ImageView;
+import com.nostra13.universalimageloader.core.assist.LoadedFrom;
+import com.nostra13.universalimageloader.core.process.BitmapProcessor;
+import com.nostra13.universalimageloader.utils.L;
+
+/**
+ * Presents process'n'display image task. Processes image {@linkplain Bitmap} and display it in {@link ImageView} using
+ * {@link DisplayBitmapTask}.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.8.0
+ */
+final class ProcessAndDisplayImageTask implements Runnable {
+
+ private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
+
+ private final ImageLoaderEngine engine;
+ private final Bitmap bitmap;
+ private final ImageLoadingInfo imageLoadingInfo;
+ private final Handler handler;
+
+ public ProcessAndDisplayImageTask(ImageLoaderEngine engine, Bitmap bitmap, ImageLoadingInfo imageLoadingInfo,
+ Handler handler) {
+ this.engine = engine;
+ this.bitmap = bitmap;
+ this.imageLoadingInfo = imageLoadingInfo;
+ this.handler = handler;
+ }
+
+ @Override
+ public void run() {
+ L.d(LOG_POSTPROCESS_IMAGE, imageLoadingInfo.memoryCacheKey);
+
+ BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor();
+ Bitmap processedBitmap = processor.process(bitmap);
+ DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine,
+ LoadedFrom.MEMORY_CACHE);
+ LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine);
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ContentLengthInputStream.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ContentLengthInputStream.java
new file mode 100644
index 000000000..786b2d5aa
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ContentLengthInputStream.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright 2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.assist;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Decorator for {@link java.io.InputStream InputStream}. Provides possibility to return defined stream length by
+ * {@link #available()} method.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com), Mariotaku
+ * @since 1.9.1
+ */
+public class ContentLengthInputStream extends InputStream {
+
+ private final InputStream stream;
+ private final int length;
+
+ public ContentLengthInputStream(InputStream stream, int length) {
+ this.stream = stream;
+ this.length = length;
+ }
+
+ @Override
+ public int available() {
+ return length;
+ }
+
+ @Override
+ public void close() throws IOException {
+ stream.close();
+ }
+
+ @Override
+ public void mark(int readLimit) {
+ stream.mark(readLimit);
+ }
+
+ @Override
+ public int read() throws IOException {
+ return stream.read();
+ }
+
+ @Override
+ public int read(byte[] buffer) throws IOException {
+ return stream.read(buffer);
+ }
+
+ @Override
+ public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+ return stream.read(buffer, byteOffset, byteCount);
+ }
+
+ @Override
+ public void reset() throws IOException {
+ stream.reset();
+ }
+
+ @Override
+ public long skip(long byteCount) throws IOException {
+ return stream.skip(byteCount);
+ }
+
+ @Override
+ public boolean markSupported() {
+ return stream.markSupported();
+ }
+}
\ No newline at end of file
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FailReason.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FailReason.java
new file mode 100644
index 000000000..f78491e58
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FailReason.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.assist;
+
+/**
+ * Presents the reason why image loading and displaying was failed
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public class FailReason {
+
+ private final FailType type;
+
+ private final Throwable cause;
+
+ public FailReason(FailType type, Throwable cause) {
+ this.type = type;
+ this.cause = cause;
+ }
+
+ /** @return {@linkplain FailType Fail type} */
+ public FailType getType() {
+ return type;
+ }
+
+ /** @return Thrown exception/error, can be null */
+ public Throwable getCause() {
+ return cause;
+ }
+
+ /** Presents type of fail while image loading */
+ public static enum FailType {
+ /** Input/output error. Can be caused by network communication fail or error while caching image on file system. */
+ IO_ERROR,
+ /**
+ * Error while
+ * {@linkplain android.graphics.BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options)
+ * decode image to Bitmap}
+ */
+ DECODING_ERROR,
+ /**
+ * {@linkplain com.nostra13.universalimageloader.core.ImageLoader#denyNetworkDownloads(boolean) Network
+ * downloads are denied} and requested image wasn't cached in disk cache before.
+ */
+ NETWORK_DENIED,
+ /** Not enough memory to create needed Bitmap for image */
+ OUT_OF_MEMORY,
+ /** Unknown error was occurred while loading image */
+ UNKNOWN
+ }
+}
\ No newline at end of file
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java
new file mode 100644
index 000000000..b2e450607
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java
@@ -0,0 +1,34 @@
+package com.nostra13.universalimageloader.core.assist;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Many streams obtained over slow connection show this
+ * problem.
+ */
+public class FlushedInputStream extends FilterInputStream {
+
+ public FlushedInputStream(InputStream inputStream) {
+ super(inputStream);
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ long totalBytesSkipped = 0L;
+ while (totalBytesSkipped < n) {
+ long bytesSkipped = in.skip(n - totalBytesSkipped);
+ if (bytesSkipped == 0L) {
+ int by_te = read();
+ if (by_te < 0) {
+ break; // we reached EOF
+ } else {
+ bytesSkipped = 1; // we read one byte
+ }
+ }
+ totalBytesSkipped += bytesSkipped;
+ }
+ return totalBytesSkipped;
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageScaleType.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageScaleType.java
new file mode 100644
index 000000000..b3eb89bd2
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageScaleType.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.assist;
+
+/**
+ * Type of image scaling during decoding.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.5.0
+ */
+public enum ImageScaleType {
+ /** Image won't be scaled */
+ NONE,
+ /**
+ * Image will be scaled down only if image size is greater than
+ * {@linkplain javax.microedition.khronos.opengles.GL10#GL_MAX_TEXTURE_SIZE maximum acceptable texture size}.
+ * Usually it's 2048x2048.
+ * If Bitmap is expected to display than it must not exceed this size (otherwise you'll get the exception
+ * "OpenGLRenderer: Bitmap too large to be uploaded into a texture".
+ * Image will be subsampled in an integer number of times (1, 2, 3, ...) to maximum texture size of device.
+ */
+ NONE_SAFE,
+ /**
+ * Image will be reduces 2-fold until next reduce step make image smaller target size.
+ * It's fast type and it's preferable for usage in lists/grids/galleries (and other
+ * {@linkplain android.widget.AdapterView adapter-views}) .
+ * Relates to {@link android.graphics.BitmapFactory.Options#inSampleSize}
+ * Note: If original image size is smaller than target size then original image won't be scaled.
+ */
+ IN_SAMPLE_POWER_OF_2,
+ /**
+ * Image will be subsampled in an integer number of times (1, 2, 3, ...). Use it if memory economy is quite
+ * important.
+ * Relates to {@link android.graphics.BitmapFactory.Options#inSampleSize}
+ * Note: If original image size is smaller than target size then original image won't be scaled.
+ */
+ IN_SAMPLE_INT,
+ /**
+ * Image will scaled-down exactly to target size (scaled width or height or both will be equal to target size;
+ * depends on {@linkplain android.widget.ImageView.ScaleType ImageView's scale type}). Use it if memory economy is
+ * critically important.
+ * Note: If original image size is smaller than target size then original image won't be scaled.
+ *
+ * NOTE: For creating result Bitmap (of exact size) additional Bitmap will be created with
+ * {@link android.graphics.Bitmap#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean)
+ * Bitmap.createBitmap(...)}.
+ * Cons: Saves memory by keeping smaller Bitmap in memory cache (comparing with IN_SAMPLE... scale types)
+ * Pros: Requires more memory in one time for creation of result Bitmap.
+ */
+ EXACTLY,
+ /**
+ * Image will scaled exactly to target size (scaled width or height or both will be equal to target size; depends on
+ * {@linkplain android.widget.ImageView.ScaleType ImageView's scale type}). Use it if memory economy is critically
+ * important.
+ * Note: If original image size is smaller than target size then original image will be stretched to
+ * target size.
+ *
+ * NOTE: For creating result Bitmap (of exact size) additional Bitmap will be created with
+ * {@link android.graphics.Bitmap#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean)
+ * Bitmap.createBitmap(...)}.
+ * Cons: Saves memory by keeping smaller Bitmap in memory cache (comparing with IN_SAMPLE... scale types)
+ * Pros: Requires more memory in one time for creation of result Bitmap.
+ */
+ EXACTLY_STRETCHED
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageSize.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageSize.java
new file mode 100644
index 000000000..54c97ca64
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ImageSize.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.assist;
+
+/**
+ * Present width and height values
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public class ImageSize {
+
+ private static final int TO_STRING_MAX_LENGHT = 9; // "9999x9999".length()
+ private static final String SEPARATOR = "x";
+
+ private final int width;
+ private final int height;
+
+ public ImageSize(int width, int height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ public ImageSize(int width, int height, int rotation) {
+ if (rotation % 180 == 0) {
+ this.width = width;
+ this.height = height;
+ } else {
+ this.width = height;
+ this.height = width;
+ }
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ /** Scales down dimensions in sampleSize times. Returns new object. */
+ public ImageSize scaleDown(int sampleSize) {
+ return new ImageSize(width / sampleSize, height / sampleSize);
+ }
+
+ /** Scales dimensions according to incoming scale. Returns new object. */
+ public ImageSize scale(float scale) {
+ return new ImageSize((int) (width * scale), (int) (height * scale));
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(TO_STRING_MAX_LENGHT).append(width).append(SEPARATOR).append(height).toString();
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/LoadedFrom.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/LoadedFrom.java
new file mode 100644
index 000000000..6889ce45a
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/LoadedFrom.java
@@ -0,0 +1,10 @@
+package com.nostra13.universalimageloader.core.assist;
+
+/**
+ * Source image loaded from.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ */
+public enum LoadedFrom {
+ NETWORK, DISC_CACHE, MEMORY_CACHE
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/QueueProcessingType.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/QueueProcessingType.java
new file mode 100644
index 000000000..0bbca5868
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/QueueProcessingType.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.assist;
+
+/**
+ * Queue processing type which will be used for display task processing
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.6.3
+ */
+public enum QueueProcessingType {
+ FIFO, LIFO
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ViewScaleType.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ViewScaleType.java
new file mode 100644
index 000000000..c618cfba8
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/ViewScaleType.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.assist;
+
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+
+/**
+ * Simplify {@linkplain ScaleType ImageView's scale type} to 2 types: {@link #FIT_INSIDE} and {@link #CROP}
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.6.1
+ */
+public enum ViewScaleType {
+ /**
+ * Scale the image uniformly (maintain the image's aspect ratio) so that at least one dimension (width or height) of
+ * the image will be equal to or less the corresponding dimension of the view.
+ */
+ FIT_INSIDE,
+ /**
+ * Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the
+ * image will be equal to or larger than the corresponding dimension of the view.
+ */
+ CROP;
+
+ /**
+ * Defines scale type of ImageView.
+ *
+ * @param imageView {@link ImageView}
+ * @return {@link #FIT_INSIDE} for
+ *
+ *
+ * {@link #CROP} for
+ *
+ *
+ */
+ public static ViewScaleType fromImageView(ImageView imageView) {
+ switch (imageView.getScaleType()) {
+ case FIT_CENTER:
+ case FIT_XY:
+ case FIT_START:
+ case FIT_END:
+ case CENTER_INSIDE:
+ return FIT_INSIDE;
+ case MATRIX:
+ case CENTER:
+ case CENTER_CROP:
+ default:
+ return CROP;
+ }
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/BlockingDeque.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/BlockingDeque.java
new file mode 100644
index 000000000..571eb6ca5
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/assist/deque/BlockingDeque.java
@@ -0,0 +1,616 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
+ */
+
+package com.nostra13.universalimageloader.core.assist.deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link Deque} that additionally supports blocking operations that wait
+ * for the deque to become non-empty when retrieving an element, and wait for
+ * space to become available in the deque when storing an element.
+ *
+ *
+ *
+ *
+ *
+ *
+ * First Element (Head)
+ *
+ *
+ *
+ * Throws exception
+ * Special value
+ * Blocks
+ * Times out
+ *
+ *
+ * Insert
+ * {@link #addFirst addFirst(e)}
+ * {@link #offerFirst offerFirst(e)}
+ * {@link #putFirst putFirst(e)}
+ * {@link #offerFirst offerFirst(e, time, unit)}
+ *
+ *
+ * Remove
+ * {@link #removeFirst removeFirst()}
+ * {@link #pollFirst pollFirst()}
+ * {@link #takeFirst takeFirst()}
+ * {@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}
+ *
+ *
+ * Examine
+ * {@link #getFirst getFirst()}
+ * {@link #peekFirst peekFirst()}
+ * not applicable
+ * not applicable
+ *
+ *
+ * Last Element (Tail)
+ *
+ *
+ *
+ * Throws exception
+ * Special value
+ * Blocks
+ * Times out
+ *
+ *
+ * Insert
+ * {@link #addLast addLast(e)}
+ * {@link #offerLast offerLast(e)}
+ * {@link #putLast putLast(e)}
+ * {@link #offerLast offerLast(e, time, unit)}
+ *
+ *
+ * Remove
+ * {@link #removeLast() removeLast()}
+ * {@link #pollLast() pollLast()}
+ * {@link #takeLast takeLast()}
+ * {@link #pollLast(long, TimeUnit) pollLast(time, unit)}
+ *
+ *
+ * Examine
+ * {@link #getLast getLast()}
+ * {@link #peekLast peekLast()}
+ * not applicable
+ * not applicable
+ *
+ *
+ *
+ *
+ *
+ * BlockingQueue Method
+ * Equivalent BlockingDeque Method
+ *
+ *
+ * Insert
+ *
+ *
+ * {@link #add add(e)}
+ * {@link #addLast addLast(e)}
+ *
+ *
+ * {@link #offer offer(e)}
+ * {@link #offerLast offerLast(e)}
+ *
+ *
+ * {@link #put put(e)}
+ * {@link #putLast putLast(e)}
+ *
+ *
+ * {@link #offer offer(e, time, unit)}
+ * {@link #offerLast offerLast(e, time, unit)}
+ *
+ *
+ * Remove
+ *
+ *
+ * {@link #remove() remove()}
+ * {@link #removeFirst() removeFirst()}
+ *
+ *
+ * {@link #poll() poll()}
+ * {@link #pollFirst() pollFirst()}
+ *
+ *
+ * {@link #take() take()}
+ * {@link #takeFirst() takeFirst()}
+ *
+ *
+ * {@link #poll(long, TimeUnit) poll(time, unit)}
+ * {@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}
+ *
+ *
+ * Examine
+ *
+ *
+ * {@link #element() element()}
+ * {@link #getFirst() getFirst()}
+ *
+ *
+ * {@link #peek() peek()}
+ * {@link #peekFirst() peekFirst()}
+ *
+ *
+ *
+ *
+ *
+ *
+ * First Element (Head)
+ * Last Element (Tail)
+ *
+ *
+ *
+ * Throws exception
+ * Special value
+ * Throws exception
+ * Special value
+ *
+ *
+ * Insert
+ * {@link #addFirst addFirst(e)}
+ * {@link #offerFirst offerFirst(e)}
+ * {@link #addLast addLast(e)}
+ * {@link #offerLast offerLast(e)}
+ *
+ *
+ * Remove
+ * {@link #removeFirst removeFirst()}
+ * {@link #pollFirst pollFirst()}
+ * {@link #removeLast removeLast()}
+ * {@link #pollLast pollLast()}
+ *
+ *
+ * Examine
+ * {@link #getFirst getFirst()}
+ * {@link #peekFirst peekFirst()}
+ * {@link #getLast getLast()}
+ * {@link #peekLast peekLast()}
+ *
+ *
+ *
+ *
+ *
+ * Queue Method
+ * Equivalent Deque Method
+ *
+ *
+ * {@link java.util.Queue#add add(e)}
+ * {@link #addLast addLast(e)}
+ *
+ *
+ * {@link java.util.Queue#offer offer(e)}
+ * {@link #offerLast offerLast(e)}
+ *
+ *
+ * {@link java.util.Queue#remove remove()}
+ * {@link #removeFirst removeFirst()}
+ *
+ *
+ * {@link java.util.Queue#poll poll()}
+ * {@link #pollFirst pollFirst()}
+ *
+ *
+ * {@link java.util.Queue#element element()}
+ * {@link #getFirst getFirst()}
+ *
+ *
+ * {@link java.util.Queue#peek peek()}
+ * {@link #peek peekFirst()}
+ *
+ *
+ *
+ *
+ *
+ * Stack Method
+ * Equivalent Deque Method
+ *
+ *
+ * {@link #push push(e)}
+ * {@link #addFirst addFirst(e)}
+ *
+ *
+ * {@link #pop pop()}
+ * {@link #removeFirst removeFirst()}
+ *
+ *
+ * {@link #peek peek()}
+ * {@link #peekFirst peekFirst()}
+ *
+ * String[] y = x.toArray(new String[0]);
+ *
+ * Note that {@code toArray(new Object[0])} is identical in function to
+ * {@code toArray()}.
+ *
+ * @param a the array into which the elements of the deque are to
+ * be stored, if it is big enough; otherwise, a new array of the
+ * same runtime type is allocated for this purpose
+ * @return an array containing all of the elements in this deque
+ * @throws ArrayStoreException if the runtime type of the specified array
+ * is not a supertype of the runtime type of every element in
+ * this deque
+ * @throws NullPointerException if the specified array is null
+ */
+ @SuppressWarnings("unchecked")
+ public
+ * Implementations have to be thread-safe.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @see com.nostra13.universalimageloader.core.imageaware.ImageAware
+ * @see com.nostra13.universalimageloader.core.assist.LoadedFrom
+ * @since 1.5.6
+ */
+public interface BitmapDisplayer {
+ /**
+ * Displays bitmap in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}.
+ * NOTE: This method is called on UI thread so it's strongly recommended not to do any heavy work in it.
+ *
+ * @param bitmap Source bitmap
+ * @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} to
+ * display Bitmap
+ * @param loadedFrom Source of loaded image
+ */
+ void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom);
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/FadeInBitmapDisplayer.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/FadeInBitmapDisplayer.java
new file mode 100644
index 000000000..e60a18e91
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/FadeInBitmapDisplayer.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich, Daniel MartÃ
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.display;
+
+import android.graphics.Bitmap;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.ImageView;
+import com.nostra13.universalimageloader.core.assist.LoadedFrom;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+
+/**
+ * Displays image with "fade in" animation
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com), Daniel MartÃ
+ * @since 1.6.4
+ */
+public class FadeInBitmapDisplayer implements BitmapDisplayer {
+
+ private final int durationMillis;
+
+ private final boolean animateFromNetwork;
+ private final boolean animateFromDisk;
+ private final boolean animateFromMemory;
+
+ /**
+ * @param durationMillis Duration of "fade-in" animation (in milliseconds)
+ */
+ public FadeInBitmapDisplayer(int durationMillis) {
+ this(durationMillis, true, true, true);
+ }
+
+ /**
+ * @param durationMillis Duration of "fade-in" animation (in milliseconds)
+ * @param animateFromNetwork Whether animation should be played if image is loaded from network
+ * @param animateFromDisk Whether animation should be played if image is loaded from disk cache
+ * @param animateFromMemory Whether animation should be played if image is loaded from memory cache
+ */
+ public FadeInBitmapDisplayer(int durationMillis, boolean animateFromNetwork, boolean animateFromDisk,
+ boolean animateFromMemory) {
+ this.durationMillis = durationMillis;
+ this.animateFromNetwork = animateFromNetwork;
+ this.animateFromDisk = animateFromDisk;
+ this.animateFromMemory = animateFromMemory;
+ }
+
+ @Override
+ public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
+ imageAware.setImageBitmap(bitmap);
+
+ if ((animateFromNetwork && loadedFrom == LoadedFrom.NETWORK) ||
+ (animateFromDisk && loadedFrom == LoadedFrom.DISC_CACHE) ||
+ (animateFromMemory && loadedFrom == LoadedFrom.MEMORY_CACHE)) {
+ animate(imageAware.getWrappedView(), durationMillis);
+ }
+ }
+
+ /**
+ * Animates {@link ImageView} with "fade-in" effect
+ *
+ * @param imageView {@link ImageView} which display image in
+ * @param durationMillis The length of the animation in milliseconds
+ */
+ public static void animate(View imageView, int durationMillis) {
+ if (imageView != null) {
+ AlphaAnimation fadeImage = new AlphaAnimation(0, 1);
+ fadeImage.setDuration(durationMillis);
+ fadeImage.setInterpolator(new DecelerateInterpolator());
+ imageView.startAnimation(fadeImage);
+ }
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedBitmapDisplayer.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedBitmapDisplayer.java
new file mode 100644
index 000000000..e265e2bc5
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedBitmapDisplayer.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.display;
+
+import android.graphics.*;
+import android.graphics.drawable.Drawable;
+
+import com.nostra13.universalimageloader.core.assist.LoadedFrom;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
+
+/**
+ * Can display bitmap with rounded corners. This implementation works only with ImageViews wrapped
+ * in ImageViewAware.
+ *
+ * This implementation is inspired by
+ *
+ * Romain Guy's article. It rounds images using custom drawable drawing. Original bitmap isn't changed.
+ *
+ *
+ * If this implementation doesn't meet your needs then consider
+ * RoundedImageView or
+ * CircularImageView projects for usage.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.5.6
+ */
+public class RoundedBitmapDisplayer implements BitmapDisplayer {
+
+ protected final int cornerRadius;
+ protected final int margin;
+
+ public RoundedBitmapDisplayer(int cornerRadiusPixels) {
+ this(cornerRadiusPixels, 0);
+ }
+
+ public RoundedBitmapDisplayer(int cornerRadiusPixels, int marginPixels) {
+ this.cornerRadius = cornerRadiusPixels;
+ this.margin = marginPixels;
+ }
+
+ @Override
+ public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
+ if (!(imageAware instanceof ImageViewAware)) {
+ throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
+ }
+
+ imageAware.setImageDrawable(new RoundedDrawable(bitmap, cornerRadius, margin));
+ }
+
+ public static class RoundedDrawable extends Drawable {
+
+ protected final float cornerRadius;
+ protected final int margin;
+
+ protected final RectF mRect = new RectF(),
+ mBitmapRect;
+ protected final BitmapShader bitmapShader;
+ protected final Paint paint;
+
+ public RoundedDrawable(Bitmap bitmap, int cornerRadius, int margin) {
+ this.cornerRadius = cornerRadius;
+ this.margin = margin;
+
+ bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+ mBitmapRect = new RectF (margin, margin, bitmap.getWidth() - margin, bitmap.getHeight() - margin);
+
+ paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setShader(bitmapShader);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mRect.set(margin, margin, bounds.width() - margin, bounds.height() - margin);
+
+ // Resize the original bitmap to fit the new bound
+ Matrix shaderMatrix = new Matrix();
+ shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL);
+ bitmapShader.setLocalMatrix(shaderMatrix);
+
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawRoundRect(mRect, cornerRadius, cornerRadius, paint);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ paint.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ paint.setColorFilter(cf);
+ }
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedVignetteBitmapDisplayer.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedVignetteBitmapDisplayer.java
new file mode 100644
index 000000000..bea59d354
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/RoundedVignetteBitmapDisplayer.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright 2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.display;
+
+import android.graphics.*;
+import com.nostra13.universalimageloader.core.assist.LoadedFrom;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
+
+/**
+ * Can display bitmap with rounded corners and vignette effect. This implementation works only with ImageViews wrapped
+ * in ImageViewAware.
+ *
+ * This implementation is inspired by
+ *
+ * Romain Guy's article. It rounds images using custom drawable drawing. Original bitmap isn't changed.
+ *
+ *
+ * If this implementation doesn't meet your needs then consider
+ * this project for usage.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.9.1
+ */
+public class RoundedVignetteBitmapDisplayer extends RoundedBitmapDisplayer {
+
+ public RoundedVignetteBitmapDisplayer(int cornerRadiusPixels, int marginPixels) {
+ super(cornerRadiusPixels, marginPixels);
+ }
+
+ @Override
+ public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
+ if (!(imageAware instanceof ImageViewAware)) {
+ throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
+ }
+
+ imageAware.setImageDrawable(new RoundedVignetteDrawable(bitmap, cornerRadius, margin));
+ }
+
+ protected static class RoundedVignetteDrawable extends RoundedDrawable {
+
+ RoundedVignetteDrawable(Bitmap bitmap, int cornerRadius, int margin) {
+ super(bitmap, cornerRadius, margin);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ RadialGradient vignette = new RadialGradient(
+ mRect.centerX(), mRect.centerY() * 1.0f / 0.7f, mRect.centerX() * 1.3f,
+ new int[]{0, 0, 0x7f000000}, new float[]{0.0f, 0.7f, 1.0f},
+ Shader.TileMode.CLAMP);
+
+ Matrix oval = new Matrix();
+ oval.setScale(1.0f, 0.7f);
+ vignette.setLocalMatrix(oval);
+
+ paint.setShader(new ComposeShader(bitmapShader, vignette, PorterDuff.Mode.SRC_OVER));
+ }
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/SimpleBitmapDisplayer.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/SimpleBitmapDisplayer.java
new file mode 100644
index 000000000..8aae7de6b
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/display/SimpleBitmapDisplayer.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.display;
+
+import android.graphics.Bitmap;
+import com.nostra13.universalimageloader.core.assist.LoadedFrom;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+
+/**
+ * Just displays {@link Bitmap} in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.5.6
+ */
+public final class SimpleBitmapDisplayer implements BitmapDisplayer {
+ @Override
+ public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
+ imageAware.setImageBitmap(bitmap);
+ }
+}
\ No newline at end of file
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java
new file mode 100644
index 000000000..964b51cef
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.download;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import com.nostra13.universalimageloader.core.DisplayImageOptions;
+import com.nostra13.universalimageloader.core.assist.ContentLengthInputStream;
+import com.nostra13.universalimageloader.utils.IoUtils;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+
+/**
+ * Provides retrieving of {@link InputStream} of image by URI from network or file system or app resources.
+ * {@link URLConnection} is used to retrieve image stream from network.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.8.0
+ */
+public class BaseImageDownloader implements ImageDownloader {
+ /** {@value} */
+ public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = 5 * 1000; // milliseconds
+ /** {@value} */
+ public static final int DEFAULT_HTTP_READ_TIMEOUT = 20 * 1000; // milliseconds
+
+ /** {@value} */
+ protected static final int BUFFER_SIZE = 32 * 1024; // 32 Kb
+ /** {@value} */
+ protected static final String ALLOWED_URI_CHARS = "@#&=*+-_.,:!?()/~'%";
+
+ protected static final int MAX_REDIRECT_COUNT = 5;
+
+ protected static final String CONTENT_CONTACTS_URI_PREFIX = "content://com.android.contacts/";
+
+ private static final String ERROR_UNSUPPORTED_SCHEME = "UIL doesn't support scheme(protocol) by default [%s]. " + "You should implement this support yourself (BaseImageDownloader.getStreamFromOtherSource(...))";
+
+ protected final Context context;
+ protected final int connectTimeout;
+ protected final int readTimeout;
+
+ public BaseImageDownloader(Context context) {
+ this.context = context.getApplicationContext();
+ this.connectTimeout = DEFAULT_HTTP_CONNECT_TIMEOUT;
+ this.readTimeout = DEFAULT_HTTP_READ_TIMEOUT;
+ }
+
+ public BaseImageDownloader(Context context, int connectTimeout, int readTimeout) {
+ this.context = context.getApplicationContext();
+ this.connectTimeout = connectTimeout;
+ this.readTimeout = readTimeout;
+ }
+
+ @Override
+ public InputStream getStream(String imageUri, Object extra) throws IOException {
+ switch (Scheme.ofUri(imageUri)) {
+ case HTTP:
+ case HTTPS:
+ return getStreamFromNetwork(imageUri, extra);
+ case FILE:
+ return getStreamFromFile(imageUri, extra);
+ case CONTENT:
+ return getStreamFromContent(imageUri, extra);
+ case ASSETS:
+ return getStreamFromAssets(imageUri, extra);
+ case DRAWABLE:
+ return getStreamFromDrawable(imageUri, extra);
+ case UNKNOWN:
+ default:
+ return getStreamFromOtherSource(imageUri, extra);
+ }
+ }
+
+ /**
+ * Retrieves {@link InputStream} of image by URI (image is located in the network).
+ *
+ * @param imageUri Image URI
+ * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
+ * DisplayImageOptions.extraForDownloader(Object)}; can be null
+ * @return {@link InputStream} of image
+ * @throws IOException if some I/O error occurs during network request or if no InputStream could be created for
+ * URL.
+ */
+ protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
+ HttpURLConnection conn = createConnection(imageUri, extra);
+
+ int redirectCount = 0;
+ while (conn.getResponseCode() / 100 == 3 && redirectCount < MAX_REDIRECT_COUNT) {
+ conn = createConnection(conn.getHeaderField("Location"), extra);
+ redirectCount++;
+ }
+
+ InputStream imageStream;
+ try {
+ imageStream = conn.getInputStream();
+ } catch (IOException e) {
+ // Read all data to allow reuse connection (http://bit.ly/1ad35PY)
+ IoUtils.readAndCloseStream(conn.getErrorStream());
+ throw e;
+ }
+ return new ContentLengthInputStream(new BufferedInputStream(imageStream, BUFFER_SIZE), conn.getContentLength());
+ }
+
+ /**
+ * Create {@linkplain HttpURLConnection HTTP connection} for incoming URL
+ *
+ * @param url URL to connect to
+ * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
+ * DisplayImageOptions.extraForDownloader(Object)}; can be null
+ * @return {@linkplain HttpURLConnection Connection} for incoming URL. Connection isn't established so it still configurable.
+ * @throws IOException if some I/O error occurs during network request or if no InputStream could be created for
+ * URL.
+ */
+ protected HttpURLConnection createConnection(String url, Object extra) throws IOException {
+ String encodedUrl = Uri.encode(url, ALLOWED_URI_CHARS);
+ HttpURLConnection conn = (HttpURLConnection) new URL(encodedUrl).openConnection();
+ conn.setConnectTimeout(connectTimeout);
+ conn.setReadTimeout(readTimeout);
+ return conn;
+ }
+
+ /**
+ * Retrieves {@link InputStream} of image by URI (image is located on the local file system or SD card).
+ *
+ * @param imageUri Image URI
+ * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
+ * DisplayImageOptions.extraForDownloader(Object)}; can be null
+ * @return {@link InputStream} of image
+ * @throws IOException if some I/O error occurs reading from file system
+ */
+ protected InputStream getStreamFromFile(String imageUri, Object extra) throws IOException {
+ String filePath = Scheme.FILE.crop(imageUri);
+ return new ContentLengthInputStream(new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE),
+ (int) new File(filePath).length());
+ }
+
+ /**
+ * Retrieves {@link InputStream} of image by URI (image is accessed using {@link ContentResolver}).
+ *
+ * @param imageUri Image URI
+ * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
+ * DisplayImageOptions.extraForDownloader(Object)}; can be null
+ * @return {@link InputStream} of image
+ * @throws FileNotFoundException if the provided URI could not be opened
+ */
+ protected InputStream getStreamFromContent(String imageUri, Object extra) throws FileNotFoundException {
+ ContentResolver res = context.getContentResolver();
+
+ Uri uri = Uri.parse(imageUri);
+ if (isVideoUri(uri)) { // video thumbnail
+ Long origId = Long.valueOf(uri.getLastPathSegment());
+ Bitmap bitmap = MediaStore.Video.Thumbnails
+ .getThumbnail(res, origId, MediaStore.Images.Thumbnails.MINI_KIND, null);
+ if (bitmap != null) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ bitmap.compress(CompressFormat.PNG, 0, bos);
+ return new ByteArrayInputStream(bos.toByteArray());
+ }
+ } else if (imageUri.startsWith(CONTENT_CONTACTS_URI_PREFIX)) { // contacts photo
+ return ContactsContract.Contacts.openContactPhotoInputStream(res, uri);
+ }
+
+ return res.openInputStream(uri);
+ }
+
+ /**
+ * Retrieves {@link InputStream} of image by URI (image is located in assets of application).
+ *
+ * @param imageUri Image URI
+ * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
+ * DisplayImageOptions.extraForDownloader(Object)}; can be null
+ * @return {@link InputStream} of image
+ * @throws IOException if some I/O error occurs file reading
+ */
+ protected InputStream getStreamFromAssets(String imageUri, Object extra) throws IOException {
+ String filePath = Scheme.ASSETS.crop(imageUri);
+ return context.getAssets().open(filePath);
+ }
+
+ /**
+ * Retrieves {@link InputStream} of image by URI (image is located in drawable resources of application).
+ *
+ * @param imageUri Image URI
+ * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
+ * DisplayImageOptions.extraForDownloader(Object)}; can be null
+ * @return {@link InputStream} of image
+ */
+ protected InputStream getStreamFromDrawable(String imageUri, Object extra) {
+ String drawableIdString = Scheme.DRAWABLE.crop(imageUri);
+ int drawableId = Integer.parseInt(drawableIdString);
+ return context.getResources().openRawResource(drawableId);
+ }
+
+ /**
+ * Retrieves {@link InputStream} of image by URI from other source with unsupported scheme. Should be overriden by
+ * successors to implement image downloading from special sources.
+ * This method is called only if image URI has unsupported scheme. Throws {@link UnsupportedOperationException} by
+ * default.
+ *
+ * @param imageUri Image URI
+ * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
+ * DisplayImageOptions.extraForDownloader(Object)}; can be null
+ * @return {@link InputStream} of image
+ * @throws IOException if some I/O error occurs
+ * @throws UnsupportedOperationException if image URI has unsupported scheme(protocol)
+ */
+ protected InputStream getStreamFromOtherSource(String imageUri, Object extra) throws IOException {
+ throw new UnsupportedOperationException(String.format(ERROR_UNSUPPORTED_SCHEME, imageUri));
+ }
+
+ private boolean isVideoUri(Uri uri) {
+ String mimeType = context.getContentResolver().getType(uri);
+
+ if (mimeType == null) {
+ return false;
+ }
+
+ return mimeType.startsWith("video/");
+ }
+}
\ No newline at end of file
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/ImageDownloader.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/ImageDownloader.java
new file mode 100644
index 000000000..3fa0fb5cc
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/download/ImageDownloader.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.download;
+
+import com.nostra13.universalimageloader.core.DisplayImageOptions;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+
+/**
+ * Provides retrieving of {@link InputStream} of image by URI.
+ * Implementations have to be thread-safe.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.4.0
+ */
+public interface ImageDownloader {
+ /**
+ * Retrieves {@link InputStream} of image by URI.
+ *
+ * @param imageUri Image URI
+ * @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
+ * DisplayImageOptions.extraForDownloader(Object)}; can be null
+ * @return {@link InputStream} of image
+ * @throws IOException if some I/O error occurs during getting image stream
+ * @throws UnsupportedOperationException if image URI has unsupported scheme(protocol)
+ */
+ InputStream getStream(String imageUri, Object extra) throws IOException;
+
+ /** Represents supported schemes(protocols) of URI. Provides convenient methods for work with schemes and URIs. */
+ public enum Scheme {
+ HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"), ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN("");
+
+ private String scheme;
+ private String uriPrefix;
+
+ Scheme(String scheme) {
+ this.scheme = scheme;
+ uriPrefix = scheme + "://";
+ }
+
+ /**
+ * Defines scheme of incoming URI
+ *
+ * @param uri URI for scheme detection
+ * @return Scheme of incoming URI
+ */
+ public static Scheme ofUri(String uri) {
+ if (uri != null) {
+ for (Scheme s : values()) {
+ if (s.belongsTo(uri)) {
+ return s;
+ }
+ }
+ }
+ return UNKNOWN;
+ }
+
+ private boolean belongsTo(String uri) {
+ return uri.toLowerCase(Locale.US).startsWith(uriPrefix);
+ }
+
+ /** Appends scheme to incoming path */
+ public String wrap(String path) {
+ return uriPrefix + path;
+ }
+
+ /** Removed scheme part ("scheme://") from incoming URI */
+ public String crop(String uri) {
+ if (!belongsTo(uri)) {
+ throw new IllegalArgumentException(String.format("URI [%1$s] doesn't have expected scheme [%2$s]", uri, scheme));
+ }
+ return uri.substring(uriPrefix.length());
+ }
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageAware.java
new file mode 100644
index 000000000..71531fdd9
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageAware.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright 2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.imageaware;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import com.nostra13.universalimageloader.core.assist.ViewScaleType;
+
+/**
+ * Represents image aware view which provides all needed properties and behavior for image processing and displaying
+ * through {@link com.nostra13.universalimageloader.core.ImageLoader ImageLoader}.
+ * It can wrap any Android {@link android.view.View View} which can be accessed by {@link #getWrappedView()}. Wrapped
+ * view is returned in {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener ImageLoadingListener}'s
+ * callbacks.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @see ViewAware
+ * @see ImageViewAware
+ * @see NonViewAware
+ * @since 1.9.0
+ */
+public interface ImageAware {
+ /**
+ * Returns width of image aware view. This value is used to define scale size for original image.
+ * Can return 0 if width is undefined.
+ * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
+ */
+ int getWidth();
+
+ /**
+ * Returns height of image aware view. This value is used to define scale size for original image.
+ * Can return 0 if height is undefined.
+ * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
+ */
+ int getHeight();
+
+ /**
+ * Returns {@linkplain com.nostra13.universalimageloader.core.assist.ViewScaleType scale type} which is used for
+ * scaling image for this image aware view. Must NOT return null.
+ */
+ ViewScaleType getScaleType();
+
+ /**
+ * Returns wrapped Android {@link android.view.View View}. Can return null if no view is wrapped or view was
+ * collected by GC.
+ * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
+ */
+ View getWrappedView();
+
+ /**
+ * Returns a flag whether image aware view is collected by GC or whatsoever. If so then ImageLoader stop processing
+ * of task for this image aware view and fires
+ * {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener#onLoadingCancelled(String,
+ * android.view.View) ImageLoadingListener#onLoadingCancelled(String, View)} callback.
+ * Mey be called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
+ *
+ * @return true - if view is collected by GC and ImageLoader should stop processing this image aware view;
+ * false - otherwise
+ */
+ boolean isCollected();
+
+ /**
+ * Returns ID of image aware view. Point of ID is similar to Object's hashCode. This ID should be unique for every
+ * image view instance and should be the same for same instances. This ID identifies processing task in ImageLoader
+ * so ImageLoader won't process two image aware views with the same ID in one time. When ImageLoader get new task
+ * it cancels old task with this ID (if any) and starts new task.
+ *
+ * It's reasonable to return hash code of wrapped view (if any) to prevent displaying non-actual images in view
+ * because of view re-using.
+ */
+ int getId();
+
+ /**
+ * Sets image drawable into this image aware view.
+ * Displays drawable in this image aware view
+ * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageForEmptyUri(
+ *android.graphics.drawable.Drawable) for empty Uri},
+ * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageOnLoading(
+ *android.graphics.drawable.Drawable) on loading} or
+ * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageOnFail(
+ *android.graphics.drawable.Drawable) on loading fail}. These drawables can be specified in
+ * {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions display options}.
+ * Also can be called in {@link com.nostra13.universalimageloader.core.display.BitmapDisplayer BitmapDisplayer}.< br />
+ * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
+ *
+ * @return true if drawable was set successfully; false - otherwise
+ */
+ boolean setImageDrawable(Drawable drawable);
+
+ /**
+ * Sets image bitmap into this image aware view.
+ * Displays loaded and decoded image {@link android.graphics.Bitmap} in this image view aware.
+ * Actually it's used only in
+ * {@link com.nostra13.universalimageloader.core.display.BitmapDisplayer BitmapDisplayer}.< br />
+ * Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
+ *
+ * @return true if bitmap was set successfully; false - otherwise
+ */
+ boolean setImageBitmap(Bitmap bitmap);
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java
new file mode 100644
index 000000000..832872dcd
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright 2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.imageaware;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.ImageView;
+import com.nostra13.universalimageloader.core.assist.ViewScaleType;
+import com.nostra13.universalimageloader.utils.L;
+
+import java.lang.reflect.Field;
+
+/**
+ * Wrapper for Android {@link android.widget.ImageView ImageView}. Keeps weak reference of ImageView to prevent memory
+ * leaks.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.9.0
+ */
+public class ImageViewAware extends ViewAware {
+
+ /**
+ * Constructor.
+ * References {@link #ImageViewAware(android.widget.ImageView, boolean) ImageViewAware(imageView, true)}.
+ *
+ * @param imageView {@link android.widget.ImageView ImageView} to work with
+ */
+ public ImageViewAware(ImageView imageView) {
+ super(imageView);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param imageView {@link android.widget.ImageView ImageView} to work with
+ * @param checkActualViewSize true - then {@link #getWidth()} and {@link #getHeight()} will check actual
+ * size of ImageView. It can cause known issues like
+ * this.
+ * But it helps to save memory because memory cache keeps bitmaps of actual (less in
+ * general) size.
+ *
+ * false - then {@link #getWidth()} and {@link #getHeight()} will NOT
+ * consider actual size of ImageView, just layout parameters.
If you set 'false'
+ * it's recommended 'android:layout_width' and 'android:layout_height' (or
+ * 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to
+ * save memory.
+ *
+ */
+ public ImageViewAware(ImageView imageView, boolean checkActualViewSize) {
+ super(imageView, checkActualViewSize);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * 3) Get maxWidth.
+ */
+ @Override
+ public int getWidth() {
+ int width = super.getWidth();
+ if (width <= 0) {
+ ImageView imageView = (ImageView) viewRef.get();
+ if (imageView != null) {
+ width = getImageViewFieldValue(imageView, "mMaxWidth"); // Check maxWidth parameter
+ }
+ }
+ return width;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * 3) Get maxHeight
+ */
+ @Override
+ public int getHeight() {
+ int height = super.getHeight();
+ if (height <= 0) {
+ ImageView imageView = (ImageView) viewRef.get();
+ if (imageView != null) {
+ height = getImageViewFieldValue(imageView, "mMaxHeight"); // Check maxHeight parameter
+ }
+ }
+ return height;
+ }
+
+ @Override
+ public ViewScaleType getScaleType() {
+ ImageView imageView = (ImageView) viewRef.get();
+ if (imageView != null) {
+ return ViewScaleType.fromImageView(imageView);
+ }
+ return super.getScaleType();
+ }
+
+ @Override
+ public ImageView getWrappedView() {
+ return (ImageView) super.getWrappedView();
+ }
+
+ @Override
+ protected void setImageDrawableInto(Drawable drawable, View view) {
+ ((ImageView) view).setImageDrawable(drawable);
+ if (drawable instanceof AnimationDrawable) {
+ ((AnimationDrawable)drawable).start();
+ }
+ }
+
+ @Override
+ protected void setImageBitmapInto(Bitmap bitmap, View view) {
+ ((ImageView) view).setImageBitmap(bitmap);
+ }
+
+ private static int getImageViewFieldValue(Object object, String fieldName) {
+ int value = 0;
+ try {
+ Field field = ImageView.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ int fieldValue = (Integer) field.get(object);
+ if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
+ value = fieldValue;
+ }
+ } catch (Exception e) {
+ L.e(e);
+ }
+ return value;
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/NonViewAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/NonViewAware.java
new file mode 100644
index 000000000..1918b5ec9
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/NonViewAware.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright 2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.imageaware;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.view.View;
+import com.nostra13.universalimageloader.core.assist.ImageSize;
+import com.nostra13.universalimageloader.core.assist.ViewScaleType;
+
+/**
+ * ImageAware which provides needed info for processing of original image but do nothing for displaying image. It's
+ * used when user need just load and decode image and get it in {@linkplain
+ * com.nostra13.universalimageloader.core.listener.ImageLoadingListener#onLoadingComplete(String, android.view.View,
+ * android.graphics.Bitmap) callback}.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.9.0
+ */
+public class NonViewAware implements ImageAware {
+
+ protected final String imageUri;
+ protected final ImageSize imageSize;
+ protected final ViewScaleType scaleType;
+
+ public NonViewAware(ImageSize imageSize, ViewScaleType scaleType) {
+ this(null, imageSize, scaleType);
+ }
+
+ public NonViewAware(String imageUri, ImageSize imageSize, ViewScaleType scaleType) {
+ if (imageSize == null) throw new IllegalArgumentException("imageSize must not be null");
+ if (scaleType == null) throw new IllegalArgumentException("scaleType must not be null");
+
+ this.imageUri = imageUri;
+ this.imageSize = imageSize;
+ this.scaleType = scaleType;
+ }
+
+ @Override
+ public int getWidth() {
+ return imageSize.getWidth();
+ }
+
+ @Override
+ public int getHeight() {
+ return imageSize.getHeight();
+ }
+
+ @Override
+ public ViewScaleType getScaleType() {
+ return scaleType;
+ }
+
+ @Override
+ public View getWrappedView() {
+ return null;
+ }
+
+ @Override
+ public boolean isCollected() {
+ return false;
+ }
+
+ @Override
+ public int getId() {
+ return TextUtils.isEmpty(imageUri) ? super.hashCode() : imageUri.hashCode();
+ }
+
+ @Override
+ public boolean setImageDrawable(Drawable drawable) { // Do nothing
+ return true;
+ }
+
+ @Override
+ public boolean setImageBitmap(Bitmap bitmap) { // Do nothing
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ViewAware.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ViewAware.java
new file mode 100644
index 000000000..af6871531
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/imageaware/ViewAware.java
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright 2014 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.imageaware;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Looper;
+import android.view.View;
+import android.view.ViewGroup;
+import com.nostra13.universalimageloader.core.assist.ViewScaleType;
+import com.nostra13.universalimageloader.utils.L;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+
+/**
+ * Wrapper for Android {@link android.view.View View}. Keeps weak reference of View to prevent memory leaks.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.9.2
+ */
+public abstract class ViewAware implements ImageAware {
+
+ public static final String WARN_CANT_SET_DRAWABLE = "Can't set a drawable into view. You should call ImageLoader on UI thread for it.";
+ public static final String WARN_CANT_SET_BITMAP = "Can't set a bitmap into view. You should call ImageLoader on UI thread for it.";
+
+ protected Reference
+ * References {@link #ViewAware(android.view.View, boolean) ImageViewAware(imageView, true)}.
+ *
+ * @param view {@link android.view.View View} to work with
+ */
+ public ViewAware(View view) {
+ this(view, true);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param view {@link android.view.View View} to work with
+ * @param checkActualViewSize true - then {@link #getWidth()} and {@link #getHeight()} will check actual
+ * size of View. It can cause known issues like
+ * this.
+ * But it helps to save memory because memory cache keeps bitmaps of actual (less in
+ * general) size.
+ *
+ * false - then {@link #getWidth()} and {@link #getHeight()} will NOT
+ * consider actual size of View, just layout parameters.
If you set 'false'
+ * it's recommended 'android:layout_width' and 'android:layout_height' (or
+ * 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to
+ * save memory.
+ */
+ public ViewAware(View view, boolean checkActualViewSize) {
+ if (view == null) throw new IllegalArgumentException("view must not be null");
+
+ this.viewRef = new WeakReference
+ * Size computing algorithm (go by steps until get non-zero value):
+ * 1) Get the actual drawn getWidth() of the View
+ * 2) Get layout_width
+ */
+ @Override
+ public int getWidth() {
+ View view = viewRef.get();
+ if (view != null) {
+ final ViewGroup.LayoutParams params = view.getLayoutParams();
+ int width = 0;
+ if (checkActualViewSize && params != null && params.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
+ width = view.getWidth(); // Get actual image width
+ }
+ if (width <= 0 && params != null) width = params.width; // Get layout width parameter
+ return width;
+ }
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Height is defined by target {@link android.view.View view} parameters, configuration
+ * parameters or device display dimensions.
+ * Size computing algorithm (go by steps until get non-zero value):
+ * 1) Get the actual drawn getHeight() of the View
+ * 2) Get layout_height
+ */
+ @Override
+ public int getHeight() {
+ View view = viewRef.get();
+ if (view != null) {
+ final ViewGroup.LayoutParams params = view.getLayoutParams();
+ int height = 0;
+ if (checkActualViewSize && params != null && params.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
+ height = view.getHeight(); // Get actual image height
+ }
+ if (height <= 0 && params != null) height = params.height; // Get layout height parameter
+ return height;
+ }
+ return 0;
+ }
+
+ @Override
+ public ViewScaleType getScaleType() {
+ return ViewScaleType.CROP;
+ }
+
+ @Override
+ public View getWrappedView() {
+ return viewRef.get();
+ }
+
+ @Override
+ public boolean isCollected() {
+ return viewRef.get() == null;
+ }
+
+ @Override
+ public int getId() {
+ View view = viewRef.get();
+ return view == null ? super.hashCode() : view.hashCode();
+ }
+
+ @Override
+ public boolean setImageDrawable(Drawable drawable) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ View view = viewRef.get();
+ if (view != null) {
+ setImageDrawableInto(drawable, view);
+ return true;
+ }
+ } else {
+ L.w(WARN_CANT_SET_DRAWABLE);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean setImageBitmap(Bitmap bitmap) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ View view = viewRef.get();
+ if (view != null) {
+ setImageBitmapInto(bitmap, view);
+ return true;
+ }
+ } else {
+ L.w(WARN_CANT_SET_BITMAP);
+ }
+ return false;
+ }
+
+ /**
+ * Should set drawable into incoming view. Incoming view is guaranteed not null.
+ * This method is called on UI thread.
+ */
+ protected abstract void setImageDrawableInto(Drawable drawable, View view);
+
+ /**
+ * Should set Bitmap into incoming view. Incoming view is guaranteed not null.< br />
+ * This method is called on UI thread.
+ */
+ protected abstract void setImageBitmapInto(Bitmap bitmap, View view);
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingListener.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingListener.java
new file mode 100644
index 000000000..7be04fbcf
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingListener.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.listener;
+
+import android.graphics.Bitmap;
+import android.view.View;
+import com.nostra13.universalimageloader.core.assist.FailReason;
+
+/**
+ * Listener for image loading process.
+ * You can use {@link SimpleImageLoadingListener} for implementing only needed methods.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @see SimpleImageLoadingListener
+ * @see com.nostra13.universalimageloader.core.assist.FailReason
+ * @since 1.0.0
+ */
+public interface ImageLoadingListener {
+
+ /**
+ * Is called when image loading task was started
+ *
+ * @param imageUri Loading image URI
+ * @param view View for image
+ */
+ void onLoadingStarted(String imageUri, View view);
+
+ /**
+ * Is called when an error was occurred during image loading
+ *
+ * @param imageUri Loading image URI
+ * @param view View for image. Can be null.
+ * @param failReason {@linkplain com.nostra13.universalimageloader.core.assist.FailReason The reason} why image
+ * loading was failed
+ */
+ void onLoadingFailed(String imageUri, View view, FailReason failReason);
+
+ /**
+ * Is called when image is loaded successfully (and displayed in View if one was specified)
+ *
+ * @param imageUri Loaded image URI
+ * @param view View for image. Can be null.
+ * @param loadedImage Bitmap of loaded and decoded image
+ */
+ void onLoadingComplete(String imageUri, View view, Bitmap loadedImage);
+
+ /**
+ * Is called when image loading task was cancelled because View for image was reused in newer task
+ *
+ * @param imageUri Loading image URI
+ * @param view View for image. Can be null.
+ */
+ void onLoadingCancelled(String imageUri, View view);
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener.java
new file mode 100644
index 000000000..c9ce6848c
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright 2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.listener;
+
+import android.view.View;
+
+/**
+ * Listener for image loading progress.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.9.1
+ */
+public interface ImageLoadingProgressListener {
+
+ /**
+ * Is called when image loading progress changed.
+ *
+ * @param imageUri Image URI
+ * @param view View for image. Can be null.
+ * @param current Downloaded size in bytes
+ * @param total Total size in bytes
+ */
+ void onProgressUpdate(String imageUri, View view, int current, int total);
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/PauseOnScrollListener.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/PauseOnScrollListener.java
new file mode 100644
index 000000000..870822f65
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/PauseOnScrollListener.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.listener;
+
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.GridView;
+import android.widget.ListView;
+import com.nostra13.universalimageloader.core.ImageLoader;
+
+/**
+ * Listener-helper for {@linkplain AbsListView list views} ({@link ListView}, {@link GridView}) which can
+ * {@linkplain ImageLoader#pause() pause ImageLoader's tasks} while list view is scrolling (touch scrolling and/or
+ * fling). It prevents redundant loadings.
+ * Set it to your list view's {@link AbsListView#setOnScrollListener(OnScrollListener) setOnScrollListener(...)}.
+ * This listener can wrap your custom {@linkplain OnScrollListener listener}.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.7.0
+ */
+public class PauseOnScrollListener implements OnScrollListener {
+
+ private ImageLoader imageLoader;
+
+ private final boolean pauseOnScroll;
+ private final boolean pauseOnFling;
+ private final OnScrollListener externalListener;
+
+ /**
+ * Constructor
+ *
+ * @param imageLoader {@linkplain ImageLoader} instance for controlling
+ * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling
+ * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling
+ */
+ public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling) {
+ this(imageLoader, pauseOnScroll, pauseOnFling, null);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param imageLoader {@linkplain ImageLoader} instance for controlling
+ * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling
+ * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling
+ * @param customListener Your custom {@link OnScrollListener} for {@linkplain AbsListView list view} which also
+ * will be get scroll events
+ */
+ public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling,
+ OnScrollListener customListener) {
+ this.imageLoader = imageLoader;
+ this.pauseOnScroll = pauseOnScroll;
+ this.pauseOnFling = pauseOnFling;
+ externalListener = customListener;
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ switch (scrollState) {
+ case OnScrollListener.SCROLL_STATE_IDLE:
+ imageLoader.resume();
+ break;
+ case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
+ if (pauseOnScroll) {
+ imageLoader.pause();
+ }
+ break;
+ case OnScrollListener.SCROLL_STATE_FLING:
+ if (pauseOnFling) {
+ imageLoader.pause();
+ }
+ break;
+ }
+ if (externalListener != null) {
+ externalListener.onScrollStateChanged(view, scrollState);
+ }
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ if (externalListener != null) {
+ externalListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
+ }
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/SimpleImageLoadingListener.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/SimpleImageLoadingListener.java
new file mode 100644
index 000000000..3e42223d5
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/listener/SimpleImageLoadingListener.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.listener;
+
+import android.graphics.Bitmap;
+import android.view.View;
+import com.nostra13.universalimageloader.core.assist.FailReason;
+
+/**
+ * A convenient class to extend when you only want to listen for a subset of all the image loading events. This
+ * implements all methods in the {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener} but does
+ * nothing.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.4.0
+ */
+public class SimpleImageLoadingListener implements ImageLoadingListener {
+ @Override
+ public void onLoadingStarted(String imageUri, View view) {
+ // Empty implementation
+ }
+
+ @Override
+ public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
+ // Empty implementation
+ }
+
+ @Override
+ public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
+ // Empty implementation
+ }
+
+ @Override
+ public void onLoadingCancelled(String imageUri, View view) {
+ // Empty implementation
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/process/BitmapProcessor.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/process/BitmapProcessor.java
new file mode 100644
index 000000000..962605dbd
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/core/process/BitmapProcessor.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.core.process;
+
+import android.graphics.Bitmap;
+import com.nostra13.universalimageloader.core.DisplayImageOptions;
+
+/**
+ * Makes some processing on {@link Bitmap}. Implementations can apply any changes to original {@link Bitmap}.
+ * Implementations have to be thread-safe.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.8.0
+ */
+public interface BitmapProcessor {
+ /**
+ * Makes some processing of incoming bitmap.
+ * This method is executing on additional thread (not on UI thread).
+ * Note: If this processor is used as {@linkplain DisplayImageOptions.Builder#preProcessor(BitmapProcessor)
+ * pre-processor} then don't forget {@linkplain Bitmap#recycle() to recycle} incoming bitmap if you return a new
+ * created one.
+ *
+ * @param bitmap Original {@linkplain Bitmap bitmap}
+ * @return Processed {@linkplain Bitmap bitmap}
+ */
+ Bitmap process(Bitmap bitmap);
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/DiskCacheUtils.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/DiskCacheUtils.java
new file mode 100644
index 000000000..2522cd6b4
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/DiskCacheUtils.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.utils;
+
+import com.nostra13.universalimageloader.cache.disc.DiskCache;
+
+import java.io.File;
+
+/**
+ * Utility for convenient work with disk cache.
+ * NOTE: This utility works with file system so avoid using it on application main thread.
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.8.0
+ */
+public final class DiskCacheUtils {
+
+ private DiskCacheUtils() {
+ }
+
+ /** Returns {@link File} of cached image or null if image was not cached in disk cache */
+ public static File findInCache(String imageUri, DiskCache diskCache) {
+ File image = diskCache.get(imageUri);
+ return image != null && image.exists() ? image : null;
+ }
+
+ /**
+ * Removed cached image file from disk cache (if image was cached in disk cache before)
+ *
+ * @return true - if cached image file existed and was deleted; false - otherwise.
+ */
+ public static boolean removeFromCache(String imageUri, DiskCache diskCache) {
+ File image = diskCache.get(imageUri);
+ return image != null && image.exists() && image.delete();
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/ImageSizeUtils.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/ImageSizeUtils.java
new file mode 100644
index 000000000..1dafddc2e
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/ImageSizeUtils.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright 2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.utils;
+
+import android.graphics.BitmapFactory;
+import android.opengl.GLES10;
+import com.nostra13.universalimageloader.core.assist.ImageSize;
+import com.nostra13.universalimageloader.core.assist.ViewScaleType;
+import com.nostra13.universalimageloader.core.imageaware.ImageAware;
+
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Provides calculations with image sizes, scales
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.8.3
+ */
+public final class ImageSizeUtils {
+
+ private static final int DEFAULT_MAX_BITMAP_DIMENSION = 2048;
+
+ private static ImageSize maxBitmapSize;
+
+ static {
+ int[] maxTextureSize = new int[1];
+ GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
+ int maxBitmapDimension = Math.max(maxTextureSize[0], DEFAULT_MAX_BITMAP_DIMENSION);
+ maxBitmapSize = new ImageSize(maxBitmapDimension, maxBitmapDimension);
+ }
+
+ private ImageSizeUtils() {
+ }
+
+ /**
+ * Defines target size for image aware view. Size is defined by target
+ * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware view} parameters, configuration
+ * parameters or device display dimensions.
+ */
+ public static ImageSize defineTargetSizeForView(ImageAware imageAware, ImageSize maxImageSize) {
+ int width = imageAware.getWidth();
+ if (width <= 0) width = maxImageSize.getWidth();
+
+ int height = imageAware.getHeight();
+ if (height <= 0) height = maxImageSize.getHeight();
+
+ return new ImageSize(width, height);
+ }
+
+ /**
+ * Computes sample size for downscaling image size (srcSize) to view size (targetSize). This sample
+ * size is used during
+ * {@linkplain BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options)
+ * decoding image} to bitmap.
+ *
+ * Examples:
+ *
+ *
+ * srcSize(100x100), targetSize(10x10), powerOf2Scale = true -> sampleSize = 8
+ * srcSize(100x100), targetSize(10x10), powerOf2Scale = false -> sampleSize = 10
+ *
+ * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> sampleSize = 5
+ * srcSize(100x100), targetSize(20x40), viewScaleType = CROP -> sampleSize = 2
+ *
+ *
+ *
+ * The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded
+ * bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16
+ * the number of pixels. Any value <= 1 is treated the same as 1.
+ *
+ * @param srcSize Original (image) size
+ * @param targetSize Target (view) size
+ * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view
+ * @param powerOf2Scale true - if sample size be a power of 2 (1, 2, 4, 8, ...)
+ * @return Computed sample size
+ */
+ public static int computeImageSampleSize(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
+ boolean powerOf2Scale) {
+ final int srcWidth = srcSize.getWidth();
+ final int srcHeight = srcSize.getHeight();
+ final int targetWidth = targetSize.getWidth();
+ final int targetHeight = targetSize.getHeight();
+
+ int scale = 1;
+
+ switch (viewScaleType) {
+ case FIT_INSIDE:
+ if (powerOf2Scale) {
+ final int halfWidth = srcWidth / 2;
+ final int halfHeight = srcHeight / 2;
+ while ((halfWidth / scale) > targetWidth || (halfHeight / scale) > targetHeight) { // ||
+ scale *= 2;
+ }
+ } else {
+ scale = Math.max(srcWidth / targetWidth, srcHeight / targetHeight); // max
+ }
+ break;
+ case CROP:
+ if (powerOf2Scale) {
+ final int halfWidth = srcWidth / 2;
+ final int halfHeight = srcHeight / 2;
+ while ((halfWidth / scale) > targetWidth && (halfHeight / scale) > targetHeight) { // &&
+ scale *= 2;
+ }
+ } else {
+ scale = Math.min(srcWidth / targetWidth, srcHeight / targetHeight); // min
+ }
+ break;
+ }
+
+ if (scale < 1) {
+ scale = 1;
+ }
+ scale = considerMaxTextureSize(srcWidth, srcHeight, scale, powerOf2Scale);
+
+ return scale;
+ }
+
+ private static int considerMaxTextureSize(int srcWidth, int srcHeight, int scale, boolean powerOf2) {
+ final int maxWidth = maxBitmapSize.getWidth();
+ final int maxHeight = maxBitmapSize.getHeight();
+ while ((srcWidth / scale) > maxWidth || (srcHeight / scale) > maxHeight) {
+ if (powerOf2) {
+ scale *= 2;
+ } else {
+ scale++;
+ }
+ }
+ return scale;
+ }
+
+ /**
+ * Computes minimal sample size for downscaling image so result image size won't exceed max acceptable OpenGL
+ * texture size.
+ * We can't create Bitmap in memory with size exceed max texture size (usually this is 2048x2048) so this method
+ * calculate minimal sample size which should be applied to image to fit into these limits.
+ *
+ * @param srcSize Original image size
+ * @return Minimal sample size
+ */
+ public static int computeMinImageSampleSize(ImageSize srcSize) {
+ final int srcWidth = srcSize.getWidth();
+ final int srcHeight = srcSize.getHeight();
+ final int targetWidth = maxBitmapSize.getWidth();
+ final int targetHeight = maxBitmapSize.getHeight();
+
+ final int widthScale = (int) Math.ceil((float) srcWidth / targetWidth);
+ final int heightScale = (int) Math.ceil((float) srcHeight / targetHeight);
+
+ return Math.max(widthScale, heightScale); // max
+ }
+
+ /**
+ * Computes scale of target size (targetSize) to source size (srcSize).
+ *
+ * Examples:
+ *
+ *
+ * srcSize(40x40), targetSize(10x10) -> scale = 0.25
+ *
+ * srcSize(10x10), targetSize(20x20), stretch = false -> scale = 1
+ * srcSize(10x10), targetSize(20x20), stretch = true -> scale = 2
+ *
+ * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> scale = 0.2
+ * srcSize(100x100), targetSize(20x40), viewScaleType = CROP -> scale = 0.4
+ *
+ *
+ * @param srcSize Source (image) size
+ * @param targetSize Target (view) size
+ * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view
+ * @param stretch Whether source size should be stretched if target size is larger than source size. If false
+ * then result scale value can't be greater than 1.
+ * @return Computed scale
+ */
+ public static float computeImageScale(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
+ boolean stretch) {
+ final int srcWidth = srcSize.getWidth();
+ final int srcHeight = srcSize.getHeight();
+ final int targetWidth = targetSize.getWidth();
+ final int targetHeight = targetSize.getHeight();
+
+ final float widthScale = (float) srcWidth / targetWidth;
+ final float heightScale = (float) srcHeight / targetHeight;
+
+ final int destWidth;
+ final int destHeight;
+ if ((viewScaleType == ViewScaleType.FIT_INSIDE && widthScale >= heightScale) || (viewScaleType == ViewScaleType.CROP && widthScale < heightScale)) {
+ destWidth = targetWidth;
+ destHeight = (int) (srcHeight / widthScale);
+ } else {
+ destWidth = (int) (srcWidth / heightScale);
+ destHeight = targetHeight;
+ }
+
+ float scale = 1;
+ if ((!stretch && destWidth < srcWidth && destHeight < srcHeight) || (stretch && destWidth != srcWidth && destHeight != srcHeight)) {
+ scale = (float) destWidth / srcWidth;
+ }
+
+ return scale;
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/IoUtils.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/IoUtils.java
new file mode 100644
index 000000000..fb900fb1f
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/IoUtils.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.utils;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Provides I/O operations
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public final class IoUtils {
+
+ /** {@value} */
+ public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 KB
+ /** {@value} */
+ public static final int DEFAULT_IMAGE_TOTAL_SIZE = 500 * 1024; // 500 Kb
+ /** {@value} */
+ public static final int CONTINUE_LOADING_PERCENTAGE = 75;
+
+ private IoUtils() {
+ }
+
+ /**
+ * Copies stream, fires progress events by listener, can be interrupted by listener. Uses buffer size =
+ * {@value #DEFAULT_BUFFER_SIZE} bytes.
+ *
+ * @param is Input stream
+ * @param os Output stream
+ * @param listener null-ok; Listener of copying progress and controller of copying interrupting
+ * @return true - if stream copied successfully; false - if copying was interrupted by listener
+ * @throws IOException
+ */
+ public static boolean copyStream(InputStream is, OutputStream os, CopyListener listener) throws IOException {
+ return copyStream(is, os, listener, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Copies stream, fires progress events by listener, can be interrupted by listener.
+ *
+ * @param is Input stream
+ * @param os Output stream
+ * @param listener null-ok; Listener of copying progress and controller of copying interrupting
+ * @param bufferSize Buffer size for copying, also represents a step for firing progress listener callback, i.e.
+ * progress event will be fired after every copied bufferSize bytes
+ * @return true - if stream copied successfully; false - if copying was interrupted by listener
+ * @throws IOException
+ */
+ public static boolean copyStream(InputStream is, OutputStream os, CopyListener listener, int bufferSize)
+ throws IOException {
+ int current = 0;
+ int total = is.available();
+ if (total <= 0) {
+ total = DEFAULT_IMAGE_TOTAL_SIZE;
+ }
+
+ final byte[] bytes = new byte[bufferSize];
+ int count;
+ if (shouldStopLoading(listener, current, total)) return false;
+ while ((count = is.read(bytes, 0, bufferSize)) != -1) {
+ os.write(bytes, 0, count);
+ current += count;
+ if (shouldStopLoading(listener, current, total)) return false;
+ }
+ os.flush();
+ return true;
+ }
+
+ private static boolean shouldStopLoading(CopyListener listener, int current, int total) {
+ if (listener != null) {
+ boolean shouldContinue = listener.onBytesCopied(current, total);
+ if (!shouldContinue) {
+ if (100 * current / total < CONTINUE_LOADING_PERCENTAGE) {
+ return true; // if loaded more than 75% then continue loading anyway
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Reads all data from stream and close it silently
+ *
+ * @param is Input stream
+ */
+ public static void readAndCloseStream(InputStream is) {
+ final byte[] bytes = new byte[DEFAULT_BUFFER_SIZE];
+ try {
+ while (is.read(bytes, 0, DEFAULT_BUFFER_SIZE) != -1) {
+ }
+ } catch (IOException e) {
+ // Do nothing
+ } finally {
+ closeSilently(is);
+ }
+ }
+
+ public static void closeSilently(Closeable closeable) {
+ try {
+ closeable.close();
+ } catch (Exception e) {
+ // Do nothing
+ }
+ }
+
+ /** Listener and controller for copy process */
+ public static interface CopyListener {
+ /**
+ * @param current Loaded bytes
+ * @param total Total bytes for loading
+ * @return true - if copying should be continued; false - if copying should be interrupted
+ */
+ boolean onBytesCopied(int current, int total);
+ }
+}
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/L.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/L.java
new file mode 100644
index 000000000..85d0e5033
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/L.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.utils;
+
+import android.util.Log;
+import com.nostra13.universalimageloader.core.ImageLoader;
+
+/**
+ * "Less-word" analog of Android {@link android.util.Log logger}
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.6.4
+ */
+public final class L {
+
+ private static final String LOG_FORMAT = "%1$s\n%2$s";
+ private static volatile boolean writeDebugLogs = false;
+ private static volatile boolean writeLogs = true;
+
+ private L() {
+ }
+
+ /**
+ * Enables logger (if {@link #disableLogging()} was called before)
+ *
+ * @deprecated Use {@link #writeLogs(boolean) writeLogs(true)} instead
+ */
+ @Deprecated
+ public static void enableLogging() {
+ writeLogs(true);
+ }
+
+ /**
+ * Disables logger, no logs will be passed to LogCat, all log methods will do nothing
+ *
+ * @deprecated Use {@link #writeLogs(boolean) writeLogs(false)} instead
+ */
+ @Deprecated
+ public static void disableLogging() {
+ writeLogs(false);
+ }
+
+ /**
+ * Enables/disables detail logging of {@link ImageLoader} work.
+ * Consider {@link com.nostra13.universalimageloader.utils.L#disableLogging()} to disable
+ * ImageLoader logging completely (even error logs)
+ * Debug logs are disabled by default.
+ */
+ public static void writeDebugLogs(boolean writeDebugLogs) {
+ L.writeDebugLogs = writeDebugLogs;
+ }
+
+ /** Enables/disables logging of {@link ImageLoader} completely (even error logs). */
+ public static void writeLogs(boolean writeLogs) {
+ L.writeLogs = writeLogs;
+ }
+
+ public static void d(String message, Object... args) {
+ if (writeDebugLogs) {
+ log(Log.DEBUG, null, message, args);
+ }
+ }
+
+ public static void i(String message, Object... args) {
+ log(Log.INFO, null, message, args);
+ }
+
+ public static void w(String message, Object... args) {
+ log(Log.WARN, null, message, args);
+ }
+
+ public static void e(Throwable ex) {
+ log(Log.ERROR, ex, null);
+ }
+
+ public static void e(String message, Object... args) {
+ log(Log.ERROR, null, message, args);
+ }
+
+ public static void e(Throwable ex, String message, Object... args) {
+ log(Log.ERROR, ex, message, args);
+ }
+
+ private static void log(int priority, Throwable ex, String message, Object... args) {
+ if (!writeLogs) return;
+ if (args.length > 0) {
+ message = String.format(message, args);
+ }
+
+ String log;
+ if (ex == null) {
+ log = message;
+ } else {
+ String logMessage = message == null ? ex.getMessage() : message;
+ String logBody = Log.getStackTraceString(ex);
+ log = String.format(LOG_FORMAT, logMessage, logBody);
+ }
+ Log.println(priority, ImageLoader.TAG, log);
+ }
+}
\ No newline at end of file
diff --git a/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/MemoryCacheUtils.java b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/MemoryCacheUtils.java
new file mode 100644
index 000000000..e7f93a59c
--- /dev/null
+++ b/extern/UniversalImageLoader/library/src/com/nostra13/universalimageloader/utils/MemoryCacheUtils.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright 2011-2013 Sergey Tarasevich
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package com.nostra13.universalimageloader.utils;
+
+import android.graphics.Bitmap;
+
+import com.nostra13.universalimageloader.cache.memory.MemoryCache;
+import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
+import com.nostra13.universalimageloader.core.assist.ImageSize;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Utility for generating of keys for memory cache, key comparing and other work with memory cache
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.6.3
+ */
+public final class MemoryCacheUtils {
+
+ private static final String URI_AND_SIZE_SEPARATOR = "_";
+ private static final String WIDTH_AND_HEIGHT_SEPARATOR = "x";
+
+ private MemoryCacheUtils() {
+ }
+
+ /**
+ * Generates key for memory cache for incoming image (URI + size).
+ * Pattern for cache key - [imageUri]_[width]x[height].
+ */
+ public static String generateKey(String imageUri, ImageSize targetSize) {
+ return new StringBuilder(imageUri).append(URI_AND_SIZE_SEPARATOR).append(targetSize.getWidth()).append(WIDTH_AND_HEIGHT_SEPARATOR).append(targetSize.getHeight()).toString();
+ }
+
+ public static Comparator
+ * Note: Memory cache can contain multiple sizes of the same image if only you didn't set
+ * {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory()
+ * denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration}
+ */
+ public static List
+ * Note: Memory cache can contain multiple sizes of the same image if only you didn't set
+ * {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory()
+ * denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration}
+ */
+ public static List
+ * Note: Memory cache can contain multiple sizes of the same image if only you didn't set
+ * {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory()
+ * denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration}
+ */
+ public static void removeFromCache(String imageUri, MemoryCache memoryCache) {
+ List
+ * NOTE: Can be null in some unpredictable cases (if SD card is unmounted and
+ * {@link android.content.Context#getCacheDir() Context.getCacheDir()} returns null).
+ */
+ public static File getCacheDirectory(Context context) {
+ return getCacheDirectory(context, true);
+ }
+
+ /**
+ * Returns application cache directory. Cache directory will be created on SD card
+ * ("/Android/data/[app_package_name]/cache") (if card is mounted and app has appropriate permission) or
+ * on device's file system depending incoming parameters.
+ *
+ * @param context Application context
+ * @param preferExternal Whether prefer external location for cache
+ * @return Cache {@link File directory}.
+ * NOTE: Can be null in some unpredictable cases (if SD card is unmounted and
+ * {@link android.content.Context#getCacheDir() Context.getCacheDir()} returns null).
+ */
+ public static File getCacheDirectory(Context context, boolean preferExternal) {
+ File appCacheDir = null;
+ String externalStorageState;
+ try {
+ externalStorageState = Environment.getExternalStorageState();
+ } catch (NullPointerException e) { // (sh)it happens (Issue #660)
+ externalStorageState = "";
+ }
+ if (preferExternal && MEDIA_MOUNTED.equals(externalStorageState) && hasExternalStoragePermission(context)) {
+ appCacheDir = getExternalCacheDir(context);
+ }
+ if (appCacheDir == null) {
+ appCacheDir = context.getCacheDir();
+ }
+ if (appCacheDir == null) {
+ String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache/";
+ L.w("Can't define system cache directory! '%s' will be used.", cacheDirPath);
+ appCacheDir = new File(cacheDirPath);
+ }
+ return appCacheDir;
+ }
+
+ /**
+ * Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be
+ * created on SD card ("/Android/data/[app_package_name]/cache/uil-images") if card is mounted and app has
+ * appropriate permission. Else - Android defines cache directory on device's file system.
+ *
+ * @param context Application context
+ * @return Cache {@link File directory}
+ */
+ public static File getIndividualCacheDirectory(Context context) {
+ File cacheDir = getCacheDirectory(context);
+ File individualCacheDir = new File(cacheDir, INDIVIDUAL_DIR_NAME);
+ if (!individualCacheDir.exists()) {
+ if (!individualCacheDir.mkdir()) {
+ individualCacheDir = cacheDir;
+ }
+ }
+ return individualCacheDir;
+ }
+
+ /**
+ * Returns specified application cache directory. Cache directory will be created on SD card by defined path if card
+ * is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system.
+ *
+ * @param context Application context
+ * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
+ * @return Cache {@link File directory}
+ */
+ public static File getOwnCacheDirectory(Context context, String cacheDir) {
+ File appCacheDir = null;
+ if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) {
+ appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
+ }
+ if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) {
+ appCacheDir = context.getCacheDir();
+ }
+ return appCacheDir;
+ }
+
+ private static File getExternalCacheDir(Context context) {
+ File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data");
+ File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache");
+ if (!appCacheDir.exists()) {
+ if (!appCacheDir.mkdirs()) {
+ L.w("Unable to create external cache directory");
+ return null;
+ }
+ try {
+ new File(appCacheDir, ".nomedia").createNewFile();
+ } catch (IOException e) {
+ L.i("Can't create \".nomedia\" file in application external cache directory");
+ }
+ }
+ return appCacheDir;
+ }
+
+ private static boolean hasExternalStoragePermission(Context context) {
+ int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION);
+ return perm == PackageManager.PERMISSION_GRANTED;
+ }
+}