@@ -69,21 +69,6 @@ public class IqGenerator extends AbstractGenerator {
return packet;
}
- public Iq retrieveBookmarks() {
- return retrieve(Namespace.BOOKMARKS2, null);
- }
-
- public Iq retrieveMds() {
- return retrieve(Namespace.MDS_DISPLAYED, null);
- }
-
- public Iq publishNick(String nick) {
- final Element item = new Element("item");
- item.setAttribute("id", "current");
- item.addChild("nick", Namespace.NICK).setContent(nick);
- return publish(Namespace.NICK, item);
- }
-
public Iq deleteNode(final String node) {
final var packet = new Iq(Iq.Type.SET);
final Element pubsub = packet.addChild("pubsub", Namespace.PUBSUB_OWNER);
@@ -91,45 +76,6 @@ public class IqGenerator extends AbstractGenerator {
return packet;
}
- public Iq deleteItem(final String node, final String id) {
- final var packet = new Iq(Iq.Type.SET);
- final Element pubsub = packet.addChild("pubsub", Namespace.PUBSUB);
- final Element retract = pubsub.addChild("retract");
- retract.setAttribute("node", node);
- retract.setAttribute("notify", "true");
- retract.addChild("item").setAttribute("id", id);
- return packet;
- }
-
- public Iq publishAvatar(Avatar avatar, Bundle options) {
- final Element item = new Element("item");
- item.setAttribute("id", avatar.sha1sum);
- final Element data = item.addChild("data", Namespace.AVATAR_DATA);
- data.setContent(avatar.image);
- return publish(Namespace.AVATAR_DATA, item, options);
- }
-
- public Iq publishElement(
- final String namespace, final Element element, String id, final Bundle options) {
- final Element item = new Element("item");
- item.setAttribute("id", id);
- item.addChild(element);
- return publish(namespace, item, options);
- }
-
- public Iq publishAvatarMetadata(final Avatar avatar, final Bundle options) {
- final Element item = new Element("item");
- item.setAttribute("id", avatar.sha1sum);
- final Element metadata = item.addChild("metadata", Namespace.AVATAR_METADATA);
- final Element info = metadata.addChild("info");
- info.setAttribute("bytes", avatar.size);
- info.setAttribute("id", avatar.sha1sum);
- info.setAttribute("height", avatar.height);
- info.setAttribute("width", avatar.height);
- info.setAttribute("type", avatar.type);
- return publish(Namespace.AVATAR_METADATA, item, options);
- }
-
public Iq retrievePepAvatar(final Avatar avatar) {
final Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum);
@@ -143,7 +143,6 @@ import eu.siacs.conversations.xmpp.manager.PresenceManager;
import eu.siacs.conversations.xmpp.manager.PrivateStorageManager;
import eu.siacs.conversations.xmpp.manager.RosterManager;
import eu.siacs.conversations.xmpp.pep.Avatar;
-import eu.siacs.conversations.xmpp.pep.PublishOptions;
import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.IqErrorException;
import im.conversations.android.xmpp.model.avatar.Metadata;
@@ -1653,7 +1652,6 @@ public class XmppConnectionService extends Service {
final XmppConnection connection = new XmppConnection(account, this);
connection.setOnJinglePacketReceivedListener((mJingleConnectionManager::deliverPacket));
connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
- connection.addOnAdvancedStreamFeaturesAvailableListener(this.mAvatarService);
return connection;
}
@@ -3561,6 +3559,7 @@ public class XmppConnectionService extends Service {
updateAccountUi();
}
};
+ // TODO execute this via the respective Managers
deleteVcardAvatar(account, onDeleted);
deletePepNode(account, Namespace.AVATAR_DATA);
deletePepNode(account, Namespace.AVATAR_METADATA, onDeleted);
@@ -4368,15 +4367,36 @@ public class XmppConnectionService extends Service {
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)) {
- Log.d(Config.LOGTAG, "unable to save vcard");
- callback.onAvatarPublicationFailed(R.string.error_saving_avatar);
- return;
- }
- publishAvatar(account, avatar, open, callback);
- } else {
+ if (avatar == null) {
callback.onAvatarPublicationFailed(R.string.error_publish_avatar_converting);
+ return;
+ }
+ if (fileBackend.save(avatar)) {
+ final var connection = account.getXmppConnection();
+ final var future = connection.getManager(AvatarManager.class).publish(avatar, open);
+ Futures.addCallback(
+ future,
+ new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(Void result) {
+ callback.onAvatarPublicationSucceeded();
+ }
+
+ @Override
+ public void onFailure(@NonNull Throwable t) {
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid() + ": could not publish avatar",
+ t);
+ callback.onAvatarPublicationFailed(
+ R.string.error_publish_avatar_server_reject);
+ }
+ },
+ MoreExecutors.directExecutor());
+
+ } else {
+ Log.d(Config.LOGTAG, "could not save avatar");
+ callback.onAvatarPublicationFailed(R.string.error_saving_avatar);
}
}
@@ -4430,211 +4450,6 @@ public class XmppConnectionService extends Service {
});
}
- public void publishAvatar(
- final Account account,
- final Avatar avatar,
- final boolean open,
- final OnAvatarPublication callback) {
- final Bundle options;
- if (account.getXmppConnection().getFeatures().pepPublishOptions()) {
- options = open ? PublishOptions.openAccess() : PublishOptions.presenceAccess();
- } else {
- options = null;
- }
- publishAvatar(account, avatar, options, true, callback);
- }
-
- public void publishAvatar(
- Account account,
- final Avatar avatar,
- final Bundle options,
- final boolean retry,
- final OnAvatarPublication callback) {
- Log.d(
- Config.LOGTAG,
- account.getJid().asBareJid() + ": publishing avatar. options=" + options);
- final Iq packet = this.mIqGenerator.publishAvatar(avatar, options);
- this.sendIqPacket(
- account,
- packet,
- result -> {
- if (result.getType() == Iq.Type.RESULT) {
- publishAvatarMetadata(account, avatar, options, true, callback);
- } else if (retry && PublishOptions.preconditionNotMet(result)) {
- pushNodeConfiguration(
- account,
- Namespace.AVATAR_DATA,
- options,
- new OnConfigurationPushed() {
- @Override
- public void onPushSucceeded() {
- Log.d(
- Config.LOGTAG,
- account.getJid().asBareJid()
- + ": changed node configuration for avatar"
- + " node");
- publishAvatar(account, avatar, options, false, callback);
- }
-
- @Override
- public void onPushFailed() {
- Log.d(
- Config.LOGTAG,
- account.getJid().asBareJid()
- + ": unable to change node configuration"
- + " for avatar node");
- publishAvatar(account, avatar, null, false, callback);
- }
- });
- } else {
- Element error = result.findChild("error");
- Log.d(
- Config.LOGTAG,
- account.getJid().asBareJid()
- + ": server rejected avatar "
- + (avatar.size / 1024)
- + "KiB "
- + (error != null ? error.toString() : ""));
- if (callback != null) {
- callback.onAvatarPublicationFailed(
- R.string.error_publish_avatar_server_reject);
- }
- }
- });
- }
-
- public void publishAvatarMetadata(
- Account account,
- final Avatar avatar,
- final Bundle options,
- final boolean retry,
- final OnAvatarPublication callback) {
- final Iq packet =
- XmppConnectionService.this.mIqGenerator.publishAvatarMetadata(avatar, options);
- sendIqPacket(
- account,
- packet,
- result -> {
- if (result.getType() == Iq.Type.RESULT) {
- if (account.setAvatar(avatar.getFilename())) {
- getAvatarService().clear(account);
- databaseBackend.updateAccount(account);
- notifyAccountAvatarHasChanged(account);
- }
- Log.d(
- Config.LOGTAG,
- account.getJid().asBareJid()
- + ": published avatar "
- + (avatar.size / 1024)
- + "KiB");
- if (callback != null) {
- callback.onAvatarPublicationSucceeded();
- }
- } else if (retry && PublishOptions.preconditionNotMet(result)) {
- pushNodeConfiguration(
- account,
- Namespace.AVATAR_METADATA,
- options,
- new OnConfigurationPushed() {
- @Override
- public void onPushSucceeded() {
- Log.d(
- Config.LOGTAG,
- account.getJid().asBareJid()
- + ": changed node configuration for avatar"
- + " meta data node");
- publishAvatarMetadata(
- account, avatar, options, false, callback);
- }
-
- @Override
- public void onPushFailed() {
- Log.d(
- Config.LOGTAG,
- account.getJid().asBareJid()
- + ": unable to change node configuration"
- + " for avatar meta data node");
- publishAvatarMetadata(
- account, avatar, null, false, callback);
- }
- });
- } else {
- if (callback != null) {
- callback.onAvatarPublicationFailed(
- R.string.error_publish_avatar_server_reject);
- }
- }
- });
- }
-
- public void republishAvatarIfNeeded(final Account account) {
- if (account.getAxolotlService().isPepBroken()) {
- Log.d(
- Config.LOGTAG,
- account.getJid().asBareJid()
- + ": skipping republication of avatar because pep is broken");
- return;
- }
- final Iq packet = this.mIqGenerator.retrieveAvatarMetaData(null);
- this.sendIqPacket(
- account,
- packet,
- new Consumer<Iq>() {
-
- private Avatar parseAvatar(final Iq packet) {
- final var pubsub = packet.getExtension(PubSub.class);
- if (pubsub == null) {
- return null;
- }
- final var items = pubsub.getItems();
- if (items == null) {
- return null;
- }
- final var item = items.getFirstItemWithId(Metadata.class);
- if (item == null) {
- return null;
- }
- return Avatar.parseMetadata(item.getKey(), item.getValue());
- }
-
- private boolean errorIsItemNotFound(Iq packet) {
- Element error = packet.findChild("error");
- return packet.getType() == Iq.Type.ERROR
- && error != null
- && error.hasChild("item-not-found");
- }
-
- @Override
- public void accept(final Iq packet) {
- if (packet.getType() == Iq.Type.RESULT || errorIsItemNotFound(packet)) {
- final Avatar serverAvatar = parseAvatar(packet);
- if (serverAvatar == null && account.getAvatar() != null) {
- final Avatar avatar =
- fileBackend.getStoredPepAvatar(account.getAvatar());
- if (avatar != null) {
- Log.d(
- Config.LOGTAG,
- account.getJid().asBareJid()
- + ": avatar on server was null. republishing");
- // publishing as 'open' - old server (that requires
- // republication) likely doesn't support access models anyway
- publishAvatar(
- account,
- fileBackend.getStoredPepAvatar(account.getAvatar()),
- true,
- null);
- } else {
- Log.e(
- Config.LOGTAG,
- account.getJid().asBareJid()
- + ": error rereading avatar");
- }
- }
- }
- }
- });
- }
-
public void cancelAvatarFetches(final Account account) {
synchronized (mInProgressAvatarFetches) {
for (final Iterator<String> iterator = mInProgressAvatarFetches.iterator();
@@ -1,6 +1,9 @@
package eu.siacs.conversations.xmpp.manager;
import android.util.Log;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.services.XmppConnectionService;
@@ -8,6 +11,9 @@ import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.pep.Avatar;
+import im.conversations.android.xmpp.NodeConfiguration;
+import im.conversations.android.xmpp.model.avatar.Data;
+import im.conversations.android.xmpp.model.avatar.Info;
import im.conversations.android.xmpp.model.avatar.Metadata;
import im.conversations.android.xmpp.model.pubsub.Items;
@@ -24,31 +30,35 @@ public class AvatarManager extends AbstractManager {
final var account = getAccount();
// TODO support retract
final var entry = items.getFirstItemWithId(Metadata.class);
+ Log.d(Config.LOGTAG, "<-- " + entry + " (" + from + ")");
final var avatar =
entry == null ? null : Avatar.parseMetadata(entry.getKey(), entry.getValue());
- if (avatar != null) {
- avatar.owner = from.asBareJid();
- if (service.getFileBackend().isAvatarCached(avatar)) {
- if (account.getJid().asBareJid().equals(from)) {
- if (account.setAvatar(avatar.getFilename())) {
- service.databaseBackend.updateAccount(account);
- service.notifyAccountAvatarHasChanged(account);
- }
- service.getAvatarService().clear(account);
+ if (avatar == null) {
+ Log.d(Config.LOGTAG, "could not parse avatar metadata from " + from);
+ return;
+ }
+ avatar.owner = from.asBareJid();
+ if (service.getFileBackend().isAvatarCached(avatar)) {
+ if (account.getJid().asBareJid().equals(from)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ service.databaseBackend.updateAccount(account);
+ service.notifyAccountAvatarHasChanged(account);
+ }
+ service.getAvatarService().clear(account);
+ service.updateConversationUi();
+ service.updateAccountUi();
+ } else {
+ final Contact contact = account.getRoster().getContact(from);
+ if (contact.setAvatar(avatar)) {
+ connection.getManager(RosterManager.class).writeToDatabaseAsync();
+ service.getAvatarService().clear(contact);
service.updateConversationUi();
- service.updateAccountUi();
- } else {
- final Contact contact = account.getRoster().getContact(from);
- if (contact.setAvatar(avatar)) {
- connection.getManager(RosterManager.class).writeToDatabaseAsync();
- service.getAvatarService().clear(contact);
- service.updateConversationUi();
- service.updateRosterUi();
- }
+ service.updateRosterUi();
}
- } else if (service.isDataSaverDisabled()) {
- service.fetchAvatar(account, avatar);
}
+ } else if (service.isDataSaverDisabled()) {
+ // TODO use internal mechanism to fetch PEP avatars
+ service.fetchAvatar(account, avatar);
}
}
@@ -63,6 +73,29 @@ public class AvatarManager extends AbstractManager {
}
}
+ public ListenableFuture<Void> publish(final Avatar avatar, final boolean open) {
+ final NodeConfiguration configuration =
+ open ? NodeConfiguration.OPEN : NodeConfiguration.PRESENCE;
+ final var avatarData = new Data();
+ avatarData.setContent(avatar.getImageAsBytes());
+ final var future =
+ getManager(PepManager.class).publish(avatarData, avatar.sha1sum, configuration);
+ return Futures.transformAsync(
+ future,
+ v -> {
+ final var id = avatar.sha1sum;
+ final var metadata = new Metadata();
+ final var info = metadata.addExtension(new Info());
+ info.setBytes(avatar.size);
+ info.setId(avatar.sha1sum);
+ info.setHeight(avatar.height);
+ info.setWidth(avatar.width);
+ info.setType(avatar.type);
+ return getManager(PepManager.class).publish(metadata, id, configuration);
+ },
+ MoreExecutors.directExecutor());
+ }
+
public boolean hasPepToVCardConversion() {
return getManager(DiscoManager.class).hasAccountFeature(Namespace.AVATAR_CONVERSION);
}