1package eu.siacs.conversations.xmpp.manager;
2
3import android.util.Log;
4import com.google.common.util.concurrent.Futures;
5import com.google.common.util.concurrent.ListenableFuture;
6import com.google.common.util.concurrent.MoreExecutors;
7import eu.siacs.conversations.Config;
8import eu.siacs.conversations.entities.Contact;
9import eu.siacs.conversations.services.XmppConnectionService;
10import eu.siacs.conversations.xml.Namespace;
11import eu.siacs.conversations.xmpp.Jid;
12import eu.siacs.conversations.xmpp.XmppConnection;
13import eu.siacs.conversations.xmpp.pep.Avatar;
14import im.conversations.android.xmpp.NodeConfiguration;
15import im.conversations.android.xmpp.model.avatar.Data;
16import im.conversations.android.xmpp.model.avatar.Info;
17import im.conversations.android.xmpp.model.avatar.Metadata;
18import im.conversations.android.xmpp.model.pubsub.Items;
19
20public class AvatarManager extends AbstractManager {
21
22 private final XmppConnectionService service;
23
24 public AvatarManager(final XmppConnectionService service, XmppConnection connection) {
25 super(service.getApplicationContext(), connection);
26 this.service = service;
27 }
28
29 public void handleItems(final Jid from, final Items items) {
30 final var account = getAccount();
31 // TODO support retract
32 final var entry = items.getFirstItemWithId(Metadata.class);
33 Log.d(Config.LOGTAG, "<-- " + entry + " (" + from + ")");
34 final var avatar =
35 entry == null ? null : Avatar.parseMetadata(entry.getKey(), entry.getValue());
36 if (avatar == null) {
37 Log.d(Config.LOGTAG, "could not parse avatar metadata from " + from);
38 return;
39 }
40 avatar.owner = from.asBareJid();
41 if (service.getFileBackend().isAvatarCached(avatar)) {
42 if (account.getJid().asBareJid().equals(from)) {
43 if (account.setAvatar(avatar.getFilename())) {
44 service.databaseBackend.updateAccount(account);
45 service.notifyAccountAvatarHasChanged(account);
46 }
47 service.getAvatarService().clear(account);
48 service.updateConversationUi();
49 service.updateAccountUi();
50 } else {
51 final Contact contact = account.getRoster().getContact(from);
52 if (contact.setAvatar(avatar)) {
53 connection.getManager(RosterManager.class).writeToDatabaseAsync();
54 service.getAvatarService().clear(contact);
55 service.updateConversationUi();
56 service.updateRosterUi();
57 }
58 }
59 } else if (service.isDataSaverDisabled()) {
60 // TODO use internal mechanism to fetch PEP avatars
61 service.fetchAvatar(account, avatar);
62 }
63 }
64
65 public void handleDelete(final Jid from) {
66 final var account = getAccount();
67 final boolean isAccount = account.getJid().asBareJid().equals(from);
68 if (isAccount) {
69 account.setAvatar(null);
70 getDatabase().updateAccount(account);
71 service.getAvatarService().clear(account);
72 Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": deleted avatar metadata node");
73 }
74 }
75
76 public ListenableFuture<Void> publish(final Avatar avatar, final boolean open) {
77 final NodeConfiguration configuration =
78 open ? NodeConfiguration.OPEN : NodeConfiguration.PRESENCE;
79 final var avatarData = new Data();
80 avatarData.setContent(avatar.getImageAsBytes());
81 final var future =
82 getManager(PepManager.class).publish(avatarData, avatar.sha1sum, configuration);
83 return Futures.transformAsync(
84 future,
85 v -> {
86 final var id = avatar.sha1sum;
87 final var metadata = new Metadata();
88 final var info = metadata.addExtension(new Info());
89 info.setBytes(avatar.size);
90 info.setId(avatar.sha1sum);
91 info.setHeight(avatar.height);
92 info.setWidth(avatar.width);
93 info.setType(avatar.type);
94 return getManager(PepManager.class).publish(metadata, id, configuration);
95 },
96 MoreExecutors.directExecutor());
97 }
98
99 public boolean hasPepToVCardConversion() {
100 return getManager(DiscoManager.class).hasAccountFeature(Namespace.AVATAR_CONVERSION);
101 }
102}