Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 68 additions & 23 deletions src/main/java/eu/siacs/conversations/persistance/FileBackend.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
import android.annotation.TargetApi;
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;
Expand Down Expand Up @@ -852,10 +856,10 @@ private static int getRotation(final InputStream inputStream) throws IOException
}
}

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);
Expand All @@ -865,40 +869,81 @@ public Bitmap getThumbnail(Message message, int size, boolean cacheOnly) throws
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);
}
}
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) {
Bitmap overlay = BitmapFactory.decodeResource(mXmppConnectionService.getResources(), resource);
Canvas canvas = new Canvas(bitmap);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@ private Builder buildSingleConversations(final ArrayList<Message> messages, fina

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
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;
Expand Down Expand Up @@ -475,6 +477,7 @@ public void onStatusChanged(final Account account) {
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();

Expand Down Expand Up @@ -1124,13 +1127,23 @@ public void onCreate() {
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());
}
Expand Down Expand Up @@ -4291,6 +4304,10 @@ public LruCache<String, Bitmap> getBitmapCache() {
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()) {
Expand Down
25 changes: 16 additions & 9 deletions src/main/java/eu/siacs/conversations/ui/XmppActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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;
Expand Down Expand Up @@ -876,16 +877,19 @@ public AvatarService avatarService() {
}

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);
Expand Down Expand Up @@ -939,7 +943,7 @@ public boolean execute(XmppActivity activity) {
}
}

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;

Expand All @@ -948,15 +952,15 @@ private BitmapWorkerTask(ImageView imageView) {
}

@Override
protected Bitmap doInBackground(Message... params) {
protected Drawable doInBackground(Message... params) {
if (isCancelled()) {
return null;
}
message = params[0];
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;
}
Expand All @@ -966,12 +970,15 @@ protected Bitmap doInBackground(Message... params) {
}

@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();
}
}
}
}
Expand Down