@@ -2,11 +2,15 @@ package eu.siacs.conversations.persistance;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.ImageDecoder;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
@@ -914,10 +918,10 @@ public class FileBackend {
}
}
- public Bitmap getThumbnail(Message message, int size, boolean cacheOnly) throws IOException {
+ public Drawable getThumbnail(Message message, Resources res, int size, boolean cacheOnly) throws IOException {
final String uuid = message.getUuid();
- final LruCache<String, Bitmap> cache = mXmppConnectionService.getBitmapCache();
- Bitmap thumbnail = cache.get(uuid);
+ final LruCache<String, Drawable> cache = mXmppConnectionService.getDrawableCache();
+ Drawable thumbnail = cache.get(uuid);
if ((thumbnail == null) && (!cacheOnly)) {
synchronized (THUMBNAIL_LOCK) {
thumbnail = cache.get(uuid);
@@ -927,27 +931,14 @@ public class FileBackend {
DownloadableFile file = getFile(message);
final String mime = file.getMimeType();
if ("application/pdf".equals(mime) && Compatibility.runsTwentyOne()) {
- thumbnail = getPdfDocumentPreview(file, size);
+ thumbnail = new BitmapDrawable(res, getPdfDocumentPreview(file, size));
} else if (mime.startsWith("video/")) {
- thumbnail = getVideoPreview(file, size);
+ thumbnail = new BitmapDrawable(res, getVideoPreview(file, size));
} else {
- final Bitmap fullSize = getFullSizeImagePreview(file, size);
- if (fullSize == null) {
+ thumbnail = getImagePreview(file, res, size, mime);
+ if (thumbnail == null) {
throw new FileNotFoundException();
}
- thumbnail = resize(fullSize, size);
- thumbnail = rotate(thumbnail, getRotation(file));
- if (mime.equals("image/gif")) {
- Bitmap withGifOverlay = thumbnail.copy(Bitmap.Config.ARGB_8888, true);
- drawOverlay(
- withGifOverlay,
- paintOverlayBlack(withGifOverlay)
- ? R.drawable.play_gif_black
- : R.drawable.play_gif_white,
- 1.0f);
- thumbnail.recycle();
- thumbnail = withGifOverlay;
- }
}
cache.put(uuid, thumbnail);
}
@@ -955,15 +946,64 @@ public class FileBackend {
return thumbnail;
}
- private Bitmap getFullSizeImagePreview(File file, int size) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = calcSampleSize(file, size);
- try {
- return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
- } catch (OutOfMemoryError e) {
- options.inSampleSize *= 2;
- return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ public Bitmap getThumbnailBitmap(Message message, Resources res, int size) throws IOException {
+ final Drawable drawable = getThumbnail(message, res, size, false);
+ if (drawable == null) return null;
+ return drawDrawable(drawable);
+ }
+
+ private Drawable getImagePreview(File file, Resources res, int size, final String mime) throws IOException {
+ if (android.os.Build.VERSION.SDK_INT >= 28) {
+ ImageDecoder.Source source = ImageDecoder.createSource(file);
+ return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+ int w = info.getSize().getWidth();
+ int h = info.getSize().getHeight();
+ int scalledW;
+ int scalledH;
+ if (w <= h) {
+ scalledW = Math.max((int) (w / ((double) h / size)), 1);
+ scalledH = size;
+ } else {
+ scalledW = size;
+ scalledH = Math.max((int) (h / ((double) w / size)), 1);
+ }
+ decoder.setTargetSize(scalledW, scalledH);
+ });
+ } else {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(file, size);
+ Bitmap bitmap = null;
+ try {
+ bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ } catch (OutOfMemoryError e) {
+ options.inSampleSize *= 2;
+ bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ }
+ bitmap = resize(bitmap, size);
+ bitmap = rotate(bitmap, getRotation(file));
+ if (mime.equals("image/gif")) {
+ Bitmap withGifOverlay = bitmap.copy(Bitmap.Config.ARGB_8888, true);
+ drawOverlay(withGifOverlay, paintOverlayBlack(withGifOverlay) ? R.drawable.play_gif_black : R.drawable.play_gif_white, 1.0f);
+ bitmap.recycle();
+ bitmap = withGifOverlay;
+ }
+ return new BitmapDrawable(res, bitmap);
+ }
+ }
+
+ protected Bitmap drawDrawable(Drawable drawable) {
+ Bitmap bitmap = null;
+
+ if (drawable instanceof BitmapDrawable) {
+ bitmap = ((BitmapDrawable) drawable).getBitmap();
+ if (bitmap != null) return bitmap;
}
+
+ bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
}
private void drawOverlay(Bitmap bitmap, int resource, float factor) {
@@ -1062,7 +1062,7 @@ public class NotificationService {
private void modifyForImage(final Builder builder, final Message message, final ArrayList<Message> messages) {
try {
- final Bitmap bitmap = mXmppConnectionService.getFileBackend().getThumbnail(message, getPixel(288), false);
+ final Bitmap bitmap = mXmppConnectionService.getFileBackend().getThumbnailBitmap(message, mXmppConnectionService.getResources(), getPixel(288));
final ArrayList<Message> tmp = new ArrayList<>();
for (final Message msg : messages) {
if (msg.getType() == Message.TYPE_TEXT
@@ -18,6 +18,8 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.Network;
@@ -486,6 +488,7 @@ public class XmppConnectionService extends Service {
private PgpEngine mPgpEngine = null;
private WakeLock wakeLock;
private LruCache<String, Bitmap> mBitmapCache;
+ private LruCache<String, Drawable> mDrawableCache;
private final BroadcastReceiver mInternalEventReceiver = new InternalEventReceiver();
private final BroadcastReceiver mInternalScreenEventReceiver = new InternalEventReceiver();
@@ -1151,13 +1154,23 @@ public class XmppConnectionService extends Service {
this.mRandom = new SecureRandom();
updateMemorizingTrustmanager();
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
- final int cacheSize = maxMemory / 8;
+ final int cacheSize = maxMemory / 9;
this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(final String key, final Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
+ this.mDrawableCache = new LruCache<String, Drawable>(cacheSize) {
+ @Override
+ protected int sizeOf(final String key, final Drawable drawable) {
+ if (drawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) drawable).getBitmap().getByteCount() / 1024;
+ } else {
+ return drawable.getIntrinsicWidth() * drawable.getIntrinsicHeight() * 40 / 1024;
+ }
+ }
+ };
if (mLastActivity == 0) {
mLastActivity = getPreferences().getLong(SETTING_LAST_ACTIVITY_TS, System.currentTimeMillis());
}
@@ -4327,6 +4340,10 @@ public class XmppConnectionService extends Service {
return this.mBitmapCache;
}
+ public LruCache<String, Drawable> getDrawableCache() {
+ return this.mDrawableCache;
+ }
+
public Collection<String> getKnownHosts() {
final Set<String> hosts = new HashSet<>();
for (final Account account : getAccounts()) {
@@ -22,6 +22,7 @@ import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point;
+import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
@@ -876,16 +877,19 @@ public abstract class XmppActivity extends ActionBarActivity {
}
public void loadBitmap(Message message, ImageView imageView) {
- Bitmap bm;
+ Drawable bm;
try {
- bm = xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288), true);
+ bm = xmppConnectionService.getFileBackend().getThumbnail(message, getResources(), (int) (metrics.density * 288), true);
} catch (IOException e) {
bm = null;
}
if (bm != null) {
cancelPotentialWork(message, imageView);
- imageView.setImageBitmap(bm);
+ imageView.setImageDrawable(bm);
imageView.setBackgroundColor(0x00000000);
+ if (Build.VERSION.SDK_INT >= 28 && bm instanceof AnimatedImageDrawable) {
+ ((AnimatedImageDrawable) bm).start();
+ }
} else {
if (cancelPotentialWork(message, imageView)) {
imageView.setBackgroundColor(0xff333333);
@@ -939,7 +943,7 @@ public abstract class XmppActivity extends ActionBarActivity {
}
}
- static class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
+ static class BitmapWorkerTask extends AsyncTask<Message, Void, Drawable> {
private final WeakReference<ImageView> imageViewReference;
private Message message = null;
@@ -948,7 +952,7 @@ public abstract class XmppActivity extends ActionBarActivity {
}
@Override
- protected Bitmap doInBackground(Message... params) {
+ protected Drawable doInBackground(Message... params) {
if (isCancelled()) {
return null;
}
@@ -956,7 +960,7 @@ public abstract class XmppActivity extends ActionBarActivity {
try {
final XmppActivity activity = find(imageViewReference);
if (activity != null && activity.xmppConnectionService != null) {
- return activity.xmppConnectionService.getFileBackend().getThumbnail(message, (int) (activity.metrics.density * 288), false);
+ return activity.xmppConnectionService.getFileBackend().getThumbnail(message, imageViewReference.get().getContext().getResources(), (int) (activity.metrics.density * 288), false);
} else {
return null;
}
@@ -966,12 +970,15 @@ public abstract class XmppActivity extends ActionBarActivity {
}
@Override
- protected void onPostExecute(final Bitmap bitmap) {
+ protected void onPostExecute(final Drawable drawable) {
if (!isCancelled()) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
- imageView.setImageBitmap(bitmap);
- imageView.setBackgroundColor(bitmap == null ? 0xff333333 : 0x00000000);
+ imageView.setImageDrawable(drawable);
+ imageView.setBackgroundColor(drawable == null ? 0xff333333 : 0x00000000);
+ if (Build.VERSION.SDK_INT >= 28 && drawable instanceof AnimatedImageDrawable) {
+ ((AnimatedImageDrawable) drawable).start();
+ }
}
}
}