Skip to content

Commit

Permalink
Added LottieConfig with NetworkFetcher and NetworkCache customization (
Browse files Browse the repository at this point in the history
  • Loading branch information
egroden authored Oct 19, 2020
1 parent ae030bb commit 81b4e78
Show file tree
Hide file tree
Showing 11 changed files with 403 additions and 117 deletions.
57 changes: 57 additions & 0 deletions lottie/src/main/java/com/airbnb/lottie/L.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
package com.airbnb.lottie;

import android.content.Context;

import com.airbnb.lottie.network.LottieNetworkCacheProvider;
import com.airbnb.lottie.network.DefaultLottieNetworkFetcher;
import com.airbnb.lottie.network.LottieNetworkFetcher;
import com.airbnb.lottie.network.NetworkCache;
import com.airbnb.lottie.network.NetworkFetcher;

import java.io.File;

import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.core.os.TraceCompat;

Expand All @@ -16,6 +27,12 @@ public class L {
private static int traceDepth = 0;
private static int depthPastMaxDepth = 0;

private static LottieNetworkFetcher fetcher;
private static LottieNetworkCacheProvider cacheProvider;

private static volatile NetworkFetcher networkFetcher;
private static volatile NetworkCache networkCache;

public static void setTraceEnabled(boolean enabled) {
if (traceEnabled == enabled) {
return;
Expand Down Expand Up @@ -60,4 +77,44 @@ public static float endSection(String section) {
TraceCompat.endSection();
return (System.nanoTime() - startTimeNs[traceDepth]) / 1000000f;
}

public static void setFetcher(LottieNetworkFetcher customFetcher) {
fetcher = customFetcher;
}

public static void setCacheProvider(LottieNetworkCacheProvider customProvider) {
cacheProvider = customProvider;
}

@NonNull
public static NetworkFetcher networkFetcher(@NonNull Context context) {
NetworkFetcher local = networkFetcher;
if (local == null) {
synchronized (NetworkFetcher.class) {
local = networkFetcher;
if (local == null) {
networkFetcher = local = new NetworkFetcher(networkCache(context), fetcher != null ? fetcher : new DefaultLottieNetworkFetcher());
}
}
}
return local;
}

@NonNull
public static NetworkCache networkCache(@NonNull final Context context) {
NetworkCache local = networkCache;
if (local == null) {
synchronized (NetworkCache.class) {
local = networkCache;
if (local == null) {
networkCache = local = new NetworkCache(cacheProvider != null ? cacheProvider : new LottieNetworkCacheProvider() {
@Override @NonNull public File getCacheDir() {
return new File(context.getCacheDir(), "lottie_network_cache");
}
});
}
}
}
return local;
}
}
17 changes: 17 additions & 0 deletions lottie/src/main/java/com/airbnb/lottie/Lottie.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.airbnb.lottie;

import androidx.annotation.NonNull;

/**
* Class for initializing the library with custom config
*/
public class Lottie {

private Lottie() {
}

public static void initialize(@NonNull final LottieConfig lottieConfig) {
L.setFetcher(lottieConfig.networkFetcher);
L.setCacheProvider(lottieConfig.cacheProvider);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;

import com.airbnb.lottie.model.LottieCompositionCache;
import com.airbnb.lottie.network.NetworkCache;
import com.airbnb.lottie.network.NetworkFetcher;
import com.airbnb.lottie.parser.LottieCompositionMoshiParser;
import com.airbnb.lottie.parser.moshi.JsonReader;

import com.airbnb.lottie.utils.Utils;

import org.json.JSONObject;

import java.io.ByteArrayInputStream;
Expand Down Expand Up @@ -65,14 +63,14 @@ public static void setMaxCacheSize(int size) {
public static void clearCache(Context context) {
taskCache.clear();
LottieCompositionCache.getInstance().clear();
new NetworkCache(context).clear();
L.networkCache(context).clear();
}

/**
* Fetch an animation from an http url. Once it is downloaded once, Lottie will cache the file to disk for
* future use. Because of this, you may call `fromUrl` ahead of time to warm the cache if you think you
* might need an animation in the future.
*
* <p>
* To skip the cache, add null as a third parameter.
*/
public static LottieTask<LottieComposition> fromUrl(final Context context, final String url) {
Expand All @@ -88,7 +86,7 @@ public static LottieTask<LottieComposition> fromUrl(final Context context, final
return cache(cacheKey, new Callable<LottieResult<LottieComposition>>() {
@Override
public LottieResult<LottieComposition> call() {
return NetworkFetcher.fetchSync(context, url, cacheKey);
return L.networkFetcher(context).fetchSync(url, cacheKey);
}
});
}
Expand All @@ -111,14 +109,14 @@ public static LottieResult<LottieComposition> fromUrlSync(Context context, Strin
*/
@WorkerThread
public static LottieResult<LottieComposition> fromUrlSync(Context context, String url, @Nullable String cacheKey) {
return NetworkFetcher.fetchSync(context, url, cacheKey);
return L.networkFetcher(context).fetchSync(url, cacheKey);
}

/**
* Parse an animation from src/main/assets. It is recommended to use {@link #fromRawRes(Context, int)} instead.
* The asset file name will be used as a cache key so future usages won't have to parse the json again.
* However, if your animation has images, you may package the json and images as a single flattened zip file in assets.
*
* <p>
* To skip the cache, add null as a third parameter.
*
* @see #fromZipStream(ZipInputStream, String)
Expand All @@ -132,7 +130,7 @@ public static LottieTask<LottieComposition> fromAsset(Context context, final Str
* Parse an animation from src/main/assets. It is recommended to use {@link #fromRawRes(Context, int)} instead.
* The asset file name will be used as a cache key so future usages won't have to parse the json again.
* However, if your animation has images, you may package the json and images as a single flattened zip file in assets.
*
* <p>
* Pass null as the cache key to skip the cache.
*
* @see #fromZipStream(ZipInputStream, String)
Expand All @@ -152,22 +150,22 @@ public LottieResult<LottieComposition> call() {
* Parse an animation from src/main/assets. It is recommended to use {@link #fromRawRes(Context, int)} instead.
* The asset file name will be used as a cache key so future usages won't have to parse the json again.
* However, if your animation has images, you may package the json and images as a single flattened zip file in assets.
*
* <p>
* To skip the cache, add null as a third parameter.
*
* @see #fromZipStreamSync(ZipInputStream, String)
*/
@WorkerThread
public static LottieResult<LottieComposition> fromAssetSync(Context context, String fileName) {
String cacheKey = "asset_" + fileName;
return fromAssetSync(context, fileName, cacheKey);
String cacheKey = "asset_" + fileName;
return fromAssetSync(context, fileName, cacheKey);
}

/**
* Parse an animation from src/main/assets. It is recommended to use {@link #fromRawRes(Context, int)} instead.
* The asset file name will be used as a cache key so future usages won't have to parse the json again.
* However, if your animation has images, you may package the json and images as a single flattened zip file in assets.
*
* <p>
* Pass null as the cache key to skip the cache.
*
* @see #fromZipStreamSync(ZipInputStream, String)
Expand All @@ -191,7 +189,7 @@ public static LottieResult<LottieComposition> fromAssetSync(Context context, Str
* The resource id will be used as a cache key so future usages won't parse the json again.
* Note: to correctly load dark mode (-night) resources, make sure you pass Activity as a context (instead of e.g. the application context).
* The Activity won't be leaked.
*
* <p>
* To skip the cache, add null as a third parameter.
*/
public static LottieTask<LottieComposition> fromRawRes(Context context, @RawRes final int rawRes) {
Expand All @@ -204,7 +202,7 @@ public static LottieTask<LottieComposition> fromRawRes(Context context, @RawRes
* The resource id will be used as a cache key so future usages won't parse the json again.
* Note: to correctly load dark mode (-night) resources, make sure you pass Activity as a context (instead of e.g. the application context).
* The Activity won't be leaked.
*
* <p>
* Pass null as the cache key to skip caching.
*/
public static LottieTask<LottieComposition> fromRawRes(Context context, @RawRes final int rawRes, @Nullable final String cacheKey) {
Expand All @@ -227,7 +225,7 @@ public LottieResult<LottieComposition> call() {
* The resource id will be used as a cache key so future usages won't parse the json again.
* Note: to correctly load dark mode (-night) resources, make sure you pass Activity as a context (instead of e.g. the application context).
* The Activity won't be leaked.
*
* <p>
* To skip the cache, add null as a third parameter.
*/
@WorkerThread
Expand All @@ -241,7 +239,7 @@ public static LottieResult<LottieComposition> fromRawResSync(Context context, @R
* The resource id will be used as a cache key so future usages won't parse the json again.
* Note: to correctly load dark mode (-night) resources, make sure you pass Activity as a context (instead of e.g. the application context).
* The Activity won't be leaked.
*
* <p>
* Pass null as the cache key to skip caching.
*/
@WorkerThread
Expand Down
69 changes: 69 additions & 0 deletions lottie/src/main/java/com/airbnb/lottie/LottieConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.airbnb.lottie;

import com.airbnb.lottie.network.LottieNetworkFetcher;
import com.airbnb.lottie.network.LottieNetworkCacheProvider;

import java.io.File;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
* Class for custom library configuration
*/
public class LottieConfig {

@Nullable final LottieNetworkFetcher networkFetcher;
@Nullable final LottieNetworkCacheProvider cacheProvider;

private LottieConfig(@Nullable LottieNetworkFetcher networkFetcher, @Nullable LottieNetworkCacheProvider cacheProvider) {
this.networkFetcher = networkFetcher;
this.cacheProvider = cacheProvider;
}

public static final class Builder {

@Nullable
private LottieNetworkFetcher networkFetcher;
@Nullable
private LottieNetworkCacheProvider cacheProvider;

@NonNull
public Builder setNetworkFetcher(@NonNull LottieNetworkFetcher fetcher) {
this.networkFetcher = fetcher;
return this;
}

@NonNull
public Builder setCacheDir(@NonNull final File file) {
cacheProvider = new LottieNetworkCacheProvider() {
@Override @NonNull public File getCacheDir() {
if (!file.isDirectory()) {
throw new IllegalArgumentException("cache file must be a directory");
}
return file;
}
};
return this;
}

@NonNull
public Builder setCacheProvider(@NonNull final LottieNetworkCacheProvider fileCacheProvider) {
cacheProvider = new LottieNetworkCacheProvider() {
@NonNull @Override public File getCacheDir() {
File file = fileCacheProvider.getCacheDir();
if (!file.isDirectory()) {
throw new IllegalArgumentException("cache file must be a directory");
}
return file;
}
};
return this;
}

@NonNull
public LottieConfig build() {
return new LottieConfig(networkFetcher, cacheProvider);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.airbnb.lottie.network;

import com.airbnb.lottie.utils.Logger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class DefaultLottieFetchResult implements LottieFetchResult {

@NonNull
private final HttpURLConnection connection;

public DefaultLottieFetchResult(@NonNull HttpURLConnection connection) {
this.connection = connection;
}

@Override public boolean isSuccessful() {
try {
return connection.getResponseCode() / 100 == 2;
} catch (IOException e) {
return false;
}
}

@NonNull @Override public InputStream bodyByteStream() throws IOException {
return connection.getInputStream();
}

@Nullable @Override public String contentType() {
return connection.getContentType();
}

@Nullable @Override public String error() {
try {
return isSuccessful() ? null :
"Unable to fetch " + connection.getURL() + ". Failed with " + connection.getResponseCode() + "\n" + getErrorFromConnection(connection);
} catch (IOException e) {
Logger.warning("get error failed ", e);
return e.getMessage();
}
}

@Override public void close() {
connection.disconnect();
}

private String getErrorFromConnection(HttpURLConnection connection) throws IOException {
BufferedReader r = new BufferedReader(new InputStreamReader(connection.getErrorStream()));
StringBuilder error = new StringBuilder();
String line;

try {
while ((line = r.readLine()) != null) {
error.append(line).append('\n');
}
} catch (Exception e) {
throw e;
} finally {
try {
r.close();
} catch (Exception e) {
// Do nothing.
}
}
return error.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.airbnb.lottie.network;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

import androidx.annotation.NonNull;

public class DefaultLottieNetworkFetcher implements LottieNetworkFetcher {

@Override
@NonNull
public LottieFetchResult fetchSync(@NonNull String url) throws IOException {
final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
connection.connect();
return new DefaultLottieFetchResult(connection);
}
}
Loading

0 comments on commit 81b4e78

Please sign in to comment.