blob: f0f29e132e188e2ecaa642728c1482e7d4804c07 [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.utils.Logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Helper class to save and restore animations fetched from an URL to the app disk cache.
*/
public class NetworkCache {
@NonNull
private final LottieNetworkCacheProvider cacheProvider;
public NetworkCache(@NonNull LottieNetworkCacheProvider cacheProvider) {
this.cacheProvider = cacheProvider;
}
public void clear() {
File parentDir = parentDir();
if (parentDir.exists()) {
File[] files = parentDir.listFiles();
if (files != null && files.length > 0) {
for (File file : parentDir.listFiles()) {
file.delete();
}
}
parentDir.delete();
}
}
/**
* If the animation doesn't exist in the cache, null will be returned.
*
* Once the animation is successfully parsed, {@link #renameTempFile(FileExtension)} must be
* called to move the file from a temporary location to its permanent cache location so it can
* be used in the future.
*/
@Nullable
@WorkerThread
Pair<FileExtension, InputStream> fetch(String url) {
File cachedFile;
try {
cachedFile = getCachedFile(url);
} catch (FileNotFoundException e) {
return null;
}
if (cachedFile == null) {
return null;
}
FileInputStream inputStream;
try {
inputStream = new FileInputStream(cachedFile);
} catch (FileNotFoundException e) {
return null;
}
FileExtension extension;
if (cachedFile.getAbsolutePath().endsWith(".zip")) {
extension = FileExtension.ZIP;
} else {
extension = FileExtension.JSON;
}
Logger.debug("Cache hit for " + url + " at " + cachedFile.getAbsolutePath());
return new Pair<>(extension, (InputStream) inputStream);
}
/**
* Writes an InputStream from a network response to a temporary file. If the file successfully parses
* to an composition, {@link #renameTempFile(FileExtension)} should be called to move the file
* to its final location for future cache hits.
*/
File writeTempCacheFile(String url, InputStream stream, FileExtension extension) throws IOException {
String fileName = filenameForUrl(url, extension, true);
File file = new File(parentDir(), fileName);
try {
OutputStream output = new FileOutputStream(file);
//noinspection TryFinallyCanBeTryWithResources
try {
byte[] buffer = new byte[1024];
int read;
while ((read = stream.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.flush();
} finally {
output.close();
}
} finally {
stream.close();
}
return file;
}
/**
* If the file created by {@link #writeTempCacheFile(InputStream, FileExtension)} was successfully parsed,
* this should be called to remove the temporary part of its name which will allow it to be a cache hit in the future.
*/
void renameTempFile(String url, FileExtension extension) {
String fileName = filenameForUrl(url, extension, true);
File file = new File(parentDir(), fileName);
String newFileName = file.getAbsolutePath().replace(".temp", "");
File newFile = new File(newFileName);
boolean renamed = file.renameTo(newFile);
Logger.debug("Copying temp file to real file (" + newFile + ")");
if (!renamed) {
Logger.warning("Unable to rename cache file " + file.getAbsolutePath() + " to " + newFile.getAbsolutePath() + ".");
}
}
/**
* Returns the cache file for the given url if it exists. Checks for both json and zip.
* Returns null if neither exist.
*/
@Nullable
private File getCachedFile(String url) throws FileNotFoundException {
File jsonFile = new File(parentDir(), filenameForUrl(url, FileExtension.JSON, false));
if (jsonFile.exists()) {
return jsonFile;
}
File zipFile = new File(parentDir(), filenameForUrl(url, FileExtension.ZIP, false));
if (zipFile.exists()) {
return zipFile;
}
return null;
}
private File parentDir() {
File file = cacheProvider.getCacheDir();
if (file.isFile()) {
file.delete();
}
if (!file.exists()) {
file.mkdirs();
}
return file;
}
private static String filenameForUrl(String url, FileExtension extension, boolean isTemp) {
return "lottie_cache_" + url.replaceAll("\\W+", "") + (isTemp ? extension.tempExtension(): extension.extension);
}
}