VCardManager.java

  1package eu.siacs.conversations.xmpp.manager;
  2
  3import android.content.Context;
  4import android.util.Log;
  5import com.google.common.util.concurrent.Futures;
  6import com.google.common.util.concurrent.ListenableFuture;
  7import com.google.common.util.concurrent.MoreExecutors;
  8import eu.siacs.conversations.Config;
  9import eu.siacs.conversations.xmpp.Jid;
 10import eu.siacs.conversations.xmpp.XmppConnection;
 11import im.conversations.android.xmpp.IqErrorException;
 12import im.conversations.android.xmpp.model.error.Condition;
 13import im.conversations.android.xmpp.model.stanza.Iq;
 14import im.conversations.android.xmpp.model.vcard.BinaryValue;
 15import im.conversations.android.xmpp.model.vcard.Photo;
 16import im.conversations.android.xmpp.model.vcard.VCard;
 17import java.util.Objects;
 18
 19public class VCardManager extends AbstractManager {
 20
 21    public VCardManager(final Context context, final XmppConnection connection) {
 22        super(context, connection);
 23    }
 24
 25    public ListenableFuture<VCard> retrieve(final Jid address) {
 26        final var iq = new Iq(Iq.Type.GET, new VCard());
 27        iq.setTo(address);
 28        return Futures.transform(
 29                this.connection.sendIqPacket(iq),
 30                result -> {
 31                    final var vCard = result.getExtension(VCard.class);
 32                    if (vCard == null) {
 33                        throw new IllegalStateException("Result did not include vCard");
 34                    }
 35                    return vCard;
 36                },
 37                MoreExecutors.directExecutor());
 38    }
 39
 40    public ListenableFuture<byte[]> retrievePhoto(final Jid address) {
 41
 42        // TODO add a caching variant
 43
 44        final var vCardFuture = retrieve(address);
 45        return Futures.transform(
 46                vCardFuture,
 47                vCard -> {
 48                    final var photo = vCard.getPhoto();
 49                    if (photo == null) {
 50                        throw new IllegalStateException(
 51                                String.format("No photo in vCard of %s", address));
 52                    }
 53                    final var binaryValue = photo.getBinaryValue();
 54                    if (binaryValue == null) {
 55                        throw new IllegalStateException(
 56                                String.format("Photo has no binary value in vCard of %s", address));
 57                    }
 58                    return binaryValue.asBytes();
 59                },
 60                MoreExecutors.directExecutor());
 61    }
 62
 63    public ListenableFuture<Void> publish(final VCard vCard) {
 64        return publish(getAccount().getJid().asBareJid(), vCard);
 65    }
 66
 67    public ListenableFuture<Void> publish(final Jid address, final VCard vCard) {
 68        final var iq = new Iq(Iq.Type.SET, vCard);
 69        iq.setTo(address);
 70        return Futures.transform(
 71                connection.sendIqPacket(iq), result -> null, MoreExecutors.directExecutor());
 72    }
 73
 74    public ListenableFuture<Void> deletePhoto() {
 75        final var vCardFuture = retrieve(getAccount().getJid().asBareJid());
 76        return Futures.transformAsync(
 77                vCardFuture,
 78                vCard -> {
 79                    final var photo = vCard.getPhoto();
 80                    if (photo == null) {
 81                        return Futures.immediateFuture(null);
 82                    }
 83                    Log.d(
 84                            Config.LOGTAG,
 85                            "deleting photo from vCard. binaryValue="
 86                                    + Objects.nonNull(photo.getBinaryValue()));
 87                    photo.clearChildren();
 88                    return publish(vCard);
 89                },
 90                MoreExecutors.directExecutor());
 91    }
 92
 93    public ListenableFuture<Void> publishPhoto(
 94            final Jid address, final String type, final byte[] image) {
 95        final var retrieveFuture = this.retrieve(address);
 96
 97        final var caughtFuture =
 98                Futures.catchingAsync(
 99                        retrieveFuture,
100                        IqErrorException.class,
101                        ex -> {
102                            final var error = ex.getError();
103                            if (error != null
104                                    && error.getCondition() instanceof Condition.ItemNotFound) {
105                                return Futures.immediateFuture(null);
106                            } else {
107                                return Futures.immediateFailedFuture(ex);
108                            }
109                        },
110                        MoreExecutors.directExecutor());
111
112        return Futures.transformAsync(
113                caughtFuture,
114                existing -> {
115                    final VCard vCard;
116                    if (existing == null) {
117                        Log.d(Config.LOGTAG, "item-not-found. created fresh vCard");
118                        vCard = new VCard();
119                    } else {
120                        vCard = existing;
121                    }
122                    final var photo = new Photo();
123                    photo.setType(type);
124                    photo.addExtension(new BinaryValue()).setContent(image);
125                    vCard.setExtension(photo);
126                    return publish(address, vCard);
127                },
128                MoreExecutors.directExecutor());
129    }
130}