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        final var vCardFuture = retrieve(address);
 42        return Futures.transform(
 43                vCardFuture,
 44                vCard -> {
 45                    final var photo = vCard.getPhoto();
 46                    if (photo == null) {
 47                        throw new IllegalStateException(
 48                                String.format("No photo in vCard of %s", address));
 49                    }
 50                    final var binaryValue = photo.getBinaryValue();
 51                    if (binaryValue == null) {
 52                        throw new IllegalStateException(
 53                                String.format("Photo has no binary value in vCard of %s", address));
 54                    }
 55                    return binaryValue.asBytes();
 56                },
 57                MoreExecutors.directExecutor());
 58    }
 59
 60    public ListenableFuture<Void> publish(final VCard vCard) {
 61        return publish(getAccount().getJid().asBareJid(), vCard);
 62    }
 63
 64    public ListenableFuture<Void> publish(final Jid address, final VCard vCard) {
 65        final var iq = new Iq(Iq.Type.SET, vCard);
 66        iq.setTo(address);
 67        return Futures.transform(
 68                connection.sendIqPacket(iq), result -> null, MoreExecutors.directExecutor());
 69    }
 70
 71    public ListenableFuture<Void> deletePhoto() {
 72        final var vCardFuture = retrieve(getAccount().getJid().asBareJid());
 73        return Futures.transformAsync(
 74                vCardFuture,
 75                vCard -> {
 76                    final var photo = vCard.getPhoto();
 77                    if (photo == null) {
 78                        return Futures.immediateFuture(null);
 79                    }
 80                    Log.d(
 81                            Config.LOGTAG,
 82                            "deleting photo from vCard. binaryValue="
 83                                    + Objects.nonNull(photo.getBinaryValue()));
 84                    photo.clearChildren();
 85                    return publish(vCard);
 86                },
 87                MoreExecutors.directExecutor());
 88    }
 89
 90    public ListenableFuture<Void> publishPhoto(
 91            final Jid address, final String type, final byte[] image) {
 92        final var retrieveFuture = this.retrieve(address);
 93
 94        final var caughtFuture =
 95                Futures.catchingAsync(
 96                        retrieveFuture,
 97                        IqErrorException.class,
 98                        ex -> {
 99                            final var error = ex.getError();
100                            if (error != null
101                                    && error.getCondition() instanceof Condition.ItemNotFound) {
102                                return Futures.immediateFuture(null);
103                            } else {
104                                return Futures.immediateFailedFuture(ex);
105                            }
106                        },
107                        MoreExecutors.directExecutor());
108
109        return Futures.transformAsync(
110                caughtFuture,
111                existing -> {
112                    final VCard vCard;
113                    if (existing == null) {
114                        Log.d(Config.LOGTAG, "item-not-found. created fresh vCard");
115                        vCard = new VCard();
116                    } else {
117                        vCard = existing;
118                    }
119                    final var photo = new Photo();
120                    photo.setType(type);
121                    photo.addExtension(new BinaryValue()).setContent(image);
122                    vCard.setExtension(photo);
123                    return publish(address, vCard);
124                },
125                MoreExecutors.directExecutor());
126    }
127}