M src/main/java/eu/siacs/conversations/Config.java => src/main/java/eu/siacs/conversations/Config.java +1 -1
@@ 86,7 86,7 @@ public final class Config {
public static final int AVATAR_SIZE = 192;
public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.JPEG;
- public static final int AVATAR_CHAR_LIMIT = 9400;
+ public static final int AVATAR_CHAR_LIMIT = 100000;
public static final int IMAGE_SIZE = 1920;
public static final Bitmap.CompressFormat IMAGE_FORMAT = Bitmap.CompressFormat.JPEG;
M src/main/java/eu/siacs/conversations/persistance/FileBackend.java => src/main/java/eu/siacs/conversations/persistance/FileBackend.java +70 -7
@@ 2,6 2,7 @@ package eu.siacs.conversations.persistance;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ 1504,21 1505,60 @@ public class FileBackend {
}
private Avatar getUncompressedAvatar(Uri uri) {
- Bitmap bitmap = null;
try {
- bitmap =
+ if (android.os.Build.VERSION.SDK_INT >= 28) {
+ ImageDecoder.Source source = ImageDecoder.createSource(mXmppConnectionService.getContentResolver(), uri);
+ int[] size = new int[] { 0, 0 };
+ boolean[] animated = new boolean[] { false };
+ String[] mimeType = new String[] { null };
+ Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+ mimeType[0] = info.getMimeType();
+ animated[0] = info.isAnimated();
+ size[0] = info.getSize().getWidth();
+ size[1] = info.getSize().getHeight();
+ });
+
+ if (animated[0]) {
+ Avatar avatar = getPepAvatar(uri, size[0], size[1], mimeType[0]);
+ if (avatar != null) return avatar;
+ }
+
+ return getPepAvatar(drawDrawable(drawable), Bitmap.CompressFormat.PNG, 100);
+ } else {
+ Bitmap bitmap =
BitmapFactory.decodeStream(
mXmppConnectionService.getContentResolver().openInputStream(uri));
- return getPepAvatar(bitmap, Bitmap.CompressFormat.PNG, 100);
+ return getPepAvatar(bitmap, Bitmap.CompressFormat.PNG, 100);
+ }
} catch (Exception e) {
return null;
- } finally {
- if (bitmap != null) {
- bitmap.recycle();
- }
}
}
+ private Avatar getPepAvatar(Uri uri, int width, int height, final String mimeType) throws IOException, NoSuchAlgorithmException {
+ AssetFileDescriptor fd = mXmppConnectionService.getContentResolver().openAssetFileDescriptor(uri, "r");
+ if (fd.getLength() > Config.AVATAR_CHAR_LIMIT) return null; // Too big to use raw file
+
+ ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
+ Base64OutputStream mBase64OutputStream =
+ new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ DigestOutputStream mDigestOutputStream =
+ new DigestOutputStream(mBase64OutputStream, digest);
+
+ ByteStreams.copy(fd.createInputStream(), mDigestOutputStream);
+ mDigestOutputStream.flush();
+ mDigestOutputStream.close();
+
+ final Avatar avatar = new Avatar();
+ avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ avatar.image = new String(mByteArrayOutputStream.toByteArray());
+ avatar.type = mimeType;
+ avatar.width = width;
+ avatar.height = height;
+ return avatar;
+ }
+
private Avatar getPepAvatar(Bitmap bitmap, Bitmap.CompressFormat format, int quality) {
try {
ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
@@ 1696,6 1736,29 @@ public class FileBackend {
return Uri.fromFile(getAvatarFile(avatar));
}
+ public Drawable cropCenterSquareDrawable(Uri image, int size) {
+ if (android.os.Build.VERSION.SDK_INT >= 28) {
+ try {
+ ImageDecoder.Source source = ImageDecoder.createSource(mXmppConnectionService.getContentResolver(), image);
+ return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+ int w = info.getSize().getWidth();
+ int h = info.getSize().getHeight();
+ Rect r = rectForSize(w, h, size);
+ decoder.setTargetSize(r.width(), r.height());
+
+ int newSize = Math.min(r.width(), r.height());
+ int left = (r.width() - newSize) / 2;
+ int top = (r.height() - newSize) / 2;
+ decoder.setCrop(new Rect(left, top, left + newSize, top + newSize));
+ });
+ } catch (final IOException e) {
+ return null;
+ }
+ } else {
+ return new BitmapDrawable(cropCenterSquare(image, size));
+ }
+ }
+
public Bitmap cropCenterSquare(Uri image, int size) {
if (image == null) {
return null;
M src/main/java/eu/siacs/conversations/ui/PublishGroupChatProfilePictureActivity.java => src/main/java/eu/siacs/conversations/ui/PublishGroupChatProfilePictureActivity.java +22 -2
@@ 31,9 31,11 @@ package eu.siacs.conversations.ui;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ 83,10 85,13 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
bitmap = xmppConnectionService.getAvatarService().get(conversation, size);
} else {
Log.d(Config.LOGTAG, "loading " + uri.toString() + " into preview");
- bitmap = new BitmapDrawable(xmppConnectionService.getFileBackend().cropCenterSquare(uri, size));
+ bitmap = xmppConnectionService.getFileBackend().cropCenterSquareDrawable(uri, size);
}
this.binding.accountImage.setImageDrawable(bitmap);
this.binding.publishButton.setEnabled(uri != null);
+ if (Build.VERSION.SDK_INT >= 28 && bitmap instanceof AnimatedImageDrawable) {
+ ((AnimatedImageDrawable) bitmap).start();
+ }
}
@Override
@@ 131,9 136,24 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
}
} else if (requestCode == REQUEST_CHOOSE_PICTURE) {
if (resultCode == RESULT_OK) {
- PublishProfilePictureActivity.cropUri(this, data.getData());
+ cropUri(data.getData());
+ }
+ }
+ }
+
+ public void cropUri(final Uri uri) {
+ if (Build.VERSION.SDK_INT >= 28) {
+ this.uri = uri;
+ reloadAvatar();
+ if (this.binding.accountImage.getDrawable() instanceof AnimatedImageDrawable) {
+ return;
}
}
+
+ CropImage.activity(uri).setOutputCompressFormat(Bitmap.CompressFormat.PNG)
+ .setAspectRatio(1, 1)
+ .setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
+ .start(this);
}
@Override
M src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java => src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java +17 -5
@@ 2,6 2,7 @@ package eu.siacs.conversations.ui;
import android.app.Activity;
import android.content.Intent;
+import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.Bitmap;
@@ 174,7 175,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
}
} else if (requestCode == REQUEST_CHOOSE_PICTURE) {
if (resultCode == RESULT_OK) {
- cropUri(this, data.getData());
+ cropUri(data.getData());
}
}
}
@@ 227,7 228,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
final Uri uri = intent != null ? intent.getData() : null;
if (uri != null && handledExternalUri.compareAndSet(false,true)) {
- cropUri(this, uri);
+ cropUri(uri);
return;
}
@@ 237,11 238,19 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
configureActionBar(getSupportActionBar(), !this.mInitialAccountSetup && !handledExternalUri.get());
}
- public static void cropUri(final Activity activity, final Uri uri) {
+ public void cropUri(final Uri uri) {
+ if (Build.VERSION.SDK_INT >= 28) {
+ loadImageIntoPreview(uri);
+ if (this.avatar.getDrawable() instanceof AnimatedImageDrawable) {
+ this.avatarUri = uri;
+ return;
+ }
+ }
+
CropImage.activity(uri).setOutputCompressFormat(Bitmap.CompressFormat.PNG)
.setAspectRatio(1, 1)
.setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
- .start(activity);
+ .start(this);
}
protected void loadImageIntoPreview(Uri uri) {
@@ 251,7 260,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
bm = avatarService().get(account, (int) getResources().getDimension(R.dimen.publish_avatar_size));
} else {
try {
- bm = new BitmapDrawable(xmppConnectionService.getFileBackend().cropCenterSquare(uri, (int) getResources().getDimension(R.dimen.publish_avatar_size)));
+ bm = xmppConnectionService.getFileBackend().cropCenterSquareDrawable(uri, (int) getResources().getDimension(R.dimen.publish_avatar_size));
} catch (Exception e) {
Log.d(Config.LOGTAG, "unable to load bitmap into image view", e);
}
@@ 265,6 274,9 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
return;
}
this.avatar.setImageDrawable(bm);
+ if (Build.VERSION.SDK_INT >= 28 && bm instanceof AnimatedImageDrawable) {
+ ((AnimatedImageDrawable) bm).start();
+ }
if (support) {
togglePublishButton(uri != null, R.string.publish);
this.hintOrWarning.setVisibility(View.INVISIBLE);