@@ -48,7 +48,7 @@ public final class Config {
public static final boolean ALLOW_NON_TLS_CONNECTIONS = false; //very dangerous. you should have a good reason to set this to true
- public static final long CONTACT_SYNC_RETRY_INTERVAL = 1000L * 60 * 5;
+ public static final long CONTACT_SYNC_RETRY_INTERVAL = 1000L * 60 * 1;
//Notification settings
@@ -4,10 +4,12 @@ import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
+import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
import android.util.Log;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -65,4 +67,13 @@ public class PhoneNumberContact extends AbstractPhoneContact {
}
return contacts;
}
+
+ public static PhoneNumberContact findByUri(Collection<PhoneNumberContact> haystack, Uri needle) {
+ for(PhoneNumberContact contact : haystack) {
+ if (needle.equals(contact.getLookupUri())) {
+ return contact;
+ }
+ }
+ return null;
+ }
}
@@ -0,0 +1,114 @@
+package eu.siacs.conversations.entities;
+
+import android.util.Base64;
+import android.util.Log;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.android.PhoneNumberContact;
+import eu.siacs.conversations.xml.Element;
+import rocks.xmpp.addr.Jid;
+
+public class Entry implements Comparable<Entry> {
+ private final List<Jid> jids;
+ private final String number;
+
+ private Entry(String number, List<Jid> jids) {
+ this.number = number;
+ this.jids = jids;
+ }
+
+ public static Entry of(Element element) {
+ final String number = element.getAttribute("number");
+ final List<Jid> jids = new ArrayList<>();
+ for (Element jidElement : element.getChildren()) {
+ String content = jidElement.getContent();
+ if (content != null) {
+ jids.add(Jid.of(content));
+ }
+ }
+ return new Entry(number, jids);
+ }
+
+ public static List<Entry> ofPhoneBook(Element phoneBook) {
+ List<Entry> entries = new ArrayList<>();
+ for (Element entry : phoneBook.getChildren()) {
+ if ("entry".equals(entry.getName())) {
+ entries.add(of(entry));
+ }
+ }
+ return entries;
+ }
+
+ public static String statusQuo(final Collection<PhoneNumberContact> phoneNumberContacts, Collection<Contact> systemContacts) {
+ return statusQuo(ofPhoneNumberContactsAndContacts(phoneNumberContacts, systemContacts));
+ }
+
+ private static String statusQuo(final List<Entry> entries) {
+ Collections.sort(entries);
+ StringBuilder builder = new StringBuilder();
+ for(Entry entry : entries) {
+ if (builder.length() != 0) {
+ builder.append('\u001d');
+ }
+ builder.append(entry.getNumber());
+ List<Jid> jids = entry.getJids();
+ Collections.sort(jids);
+ for(Jid jid : jids) {
+ builder.append('\u001e');
+ builder.append(jid.asBareJid().toEscapedString());
+ }
+ }
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return "";
+ }
+ Log.d(Config.LOGTAG,"status quo string: "+builder.toString());
+ byte[] sha1 = md.digest(builder.toString().getBytes());
+ return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
+ }
+
+ private static List<Entry> ofPhoneNumberContactsAndContacts(final Collection<PhoneNumberContact> phoneNumberContacts, Collection<Contact> systemContacts) {
+ ArrayList<Entry> entries = new ArrayList<>();
+ for(Contact contact : systemContacts) {
+ PhoneNumberContact phoneNumberContact = PhoneNumberContact.findByUri(phoneNumberContacts, contact.getSystemAccount());
+ if (phoneNumberContact != null && phoneNumberContact.getPhoneNumber() != null) {
+ Entry entry = findOrCreateByPhoneNumber(entries, phoneNumberContact.getPhoneNumber());
+ entry.jids.add(contact.getJid().asBareJid());
+ }
+ }
+ return entries;
+ }
+
+ private static Entry findOrCreateByPhoneNumber(final List<Entry> entries, String number) {
+ for(Entry entry : entries) {
+ if (entry.number.equals(number)) {
+ return entry;
+ }
+ }
+ Entry entry = new Entry(number, new ArrayList<>());
+ entries.add(entry);
+ return entry;
+ }
+
+ public List<Jid> getJids() {
+ return jids;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ @Override
+ public int compareTo(Entry o) {
+ return number.compareTo(o.number);
+ }
+}
@@ -34,6 +34,7 @@ import eu.siacs.conversations.android.PhoneNumberContact;
import eu.siacs.conversations.crypto.sasl.Plain;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Entry;
import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
@@ -286,7 +287,7 @@ public class QuickConversationsService extends AbstractQuickConversationsService
if (uri == null) {
continue;
}
- PhoneNumberContact phoneNumberContact = findByUri(contacts, uri);
+ PhoneNumberContact phoneNumberContact = PhoneNumberContact.findByUri(contacts, uri);
final boolean needsCacheClean;
if (phoneNumberContact != null) {
needsCacheClean = contact.setPhoneContact(phoneNumberContact);
@@ -320,13 +321,17 @@ public class QuickConversationsService extends AbstractQuickConversationsService
}
IqPacket query = new IqPacket(IqPacket.TYPE.GET);
query.setTo(syncServer);
- query.addChild(new Element("phone-book", Namespace.SYNCHRONIZATION).setChildren(entries));
+ Element book = new Element("phone-book", Namespace.SYNCHRONIZATION).setChildren(entries);
+ String statusQuo = Entry.statusQuo(contacts.values(), account.getRoster().getWithSystemAccounts(PhoneNumberContact.class));
+ Log.d(Config.LOGTAG,"status quo="+statusQuo);
+ book.setAttribute("ver",statusQuo);
+ query.addChild(book);
mLastSyncAttempt = Attempt.create(hash);
service.sendIqPacket(account, query, (a, response) -> {
if (response.getType() == IqPacket.TYPE.RESULT) {
- List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(PhoneNumberContact.class);
final Element phoneBook = response.findChild("phone-book", Namespace.SYNCHRONIZATION);
if (phoneBook != null) {
+ List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(PhoneNumberContact.class);
for(Entry entry : Entry.ofPhoneBook(phoneBook)) {
PhoneNumberContact phoneContact = contacts.get(entry.getNumber());
if (phoneContact == null) {
@@ -341,12 +346,14 @@ public class QuickConversationsService extends AbstractQuickConversationsService
withSystemAccounts.remove(contact);
}
}
- }
- for (Contact contact : withSystemAccounts) {
- final boolean needsCacheClean = contact.unsetPhoneContact(PhoneNumberContact.class);
- if (needsCacheClean) {
- service.getAvatarService().clear(contact);
+ for (Contact contact : withSystemAccounts) {
+ final boolean needsCacheClean = contact.unsetPhoneContact(PhoneNumberContact.class);
+ if (needsCacheClean) {
+ service.getAvatarService().clear(contact);
+ }
}
+ } else {
+ Log.d(Config.LOGTAG,account.getJid().asBareJid()+": phone number contact list remains unchanged");
}
}
service.syncRoster(account);
@@ -355,14 +362,6 @@ public class QuickConversationsService extends AbstractQuickConversationsService
return true;
}
- private static PhoneNumberContact findByUri(Collection<PhoneNumberContact> haystack, Uri needle) {
- for(PhoneNumberContact contact : haystack) {
- if (needle.equals(contact.getLookupUri())) {
- return contact;
- }
- }
- return null;
- }
private static class Attempt {
private final long timestamp;
@@ -382,46 +381,6 @@ public class QuickConversationsService extends AbstractQuickConversationsService
}
}
- public static class Entry {
- private final List<Jid> jids;
- private final String number;
-
- private Entry(String number, List<Jid> jids) {
- this.number = number;
- this.jids = jids;
- }
-
- public static Entry of(Element element) {
- final String number = element.getAttribute("number");
- final List<Jid> jids = new ArrayList<>();
- for (Element jidElement : element.getChildren()) {
- String content = jidElement.getContent();
- if (content != null) {
- jids.add(Jid.of(content));
- }
- }
- return new Entry(number, jids);
- }
-
- public static List<Entry> ofPhoneBook(Element phoneBook) {
- List<Entry> entries = new ArrayList<>();
- for (Element entry : phoneBook.getChildren()) {
- if ("entry".equals(entry.getName())) {
- entries.add(of(entry));
- }
- }
- return entries;
- }
-
- public List<Jid> getJids() {
- return jids;
- }
-
- public String getNumber() {
- return number;
- }
- }
-
public interface OnVerificationRequested {
void onVerificationRequestFailed(int code);