• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.airbnb.lottie.network;
2 
3 import android.content.Context;
4 import android.util.Pair;
5 
6 import androidx.annotation.NonNull;
7 import androidx.annotation.Nullable;
8 import androidx.annotation.RestrictTo;
9 import androidx.annotation.WorkerThread;
10 
11 import com.airbnb.lottie.LottieComposition;
12 import com.airbnb.lottie.LottieCompositionFactory;
13 import com.airbnb.lottie.LottieResult;
14 import com.airbnb.lottie.utils.Logger;
15 
16 import java.io.File;
17 import java.io.FileInputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.util.zip.GZIPInputStream;
21 import java.util.zip.ZipInputStream;
22 
23 @RestrictTo(RestrictTo.Scope.LIBRARY)
24 public class NetworkFetcher {
25 
26   @Nullable
27   private final NetworkCache networkCache;
28   @NonNull
29   private final LottieNetworkFetcher fetcher;
30 
NetworkFetcher(@ullable NetworkCache networkCache, @NonNull LottieNetworkFetcher fetcher)31   public NetworkFetcher(@Nullable NetworkCache networkCache, @NonNull LottieNetworkFetcher fetcher) {
32     this.networkCache = networkCache;
33     this.fetcher = fetcher;
34   }
35 
36   @NonNull
37   @WorkerThread
fetchSync(Context context, @NonNull String url, @Nullable String cacheKey)38   public LottieResult<LottieComposition> fetchSync(Context context, @NonNull String url, @Nullable String cacheKey) {
39     LottieComposition result = fetchFromCache(context, url, cacheKey);
40     if (result != null) {
41       return new LottieResult<>(result);
42     }
43 
44     Logger.debug("Animation for " + url + " not found in cache. Fetching from network.");
45 
46     return fetchFromNetwork(context, url, cacheKey);
47   }
48 
49   @Nullable
50   @WorkerThread
fetchFromCache(Context context, @NonNull String url, @Nullable String cacheKey)51   private LottieComposition fetchFromCache(Context context, @NonNull String url, @Nullable String cacheKey) {
52     if (cacheKey == null || networkCache == null) {
53       return null;
54     }
55     Pair<FileExtension, InputStream> cacheResult = networkCache.fetch(url);
56     if (cacheResult == null) {
57       return null;
58     }
59 
60     FileExtension extension = cacheResult.first;
61     InputStream inputStream = cacheResult.second;
62     LottieResult<LottieComposition> result;
63     switch (extension) {
64       case ZIP:
65         result = LottieCompositionFactory.fromZipStreamSync(context, new ZipInputStream(inputStream), cacheKey);
66         break;
67       case GZIP:
68         try {
69           result = LottieCompositionFactory.fromJsonInputStreamSync(new GZIPInputStream(inputStream), cacheKey);
70         } catch (IOException e) {
71           result = new LottieResult<>(e);
72         }
73         break;
74       default:
75         result = LottieCompositionFactory.fromJsonInputStreamSync(inputStream, cacheKey);
76     }
77     if (result.getValue() != null) {
78       return result.getValue();
79     }
80     return null;
81   }
82 
83   @NonNull
84   @WorkerThread
fetchFromNetwork(Context context, @NonNull String url, @Nullable String cacheKey)85   private LottieResult<LottieComposition> fetchFromNetwork(Context context, @NonNull String url, @Nullable String cacheKey) {
86     Logger.debug("Fetching " + url);
87 
88     LottieFetchResult fetchResult = null;
89     try {
90       fetchResult = fetcher.fetchSync(url);
91       if (fetchResult.isSuccessful()) {
92         InputStream inputStream = fetchResult.bodyByteStream();
93         String contentType = fetchResult.contentType();
94         LottieResult<LottieComposition> result = fromInputStream(context, url, inputStream, contentType, cacheKey);
95         Logger.debug("Completed fetch from network. Success: " + (result.getValue() != null));
96         return result;
97       } else {
98         return new LottieResult<>(new IllegalArgumentException(fetchResult.error()));
99       }
100     } catch (Exception e) {
101       return new LottieResult<>(e);
102     } finally {
103       if (fetchResult != null) {
104         try {
105           fetchResult.close();
106         } catch (IOException e) {
107           Logger.warning("LottieFetchResult close failed ", e);
108         }
109       }
110     }
111   }
112 
113   @NonNull
fromInputStream(Context context, @NonNull String url, @NonNull InputStream inputStream, @Nullable String contentType, @Nullable String cacheKey)114   private LottieResult<LottieComposition> fromInputStream(Context context, @NonNull String url, @NonNull InputStream inputStream, @Nullable String contentType,
115       @Nullable String cacheKey) throws IOException {
116     FileExtension extension;
117     LottieResult<LottieComposition> result;
118     if (contentType == null) {
119       // Assume JSON for best effort parsing. If it fails, it will just deliver the parse exception
120       // in the result which is more useful than failing here.
121       contentType = "application/json";
122     }
123     if (contentType.contains("application/zip") ||
124         contentType.contains("application/x-zip") ||
125         contentType.contains("application/x-zip-compressed") ||
126         url.split("\\?")[0].endsWith(".lottie")) {
127       Logger.debug("Handling zip response.");
128       extension = FileExtension.ZIP;
129       result = fromZipStream(context, url, inputStream, cacheKey);
130     } else if (contentType.contains("application/gzip") ||
131         contentType.contains("application/x-gzip") ||
132         url.split("\\?")[0].endsWith(".tgs")) {
133       Logger.debug("Handling gzip response.");
134       extension = FileExtension.GZIP;
135       result = fromGzipStream(url, inputStream, cacheKey);
136     } else {
137       Logger.debug("Received json response.");
138       extension = FileExtension.JSON;
139       result = fromJsonStream(url, inputStream, cacheKey);
140     }
141 
142     if (cacheKey != null && result.getValue() != null && networkCache != null) {
143       networkCache.renameTempFile(url, extension);
144     }
145 
146     return result;
147   }
148 
149   @NonNull
fromZipStream(Context context, @NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)150   private LottieResult<LottieComposition> fromZipStream(Context context, @NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)
151       throws IOException {
152     if (cacheKey == null || networkCache == null) {
153       return LottieCompositionFactory.fromZipStreamSync(context, new ZipInputStream(inputStream), null);
154     }
155     File file = networkCache.writeTempCacheFile(url, inputStream, FileExtension.ZIP);
156     return LottieCompositionFactory.fromZipStreamSync(context, new ZipInputStream(new FileInputStream(file)), url);
157   }
158 
159   @NonNull
fromGzipStream(@onNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)160   private LottieResult<LottieComposition> fromGzipStream(@NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)
161       throws IOException {
162     if (cacheKey == null || networkCache == null) {
163       return LottieCompositionFactory.fromJsonInputStreamSync(new GZIPInputStream(inputStream), null);
164     }
165     File file = networkCache.writeTempCacheFile(url, inputStream, FileExtension.GZIP);
166     return LottieCompositionFactory.fromJsonInputStreamSync(new GZIPInputStream(new FileInputStream(file)), url);
167   }
168 
169   @NonNull
fromJsonStream(@onNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)170   private LottieResult<LottieComposition> fromJsonStream(@NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)
171       throws IOException {
172     if (cacheKey == null || networkCache == null) {
173       return LottieCompositionFactory.fromJsonInputStreamSync(inputStream, null);
174     }
175     File file = networkCache.writeTempCacheFile(url, inputStream, FileExtension.JSON);
176     return LottieCompositionFactory.fromJsonInputStreamSync(new FileInputStream(file.getAbsolutePath()), url);
177   }
178 }
179