added image cache for emoji images

This commit is contained in:
nuclearfog 2023-03-25 00:21:04 +01:00
parent 96f6aba71e
commit af381a8feb
No known key found for this signature in database
GPG Key ID: 03488A185C476379
2 changed files with 131 additions and 34 deletions

View File

@ -11,11 +11,9 @@ import org.nuclearfog.twidda.backend.api.Connection;
import org.nuclearfog.twidda.backend.api.ConnectionException;
import org.nuclearfog.twidda.backend.api.ConnectionManager;
import org.nuclearfog.twidda.backend.helper.MediaStatus;
import org.nuclearfog.twidda.backend.utils.ImageCache;
import org.nuclearfog.twidda.model.Emoji;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Map;
import java.util.TreeMap;
@ -27,18 +25,15 @@ import java.util.TreeMap;
*/
public class EmojiLoader extends AsyncExecutor<EmojiLoader.EmojiParam, EmojiLoader.EmojiResult> {
private static final String FOLDER = "emojis";
private File imageFolder;
private Connection connection;
private ImageCache cache;
/**
*
*/
public EmojiLoader(Context context) {
connection = ConnectionManager.getDefaultConnection(context);
imageFolder = new File(context.getExternalCacheDir(), FOLDER);
imageFolder.mkdirs();
cache = ImageCache.getInstance(context);
}
@ -46,37 +41,17 @@ public class EmojiLoader extends AsyncExecutor<EmojiLoader.EmojiParam, EmojiLoad
@Override
protected EmojiResult doInBackground(@NonNull EmojiParam param) {
try {
Map<String, File> files = new TreeMap<>();
Map<String, Bitmap> result = new TreeMap<>();
// cache all local image files first
File[] imageFiles = imageFolder.listFiles();
if (imageFiles != null) {
for (File file : imageFiles) {
files.put(file.getName(), file);
}
}
for (Emoji emoji : param.emojis) {
File file = files.get(emoji.getCode());
if (file == null) {
// download image to cache
Bitmap icon = cache.getImage(emoji.getCode());
if (icon == null) {
MediaStatus media = connection.downloadImage(emoji.getUrl());
InputStream input = media.getStream();
file = new File(imageFolder, emoji.getCode());
file.createNewFile();
FileOutputStream output = new FileOutputStream(file);
Bitmap icon = BitmapFactory.decodeStream(input);
icon.compress(Bitmap.CompressFormat.PNG, 1, output);
// resize image
icon = Bitmap.createScaledBitmap(icon, icon.getWidth() / icon.getHeight() * param.size, param.size, false);
result.put(emoji.getCode(), icon);
} else {
// load image from cache
FileInputStream inputStream = new FileInputStream(file);
Bitmap icon = BitmapFactory.decodeStream(inputStream);
// resize image
icon = Bitmap.createScaledBitmap(icon, icon.getWidth() / icon.getHeight() * param.size, param.size, false);
result.put(emoji.getCode(), icon);
icon = BitmapFactory.decodeStream(input);
cache.putImage(emoji.getCode(), icon);
}
icon = Bitmap.createScaledBitmap(icon, icon.getWidth() / icon.getHeight() * param.size, param.size, false);
result.put(emoji.getCode(), icon);
}
return new EmojiResult(result, null);
} catch (ConnectionException exception) {
@ -90,6 +65,7 @@ public class EmojiLoader extends AsyncExecutor<EmojiLoader.EmojiParam, EmojiLoad
*
*/
public static class EmojiParam {
Emoji[] emojis;
int size;

View File

@ -0,0 +1,121 @@
package org.nuclearfog.twidda.backend.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
/**
* This class represents an image cache for emoji icons
* There are both, image cache and file cache. If image doesn't exist in the cache, the file cache is used to search for the file.
* A new image will be cached and saved as file to the local cache storage.
*
* @author nuclearfog
*/
public class ImageCache {
/**
* size of the lru cache (max entry count)
*/
private static final int SIZE = 64;
/**
* folder name of the local cache
*/
private static final String FOLDER = "images";
private static ImageCache instance;
@Nullable
private File imageFolder;
private LruCache<String, Bitmap> cache;
private Map<String, File> files;
/**
* @param context context used to determine cache folder path
*/
private ImageCache(Context context) {
files = new TreeMap<>();
cache = new LruCache<>(SIZE);
try {
imageFolder = new File(context.getExternalCacheDir(), FOLDER);
imageFolder.mkdirs();
File[] imageFiles = imageFolder.listFiles();
if (imageFiles != null) {
for (File file : imageFiles) {
files.put(file.getName(), file);
}
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
/**
* put image to cache and save as file if not exists
*
* @param key key of the image (tag)
* @param image image bitmap
*/
public void putImage(String key, Bitmap image) {
cache.put(key, image);
if (imageFolder != null && !files.containsKey(key)) {
try {
File file = new File(imageFolder, key);
file.createNewFile();
FileOutputStream output = new FileOutputStream(file);
image.compress(Bitmap.CompressFormat.PNG, 1, output);
files.put(key, file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* get image from cache or file
*
* @param key key of the image (tag)
* @return image bitmap or null if not found
*/
@Nullable
public Bitmap getImage(String key) {
Bitmap result = cache.get(key);
if (result == null) {
File file = files.get(key);
if (file != null) {
try {
FileInputStream inputStream = new FileInputStream(file);
result = BitmapFactory.decodeStream(inputStream);
cache.put(key, result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
/**
* create singleton instance of this class
*
* @return singleton instance of this class
*/
public static ImageCache getInstance(Context context) {
if (instance == null)
instance = new ImageCache(context);
return instance;
}
}