aboutsummaryrefslogtreecommitdiff
path: root/integration
diff options
context:
space:
mode:
authorSam Judd <judds@google.com>2014-06-28 11:05:23 -0700
committerSam Judd <judds@google.com>2014-06-28 21:02:36 -0700
commit700284ec94a2c78a6157a31dea1d70d0de51b9f1 (patch)
treebd32953a7c8387d7eb14b860a8aa0001b960df99 /integration
parent0181060403cb0a7a2b344e4b2f55667194f09732 (diff)
downloadglide-700284ec94a2c78a6157a31dea1d70d0de51b9f1.tar.gz
Make Volley an optional dependency
Diffstat (limited to 'integration')
-rw-r--r--integration/pom.xml19
-rw-r--r--integration/volley/AndroidManifest.xml8
-rw-r--r--integration/volley/build.gradle33
-rw-r--r--integration/volley/gradle.properties6
-rw-r--r--integration/volley/lint.xml4
-rw-r--r--integration/volley/pom.xml30
-rw-r--r--integration/volley/project.properties6
-rw-r--r--integration/volley/src/main/java/com/bumptech/glide/volley/RequestQueueWrapper.java59
-rw-r--r--integration/volley/src/main/java/com/bumptech/glide/volley/VolleyDiskCacheWrapper.java337
-rw-r--r--integration/volley/src/main/java/com/bumptech/glide/volley/VolleyRequestFuture.java165
-rw-r--r--integration/volley/src/main/java/com/bumptech/glide/volley/VolleyStreamFetcher.java102
-rw-r--r--integration/volley/src/main/java/com/bumptech/glide/volley/VolleyUrlLoader.java64
12 files changed, 833 insertions, 0 deletions
diff --git a/integration/pom.xml b/integration/pom.xml
new file mode 100644
index 00000000..66241c2e
--- /dev/null
+++ b/integration/pom.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.github.bumptech.glide</groupId>
+ <artifactId>glide-parent</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>glide-integration</artifactId>
+ <packaging>pom</packaging>
+ <name>Glide Integration</name>
+
+ <modules>
+ <module>volley</module>
+ </modules>
+</project>
diff --git a/integration/volley/AndroidManifest.xml b/integration/volley/AndroidManifest.xml
new file mode 100644
index 00000000..b644aab7
--- /dev/null
+++ b/integration/volley/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.bumptech.glide.volley"
+ android:versionCode="1"
+ android:versionName="1.0.0" >
+ <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="19" />
+ <application />
+</manifest>
diff --git a/integration/volley/build.gradle b/integration/volley/build.gradle
new file mode 100644
index 00000000..c8ab369f
--- /dev/null
+++ b/integration/volley/build.gradle
@@ -0,0 +1,33 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.11.2'
+ }
+}
+
+apply plugin: 'android-library'
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile project(':library')
+
+ compile 'com.mcxiaoke.volley:library:1.0.+'
+}
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion = '19.1.0'
+ sourceSets {
+ main {
+ java.srcDirs = ['src/main/java']
+ manifest.srcFile 'AndroidManifest.xml'
+ }
+ }
+}
+
+apply from: "https://raw.githubusercontent.com/mcxiaoke/gradle-mvn-push/master/gradle-mvn-push.gradle"
diff --git a/integration/volley/gradle.properties b/integration/volley/gradle.properties
new file mode 100644
index 00000000..0f9e81b5
--- /dev/null
+++ b/integration/volley/gradle.properties
@@ -0,0 +1,6 @@
+POM_NAME=Glide Volley Integration
+POM_ARTIFACT_ID=glide-volley-integration
+POM_PACKAGING=aar
+POM_LICENCE_NAME=The Apache Software License, Version 2.0
+POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
+POM_LICENCE_DIST=repo
diff --git a/integration/volley/lint.xml b/integration/volley/lint.xml
new file mode 100644
index 00000000..d9d6c9ff
--- /dev/null
+++ b/integration/volley/lint.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lint>
+ <issue id="AllowBackup" severity="ignore" />
+</lint>
diff --git a/integration/volley/pom.xml b/integration/volley/pom.xml
new file mode 100644
index 00000000..f1bf8725
--- /dev/null
+++ b/integration/volley/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.github.bumptech.glide</groupId>
+ <artifactId>glide-integration</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>glide-volley-integration</artifactId>
+ <packaging>aar</packaging>
+ <name>Glide Volley Integration</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.github.bumptech.glide</groupId>
+ <artifactId>library</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ <type>aar</type>
+ </dependency>
+ <dependency>
+ <groupId>com.mcxiaoke.volley</groupId>
+ <artifactId>library</artifactId>
+ <version>1.0.4</version>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/integration/volley/project.properties b/integration/volley/project.properties
new file mode 100644
index 00000000..d226f51c
--- /dev/null
+++ b/integration/volley/project.properties
@@ -0,0 +1,6 @@
+target=android-19
+
+# https://code.google.com/p/android/issues/detail?id=40487
+renderscript.opt.level=O0
+
+android.library=true
diff --git a/integration/volley/src/main/java/com/bumptech/glide/volley/RequestQueueWrapper.java b/integration/volley/src/main/java/com/bumptech/glide/volley/RequestQueueWrapper.java
new file mode 100644
index 00000000..b31f44b1
--- /dev/null
+++ b/integration/volley/src/main/java/com/bumptech/glide/volley/RequestQueueWrapper.java
@@ -0,0 +1,59 @@
+package com.bumptech.glide.volley;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.net.http.AndroidHttpClient;
+import android.os.Build;
+import com.android.volley.Cache;
+import com.android.volley.Network;
+import com.android.volley.RequestQueue;
+import com.android.volley.toolbox.BasicNetwork;
+import com.android.volley.toolbox.HttpClientStack;
+import com.android.volley.toolbox.HttpStack;
+import com.android.volley.toolbox.HurlStack;
+import com.android.volley.toolbox.NoCache;
+import com.android.volley.toolbox.Volley;
+import com.bumptech.glide.load.engine.cache.DiskCache;
+
+import static android.content.pm.PackageManager.NameNotFoundException;
+
+/**
+ * A clone of the {@link Volley#newRequestQueue(Context)} allowing the user to set a disk cache and defaulting to
+ * no disk cache.
+ */
+public class RequestQueueWrapper {
+
+ public static RequestQueue getRequestQueue(Context context) {
+ return getRequestQueue(context, new NoCache());
+ }
+
+ public static RequestQueue getRequestQueue(Context context, DiskCache diskCache) {
+ return getRequestQueue(context, new VolleyDiskCacheWrapper(diskCache));
+ }
+
+ public static RequestQueue getRequestQueue(Context context, Cache diskCache) {
+ String userAgent = "volley/0";
+ try {
+ String packageName = context.getPackageName();
+ PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
+ userAgent = packageName + "/" + info.versionCode;
+ } catch (NameNotFoundException e) {
+ }
+
+ final HttpStack stack;
+ if (Build.VERSION.SDK_INT >= 9) {
+ stack = new HurlStack();
+ } else {
+ // Prior to Gingerbread, HttpUrlConnection was unreliable.
+ // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
+ stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
+ }
+
+ Network network = new BasicNetwork(stack);
+
+
+ RequestQueue queue = new RequestQueue(diskCache, network);
+ queue.start();
+ return queue;
+ }
+}
diff --git a/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyDiskCacheWrapper.java b/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyDiskCacheWrapper.java
new file mode 100644
index 00000000..6bc3dac2
--- /dev/null
+++ b/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyDiskCacheWrapper.java
@@ -0,0 +1,337 @@
+package com.bumptech.glide.volley;
+
+import android.util.Log;
+import com.android.volley.Cache;
+import com.android.volley.toolbox.ByteArrayPool;
+import com.android.volley.toolbox.DiskBasedCache;
+import com.android.volley.toolbox.PoolingByteArrayOutputStream;
+import com.bumptech.glide.load.engine.cache.StringKey;
+import com.bumptech.glide.load.engine.cache.DiskCache;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Closely based on {@link DiskBasedCache}.
+ */
+public class VolleyDiskCacheWrapper implements Cache {
+ private static final String TAG = "VolleyDiskCacheWrapper";
+ /** Magic number for current version of cache file format. */
+ private static final int CACHE_MAGIC = 0x20120504;
+ // 2 mb.
+ private static final int BYTE_POOL_SIZE = 2 * 1024 * 1024;
+ // 8 kb.
+ private static final int DEFAULT_BYTE_ARRAY_SIZE = 8 * 1024;
+
+ private final DiskCache diskCache;
+ private final ByteArrayPool byteArrayPool;
+
+ public VolleyDiskCacheWrapper(DiskCache diskCache) {
+ this.diskCache = diskCache;
+ this.byteArrayPool = new ByteArrayPool(BYTE_POOL_SIZE);
+ }
+
+ @Override
+ public Entry get(String key) {
+ InputStream result = diskCache.get(new StringKey(key));
+ if (result == null) {
+ return null;
+ }
+ try {
+ CacheHeader header = readHeader(result);
+ byte[] data = streamToBytes(result);
+ return header.toCacheEntry(data);
+ } catch (IOException e) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ e.printStackTrace();
+ }
+ diskCache.delete(new StringKey(key));
+ } finally {
+ try {
+ result.close();
+ } catch (IOException e) { }
+ }
+ return null;
+ }
+
+ @Override
+ public void put(final String key, final Entry entry) {
+ diskCache.put(new StringKey(key), new DiskCache.Writer() {
+ @Override
+ public boolean write(OutputStream os) {
+ CacheHeader header = new CacheHeader(key, entry);
+ boolean success = header.writeHeader(os);
+ if (success) {
+ try {
+ os.write(entry.data);
+ } catch (IOException e) {
+ success = false;
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Unable to write data", e);
+ }
+ }
+ }
+ return success;
+ }
+ });
+ }
+
+ @Override
+ public void initialize() { }
+
+ @Override
+ public void invalidate(String key, boolean fullExpire) {
+ Entry entry = get(key);
+ if (entry != null) {
+ entry.softTtl = 0;
+ if (fullExpire) {
+ entry.ttl = 0;
+ }
+ put(key, entry);
+ }
+ }
+
+ @Override
+ public void remove(String key) {
+ diskCache.delete(new StringKey(key));
+ }
+
+ @Override
+ public void clear() { }
+
+ /**
+ * Reads the header off of an InputStream and returns a CacheHeader object.
+ * @param is The InputStream to read from.
+ * @throws IOException
+ */
+ public CacheHeader readHeader(InputStream is) throws IOException {
+ CacheHeader entry = new CacheHeader();
+ int magic = readInt(is);
+ if (magic != CACHE_MAGIC) {
+ // don't bother deleting, it'll get pruned eventually
+ throw new IOException();
+ }
+ entry.key = readString(is);
+ entry.etag = readString(is);
+ if (entry.etag.equals("")) {
+ entry.etag = null;
+ }
+ entry.serverDate = readLong(is);
+ entry.ttl = readLong(is);
+ entry.softTtl = readLong(is);
+ entry.responseHeaders = readStringStringMap(is);
+ return entry;
+ }
+
+ /**
+ * Handles holding onto the cache headers for an entry.
+ */
+ // Visible for testing.
+ class CacheHeader {
+ /** The size of the data identified by this CacheHeader. (This is not
+ * serialized to disk. */
+ public long size;
+
+ /** The key that identifies the cache entry. */
+ public String key;
+
+ /** ETag for cache coherence. */
+ public String etag;
+
+ /** Date of this response as reported by the server. */
+ public long serverDate;
+
+ /** TTL for this record. */
+ public long ttl;
+
+ /** Soft TTL for this record. */
+ public long softTtl;
+
+ /** Headers from the response resulting in this cache entry. */
+ public Map<String, String> responseHeaders;
+
+ private CacheHeader() { }
+
+ /**
+ * Instantiates a new CacheHeader object
+ * @param key The key that identifies the cache entry
+ * @param entry The cache entry.
+ */
+ public CacheHeader(String key, Entry entry) {
+ this.key = key;
+ this.size = entry.data.length;
+ this.etag = entry.etag;
+ this.serverDate = entry.serverDate;
+ this.ttl = entry.ttl;
+ this.softTtl = entry.softTtl;
+ this.responseHeaders = entry.responseHeaders;
+ }
+
+ /**
+ * Creates a cache entry for the specified data.
+ */
+ public Entry toCacheEntry(byte[] data) {
+ Entry e = new Entry();
+ e.data = data;
+ e.etag = etag;
+ e.serverDate = serverDate;
+ e.ttl = ttl;
+ e.softTtl = softTtl;
+ e.responseHeaders = responseHeaders;
+ return e;
+ }
+
+ /**
+ * Writes the contents of this CacheHeader to the specified OutputStream.
+ */
+ public boolean writeHeader(OutputStream os) {
+ try {
+ writeInt(os, CACHE_MAGIC);
+ writeString(os, key);
+ writeString(os, etag == null ? "" : etag);
+ writeLong(os, serverDate);
+ writeLong(os, ttl);
+ writeLong(os, softTtl);
+ writeStringStringMap(responseHeaders, os);
+ os.flush();
+ return true;
+ } catch (IOException e) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d("%s", e.toString());
+ }
+ return false;
+ }
+ }
+ }
+
+ /*
+ * Homebrewed simple serialization system used for reading and writing cache
+ * headers on disk. Once upon a time, this used the standard Java
+ * Object{Input,Output}Stream, but the default implementation relies heavily
+ * on reflection (even for standard types) and generates a ton of garbage.
+ */
+
+ /**
+ * Simple wrapper around {@link InputStream#read()} that throws EOFException
+ * instead of returning -1.
+ */
+ private static int read(InputStream is) throws IOException {
+ int b = is.read();
+ if (b == -1) {
+ throw new EOFException();
+ }
+ return b;
+ }
+
+ static void writeInt(OutputStream os, int n) throws IOException {
+ os.write((n >> 0) & 0xff);
+ os.write((n >> 8) & 0xff);
+ os.write((n >> 16) & 0xff);
+ os.write((n >> 24) & 0xff);
+ }
+
+ static int readInt(InputStream is) throws IOException {
+ int n = 0;
+ n |= (read(is) << 0);
+ n |= (read(is) << 8);
+ n |= (read(is) << 16);
+ n |= (read(is) << 24);
+ return n;
+ }
+
+ static void writeLong(OutputStream os, long n) throws IOException {
+ os.write((byte)(n >>> 0));
+ os.write((byte)(n >>> 8));
+ os.write((byte)(n >>> 16));
+ os.write((byte)(n >>> 24));
+ os.write((byte)(n >>> 32));
+ os.write((byte)(n >>> 40));
+ os.write((byte)(n >>> 48));
+ os.write((byte)(n >>> 56));
+ }
+
+ static long readLong(InputStream is) throws IOException {
+ long n = 0;
+ n |= ((read(is) & 0xFFL) << 0);
+ n |= ((read(is) & 0xFFL) << 8);
+ n |= ((read(is) & 0xFFL) << 16);
+ n |= ((read(is) & 0xFFL) << 24);
+ n |= ((read(is) & 0xFFL) << 32);
+ n |= ((read(is) & 0xFFL) << 40);
+ n |= ((read(is) & 0xFFL) << 48);
+ n |= ((read(is) & 0xFFL) << 56);
+ return n;
+ }
+
+ static void writeString(OutputStream os, String s) throws IOException {
+ byte[] b = s.getBytes("UTF-8");
+ writeLong(os, b.length);
+ os.write(b, 0, b.length);
+ }
+
+ String readString(InputStream is) throws IOException {
+ int n = (int) readLong(is);
+ byte[] b = streamToBytes(is, n, byteArrayPool.getBuf(n));
+ String result = new String(b, "UTF-8");
+ byteArrayPool.returnBuf(b);
+ return result;
+ }
+
+ static void writeStringStringMap(Map<String, String> map, OutputStream os) throws IOException {
+ if (map != null) {
+ writeInt(os, map.size());
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ writeString(os, entry.getKey());
+ writeString(os, entry.getValue());
+ }
+ } else {
+ writeInt(os, 0);
+ }
+ }
+
+ Map<String, String> readStringStringMap(InputStream is) throws IOException {
+ int size = readInt(is);
+ Map<String, String> result = (size == 0)
+ ? Collections.<String, String>emptyMap()
+ : new HashMap<String, String>(size);
+ for (int i = 0; i < size; i++) {
+ String key = readString(is).intern();
+ String value = readString(is).intern();
+ result.put(key, value);
+ }
+ return result;
+ }
+
+ /**
+ * Reads the contents of an InputStream into a byte[].
+ */
+ private static byte[] streamToBytes(InputStream in, int length, byte[] bytes) throws IOException {
+ int count;
+ int pos = 0;
+ while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) {
+ pos += count;
+ }
+ if (pos != length) {
+ throw new IOException("Expected " + length + " bytes, read " + pos + " bytes");
+ }
+ return bytes;
+ }
+
+ private byte[] streamToBytes(InputStream in) throws IOException {
+ PoolingByteArrayOutputStream outputStream = new PoolingByteArrayOutputStream(byteArrayPool);
+ byte[] bytes = byteArrayPool.getBuf(DEFAULT_BYTE_ARRAY_SIZE);
+ int pos = 0;
+ while ((in.read(bytes, pos, bytes.length - pos)) != -1) {
+ outputStream.write(bytes);
+ }
+ byteArrayPool.returnBuf(bytes);
+ byte[] result = outputStream.toByteArray();
+ outputStream.close();
+ return result;
+ }
+}
diff --git a/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyRequestFuture.java b/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyRequestFuture.java
new file mode 100644
index 00000000..460364bb
--- /dev/null
+++ b/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyRequestFuture.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.bumptech.glide.volley;
+
+import com.android.volley.Request;
+import com.android.volley.Response;
+import com.android.volley.VolleyError;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * TODO: contribute cancel modifications to volley and remove this class.
+ *
+ * A Future that represents a Volley request.
+ *
+ * Used by providing as your response and error listeners. For example:
+ * <pre>
+ * RequestFuture&lt;JSONObject&gt; future = RequestFuture.newFuture();
+ * MyRequest request = new MyRequest(URL, future, future);
+ *
+ * // If you want to be able to cancel the request:
+ * future.setRequest(requestQueue.add(request));
+ *
+ * // Otherwise:
+ * requestQueue.add(request);
+ *
+ * try {
+ * JSONObject response = future.get();
+ * // do something with response
+ * } catch (InterruptedException e) {
+ * // handle the error
+ * } catch (ExecutionException e) {
+ * // handle the error
+ * }
+ * </pre>
+ *
+ * @param <T> The type of parsed response this future expects.
+ */
+public class VolleyRequestFuture<T> implements Future<T>, Response.Listener<T>,
+ Response.ErrorListener {
+ private Request<?> mRequest;
+ private boolean mResultReceived = false;
+ private T mResult;
+ private VolleyError mException;
+ private boolean mIsCancelled = false;
+
+ public static <E> VolleyRequestFuture<E> newFuture() {
+ return new VolleyRequestFuture<E>();
+ }
+
+ public VolleyRequestFuture() {}
+
+ public synchronized void setRequest(Request<?> request) {
+ mRequest = request;
+ if (mIsCancelled && mRequest != null) {
+ mRequest.cancel();
+ }
+ }
+
+ @Override
+ public synchronized boolean cancel(boolean mayInterruptIfRunning) {
+ if (isDone()) {
+ return false;
+ }
+ mIsCancelled = true;
+ if (mRequest != null) {
+ mRequest.cancel();
+ }
+ notifyAll();
+
+ return true;
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ try {
+ return doGet(null);
+ } catch (TimeoutException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public T get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return doGet(TimeUnit.MILLISECONDS.convert(timeout, unit));
+ }
+
+ private synchronized T doGet(Long timeoutMs)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ if (mException != null) {
+ throw new ExecutionException(mException);
+ }
+
+ if (mResultReceived) {
+ return mResult;
+ }
+
+ if (isCancelled()) {
+ throw new CancellationException();
+ }
+
+ if (timeoutMs == null) {
+ wait(0);
+ } else if (timeoutMs > 0) {
+ wait(timeoutMs);
+ }
+
+ if (mException != null) {
+ throw new ExecutionException(mException);
+ }
+
+ if (isCancelled()) {
+ throw new CancellationException();
+ }
+
+ if (!mResultReceived) {
+ throw new TimeoutException();
+ }
+
+ return mResult;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return mIsCancelled;
+ }
+
+ @Override
+ public synchronized boolean isDone() {
+ return mResultReceived || mException != null || isCancelled();
+ }
+
+ @Override
+ public synchronized void onResponse(T response) {
+ mResultReceived = true;
+ mResult = response;
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void onErrorResponse(VolleyError error) {
+ mException = error;
+ notifyAll();
+ }
+}
+
diff --git a/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyStreamFetcher.java b/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyStreamFetcher.java
new file mode 100644
index 00000000..76e2bbcc
--- /dev/null
+++ b/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyStreamFetcher.java
@@ -0,0 +1,102 @@
+package com.bumptech.glide.volley;
+
+import com.android.volley.NetworkResponse;
+import com.android.volley.Request;
+import com.android.volley.RequestQueue;
+import com.android.volley.Response;
+import com.android.volley.toolbox.HttpHeaderParser;
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.data.DataFetcher;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * A DataFetcher backed by volley for fetching images via http.
+ */
+public class VolleyStreamFetcher implements DataFetcher<InputStream> {
+ private final RequestQueue requestQueue;
+ private final String url;
+ private VolleyRequestFuture<InputStream> requestFuture;
+
+ @SuppressWarnings("unused")
+ public VolleyStreamFetcher(RequestQueue requestQueue, String url) {
+ this(requestQueue, url, null);
+ }
+
+ public VolleyStreamFetcher(RequestQueue requestQueue, String url, VolleyRequestFuture<InputStream> requestFuture) {
+ this.requestQueue = requestQueue;
+ this.url = url;
+ this.requestFuture = requestFuture;
+ if (requestFuture == null) {
+ this.requestFuture = VolleyRequestFuture.newFuture();
+ }
+ }
+
+ @Override
+ public InputStream loadData(Priority priority) throws Exception {
+ GlideRequest request = new GlideRequest(url, requestFuture, glideToVolleyPriority(priority));
+
+ requestFuture.setRequest(requestQueue.add(request));
+
+ return requestFuture.get();
+ }
+
+ @Override
+ public void cleanup() {
+ // Do nothing.
+ }
+
+ @Override
+ public String getId() {
+ return url;
+ }
+
+ @Override
+ public void cancel() {
+ VolleyRequestFuture<InputStream> localFuture = requestFuture;
+ if (localFuture != null) {
+ localFuture.cancel(true);
+ }
+ }
+
+ private static Request.Priority glideToVolleyPriority(Priority priority) {
+ switch (priority) {
+ case LOW:
+ return Request.Priority.LOW;
+ case HIGH:
+ return Request.Priority.HIGH;
+ case IMMEDIATE:
+ return Request.Priority.IMMEDIATE;
+ default:
+ return Request.Priority.NORMAL;
+
+ }
+ }
+
+ private static class GlideRequest extends Request<byte[]> {
+ private final VolleyRequestFuture<InputStream> future;
+ private Priority priority;
+
+ public GlideRequest(String url, VolleyRequestFuture<InputStream> future, Priority priority) {
+ super(Method.GET, url, future);
+ this.future = future;
+ this.priority = priority;
+ }
+
+ @Override
+ public Priority getPriority() {
+ return priority;
+ }
+
+ @Override
+ protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {
+ return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response));
+ }
+
+ @Override
+ protected void deliverResponse(byte[] response) {
+ future.onResponse(new ByteArrayInputStream(response));
+ }
+ }
+}
diff --git a/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyUrlLoader.java b/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyUrlLoader.java
new file mode 100644
index 00000000..43705a65
--- /dev/null
+++ b/integration/volley/src/main/java/com/bumptech/glide/volley/VolleyUrlLoader.java
@@ -0,0 +1,64 @@
+package com.bumptech.glide.volley;
+
+import android.content.Context;
+import com.android.volley.RequestQueue;
+import com.bumptech.glide.load.model.GenericLoaderFactory;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+import com.bumptech.glide.load.data.DataFetcher;
+
+import java.io.InputStream;
+
+/**
+ * A simple model loader for fetching images for a given url
+ */
+public class VolleyUrlLoader implements ModelLoader<GlideUrl, InputStream> {
+
+ public interface FutureFactory {
+ public VolleyRequestFuture<InputStream> build();
+ }
+
+ public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
+ private final FutureFactory futureFactory;
+ private RequestQueue requestQueue;
+
+ public Factory(RequestQueue requestQueue) {
+ this(requestQueue, new DefaultFutureFactory());
+ }
+
+ public Factory(RequestQueue requestQueue, FutureFactory futureFactory) {
+ this.requestQueue = requestQueue;
+ this.futureFactory = futureFactory;
+ }
+
+ @Override
+ public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
+ return new VolleyUrlLoader(requestQueue, futureFactory);
+ }
+
+ @Override
+ public void teardown() { }
+ }
+
+ private final RequestQueue requestQueue;
+ private final FutureFactory futureFactory;
+
+ public VolleyUrlLoader(RequestQueue requestQueue, FutureFactory futureFactory) {
+ this.requestQueue = requestQueue;
+ this.futureFactory = futureFactory;
+ }
+
+ @Override
+ public DataFetcher<InputStream> getResourceFetcher(GlideUrl url, int width, int height) {
+ return new VolleyStreamFetcher(requestQueue, url.toString(), futureFactory.build());
+ }
+
+ private static class DefaultFutureFactory implements FutureFactory {
+ @Override
+ public VolleyRequestFuture<InputStream> build() {
+ return VolleyRequestFuture.newFuture();
+ }
+ }
+
+}