blob: c9ae2616cb7f1880410a9cdd03183d68fa66857c [file] [log] [blame]
package com.airbnb.lottie.network;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieCompositionFactory;
import com.airbnb.lottie.LottieResult;
import com.airbnb.lottie.utils.Logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipInputStream;
public class NetworkFetcher {
@NonNull
private final NetworkCache networkCache;
@NonNull
private final LottieNetworkFetcher fetcher;
public NetworkFetcher(@NonNull NetworkCache networkCache, @NonNull LottieNetworkFetcher fetcher) {
this.networkCache = networkCache;
this.fetcher = fetcher;
}
@NonNull
@WorkerThread
public LottieResult<LottieComposition> fetchSync(@NonNull String url, @Nullable String cacheKey) {
LottieComposition result = fetchFromCache(url, cacheKey);
if (result != null) {
return new LottieResult<>(result);
}
Logger.debug("Animation for " + url + " not found in cache. Fetching from network.");
return fetchFromNetwork(url, cacheKey);
}
@Nullable
@WorkerThread
private LottieComposition fetchFromCache(@NonNull String url, @Nullable String cacheKey) {
if (cacheKey == null) {
return null;
}
Pair<FileExtension, InputStream> cacheResult = networkCache.fetch(url);
if (cacheResult == null) {
return null;
}
FileExtension extension = cacheResult.first;
InputStream inputStream = cacheResult.second;
LottieResult<LottieComposition> result;
if (extension == FileExtension.ZIP) {
result = LottieCompositionFactory.fromZipStreamSync(new ZipInputStream(inputStream), url);
} else {
result = LottieCompositionFactory.fromJsonInputStreamSync(inputStream, url);
}
if (result.getValue() != null) {
return result.getValue();
}
return null;
}
@NonNull
@WorkerThread
private LottieResult<LottieComposition> fetchFromNetwork(@NonNull String url, @Nullable String cacheKey) {
Logger.debug("Fetching " + url);
LottieFetchResult fetchResult = null;
try {
fetchResult = fetcher.fetchSync(url);
if (fetchResult.isSuccessful()) {
InputStream inputStream = fetchResult.bodyByteStream();
String contentType = fetchResult.contentType();
LottieResult<LottieComposition> result = fromInputStream(url, inputStream, contentType, cacheKey);
Logger.debug("Completed fetch from network. Success: " + (result.getValue() != null));
return result;
} else {
return new LottieResult<>(new IllegalArgumentException(fetchResult.error()));
}
} catch (Exception e) {
return new LottieResult<>(e);
} finally {
if (fetchResult != null) {
try {
fetchResult.close();
} catch (IOException e) {
Logger.warning("LottieFetchResult close failed ", e);
}
}
}
}
@NonNull
private LottieResult<LottieComposition> fromInputStream(@NonNull String url, @NonNull InputStream inputStream, @Nullable String contentType,
@Nullable String cacheKey) throws IOException {
FileExtension extension;
LottieResult<LottieComposition> result;
if (contentType == null) {
// Assume JSON for best effort parsing. If it fails, it will just deliver the parse exception
// in the result which is more useful than failing here.
contentType = "application/json";
}
if (contentType.contains("application/zip") ||
contentType.contains("application/x-zip") ||
contentType.contains("application/x-zip-compressed") ||
url.split("\\?")[0].endsWith(".lottie")) {
Logger.debug("Handling zip response.");
extension = FileExtension.ZIP;
result = fromZipStream(url, inputStream, cacheKey);
} else {
Logger.debug("Received json response.");
extension = FileExtension.JSON;
result = fromJsonStream(url, inputStream, cacheKey);
}
if (cacheKey != null && result.getValue() != null) {
networkCache.renameTempFile(url, extension);
}
return result;
}
@NonNull
private LottieResult<LottieComposition> fromZipStream(@NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)
throws IOException {
if (cacheKey == null) {
return LottieCompositionFactory.fromZipStreamSync(new ZipInputStream(inputStream), null);
}
File file = networkCache.writeTempCacheFile(url, inputStream, FileExtension.ZIP);
return LottieCompositionFactory.fromZipStreamSync(new ZipInputStream(new FileInputStream(file)), url);
}
@NonNull
private LottieResult<LottieComposition> fromJsonStream(@NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)
throws IOException {
if (cacheKey == null) {
return LottieCompositionFactory.fromJsonInputStreamSync(inputStream, null);
}
File file = networkCache.writeTempCacheFile(url, inputStream, FileExtension.JSON);
return LottieCompositionFactory.fromJsonInputStreamSync(new FileInputStream(file.getAbsolutePath()), url);
}
}