Contact.java

  1package eu.siacs.conversations.entities;
  2
  3import android.content.ComponentName;
  4import android.content.ContentValues;
  5import android.content.Context;
  6import android.content.pm.PackageManager;
  7import android.database.Cursor;
  8import android.graphics.drawable.Icon;
  9import android.net.Uri;
 10import android.os.Build;
 11import android.os.Bundle;
 12import android.telecom.PhoneAccount;
 13import android.telecom.PhoneAccountHandle;
 14import android.telecom.TelecomManager;
 15import android.text.TextUtils;
 16import android.util.Log;
 17
 18import androidx.annotation.NonNull;
 19
 20import com.google.common.base.Strings;
 21
 22import org.json.JSONArray;
 23import org.json.JSONException;
 24import org.json.JSONObject;
 25
 26import java.util.ArrayList;
 27import java.util.Collection;
 28import java.util.HashSet;
 29import java.util.List;
 30import java.util.Locale;
 31import java.util.Objects;
 32
 33import eu.siacs.conversations.BuildConfig;
 34import eu.siacs.conversations.Config;
 35import eu.siacs.conversations.R;
 36import eu.siacs.conversations.android.AbstractPhoneContact;
 37import eu.siacs.conversations.android.JabberIdContact;
 38import eu.siacs.conversations.persistance.FileBackend;
 39import eu.siacs.conversations.services.AvatarService;
 40import eu.siacs.conversations.services.QuickConversationsService;
 41import eu.siacs.conversations.services.XmppConnectionService;
 42import eu.siacs.conversations.utils.JidHelper;
 43import eu.siacs.conversations.utils.UIHelper;
 44import eu.siacs.conversations.xml.Element;
 45import eu.siacs.conversations.xmpp.Jid;
 46import eu.siacs.conversations.xmpp.jingle.RtpCapability;
 47import eu.siacs.conversations.xmpp.pep.Avatar;
 48
 49public class Contact implements ListItem, Blockable {
 50    public static final String TABLENAME = "contacts";
 51
 52    public static final String SYSTEMNAME = "systemname";
 53    public static final String SERVERNAME = "servername";
 54    public static final String PRESENCE_NAME = "presence_name";
 55    public static final String JID = "jid";
 56    public static final String OPTIONS = "options";
 57    public static final String SYSTEMACCOUNT = "systemaccount";
 58    public static final String PHOTOURI = "photouri";
 59    public static final String KEYS = "pgpkey";
 60    public static final String ACCOUNT = "accountUuid";
 61    public static final String AVATAR = "avatar";
 62    public static final String LAST_PRESENCE = "last_presence";
 63    public static final String LAST_TIME = "last_time";
 64    public static final String GROUPS = "groups";
 65    public static final String RTP_CAPABILITY = "rtpCapability";
 66    private String accountUuid;
 67    private String systemName;
 68    private String serverName;
 69    private String presenceName;
 70    private String commonName;
 71    protected Jid jid;
 72    private int subscription = 0;
 73    private Uri systemAccount;
 74    private String photoUri;
 75    private final JSONObject keys;
 76    private JSONArray groups = new JSONArray();
 77    private JSONArray systemTags = new JSONArray();
 78    private final Presences presences = new Presences();
 79    protected Account account;
 80    protected Avatar avatar;
 81
 82    private boolean mActive = false;
 83    private long mLastseen = 0;
 84    private String mLastPresence = null;
 85    private RtpCapability.Capability rtpCapability;
 86
 87    public Contact(Contact other) {
 88        this(null, other.systemName, other.serverName, other.presenceName, other.jid, other.subscription, other.photoUri, other.systemAccount, other.keys == null ? null : other.keys.toString(), other.getAvatar() == null ? null : other.getAvatar().sha1sum, other.mLastseen, other.mLastPresence, other.groups == null ? null : other.groups.toString(), other.rtpCapability);
 89        setAccount(other.getAccount());
 90    }
 91
 92    public Contact(final String account, final String systemName, final String serverName, final String presenceName,
 93                   final Jid jid, final int subscription, final String photoUri,
 94                   final Uri systemAccount, final String keys, final String avatar, final long lastseen,
 95                   final String presence, final String groups, final RtpCapability.Capability rtpCapability) {
 96        this.accountUuid = account;
 97        this.systemName = systemName;
 98        this.serverName = serverName;
 99        this.presenceName = presenceName;
100        this.jid = jid;
101        this.subscription = subscription;
102        this.photoUri = photoUri;
103        this.systemAccount = systemAccount;
104        JSONObject tmpJsonObject;
105        try {
106            tmpJsonObject = (keys == null ? new JSONObject("") : new JSONObject(keys));
107        } catch (JSONException e) {
108            tmpJsonObject = new JSONObject();
109        }
110        this.keys = tmpJsonObject;
111        if (avatar != null) {
112            this.avatar = new Avatar();
113            this.avatar.sha1sum = avatar;
114            this.avatar.origin = Avatar.Origin.VCARD; //always assume worst
115        }
116        try {
117            this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
118        } catch (JSONException e) {
119            this.groups = new JSONArray();
120        }
121        this.mLastseen = lastseen;
122        this.mLastPresence = presence;
123        this.rtpCapability = rtpCapability;
124    }
125
126    public Contact(final Jid jid) {
127        this.jid = jid;
128        this.keys = new JSONObject();
129    }
130
131    public static Contact fromCursor(final Cursor cursor) {
132        final Jid jid;
133        try {
134            jid = Jid.of(cursor.getString(cursor.getColumnIndex(JID)));
135        } catch (final IllegalArgumentException e) {
136            // TODO: Borked DB... handle this somehow?
137            return null;
138        }
139        Uri systemAccount;
140        try {
141            systemAccount = Uri.parse(cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)));
142        } catch (Exception e) {
143            systemAccount = null;
144        }
145        return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)),
146                cursor.getString(cursor.getColumnIndex(SYSTEMNAME)),
147                cursor.getString(cursor.getColumnIndex(SERVERNAME)),
148                cursor.getString(cursor.getColumnIndex(PRESENCE_NAME)),
149                jid,
150                cursor.getInt(cursor.getColumnIndex(OPTIONS)),
151                cursor.getString(cursor.getColumnIndex(PHOTOURI)),
152                systemAccount,
153                cursor.getString(cursor.getColumnIndex(KEYS)),
154                cursor.getString(cursor.getColumnIndex(AVATAR)),
155                cursor.getLong(cursor.getColumnIndex(LAST_TIME)),
156                cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
157                cursor.getString(cursor.getColumnIndex(GROUPS)),
158                RtpCapability.Capability.of(cursor.getString(cursor.getColumnIndex(RTP_CAPABILITY))));
159    }
160
161    public String getDisplayName() {
162        if (isSelf() && TextUtils.isEmpty(this.systemName)) {
163            final String displayName = account.getDisplayName();
164            if (!Strings.isNullOrEmpty(displayName)) {
165                return displayName;
166            }
167        }
168        if (Config.X509_VERIFICATION && !TextUtils.isEmpty(this.commonName)) {
169            return this.commonName;
170        } else if (!TextUtils.isEmpty(this.systemName)) {
171            return this.systemName;
172        } else if (!TextUtils.isEmpty(this.serverName)) {
173            return this.serverName;
174        }
175
176        ListItem bookmark = account.getBookmark(jid);
177        if (bookmark != null) {
178            return bookmark.getDisplayName();
179        } else if (!TextUtils.isEmpty(this.presenceName) && ((QuickConversationsService.isQuicksy() && JidHelper.isQuicksyDomain(jid.getDomain())) || mutualPresenceSubscription())) {
180            return this.presenceName;
181        } else if (jid.getLocal() != null) {
182            return JidHelper.localPartOrFallback(jid);
183        } else {
184            return jid.getDomain().toEscapedString();
185        }
186    }
187
188    public String getPublicDisplayName() {
189        if (!TextUtils.isEmpty(this.presenceName)) {
190            return this.presenceName;
191        } else if (jid.getLocal() != null) {
192            return JidHelper.localPartOrFallback(jid);
193        } else {
194            return jid.getDomain().toEscapedString();
195        }
196    }
197
198    public String getProfilePhoto() {
199        return this.photoUri;
200    }
201
202    public Jid getJid() {
203        return jid;
204    }
205
206    public List<Tag> getGroupTags() {
207        final ArrayList<Tag> tags = new ArrayList<>();
208        for (final String group : getGroups(true)) {
209            tags.add(new Tag(group, UIHelper.getColorForName(group)));
210        }
211        return tags;
212    }
213
214    @Override
215    public List<Tag> getTags(Context context) {
216        final HashSet<Tag> tags = new HashSet<>();
217        tags.addAll(getGroupTags());
218        for (final String tag : getSystemTags(true)) {
219            tags.add(new Tag(tag, UIHelper.getColorForName(tag)));
220        }
221        Presence.Status status = getShownStatus();
222        if (status != Presence.Status.OFFLINE) {
223            tags.add(UIHelper.getTagForStatus(context, status));
224        }
225        if (isBlocked()) {
226            tags.add(new Tag(context.getString(R.string.blocked), 0xff2e2f3b));
227        }
228        if (!showInRoster() && getSystemAccount() != null) {
229            tags.add(new Tag("Android", UIHelper.getColorForName("Android")));
230        }
231        return new ArrayList<>(tags);
232    }
233
234    public boolean match(Context context, String needle) {
235        if (TextUtils.isEmpty(needle)) {
236            return true;
237        }
238        needle = needle.toLowerCase(Locale.US).trim();
239        String[] parts = needle.split("[,\\s]+");
240        if (parts.length > 1) {
241            for (String part : parts) {
242                if (!match(context, part)) {
243                    return false;
244                }
245            }
246            return true;
247        } else if(parts.length > 0) {
248            return jid.toString().contains(parts[0]) ||
249                    getDisplayName().toLowerCase(Locale.US).contains(parts[0]) ||
250                    matchInTag(context, parts[0]);
251        } else {
252            return jid.toString().contains(needle) ||
253                    getDisplayName().toLowerCase(Locale.US).contains(needle);
254        }
255    }
256
257    private boolean matchInTag(Context context, String needle) {
258        needle = needle.toLowerCase(Locale.US);
259        for (Tag tag : getTags(context)) {
260            if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
261                return true;
262            }
263        }
264        return false;
265    }
266
267    public ContentValues getContentValues() {
268        synchronized (this.keys) {
269            final ContentValues values = new ContentValues();
270            values.put(ACCOUNT, accountUuid);
271            values.put(SYSTEMNAME, systemName);
272            values.put(SERVERNAME, serverName);
273            values.put(PRESENCE_NAME, presenceName);
274            values.put(JID, jid.toString());
275            values.put(OPTIONS, subscription);
276            values.put(SYSTEMACCOUNT, systemAccount != null ? systemAccount.toString() : null);
277            values.put(PHOTOURI, photoUri);
278            values.put(KEYS, keys.toString());
279            values.put(AVATAR, avatar == null ? null : avatar.getFilename());
280            values.put(LAST_PRESENCE, mLastPresence);
281            values.put(LAST_TIME, mLastseen);
282            values.put(GROUPS, groups.toString());
283            values.put(RTP_CAPABILITY, rtpCapability == null ? null : rtpCapability.toString());
284            return values;
285        }
286    }
287
288    public Account getAccount() {
289        return this.account;
290    }
291
292    public void setAccount(Account account) {
293        this.account = account;
294        this.accountUuid = account.getUuid();
295    }
296
297    public Presences getPresences() {
298        return this.presences;
299    }
300
301    public void updatePresence(final String resource, final Presence presence) {
302        this.presences.updatePresence(resource, presence);
303    }
304
305    public void removePresence(final String resource) {
306        this.presences.removePresence(resource);
307    }
308
309    public void clearPresences() {
310        this.presences.clearPresences();
311        this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
312    }
313
314    public Presence.Status getShownStatus() {
315        return this.presences.getShownStatus();
316    }
317
318    public Jid resourceWhichSupport(final String namespace) {
319        final String resource = getPresences().firstWhichSupport(namespace);
320        if (resource == null) return null;
321
322        return resource.equals("") ? getJid() : getJid().withResource(resource);
323    }
324
325    public boolean setPhotoUri(String uri) {
326        if (uri != null && !uri.equals(this.photoUri)) {
327            this.photoUri = uri;
328            return true;
329        } else if (this.photoUri != null && uri == null) {
330            this.photoUri = null;
331            return true;
332        } else {
333            return false;
334        }
335    }
336
337    public void setServerName(String serverName) {
338        this.serverName = serverName;
339    }
340
341    public boolean setSystemName(String systemName) {
342        final String old = getDisplayName();
343        this.systemName = systemName;
344        return !old.equals(getDisplayName());
345    }
346
347    public boolean setSystemTags(Collection<String> systemTags) {
348        final JSONArray old = this.systemTags;
349        this.systemTags = new JSONArray();
350        for(String tag : systemTags) {
351            this.systemTags.put(tag);
352        }
353        return !old.equals(this.systemTags);
354    }
355
356    public boolean setPresenceName(String presenceName) {
357        final String old = getDisplayName();
358        this.presenceName = presenceName;
359        return !old.equals(getDisplayName());
360    }
361
362    public Uri getSystemAccount() {
363        return systemAccount;
364    }
365
366    public void setSystemAccount(Uri lookupUri) {
367        this.systemAccount = lookupUri;
368    }
369
370    public void setGroups(List<String> groups) {
371        this.groups = new JSONArray(groups);
372    }
373
374    private Collection<String> getGroups(final boolean unique) {
375        final Collection<String> groups = unique ? new HashSet<>() : new ArrayList<>();
376        for (int i = 0; i < this.groups.length(); ++i) {
377            try {
378                groups.add(this.groups.getString(i));
379            } catch (final JSONException ignored) {
380            }
381        }
382        return groups;
383    }
384
385    public void copySystemTagsToGroups() {
386        for (String tag : getSystemTags(true)) {
387            this.groups.put(tag);
388        }
389    }
390
391    private Collection<String> getSystemTags(final boolean unique) {
392        final Collection<String> tags = unique ? new HashSet<>() : new ArrayList<>();
393        for (int i = 0; i < this.systemTags.length(); ++i) {
394            try {
395                tags.add(this.systemTags.getString(i));
396            } catch (final JSONException ignored) {
397            }
398        }
399        return tags;
400    }
401
402    public long getPgpKeyId() {
403        synchronized (this.keys) {
404            if (this.keys.has("pgp_keyid")) {
405                try {
406                    return this.keys.getLong("pgp_keyid");
407                } catch (JSONException e) {
408                    return 0;
409                }
410            } else {
411                return 0;
412            }
413        }
414    }
415
416    public boolean setPgpKeyId(long keyId) {
417        final long previousKeyId = getPgpKeyId();
418        synchronized (this.keys) {
419            try {
420                this.keys.put("pgp_keyid", keyId);
421                return previousKeyId != keyId;
422            } catch (final JSONException ignored) {
423            }
424        }
425        return false;
426    }
427
428    public void setOption(int option) {
429        this.subscription |= 1 << option;
430    }
431
432    public void resetOption(int option) {
433        this.subscription &= ~(1 << option);
434    }
435
436    public boolean getOption(int option) {
437        return ((this.subscription & (1 << option)) != 0);
438    }
439
440    public boolean canInferPresence() {
441        return showInContactList() || isSelf();
442    }
443
444    public boolean showInRoster() {
445        return (this.getOption(Contact.Options.IN_ROSTER) && (!this
446                .getOption(Contact.Options.DIRTY_DELETE)))
447                || (this.getOption(Contact.Options.DIRTY_PUSH));
448    }
449
450    public boolean showInContactList() {
451        return showInRoster()
452                || getOption(Options.SYNCED_VIA_OTHER)
453                || (QuickConversationsService.isQuicksy() && systemAccount != null);
454    }
455
456    public void parseSubscriptionFromElement(Element item) {
457        String ask = item.getAttribute("ask");
458        String subscription = item.getAttribute("subscription");
459
460        if (subscription == null) {
461            this.resetOption(Options.FROM);
462            this.resetOption(Options.TO);
463        } else {
464            switch (subscription) {
465                case "to":
466                    this.resetOption(Options.FROM);
467                    this.setOption(Options.TO);
468                    break;
469                case "from":
470                    this.resetOption(Options.TO);
471                    this.setOption(Options.FROM);
472                    this.resetOption(Options.PREEMPTIVE_GRANT);
473                    this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
474                    break;
475                case "both":
476                    this.setOption(Options.TO);
477                    this.setOption(Options.FROM);
478                    this.resetOption(Options.PREEMPTIVE_GRANT);
479                    this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
480                    break;
481                case "none":
482                    this.resetOption(Options.FROM);
483                    this.resetOption(Options.TO);
484                    break;
485            }
486        }
487
488        // do NOT override asking if pending push request
489        if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
490            if ((ask != null) && (ask.equals("subscribe"))) {
491                this.setOption(Contact.Options.ASKING);
492            } else {
493                this.resetOption(Contact.Options.ASKING);
494            }
495        }
496    }
497
498    public void parseGroupsFromElement(Element item) {
499        this.groups = new JSONArray();
500        for (Element element : item.getChildren()) {
501            if (element.getName().equals("group") && element.getContent() != null) {
502                this.groups.put(element.getContent());
503            }
504        }
505    }
506
507    public Element asElement() {
508        final Element item = new Element("item");
509        item.setAttribute("jid", this.jid);
510        if (this.serverName != null) {
511            item.setAttribute("name", this.serverName);
512        } else {
513            item.setAttribute("name", getDisplayName());
514        }
515        for (String group : getGroups(false)) {
516            item.addChild("group").setContent(group);
517        }
518        return item;
519    }
520
521    @Override
522    public int compareTo(@NonNull final ListItem another) {
523        if (getJid().isDomainJid() && !another.getJid().isDomainJid()) {
524            return -1;
525        } else if (!getJid().isDomainJid() && another.getJid().isDomainJid()) {
526            return 1;
527        }
528
529        if (getDisplayName().equals(another.getDisplayName())) {
530            return getJid().compareTo(another.getJid());
531        }
532
533        return this.getDisplayName().compareToIgnoreCase(
534                another.getDisplayName());
535    }
536
537    public String getServer() {
538        return getJid().getDomain().toEscapedString();
539    }
540
541    public void setAvatar(Avatar avatar) {
542        setAvatar(avatar, false);
543    }
544
545    public void setAvatar(Avatar avatar, boolean previouslyOmittedPepFetch) {
546        if (this.avatar != null && this.avatar.equals(avatar)) {
547            return;
548        }
549        if (!previouslyOmittedPepFetch && this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
550            return;
551        }
552        this.avatar = avatar;
553    }
554
555    public String getAvatarFilename() {
556        return avatar == null ? null : avatar.getFilename();
557    }
558
559    public Avatar getAvatar() {
560        return avatar;
561    }
562
563    public boolean mutualPresenceSubscription() {
564        return getOption(Options.FROM) && getOption(Options.TO);
565    }
566
567    @Override
568    public boolean isBlocked() {
569        return getAccount().isBlocked(this);
570    }
571
572    @Override
573    public boolean isDomainBlocked() {
574        return getAccount().isBlocked(this.getJid().getDomain());
575    }
576
577    @Override
578    public Jid getBlockedJid() {
579        if (isDomainBlocked()) {
580            return getJid().getDomain();
581        } else {
582            return getJid();
583        }
584    }
585
586    public boolean isSelf() {
587        return account.getJid().asBareJid().equals(jid.asBareJid());
588    }
589
590    boolean isOwnServer() {
591        return account.getJid().getDomain().equals(jid.asBareJid());
592    }
593
594    public void setCommonName(String cn) {
595        this.commonName = cn;
596    }
597
598    public void flagActive() {
599        this.mActive = true;
600    }
601
602    public void flagInactive() {
603        this.mActive = false;
604    }
605
606    public boolean isActive() {
607        return this.mActive;
608    }
609
610    public boolean setLastseen(long timestamp) {
611        if (timestamp > this.mLastseen) {
612            this.mLastseen = timestamp;
613            return true;
614        } else {
615            return false;
616        }
617    }
618
619    public long getLastseen() {
620        return this.mLastseen;
621    }
622
623    public void setLastResource(String resource) {
624        this.mLastPresence = resource;
625    }
626
627    public String getLastResource() {
628        return this.mLastPresence;
629    }
630
631    public String getServerName() {
632        return serverName;
633    }
634
635    public synchronized boolean setPhoneContact(AbstractPhoneContact phoneContact) {
636        setOption(getOption(phoneContact.getClass()));
637        setSystemAccount(phoneContact.getLookupUri());
638        boolean changed = setSystemName(phoneContact.getDisplayName());
639        changed |= setPhotoUri(phoneContact.getPhotoUri());
640        return changed;
641    }
642
643    public synchronized boolean unsetPhoneContact(Class<? extends AbstractPhoneContact> clazz) {
644        resetOption(getOption(clazz));
645        boolean changed = false;
646        if (!getOption(Options.SYNCED_VIA_ADDRESSBOOK) && !getOption(Options.SYNCED_VIA_OTHER)) {
647            setSystemAccount(null);
648            changed |= setPhotoUri(null);
649            changed |= setSystemName(null);
650        }
651        return changed;
652    }
653
654    protected String phoneAccountLabel() {
655        return account.getJid().asBareJid().toString() +
656            "/" + getJid().asBareJid().toString();
657    }
658
659    public PhoneAccountHandle phoneAccountHandle() {
660        ComponentName componentName = new ComponentName(
661            BuildConfig.APPLICATION_ID,
662            "com.cheogram.android.ConnectionService"
663        );
664        return new PhoneAccountHandle(componentName, phoneAccountLabel());
665    }
666
667    // This Contact is a gateway to use for voice calls, register it with OS
668    public void registerAsPhoneAccount(XmppConnectionService ctx) {
669        if (Build.VERSION.SDK_INT < 23) return;
670        if (Build.VERSION.SDK_INT >= 33) {
671            if (!ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELECOM) && !ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE)) return;
672        } else {
673            if (!ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE)) return;
674        }
675
676        TelecomManager telecomManager = ctx.getSystemService(TelecomManager.class);
677
678        PhoneAccount phoneAccount = PhoneAccount.builder(
679            phoneAccountHandle(),
680            account.getJid().asBareJid().toString()
681        ).setAddress(
682            Uri.fromParts("xmpp", account.getJid().asBareJid().toString(), null)
683        ).setIcon(
684            Icon.createWithBitmap(FileBackend.drawDrawable(ctx.getAvatarService().get(this, AvatarService.getSystemUiAvatarSize(ctx) / 2, false)))
685        ).setHighlightColor(
686            0x7401CF
687        ).setShortDescription(
688            getJid().asBareJid().toString()
689        ).setCapabilities(
690            PhoneAccount.CAPABILITY_CALL_PROVIDER
691        ).build();
692
693        try {
694            telecomManager.registerPhoneAccount(phoneAccount);
695        } catch (final Exception e) {
696            Log.w(Config.LOGTAG, "Could not registerPhoneAccount: " + e);
697        }
698    }
699
700    // Unregister any associated PSTN gateway integration
701    public void unregisterAsPhoneAccount(Context ctx) {
702        if (Build.VERSION.SDK_INT < 23) return;
703        if (Build.VERSION.SDK_INT >= 33) {
704            if (!ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELECOM) && !ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE)) return;
705        } else {
706            if (!ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE)) return;
707        }
708
709        TelecomManager telecomManager = ctx.getSystemService(TelecomManager.class);
710
711        try {
712            telecomManager.unregisterPhoneAccount(phoneAccountHandle());
713        } catch (final SecurityException e) {
714            Log.w(Config.LOGTAG, "Could not unregister " + getJid() + " as phone account: " + e);
715        }
716    }
717
718    public static int getOption(Class<? extends AbstractPhoneContact> clazz) {
719        if (clazz == JabberIdContact.class) {
720            return Options.SYNCED_VIA_ADDRESSBOOK;
721        } else {
722            return Options.SYNCED_VIA_OTHER;
723        }
724    }
725
726    @Override
727    public int getAvatarBackgroundColor() {
728        return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName());
729    }
730
731    @Override
732    public String getAvatarName() {
733        return getDisplayName();
734    }
735
736    public boolean hasAvatarOrPresenceName() {
737        return (avatar != null && avatar.getFilename() != null) || presenceName != null;
738    }
739
740    public boolean refreshRtpCapability() {
741        final RtpCapability.Capability previous = this.rtpCapability;
742        this.rtpCapability = RtpCapability.check(this, false);
743        return !Objects.equals(previous, this.rtpCapability);
744    }
745
746    public RtpCapability.Capability getRtpCapability() {
747        return this.rtpCapability == null ? RtpCapability.Capability.NONE : this.rtpCapability;
748    }
749
750    public static final class Options {
751        public static final int TO = 0;
752        public static final int FROM = 1;
753        public static final int ASKING = 2;
754        public static final int PREEMPTIVE_GRANT = 3;
755        public static final int IN_ROSTER = 4;
756        public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
757        public static final int DIRTY_PUSH = 6;
758        public static final int DIRTY_DELETE = 7;
759        private static final int SYNCED_VIA_ADDRESSBOOK = 8;
760        public static final int SYNCED_VIA_OTHER = 9;
761    }
762}