blob: bc781403ba4daef590a9573c601dddaed7f90a7e [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.image_fetcher;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import org.chromium.base.Callback;
import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import jp.tomorrowkey.android.gifplayer.BaseGifImage;
/**
* ImageFetcher implementation that uses a disk cache.
*/
public class CachedImageFetcher extends ImageFetcher {
private static final String TAG = "CachedImageFetcher";
// The native bridge.
private ImageFetcherBridge mImageFetcherBridge;
/**
* Creates a CachedImageFetcher with the given bridge.
*
* @param bridge Bridge used to interact with native.
*/
CachedImageFetcher(ImageFetcherBridge bridge) {
mImageFetcherBridge = bridge;
}
@Override
public void destroy() {
// Do nothing, this lives for the lifetime of the application.
}
/**
* Tries to load the gif from disk, if not it falls back to the bridge.
*/
@Override
public void fetchGif(String url, String clientName, Callback<BaseGifImage> callback) {
long startTimeMillis = System.currentTimeMillis();
PostTask.postTask(TaskTraits.USER_VISIBLE, () -> {
// Try to read the gif from disk, then post back to the ui thread.
String filePath = mImageFetcherBridge.getFilePath(url);
BaseGifImage cachedGif = tryToLoadGifFromDisk(filePath);
PostTask.postTask(UiThreadTaskTraits.USER_VISIBLE, () -> {
continueFetchGifAfterDisk(url, clientName, callback, cachedGif, startTimeMillis);
});
});
}
@VisibleForTesting
void continueFetchGifAfterDisk(String url, String clientName, Callback<BaseGifImage> callback,
BaseGifImage cachedGif, long startTimeMillis) {
if (cachedGif != null) {
callback.onResult(cachedGif);
reportEvent(clientName, ImageFetcherEvent.JAVA_DISK_CACHE_HIT);
mImageFetcherBridge.reportCacheHitTime(clientName, startTimeMillis);
} else {
mImageFetcherBridge.fetchGif(url, clientName, (BaseGifImage gifFromNative) -> {
callback.onResult(gifFromNative);
mImageFetcherBridge.reportTotalFetchTimeFromNative(clientName, startTimeMillis);
});
}
}
/**
* Tries to load the gif from disk, if not it falls back to the bridge.
*/
@Override
public void fetchImage(
String url, String clientName, int width, int height, Callback<Bitmap> callback) {
long startTimeMillis = System.currentTimeMillis();
PostTask.postTask(TaskTraits.USER_VISIBLE, () -> {
// Try to read the bitmap from disk, then post back to the ui thread.
String filePath = mImageFetcherBridge.getFilePath(url);
Bitmap bitmap = tryToLoadImageFromDisk(filePath);
PostTask.postTask(UiThreadTaskTraits.USER_VISIBLE, () -> {
continueFetchImageAfterDisk(
url, clientName, width, height, callback, bitmap, startTimeMillis);
});
});
}
@VisibleForTesting
void continueFetchImageAfterDisk(String url, String clientName, int width, int height,
Callback<Bitmap> callback, Bitmap cachedBitmap, long startTimeMillis) {
if (cachedBitmap != null) {
callback.onResult(cachedBitmap);
reportEvent(clientName, ImageFetcherEvent.JAVA_DISK_CACHE_HIT);
mImageFetcherBridge.reportCacheHitTime(clientName, startTimeMillis);
} else {
mImageFetcherBridge.fetchImage(
getConfig(), url, clientName, width, height, (Bitmap bitmapFromNative) -> {
callback.onResult(bitmapFromNative);
mImageFetcherBridge.reportTotalFetchTimeFromNative(
clientName, startTimeMillis);
});
}
}
@Override
public void clear() {}
@Override
public @ImageFetcherConfig int getConfig() {
return ImageFetcherConfig.DISK_CACHE_ONLY;
}
/** Wrapper function to decode a file for disk, useful for testing. */
@VisibleForTesting
Bitmap tryToLoadImageFromDisk(String filePath) {
if (new File(filePath).exists()) {
return BitmapFactory.decodeFile(filePath, null);
} else {
return null;
}
}
@VisibleForTesting
BaseGifImage tryToLoadGifFromDisk(String filePath) {
try {
File file = new File(filePath);
byte[] fileBytes = new byte[(int) file.length()];
FileInputStream fileInputStream = new FileInputStream(filePath);
int bytesRead = fileInputStream.read(fileBytes);
if (bytesRead != fileBytes.length) return null;
return new BaseGifImage(fileBytes);
} catch (IOException e) {
Log.w(TAG, "Failed to read: %s", filePath, e);
return null;
}
}
}