diff --git a/gradle.properties b/gradle.properties index b601fcd72e..487a2710b0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -49,6 +49,7 @@ VERSION_NAME=4.15.0-SNAPSHOT ## SDK versioning COMPILE_SDK_VERSION=33 MIN_SDK_VERSION=14 +OK_HTTP_4_MIN_SDK_VERSION=21 TARGET_SDK_VERSION=32 ## AndroidX versions @@ -105,6 +106,7 @@ MOCKITO_ANDROID_VERSION=2.24.0 MOCKITO_VERSION=2.24.0 MOCKWEBSERVER_VERSION=3.0.0-RC1 OK_HTTP_VERSION=3.10.0 +OK_HTTP_4_VERSION=4.10.0 PMD_VERSION=6.0.0 ROBOLECTRIC_VERSION=4.8.1 TRUTH_VERSION=1.1.3 diff --git a/integration/okhttp4/build.gradle b/integration/okhttp4/build.gradle new file mode 100644 index 0000000000..3ead9875b0 --- /dev/null +++ b/integration/okhttp4/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.library' + +dependencies { + implementation project(':library') + annotationProcessor project(':annotation:compiler') + + api "com.squareup.okhttp3:okhttp:${OK_HTTP_4_VERSION}" + api "androidx.annotation:annotation:${ANDROID_X_ANNOTATION_VERSION}" +} + +android { + compileSdk COMPILE_SDK_VERSION as int + + defaultConfig { + minSdk OK_HTTP_4_MIN_SDK_VERSION as int + targetSdk TARGET_SDK_VERSION as int + + versionName VERSION_NAME as String + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +apply from: "${rootProject.projectDir}/scripts/upload.gradle" diff --git a/integration/okhttp4/gradle.properties b/integration/okhttp4/gradle.properties new file mode 100644 index 0000000000..bd37533a8f --- /dev/null +++ b/integration/okhttp4/gradle.properties @@ -0,0 +1,4 @@ +POM_NAME=Glide OkHttp 4.x Integration +POM_ARTIFACT_ID=okhttp4-integration +POM_PACKAGING=aar +POM_DESCRIPTION=An integration library to use OkHttp 4.x to fetch data over http/https in Glide diff --git a/integration/okhttp4/lint.xml b/integration/okhttp4/lint.xml new file mode 100644 index 0000000000..d2a05b5ae7 --- /dev/null +++ b/integration/okhttp4/lint.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/integration/okhttp4/src/main/AndroidManifest.xml b/integration/okhttp4/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..9ad5e952bf --- /dev/null +++ b/integration/okhttp4/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/integration/okhttp4/src/main/java/com/bumptech/glide/integration/okhttp3/OkHttpLibraryGlideModule.java b/integration/okhttp4/src/main/java/com/bumptech/glide/integration/okhttp3/OkHttpLibraryGlideModule.java new file mode 100644 index 0000000000..0d605ce9ce --- /dev/null +++ b/integration/okhttp4/src/main/java/com/bumptech/glide/integration/okhttp3/OkHttpLibraryGlideModule.java @@ -0,0 +1,26 @@ +package com.bumptech.glide.integration.okhttp3; + +import android.content.Context; +import androidx.annotation.NonNull; +import com.bumptech.glide.Glide; +import com.bumptech.glide.Registry; +import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.module.AppGlideModule; +import com.bumptech.glide.module.LibraryGlideModule; +import java.io.InputStream; + +/** + * Registers OkHttp related classes via Glide's annotation processor. + * + *

For Applications that depend on this library and include an {@link AppGlideModule} and Glide's + * annotation processor, this class will be automatically included. + */ +@GlideModule +public final class OkHttpLibraryGlideModule extends LibraryGlideModule { + @Override + public void registerComponents( + @NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { + registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory()); + } +} diff --git a/integration/okhttp4/src/main/java/com/bumptech/glide/integration/okhttp3/OkHttpStreamFetcher.java b/integration/okhttp4/src/main/java/com/bumptech/glide/integration/okhttp3/OkHttpStreamFetcher.java new file mode 100644 index 0000000000..ac9ca2bbd5 --- /dev/null +++ b/integration/okhttp4/src/main/java/com/bumptech/glide/integration/okhttp3/OkHttpStreamFetcher.java @@ -0,0 +1,109 @@ +package com.bumptech.glide.integration.okhttp3; + +import android.util.Log; +import androidx.annotation.NonNull; +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.HttpException; +import com.bumptech.glide.load.data.DataFetcher; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.util.ContentLengthInputStream; +import com.bumptech.glide.util.Preconditions; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import okhttp3.Call; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** Fetches an {@link InputStream} using the okhttp library. */ +public class OkHttpStreamFetcher implements DataFetcher, okhttp3.Callback { + private static final String TAG = "OkHttpFetcher"; + private final Call.Factory client; + private final GlideUrl url; + private InputStream stream; + private ResponseBody responseBody; + private DataCallback callback; + // call may be accessed on the main thread while the object is in use on other threads. All other + // accesses to variables may occur on different threads, but only one at a time. + private volatile Call call; + + // Public API. + @SuppressWarnings("WeakerAccess") + public OkHttpStreamFetcher(Call.Factory client, GlideUrl url) { + this.client = client; + this.url = url; + } + + @Override + public void loadData( + @NonNull Priority priority, @NonNull final DataCallback callback) { + Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl()); + for (Map.Entry headerEntry : url.getHeaders().entrySet()) { + String key = headerEntry.getKey(); + requestBuilder.addHeader(key, headerEntry.getValue()); + } + Request request = requestBuilder.build(); + this.callback = callback; + + call = client.newCall(request); + call.enqueue(this); + } + + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "OkHttp failed to obtain result", e); + } + + callback.onLoadFailed(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + responseBody = response.body(); + if (response.isSuccessful()) { + long contentLength = Preconditions.checkNotNull(responseBody).contentLength(); + stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength); + callback.onDataReady(stream); + } else { + callback.onLoadFailed(new HttpException(response.message(), response.code())); + } + } + + @Override + public void cleanup() { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + // Ignored + } + if (responseBody != null) { + responseBody.close(); + } + callback = null; + } + + @Override + public void cancel() { + Call local = call; + if (local != null) { + local.cancel(); + } + } + + @NonNull + @Override + public Class getDataClass() { + return InputStream.class; + } + + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.REMOTE; + } +} diff --git a/integration/okhttp4/src/main/java/com/bumptech/glide/integration/okhttp3/OkHttpUrlLoader.java b/integration/okhttp4/src/main/java/com/bumptech/glide/integration/okhttp3/OkHttpUrlLoader.java new file mode 100644 index 0000000000..6eb9823779 --- /dev/null +++ b/integration/okhttp4/src/main/java/com/bumptech/glide/integration/okhttp3/OkHttpUrlLoader.java @@ -0,0 +1,78 @@ +package com.bumptech.glide.integration.okhttp3; + +import androidx.annotation.NonNull; +import com.bumptech.glide.load.Options; +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.model.MultiModelLoaderFactory; +import java.io.InputStream; +import okhttp3.Call; +import okhttp3.OkHttpClient; + +/** A simple model loader for fetching media over http/https using OkHttp. */ +public class OkHttpUrlLoader implements ModelLoader { + + private final Call.Factory client; + + // Public API. + @SuppressWarnings("WeakerAccess") + public OkHttpUrlLoader(@NonNull Call.Factory client) { + this.client = client; + } + + @Override + public boolean handles(@NonNull GlideUrl url) { + return true; + } + + @Override + public LoadData buildLoadData( + @NonNull GlideUrl model, int width, int height, @NonNull Options options) { + return new LoadData<>(model, new OkHttpStreamFetcher(client, model)); + } + + /** The default factory for {@link OkHttpUrlLoader}s. */ + // Public API. + @SuppressWarnings("WeakerAccess") + public static class Factory implements ModelLoaderFactory { + private static volatile Call.Factory internalClient; + private final Call.Factory client; + + private static Call.Factory getInternalClient() { + if (internalClient == null) { + synchronized (Factory.class) { + if (internalClient == null) { + internalClient = new OkHttpClient(); + } + } + } + return internalClient; + } + + /** Constructor for a new Factory that runs requests using a static singleton client. */ + public Factory() { + this(getInternalClient()); + } + + /** + * Constructor for a new Factory that runs requests using given client. + * + * @param client this is typically an instance of {@code OkHttpClient}. + */ + public Factory(@NonNull Call.Factory client) { + this.client = client; + } + + @NonNull + @Override + public ModelLoader build(MultiModelLoaderFactory multiFactory) { + return new OkHttpUrlLoader(client); + } + + @Override + public void teardown() { + // Do nothing, this instance doesn't own the client. + } + } +} diff --git a/settings.gradle b/settings.gradle index 08a0115371..decc3f1ce3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,7 @@ include ':integration:gifencoder' include ':integration:ktx' include ':integration:okhttp' include ':integration:okhttp3' +include ':integration:okhttp4' include ':integration:recyclerview' include ':integration:volley' include ':testutil'