From 83bfcd3428bb9dc91813deff3190aacad1cfa5de Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 30 May 2025 12:27:38 +0200 Subject: [PATCH] route MUC avatar upload through AvatarManager --- .../services/XmppConnectionService.java | 101 ++++-------------- .../ui/PublishProfilePictureActivity.java | 2 +- .../xmpp/manager/AvatarManager.java | 26 +++-- .../android/xmpp/processor/BindProcessor.java | 3 - 4 files changed, 42 insertions(+), 90 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 58e33094d99bd059f3acd2726af7f2530c5209fc..e5a98a71c00c0f5c946bfaaf7bf84545b6045029 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -57,7 +57,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; -import com.google.common.io.BaseEncoding; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -4298,42 +4297,32 @@ public class XmppConnectionService extends Service { connection.getManager(RosterManager.class).deleteRosterItem(contact); } - // TODO get thumbnail via AvatarManager - // TODO call AvatarManager.getInbandAvatar form vcard manager and simplify publication process public void publishMucAvatar( final Conversation conversation, final Uri image, final OnAvatarPublication callback) { - new Thread( - () -> { - final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; - final int size = Config.AVATAR_SIZE; - final Avatar avatar = - getFileBackend().getPepAvatar(image, size, format); - if (avatar != null) { - if (!getFileBackend().save(avatar)) { - callback.onAvatarPublicationFailed( - R.string.error_saving_avatar); - return; - } - avatar.owner = conversation.getJid().asBareJid(); - publishMucAvatar(conversation, avatar, callback); - } else { - callback.onAvatarPublicationFailed( - R.string.error_publish_avatar_converting); - } - }) - .start(); - } + final var connection = conversation.getAccount().getXmppConnection(); + final var future = + connection + .getManager(AvatarManager.class) + .publishVCard(conversation.getJid().asBareJid(), image); + Futures.addCallback( + future, + new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + callback.onAvatarPublicationSucceeded(); + } - // TODO get rid of the async part. Manager is already async - public void publishAvatarAsync( - final Account account, - final Uri image, - final boolean open, - final OnAvatarPublication callback) { - new Thread(() -> publishAvatar(account, image, open, callback)).start(); + @Override + public void onFailure(@NonNull Throwable t) { + Log.d(Config.LOGTAG, "could not publish MUC avatar", t); + callback.onAvatarPublicationFailed( + R.string.error_publish_avatar_server_reject); + } + }, + MoreExecutors.directExecutor()); } - private void publishAvatar( + public void publishAvatar( final Account account, final Uri image, final boolean open, @@ -4363,54 +4352,6 @@ public class XmppConnectionService extends Service { MoreExecutors.directExecutor()); } - private void publishMucAvatar( - final Conversation conversation, - final Avatar avatar, - final OnAvatarPublication callback) { - final var account = conversation.getAccount(); - final var connection = account.getXmppConnection(); - final var future = - connection - .getManager(VCardManager.class) - .publishPhoto( - avatar.owner, - avatar.type, - BaseEncoding.base64().decode(avatar.image)); - Futures.addCallback( - future, - new FutureCallback<>() { - @Override - public void onSuccess(Void result) { - Log.d(Config.LOGTAG, "published muc avatar"); - final var c = account.getRoster().getContact(avatar.owner); - c.setAvatar(avatar.sha1sum); - getAvatarService().clear(c); - getAvatarService().clear(conversation.getMucOptions()); - callback.onAvatarPublicationSucceeded(); - } - - @Override - public void onFailure(@NonNull Throwable t) { - Log.d(Config.LOGTAG, "could not publish muc avatar", t); - callback.onAvatarPublicationFailed( - R.string.error_publish_avatar_server_reject); - } - }, - MoreExecutors.directExecutor()); - } - - public void cancelAvatarFetches(final Account account) { - synchronized (mInProgressAvatarFetches) { - for (final Iterator iterator = mInProgressAvatarFetches.iterator(); - iterator.hasNext(); ) { - final String KEY = iterator.next(); - if (KEY.startsWith(account.getJid().asBareJid() + "_")) { - iterator.remove(); - } - } - } - } - public ListenableFuture checkForAvatar(final Account account) { final var connection = account.getXmppConnection(); return connection diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java index 7901a39bffc853a02cbf5cd60c47ed860b5eeefb..276bedc5095549bd4b8914a58b5dfab09730d52a 100644 --- a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java @@ -112,7 +112,7 @@ public class PublishProfilePictureActivity extends XmppActivity } publishing = true; togglePublishButton(false, R.string.publishing); - xmppConnectionService.publishAvatarAsync(account, uri, open, this); + xmppConnectionService.publishAvatar(account, uri, open, this); }); this.binding.cancelButton.setOnClickListener( v -> { diff --git a/src/main/java/eu/siacs/conversations/xmpp/manager/AvatarManager.java b/src/main/java/eu/siacs/conversations/xmpp/manager/AvatarManager.java index 0c79c0708f42e300592ca555f498d213c3937c9f..b2eb0183ee2fa45ebc6cbc445f1fd6c13cccb1d4 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/manager/AvatarManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/manager/AvatarManager.java @@ -421,6 +421,7 @@ public class AvatarManager extends AbstractManager { final Uri image, final int size, final ImageFormat format, final Integer charLimit) throws Exception { final var centerSquare = FileBackend.cropCenterSquare(context, image, size); + // TODO do an alpha check. if alpha and format JPEG half size and use PNG if (charLimit == null || format == ImageFormat.PNG) { return resizeAndStoreAvatar(centerSquare, format, 90); } else { @@ -525,7 +526,7 @@ public class AvatarManager extends AbstractManager { String.format("Could not move file to %s", avatarFile.getAbsolutePath())); } - public ListenableFuture> uploadAvatar(final Uri image, final int size) { + private ListenableFuture> uploadAvatar(final Uri image, final int size) { final var avatarFutures = new ImmutableList.Builder>(); final var avatarFuture = resizeAndStoreAvatarAsync(image, size, ImageFormat.JPEG); final var avatarWithUrlFuture = @@ -580,7 +581,7 @@ public class AvatarManager extends AbstractManager { AVATAR_COMPRESSION_EXECUTOR); } - public ListenableFuture publish(final Collection avatars, final boolean open) { + private ListenableFuture publish(final Collection avatars, final boolean open) { final Info mainAvatarInfo; final byte[] mainAvatar; try { @@ -609,11 +610,24 @@ public class AvatarManager extends AbstractManager { MoreExecutors.directExecutor()); } + public ListenableFuture publishVCard(final Jid address, final Uri image) { + final var avatarThumbnailFuture = + resizeAndStoreAvatarAsync( + image, Config.AVATAR_SIZE, ImageFormat.JPEG, Config.AVATAR_CHAR_LIMIT); + return Futures.transformAsync( + avatarThumbnailFuture, + info -> { + final var avatar = + Files.asByteSource(FileBackend.getAvatarFile(context, info.getId())) + .read(); + return getManager(VCardManager.class) + .publishPhoto(address, info.getType(), avatar); + }, + AVATAR_COMPRESSION_EXECUTOR); + } + public ListenableFuture uploadAndPublish(final Uri image, final boolean open) { - final var infoFuture = - connection - .getManager(AvatarManager.class) - .uploadAvatar(image, Config.AVATAR_FULL_SIZE); + final var infoFuture = uploadAvatar(image, Config.AVATAR_FULL_SIZE); return Futures.transformAsync( infoFuture, avatars -> publish(avatars, open), MoreExecutors.directExecutor()); } diff --git a/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java index 6116649a449eb0187a73b28ef6addccc6a1c0fae..bbdecb6884fcb44f740b96e5794742d83a5d0bd7 100644 --- a/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java +++ b/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java @@ -26,10 +26,8 @@ public class BindProcessor extends XmppConnection.Delegate implements Runnable { @Override public void run() { - Log.d(Config.LOGTAG, "begin onBind()"); final var account = connection.getAccount(); final var features = connection.getFeatures(); - service.cancelAvatarFetches(account); final boolean loggedInSuccessfully = account.setOption(Account.OPTION_LOGGED_IN_SUCCESSFULLY, true); final boolean sosModified; @@ -112,6 +110,5 @@ public class BindProcessor extends XmppConnection.Delegate implements Runnable { connection.getManager(RosterManager.class).syncDirtyContacts(); service.getUnifiedPushBroker().renewUnifiedPushEndpointsOnBind(account); - Log.d(Config.LOGTAG, "end onBind()"); } }