diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index 4d62c2508..a3cbfc896 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -29,554 +29,552 @@ import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.Jid; public class Contact implements ListItem, Blockable { - public static final String TABLENAME = "contacts"; - - public static final String SYSTEMNAME = "systemname"; - public static final String SERVERNAME = "servername"; - public static final String PRESENCE_NAME = "presence_name"; - public static final String JID = "jid"; - public static final String OPTIONS = "options"; - public static final String SYSTEMACCOUNT = "systemaccount"; - public static final String PHOTOURI = "photouri"; - public static final String KEYS = "pgpkey"; - public static final String ACCOUNT = "accountUuid"; - public static final String AVATAR = "avatar"; - public static final String LAST_PRESENCE = "last_presence"; - public static final String LAST_TIME = "last_time"; - public static final String GROUPS = "groups"; - private String accountUuid; - private String systemName; - private String serverName; - private String presenceName; - private String commonName; - protected Jid jid; - private int subscription = 0; - private Uri systemAccount; - private String photoUri; - private final JSONObject keys; - private JSONArray groups = new JSONArray(); - private final Presences presences = new Presences(); - protected Account account; - protected Avatar avatar; - - private boolean mActive = false; - private long mLastseen = 0; - private String mLastPresence = null; - - public Contact(final String account, final String systemName, final String serverName, final String presenceName, - final Jid jid, final int subscription, final String photoUri, - final Uri systemAccount, final String keys, final String avatar, final long lastseen, - final String presence, final String groups) { - this.accountUuid = account; - this.systemName = systemName; - this.serverName = serverName; - this.presenceName = presenceName; - this.jid = jid; - this.subscription = subscription; - this.photoUri = photoUri; - this.systemAccount = systemAccount; - JSONObject tmpJsonObject; - try { - tmpJsonObject = (keys == null ? new JSONObject("") : new JSONObject(keys)); - } catch (JSONException e) { - tmpJsonObject = new JSONObject(); - } - this.keys = tmpJsonObject; - if (avatar != null) { - this.avatar = new Avatar(); - this.avatar.sha1sum = avatar; - this.avatar.origin = Avatar.Origin.VCARD; //always assume worst - } - try { - this.groups = (groups == null ? new JSONArray() : new JSONArray(groups)); - } catch (JSONException e) { - this.groups = new JSONArray(); - } - this.mLastseen = lastseen; - this.mLastPresence = presence; - } - - public Contact(final Jid jid) { - this.jid = jid; - this.keys = new JSONObject(); - } - - public static Contact fromCursor(final Cursor cursor) { - final Jid jid; - try { - jid = Jid.of(cursor.getString(cursor.getColumnIndex(JID))); - } catch (final IllegalArgumentException e) { - // TODO: Borked DB... handle this somehow? - return null; - } - Uri systemAccount; - try { - systemAccount = Uri.parse(cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT))); - } catch (Exception e) { - systemAccount = null; - } - return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)), - cursor.getString(cursor.getColumnIndex(SYSTEMNAME)), - cursor.getString(cursor.getColumnIndex(SERVERNAME)), - cursor.getString(cursor.getColumnIndex(PRESENCE_NAME)), - jid, - cursor.getInt(cursor.getColumnIndex(OPTIONS)), - cursor.getString(cursor.getColumnIndex(PHOTOURI)), - systemAccount, - cursor.getString(cursor.getColumnIndex(KEYS)), - cursor.getString(cursor.getColumnIndex(AVATAR)), - cursor.getLong(cursor.getColumnIndex(LAST_TIME)), - cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)), - cursor.getString(cursor.getColumnIndex(GROUPS))); - } - - public String getDisplayName() { - if (Config.X509_VERIFICATION && !TextUtils.isEmpty(this.commonName)) { - return this.commonName; - } else if (!TextUtils.isEmpty(this.systemName)) { - return this.systemName; - } else if (!TextUtils.isEmpty(this.serverName)) { - return this.serverName; - } else if (!TextUtils.isEmpty(this.presenceName) && ((QuickConversationsService.isQuicksy() && JidHelper.isQuicksyDomain(jid.getDomain())) ||mutualPresenceSubscription())) { - return this.presenceName; - } else if (jid.getLocal() != null) { - return JidHelper.localPartOrFallback(jid); - } else { - return jid.getDomain().toEscapedString(); - } - } - - public String getPublicDisplayName() { - if (!TextUtils.isEmpty(this.presenceName)) { - return this.presenceName; - } else if (jid.getLocal() != null) { - return JidHelper.localPartOrFallback(jid); - } else { - return jid.getDomain().toEscapedString(); - } - } - - public String getProfilePhoto() { - return this.photoUri; - } - - public Jid getJid() { - return jid; - } - - @Override - public List getTags(Context context) { - final ArrayList tags = new ArrayList<>(); - for (final String group : getGroups(true)) { - tags.add(new Tag(group, UIHelper.getColorForName(group))); - } - Presence.Status status = getShownStatus(); - if (status != Presence.Status.OFFLINE) { - tags.add(UIHelper.getTagForStatus(context, status)); - } - if (isBlocked()) { - tags.add(new Tag(context.getString(R.string.blocked), 0xff2e2f3b)); - } - return tags; - } - - public boolean match(Context context, String needle) { - if (TextUtils.isEmpty(needle)) { - return true; - } - needle = needle.toLowerCase(Locale.US).trim(); - String[] parts = needle.split("\\s+"); - if (parts.length > 1) { - for (String part : parts) { - if (!match(context, part)) { - return false; - } - } - return true; - } else { - return jid.toString().contains(needle) || - getDisplayName().toLowerCase(Locale.US).contains(needle) || - matchInTag(context, needle); - } - } - - private boolean matchInTag(Context context, String needle) { - needle = needle.toLowerCase(Locale.US); - for (Tag tag : getTags(context)) { - if (tag.getName().toLowerCase(Locale.US).contains(needle)) { - return true; - } - } - return false; - } - - public ContentValues getContentValues() { - synchronized (this.keys) { - final ContentValues values = new ContentValues(); - values.put(ACCOUNT, accountUuid); - values.put(SYSTEMNAME, systemName); - values.put(SERVERNAME, serverName); - values.put(PRESENCE_NAME, presenceName); - values.put(JID, jid.toString()); - values.put(OPTIONS, subscription); - values.put(SYSTEMACCOUNT, systemAccount != null ? systemAccount.toString() : null); - values.put(PHOTOURI, photoUri); - values.put(KEYS, keys.toString()); - values.put(AVATAR, avatar == null ? null : avatar.getFilename()); - values.put(LAST_PRESENCE, mLastPresence); - values.put(LAST_TIME, mLastseen); - values.put(GROUPS, groups.toString()); - return values; - } - } - - public Account getAccount() { - return this.account; - } - - public void setAccount(Account account) { - this.account = account; - this.accountUuid = account.getUuid(); - } - - public Presences getPresences() { - return this.presences; - } - - public void updatePresence(final String resource, final Presence presence) { - this.presences.updatePresence(resource, presence); - } - - public void removePresence(final String resource) { - this.presences.removePresence(resource); - } - - public void clearPresences() { - this.presences.clearPresences(); - this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST); - } - - public Presence.Status getShownStatus() { - return this.presences.getShownStatus(); - } - - public boolean setPhotoUri(String uri) { - if (uri != null && !uri.equals(this.photoUri)) { - this.photoUri = uri; - return true; - } else if (this.photoUri != null && uri == null) { - this.photoUri = null; - return true; - } else { - return false; - } - } - - public void setServerName(String serverName) { - this.serverName = serverName; - } - - public boolean setSystemName(String systemName) { - final String old = getDisplayName(); - this.systemName = systemName; - return !old.equals(getDisplayName()); - } - - public boolean setPresenceName(String presenceName) { - final String old = getDisplayName(); - this.presenceName = presenceName; - return !old.equals(getDisplayName()); - } - - public Uri getSystemAccount() { - return systemAccount; - } - - public void setSystemAccount(Uri lookupUri) { - this.systemAccount = lookupUri; - } - - private Collection getGroups(final boolean unique) { - final Collection groups = unique ? new HashSet<>() : new ArrayList<>(); - for (int i = 0; i < this.groups.length(); ++i) { - try { - groups.add(this.groups.getString(i)); - } catch (final JSONException ignored) { - } - } - return groups; - } - - public long getPgpKeyId() { - synchronized (this.keys) { - if (this.keys.has("pgp_keyid")) { - try { - return this.keys.getLong("pgp_keyid"); - } catch (JSONException e) { - return 0; - } - } else { - return 0; - } - } - } - - public boolean setPgpKeyId(long keyId) { - final long previousKeyId = getPgpKeyId(); - synchronized (this.keys) { - try { - this.keys.put("pgp_keyid", keyId); - return previousKeyId != keyId; - } catch (final JSONException ignored) { - } - } - return false; - } - - public void setOption(int option) { - this.subscription |= 1 << option; - } - - public void resetOption(int option) { - this.subscription &= ~(1 << option); - } - - public boolean getOption(int option) { - return ((this.subscription & (1 << option)) != 0); - } - - public boolean showInRoster() { - return (this.getOption(Contact.Options.IN_ROSTER) && (!this - .getOption(Contact.Options.DIRTY_DELETE))) - || (this.getOption(Contact.Options.DIRTY_PUSH)); - } - - public boolean showInContactList() { - return showInRoster() - || getOption(Options.SYNCED_VIA_OTHER) - || (QuickConversationsService.isQuicksy() && systemAccount != null); - } - - public void parseSubscriptionFromElement(Element item) { - String ask = item.getAttribute("ask"); - String subscription = item.getAttribute("subscription"); - - if (subscription == null) { - this.resetOption(Options.FROM); - this.resetOption(Options.TO); - } else { - switch (subscription) { - case "to": - this.resetOption(Options.FROM); - this.setOption(Options.TO); - break; - case "from": - this.resetOption(Options.TO); - this.setOption(Options.FROM); - this.resetOption(Options.PREEMPTIVE_GRANT); - this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST); - break; - case "both": - this.setOption(Options.TO); - this.setOption(Options.FROM); - this.resetOption(Options.PREEMPTIVE_GRANT); - this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST); - break; - case "none": - this.resetOption(Options.FROM); - this.resetOption(Options.TO); - break; - } - } - - // do NOT override asking if pending push request - if (!this.getOption(Contact.Options.DIRTY_PUSH)) { - if ((ask != null) && (ask.equals("subscribe"))) { - this.setOption(Contact.Options.ASKING); - } else { - this.resetOption(Contact.Options.ASKING); - } - } - } - - public void parseGroupsFromElement(Element item) { - this.groups = new JSONArray(); - for (Element element : item.getChildren()) { - if (element.getName().equals("group") && element.getContent() != null) { - this.groups.put(element.getContent()); - } - } - } - - public Element asElement() { - final Element item = new Element("item"); - item.setAttribute("jid", this.jid); - if (this.serverName != null) { - item.setAttribute("name", this.serverName); - } - for (String group : getGroups(false)) { - item.addChild("group").setContent(group); - } - return item; - } - - @Override - public int compareTo(@NonNull final ListItem another) { - return this.getDisplayName().compareToIgnoreCase( - another.getDisplayName()); - } - - public String getServer() { - return getJid().getDomain().toEscapedString(); - } - - public boolean setAvatar(Avatar avatar) { - return setAvatar(avatar, false); - } - - public boolean setAvatar(Avatar avatar, boolean previouslyOmittedPepFetch) { - if (this.avatar != null && this.avatar.equals(avatar)) { - return false; - } else { - if (!previouslyOmittedPepFetch && this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) { - return false; - } - this.avatar = avatar; - return true; - } - } - - public String getAvatarFilename() { - return avatar == null ? null : avatar.getFilename(); - } - - public Avatar getAvatar() { - return avatar; - } - - public boolean mutualPresenceSubscription() { - return getOption(Options.FROM) && getOption(Options.TO); - } - - @Override - public boolean isBlocked() { - return getAccount().isBlocked(this); - } - - @Override - public boolean isDomainBlocked() { - return getAccount().isBlocked(this.getJid().getDomain()); - } - - @Override - public Jid getBlockedJid() { - if (isDomainBlocked()) { - return getJid().getDomain(); - } else { - return getJid(); - } - } - - public boolean isSelf() { - return account.getJid().asBareJid().equals(jid.asBareJid()); - } - - boolean isOwnServer() { - return account.getJid().getDomain().equals(jid.asBareJid()); - } - - public void setCommonName(String cn) { - this.commonName = cn; - } - - public void flagActive() { - this.mActive = true; - } - - public void flagInactive() { - this.mActive = false; - } - - public boolean isActive() { - return this.mActive; - } - - public boolean setLastseen(long timestamp) { - if (timestamp > this.mLastseen) { - this.mLastseen = timestamp; - return true; - } else { - return false; - } - } - - public long getLastseen() { - return this.mLastseen; - } - - public void setLastResource(String resource) { - this.mLastPresence = resource; - } - - public String getLastResource() { - return this.mLastPresence; - } - - public String getServerName() { - return serverName; - } - - public synchronized boolean setPhoneContact(AbstractPhoneContact phoneContact) { - setOption(getOption(phoneContact.getClass())); - setSystemAccount(phoneContact.getLookupUri()); - boolean changed = setSystemName(phoneContact.getDisplayName()); - changed |= setPhotoUri(phoneContact.getPhotoUri()); - return changed; - } - - public synchronized boolean unsetPhoneContact(Class clazz) { - resetOption(getOption(clazz)); - boolean changed = false; - if (!getOption(Options.SYNCED_VIA_ADDRESSBOOK) && !getOption(Options.SYNCED_VIA_OTHER)) { - setSystemAccount(null); - changed |= setPhotoUri(null); - changed |= setSystemName(null); - } - return changed; - } - - public static int getOption(Class clazz) { - if (clazz == JabberIdContact.class) { - return Options.SYNCED_VIA_ADDRESSBOOK; - } else { - return Options.SYNCED_VIA_OTHER; - } - } - - @Override - public int getAvatarBackgroundColor() { - return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName()); - } - - @Override - public String getAvatarName() { - return getDisplayName(); - } - - public boolean hasAvatarOrPresenceName() { - return (avatar != null && avatar.getFilename() != null) || presenceName != null; - } - - public final class Options { - public static final int TO = 0; - public static final int FROM = 1; - public static final int ASKING = 2; - public static final int PREEMPTIVE_GRANT = 3; - public static final int IN_ROSTER = 4; - public static final int PENDING_SUBSCRIPTION_REQUEST = 5; - public static final int DIRTY_PUSH = 6; - public static final int DIRTY_DELETE = 7; - private static final int SYNCED_VIA_ADDRESSBOOK = 8; - public static final int SYNCED_VIA_OTHER = 9; - } + public static final String TABLENAME = "contacts"; + + public static final String SYSTEMNAME = "systemname"; + public static final String SERVERNAME = "servername"; + public static final String PRESENCE_NAME = "presence_name"; + public static final String JID = "jid"; + public static final String OPTIONS = "options"; + public static final String SYSTEMACCOUNT = "systemaccount"; + public static final String PHOTOURI = "photouri"; + public static final String KEYS = "pgpkey"; + public static final String ACCOUNT = "accountUuid"; + public static final String AVATAR = "avatar"; + public static final String LAST_PRESENCE = "last_presence"; + public static final String LAST_TIME = "last_time"; + public static final String GROUPS = "groups"; + private String accountUuid; + private String systemName; + private String serverName; + private String presenceName; + private String commonName; + protected Jid jid; + private int subscription = 0; + private Uri systemAccount; + private String photoUri; + private final JSONObject keys; + private JSONArray groups = new JSONArray(); + private final Presences presences = new Presences(); + protected Account account; + protected Avatar avatar; + + private boolean mActive = false; + private long mLastseen = 0; + private String mLastPresence = null; + + public Contact(final String account, final String systemName, final String serverName, final String presenceName, + final Jid jid, final int subscription, final String photoUri, + final Uri systemAccount, final String keys, final String avatar, final long lastseen, + final String presence, final String groups) { + this.accountUuid = account; + this.systemName = systemName; + this.serverName = serverName; + this.presenceName = presenceName; + this.jid = jid; + this.subscription = subscription; + this.photoUri = photoUri; + this.systemAccount = systemAccount; + JSONObject tmpJsonObject; + try { + tmpJsonObject = (keys == null ? new JSONObject("") : new JSONObject(keys)); + } catch (JSONException e) { + tmpJsonObject = new JSONObject(); + } + this.keys = tmpJsonObject; + if (avatar != null) { + this.avatar = new Avatar(); + this.avatar.sha1sum = avatar; + this.avatar.origin = Avatar.Origin.VCARD; //always assume worst + } + try { + this.groups = (groups == null ? new JSONArray() : new JSONArray(groups)); + } catch (JSONException e) { + this.groups = new JSONArray(); + } + this.mLastseen = lastseen; + this.mLastPresence = presence; + } + + public Contact(final Jid jid) { + this.jid = jid; + this.keys = new JSONObject(); + } + + public static Contact fromCursor(final Cursor cursor) { + final Jid jid; + try { + jid = Jid.of(cursor.getString(cursor.getColumnIndex(JID))); + } catch (final IllegalArgumentException e) { + // TODO: Borked DB... handle this somehow? + return null; + } + Uri systemAccount; + try { + systemAccount = Uri.parse(cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT))); + } catch (Exception e) { + systemAccount = null; + } + return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)), + cursor.getString(cursor.getColumnIndex(SYSTEMNAME)), + cursor.getString(cursor.getColumnIndex(SERVERNAME)), + cursor.getString(cursor.getColumnIndex(PRESENCE_NAME)), + jid, + cursor.getInt(cursor.getColumnIndex(OPTIONS)), + cursor.getString(cursor.getColumnIndex(PHOTOURI)), + systemAccount, + cursor.getString(cursor.getColumnIndex(KEYS)), + cursor.getString(cursor.getColumnIndex(AVATAR)), + cursor.getLong(cursor.getColumnIndex(LAST_TIME)), + cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)), + cursor.getString(cursor.getColumnIndex(GROUPS))); + } + + public String getDisplayName() { + if (Config.X509_VERIFICATION && !TextUtils.isEmpty(this.commonName)) { + return this.commonName; + } else if (!TextUtils.isEmpty(this.systemName)) { + return this.systemName; + } else if (!TextUtils.isEmpty(this.serverName)) { + return this.serverName; + } else if (!TextUtils.isEmpty(this.presenceName) && ((QuickConversationsService.isQuicksy() && JidHelper.isQuicksyDomain(jid.getDomain())) || mutualPresenceSubscription())) { + return this.presenceName; + } else if (jid.getLocal() != null) { + return JidHelper.localPartOrFallback(jid); + } else { + return jid.getDomain().toEscapedString(); + } + } + + public String getPublicDisplayName() { + if (!TextUtils.isEmpty(this.presenceName)) { + return this.presenceName; + } else if (jid.getLocal() != null) { + return JidHelper.localPartOrFallback(jid); + } else { + return jid.getDomain().toEscapedString(); + } + } + + public String getProfilePhoto() { + return this.photoUri; + } + + public Jid getJid() { + return jid; + } + + @Override + public List getTags(Context context) { + final ArrayList tags = new ArrayList<>(); + for (final String group : getGroups(true)) { + tags.add(new Tag(group, UIHelper.getColorForName(group))); + } + Presence.Status status = getShownStatus(); + if (status != Presence.Status.OFFLINE) { + tags.add(UIHelper.getTagForStatus(context, status)); + } + if (isBlocked()) { + tags.add(new Tag(context.getString(R.string.blocked), 0xff2e2f3b)); + } + return tags; + } + + public boolean match(Context context, String needle) { + if (TextUtils.isEmpty(needle)) { + return true; + } + needle = needle.toLowerCase(Locale.US).trim(); + String[] parts = needle.split("\\s+"); + if (parts.length > 1) { + for (String part : parts) { + if (!match(context, part)) { + return false; + } + } + return true; + } else { + return jid.toString().contains(needle) || + getDisplayName().toLowerCase(Locale.US).contains(needle) || + matchInTag(context, needle); + } + } + + private boolean matchInTag(Context context, String needle) { + needle = needle.toLowerCase(Locale.US); + for (Tag tag : getTags(context)) { + if (tag.getName().toLowerCase(Locale.US).contains(needle)) { + return true; + } + } + return false; + } + + public ContentValues getContentValues() { + synchronized (this.keys) { + final ContentValues values = new ContentValues(); + values.put(ACCOUNT, accountUuid); + values.put(SYSTEMNAME, systemName); + values.put(SERVERNAME, serverName); + values.put(PRESENCE_NAME, presenceName); + values.put(JID, jid.toString()); + values.put(OPTIONS, subscription); + values.put(SYSTEMACCOUNT, systemAccount != null ? systemAccount.toString() : null); + values.put(PHOTOURI, photoUri); + values.put(KEYS, keys.toString()); + values.put(AVATAR, avatar == null ? null : avatar.getFilename()); + values.put(LAST_PRESENCE, mLastPresence); + values.put(LAST_TIME, mLastseen); + values.put(GROUPS, groups.toString()); + return values; + } + } + + public Account getAccount() { + return this.account; + } + + public void setAccount(Account account) { + this.account = account; + this.accountUuid = account.getUuid(); + } + + public Presences getPresences() { + return this.presences; + } + + public void updatePresence(final String resource, final Presence presence) { + this.presences.updatePresence(resource, presence); + } + + public void removePresence(final String resource) { + this.presences.removePresence(resource); + } + + public void clearPresences() { + this.presences.clearPresences(); + this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST); + } + + public Presence.Status getShownStatus() { + return this.presences.getShownStatus(); + } + + public boolean setPhotoUri(String uri) { + if (uri != null && !uri.equals(this.photoUri)) { + this.photoUri = uri; + return true; + } else if (this.photoUri != null && uri == null) { + this.photoUri = null; + return true; + } else { + return false; + } + } + + public void setServerName(String serverName) { + this.serverName = serverName; + } + + public boolean setSystemName(String systemName) { + final String old = getDisplayName(); + this.systemName = systemName; + return !old.equals(getDisplayName()); + } + + public boolean setPresenceName(String presenceName) { + final String old = getDisplayName(); + this.presenceName = presenceName; + return !old.equals(getDisplayName()); + } + + public Uri getSystemAccount() { + return systemAccount; + } + + public void setSystemAccount(Uri lookupUri) { + this.systemAccount = lookupUri; + } + + private Collection getGroups(final boolean unique) { + final Collection groups = unique ? new HashSet<>() : new ArrayList<>(); + for (int i = 0; i < this.groups.length(); ++i) { + try { + groups.add(this.groups.getString(i)); + } catch (final JSONException ignored) { + } + } + return groups; + } + + public long getPgpKeyId() { + synchronized (this.keys) { + if (this.keys.has("pgp_keyid")) { + try { + return this.keys.getLong("pgp_keyid"); + } catch (JSONException e) { + return 0; + } + } else { + return 0; + } + } + } + + public boolean setPgpKeyId(long keyId) { + final long previousKeyId = getPgpKeyId(); + synchronized (this.keys) { + try { + this.keys.put("pgp_keyid", keyId); + return previousKeyId != keyId; + } catch (final JSONException ignored) { + } + } + return false; + } + + public void setOption(int option) { + this.subscription |= 1 << option; + } + + public void resetOption(int option) { + this.subscription &= ~(1 << option); + } + + public boolean getOption(int option) { + return ((this.subscription & (1 << option)) != 0); + } + + public boolean showInRoster() { + return (this.getOption(Contact.Options.IN_ROSTER) && (!this + .getOption(Contact.Options.DIRTY_DELETE))) + || (this.getOption(Contact.Options.DIRTY_PUSH)); + } + + public boolean showInContactList() { + return showInRoster() + || getOption(Options.SYNCED_VIA_OTHER) + || (QuickConversationsService.isQuicksy() && systemAccount != null); + } + + public void parseSubscriptionFromElement(Element item) { + String ask = item.getAttribute("ask"); + String subscription = item.getAttribute("subscription"); + + if (subscription == null) { + this.resetOption(Options.FROM); + this.resetOption(Options.TO); + } else { + switch (subscription) { + case "to": + this.resetOption(Options.FROM); + this.setOption(Options.TO); + break; + case "from": + this.resetOption(Options.TO); + this.setOption(Options.FROM); + this.resetOption(Options.PREEMPTIVE_GRANT); + this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST); + break; + case "both": + this.setOption(Options.TO); + this.setOption(Options.FROM); + this.resetOption(Options.PREEMPTIVE_GRANT); + this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST); + break; + case "none": + this.resetOption(Options.FROM); + this.resetOption(Options.TO); + break; + } + } + + // do NOT override asking if pending push request + if (!this.getOption(Contact.Options.DIRTY_PUSH)) { + if ((ask != null) && (ask.equals("subscribe"))) { + this.setOption(Contact.Options.ASKING); + } else { + this.resetOption(Contact.Options.ASKING); + } + } + } + + public void parseGroupsFromElement(Element item) { + this.groups = new JSONArray(); + for (Element element : item.getChildren()) { + if (element.getName().equals("group") && element.getContent() != null) { + this.groups.put(element.getContent()); + } + } + } + + public Element asElement() { + final Element item = new Element("item"); + item.setAttribute("jid", this.jid); + if (this.serverName != null) { + item.setAttribute("name", this.serverName); + } + for (String group : getGroups(false)) { + item.addChild("group").setContent(group); + } + return item; + } + + @Override + public int compareTo(@NonNull final ListItem another) { + return this.getDisplayName().compareToIgnoreCase( + another.getDisplayName()); + } + + public String getServer() { + return getJid().getDomain().toEscapedString(); + } + + public void setAvatar(Avatar avatar) { + setAvatar(avatar, false); + } + + public void setAvatar(Avatar avatar, boolean previouslyOmittedPepFetch) { + if (this.avatar != null && this.avatar.equals(avatar)) { + return; + } + if (!previouslyOmittedPepFetch && this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) { + return; + } + this.avatar = avatar; + } + + public String getAvatarFilename() { + return avatar == null ? null : avatar.getFilename(); + } + + public Avatar getAvatar() { + return avatar; + } + + public boolean mutualPresenceSubscription() { + return getOption(Options.FROM) && getOption(Options.TO); + } + + @Override + public boolean isBlocked() { + return getAccount().isBlocked(this); + } + + @Override + public boolean isDomainBlocked() { + return getAccount().isBlocked(this.getJid().getDomain()); + } + + @Override + public Jid getBlockedJid() { + if (isDomainBlocked()) { + return getJid().getDomain(); + } else { + return getJid(); + } + } + + public boolean isSelf() { + return account.getJid().asBareJid().equals(jid.asBareJid()); + } + + boolean isOwnServer() { + return account.getJid().getDomain().equals(jid.asBareJid()); + } + + public void setCommonName(String cn) { + this.commonName = cn; + } + + public void flagActive() { + this.mActive = true; + } + + public void flagInactive() { + this.mActive = false; + } + + public boolean isActive() { + return this.mActive; + } + + public boolean setLastseen(long timestamp) { + if (timestamp > this.mLastseen) { + this.mLastseen = timestamp; + return true; + } else { + return false; + } + } + + public long getLastseen() { + return this.mLastseen; + } + + public void setLastResource(String resource) { + this.mLastPresence = resource; + } + + public String getLastResource() { + return this.mLastPresence; + } + + public String getServerName() { + return serverName; + } + + public synchronized boolean setPhoneContact(AbstractPhoneContact phoneContact) { + setOption(getOption(phoneContact.getClass())); + setSystemAccount(phoneContact.getLookupUri()); + boolean changed = setSystemName(phoneContact.getDisplayName()); + changed |= setPhotoUri(phoneContact.getPhotoUri()); + return changed; + } + + public synchronized boolean unsetPhoneContact(Class clazz) { + resetOption(getOption(clazz)); + boolean changed = false; + if (!getOption(Options.SYNCED_VIA_ADDRESSBOOK) && !getOption(Options.SYNCED_VIA_OTHER)) { + setSystemAccount(null); + changed |= setPhotoUri(null); + changed |= setSystemName(null); + } + return changed; + } + + public static int getOption(Class clazz) { + if (clazz == JabberIdContact.class) { + return Options.SYNCED_VIA_ADDRESSBOOK; + } else { + return Options.SYNCED_VIA_OTHER; + } + } + + @Override + public int getAvatarBackgroundColor() { + return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName()); + } + + @Override + public String getAvatarName() { + return getDisplayName(); + } + + public boolean hasAvatarOrPresenceName() { + return (avatar != null && avatar.getFilename() != null) || presenceName != null; + } + + public final class Options { + public static final int TO = 0; + public static final int FROM = 1; + public static final int ASKING = 2; + public static final int PREEMPTIVE_GRANT = 3; + public static final int IN_ROSTER = 4; + public static final int PENDING_SUBSCRIPTION_REQUEST = 5; + public static final int DIRTY_PUSH = 6; + public static final int DIRTY_DELETE = 7; + private static final int SYNCED_VIA_ADDRESSBOOK = 8; + public static final int SYNCED_VIA_OTHER = 9; + } } diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index dd835134e..d2e35fc4f 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -214,13 +214,12 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); } else { - Contact contact = account.getRoster().getContact(from); - if (contact.setAvatar(avatar)) { - mXmppConnectionService.syncRoster(account); - mXmppConnectionService.getAvatarService().clear(contact); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateRosterUi(); - } + final Contact contact = account.getRoster().getContact(from); + contact.setAvatar(avatar); + mXmppConnectionService.syncRoster(account); + mXmppConnectionService.getAvatarService().clear(contact); + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateRosterUi(); } } else if (mXmppConnectionService.isDataSaverDisabled()) { mXmppConnectionService.fetchAvatar(account, avatar); diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index 7f57ef733..6d9937766 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -118,12 +118,11 @@ public class PresenceParser extends AbstractParser implements mXmppConnectionService.getAvatarService().clear(user); } if (user.getRealJid() != null) { - Contact c = conversation.getAccount().getRoster().getContact(user.getRealJid()); - if (c.setAvatar(avatar)) { - mXmppConnectionService.syncRoster(conversation.getAccount()); - mXmppConnectionService.getAvatarService().clear(c); - mXmppConnectionService.updateRosterUi(); - } + final Contact c = conversation.getAccount().getRoster().getContact(user.getRealJid()); + c.setAvatar(avatar); + mXmppConnectionService.syncRoster(conversation.getAccount()); + mXmppConnectionService.getAvatarService().clear(c); + mXmppConnectionService.updateRosterUi(); } } else if (mXmppConnectionService.isDataSaverDisabled()) { mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar); @@ -268,7 +267,8 @@ public class PresenceParser extends AbstractParser implements mXmppConnectionService.getAvatarService().clear(account); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); - } else if (contact.setAvatar(avatar)) { + } else { + contact.setAvatar(avatar); mXmppConnectionService.syncRoster(account); mXmppConnectionService.getAvatarService().clear(contact); mXmppConnectionService.updateConversationUi(); diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 98d0f3de8..977c9fc27 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -1071,7 +1071,7 @@ public class FileBackend { return null; } Avatar avatar = new Avatar(); - File file = new File(getAvatarPath(hash)); + final File file = getAvatarFile(hash); FileInputStream is = null; try { avatar.size = file.length(); @@ -1104,14 +1104,14 @@ public class FileBackend { } public boolean isAvatarCached(Avatar avatar) { - File file = new File(getAvatarPath(avatar.getFilename())); + final File file = getAvatarFile(avatar.getFilename()); return file.exists(); } public boolean save(final Avatar avatar) { File file; if (isAvatarCached(avatar)) { - file = new File(getAvatarPath(avatar.getFilename())); + file = getAvatarFile(avatar.getFilename()); avatar.size = file.length(); } else { file = new File(mXmppConnectionService.getCacheDir().getAbsolutePath() + "/" + UUID.randomUUID().toString()); @@ -1133,12 +1133,12 @@ public class FileBackend { mDigestOutputStream.close(); String sha1sum = CryptoHelper.bytesToHex(digest.digest()); if (sha1sum.equals(avatar.sha1sum)) { - File outputFile = new File(getAvatarPath(avatar.getFilename())); + final File outputFile = getAvatarFile(avatar.getFilename()); if (outputFile.getParentFile().mkdirs()) { Log.d(Config.LOGTAG, "created avatar directory"); } - String filename = getAvatarPath(avatar.getFilename()); - if (!file.renameTo(new File(filename))) { + final File avatarFile = getAvatarFile(avatar.getFilename()); + if (!file.renameTo(avatarFile)) { Log.d(Config.LOGTAG, "unable to rename " + file.getAbsolutePath() + " to " + outputFile); return false; } @@ -1159,12 +1159,34 @@ public class FileBackend { return true; } - private String getAvatarPath(String avatar) { - return mXmppConnectionService.getFilesDir().getAbsolutePath() + "/avatars/" + avatar; + public void deleteHistoricAvatarPath() { + delete(getHistoricAvatarPath()); + } + + private void delete(final File file) { + if (file.isDirectory()) { + final File[] files = file.listFiles(); + if (files != null) { + for (final File f : files) { + delete(f); + } + } + } + if (file.delete()) { + Log.d(Config.LOGTAG,"deleted "+file.getAbsolutePath()); + } + } + + private File getHistoricAvatarPath() { + return new File(mXmppConnectionService.getFilesDir(), "/avatars/"); + } + + private File getAvatarFile(String avatar) { + return new File(mXmppConnectionService.getCacheDir(), "/avatars/" + avatar); } public Uri getAvatarUri(String avatar) { - return Uri.parse("file:" + getAvatarPath(avatar)); + return Uri.fromFile(getAvatarFile(avatar)); } public Bitmap cropCenterSquare(Uri image, int size) { diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index 1b29194e6..6a00e7440 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -59,7 +59,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private static final String CHANNEL_SYMBOL = "#"; - final private ArrayList sizes = new ArrayList<>(); + final private Set sizes = new HashSet<>(); final private HashMap> conversationDependentKeys = new HashMap<>(); protected XmppConnectionService mXmppConnectionService = null; @@ -224,9 +224,8 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { public void clear(Contact contact) { synchronized (this.sizes) { - for (Integer size : sizes) { - this.mXmppConnectionService.getBitmapCache().remove( - key(contact, size)); + for (final Integer size : sizes) { + this.mXmppConnectionService.getBitmapCache().remove(key(contact, size)); } } for (Conversation conversation : mXmppConnectionService.findAllConferencesWith(contact)) { @@ -240,9 +239,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private String key(Contact contact, int size) { synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } + this.sizes.add(size); } return PREFIX_CONTACT + '\0' + @@ -255,9 +252,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private String key(MucOptions.User user, int size) { synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } + this.sizes.add(size); } return PREFIX_CONTACT + '\0' + @@ -416,12 +411,9 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private String key(final MucOptions options, int size) { synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } + this.sizes.add(size); } - return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() - + "_" + String.valueOf(size); + return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() + "_" + size; } private String key(List users, int size) { @@ -524,9 +516,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private String key(Account account, int size) { synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } + this.sizes.add(size); } return PREFIX_ACCOUNT + "_" + account.getUuid() + "_" + String.valueOf(size); @@ -561,11 +551,9 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private String key(String name, int size) { synchronized (this.sizes) { - if (!this.sizes.contains(size)) { - this.sizes.add(size); - } + this.sizes.add(size); } - return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size); + return PREFIX_GENERIC + "_" + name + "_" + size; } private static boolean drawTile(Canvas canvas, String letter, int tileColor, int left, int top, int right, int bottom) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 6ad1caf68..0f2423f63 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1127,6 +1127,7 @@ public class XmppConnectionService extends Service { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { startContactObserver(); } + mFileAddingExecutor.execute(fileBackend::deleteHistoricAvatarPath); if (Compatibility.hasStoragePermission(this)) { Log.d(Config.LOGTAG, "starting file observer"); mFileAddingExecutor.execute(this.fileObserver::startWatching); @@ -3696,19 +3697,17 @@ public class XmppConnectionService extends Service { updateConversationUi(); updateAccountUi(); } else { - Contact contact = a.getRoster().getContact(avatar.owner); - if (contact.setAvatar(avatar)) { - syncRoster(account); - getAvatarService().clear(contact); - updateConversationUi(); - updateRosterUi(); - } + final Contact contact = a.getRoster().getContact(avatar.owner); + contact.setAvatar(avatar); + syncRoster(account); + getAvatarService().clear(contact); + updateConversationUi(); + updateRosterUi(); } if (callback != null) { callback.success(avatar); } - Log.d(Config.LOGTAG, a.getJid().asBareJid() - + ": successfully fetched pep avatar for " + avatar.owner); + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": successfully fetched pep avatar for " + avatar.owner); return; } } else { @@ -3758,12 +3757,11 @@ public class XmppConnectionService extends Service { getAvatarService().clear(account); updateAccountUi(); } else { - Contact contact = account.getRoster().getContact(avatar.owner); - if (contact.setAvatar(avatar, previouslyOmittedPepFetch)) { - syncRoster(account); - getAvatarService().clear(contact); - updateRosterUi(); - } + final Contact contact = account.getRoster().getContact(avatar.owner); + contact.setAvatar(avatar, previouslyOmittedPepFetch); + syncRoster(account); + getAvatarService().clear(contact); + updateRosterUi(); } updateConversationUi(); } else { @@ -3778,11 +3776,10 @@ public class XmppConnectionService extends Service { } if (user.getRealJid() != null) { Contact contact = account.getRoster().getContact(user.getRealJid()); - if (contact.setAvatar(avatar)) { - syncRoster(account); - getAvatarService().clear(contact); - updateRosterUi(); - } + contact.setAvatar(avatar); + syncRoster(account); + getAvatarService().clear(contact); + updateRosterUi(); } } }