basic phone number sync

This commit is contained in:
Daniel Gultsch 2018-10-28 19:05:16 +01:00
parent 87cc53b8b5
commit 2dee53587b
6 changed files with 133 additions and 32 deletions

View file

@ -5,7 +5,7 @@ import android.net.Uri;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.text.TextUtils; import android.text.TextUtils;
abstract class AbstractPhoneContact { public abstract class AbstractPhoneContact {
private final Uri lookupUri; private final Uri lookupUri;
private final String displayName; private final String displayName;

View file

@ -20,6 +20,8 @@ import java.util.Locale;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.android.AbstractPhoneContact;
import eu.siacs.conversations.android.PhoneNumberContact;
import eu.siacs.conversations.utils.JidHelper; import eu.siacs.conversations.utils.JidHelper;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
@ -507,6 +509,33 @@ public class Contact implements ListItem, Blockable {
return serverName; 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<?extends AbstractPhoneContact> 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<? extends AbstractPhoneContact> clazz) {
if (clazz == PhoneNumberContact.class) {
return Options.SYNCED_VIA_ADDRESSBOOK;
} else {
return Options.SYNCED_VIA_OTHER;
}
}
public final class Options { public final class Options {
public static final int TO = 0; public static final int TO = 0;
public static final int FROM = 1; public static final int FROM = 1;
@ -516,5 +545,7 @@ public class Contact implements ListItem, Blockable {
public static final int PENDING_SUBSCRIPTION_REQUEST = 5; public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
public static final int DIRTY_PUSH = 6; public static final int DIRTY_PUSH = 6;
public static final int DIRTY_DELETE = 7; public static final int DIRTY_DELETE = 7;
private static final int SYNCED_VIA_ADDRESSBOOK = 8;
private static final int SYNCED_VIA_OTHER = 9;
} }
} }

View file

@ -5,6 +5,7 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import eu.siacs.conversations.android.AbstractPhoneContact;
import rocks.xmpp.addr.Jid; import rocks.xmpp.addr.Jid;
@ -55,11 +56,12 @@ public class Roster {
} }
} }
public List<Contact> getWithSystemAccounts() { public List<Contact> getWithSystemAccounts(Class<?extends AbstractPhoneContact> clazz) {
int option = Contact.getOption(clazz);
List<Contact> with = getContacts(); List<Contact> with = getContacts();
for(Iterator<Contact> iterator = with.iterator(); iterator.hasNext();) { for(Iterator<Contact> iterator = with.iterator(); iterator.hasNext();) {
Contact contact = iterator.next(); Contact contact = iterator.next();
if (contact.getSystemAccount() == null) { if (!contact.getOption(option)) {
iterator.remove(); iterator.remove();
} }
} }

View file

@ -1523,21 +1523,17 @@ public class XmppConnectionService extends Service {
Map<Jid, JabberIdContact> contacts = JabberIdContact.load(this); Map<Jid, JabberIdContact> contacts = JabberIdContact.load(this);
Log.d(Config.LOGTAG, "start merging phone contacts with roster"); Log.d(Config.LOGTAG, "start merging phone contacts with roster");
for (Account account : accounts) { for (Account account : accounts) {
List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(); List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(JabberIdContact.class);
for (JabberIdContact jidContact : contacts.values()) { for (JabberIdContact jidContact : contacts.values()) {
final Contact contact = account.getRoster().getContact(jidContact.getJid()); final Contact contact = account.getRoster().getContact(jidContact.getJid());
contact.setSystemAccount(jidContact.getLookupUri()); boolean needsCacheClean = contact.setPhoneContact(jidContact);
boolean needsCacheClean = contact.setPhotoUri(jidContact.getPhotoUri());
needsCacheClean |= contact.setSystemName(jidContact.getDisplayName());
if (needsCacheClean) { if (needsCacheClean) {
getAvatarService().clear(contact); getAvatarService().clear(contact);
} }
withSystemAccounts.remove(contact); withSystemAccounts.remove(contact);
} }
for (Contact contact : withSystemAccounts) { for (Contact contact : withSystemAccounts) {
contact.setSystemAccount(null); boolean needsCacheClean = contact.unsetPhoneContact(JabberIdContact.class);
boolean needsCacheClean = contact.setPhotoUri(null);
needsCacheClean |= contact.setSystemName(null);
if (needsCacheClean) { if (needsCacheClean) {
getAvatarService().clear(contact); getAvatarService().clear(contact);
} }

View file

@ -27,14 +27,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.android.JabberIdContact;
import eu.siacs.conversations.android.PhoneNumberContact; import eu.siacs.conversations.android.PhoneNumberContact;
import eu.siacs.conversations.crypto.sasl.Plain; import eu.siacs.conversations.crypto.sasl.Plain;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper; import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import io.michaelrocks.libphonenumber.android.Phonenumber; import io.michaelrocks.libphonenumber.android.Phonenumber;
@ -63,6 +66,14 @@ public class QuickConversationsService extends AbstractQuickConversationsService
super(xmppConnectionService); super(xmppConnectionService);
} }
private static long retryAfter(HttpURLConnection connection) {
try {
return SystemClock.elapsedRealtime() + (Long.parseLong(connection.getHeaderField("Retry-After")) * 1000L);
} catch (Exception e) {
return 0;
}
}
public void addOnVerificationRequestedListener(OnVerificationRequested onVerificationRequested) { public void addOnVerificationRequestedListener(OnVerificationRequested onVerificationRequested) {
synchronized (mOnVerificationRequested) { synchronized (mOnVerificationRequested) {
mOnVerificationRequested.add(onVerificationRequested); mOnVerificationRequested.add(onVerificationRequested);
@ -246,14 +257,6 @@ public class QuickConversationsService extends AbstractQuickConversationsService
} }
} }
private static long retryAfter(HttpURLConnection connection) {
try {
return SystemClock.elapsedRealtime() + (Long.parseLong(connection.getHeaderField("Retry-After")) * 1000L);
} catch (Exception e) {
return 0;
}
}
public boolean isVerifying() { public boolean isVerifying() {
return mVerificationInProgress.get(); return mVerificationInProgress.get();
} }
@ -270,7 +273,7 @@ public class QuickConversationsService extends AbstractQuickConversationsService
} }
} }
private void considerSync(Account account, Map<String, PhoneNumberContact> contacts) { private void considerSync(Account account, final Map<String, PhoneNumberContact> contacts) {
XmppConnection xmppConnection = account.getXmppConnection(); XmppConnection xmppConnection = account.getXmppConnection();
Jid syncServer = xmppConnection == null ? null : xmppConnection.findDiscoItemByFeature(Namespace.SYNCHRONIZATION); Jid syncServer = xmppConnection == null ? null : xmppConnection.findDiscoItemByFeature(Namespace.SYNCHRONIZATION);
if (syncServer == null) { if (syncServer == null) {
@ -282,12 +285,77 @@ public class QuickConversationsService extends AbstractQuickConversationsService
for (PhoneNumberContact c : contacts.values()) { for (PhoneNumberContact c : contacts.values()) {
entries.add(new Element("entry").setAttribute("number", c.getPhoneNumber())); entries.add(new Element("entry").setAttribute("number", c.getPhoneNumber()));
} }
Element phoneBook = new Element("phone-book",Namespace.SYNCHRONIZATION); IqPacket query = new IqPacket(IqPacket.TYPE.GET);
phoneBook.setChildren(entries); query.setTo(syncServer);
IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET); query.addChild(new Element("phone-book", Namespace.SYNCHRONIZATION).setChildren(entries));
iqPacket.setTo(syncServer); service.sendIqPacket(account, query, (a, response) -> {
iqPacket.addChild(phoneBook); if (response.getType() == IqPacket.TYPE.RESULT) {
service.sendIqPacket(account, iqPacket, null); List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(PhoneNumberContact.class);
final Element phoneBook = response.findChild("phone-book", Namespace.SYNCHRONIZATION);
if (phoneBook != null) {
for(Entry entry : Entry.ofPhoneBook(phoneBook)) {
PhoneNumberContact phoneContact = contacts.get(entry.getNumber());
if (phoneContact == null) {
continue;
}
for(Jid jid : entry.getJids()) {
Contact contact = account.getRoster().getContact(jid);
final boolean needsCacheClean = contact.setPhoneContact(phoneContact);
if (needsCacheClean) {
service.getAvatarService().clear(contact);
}
withSystemAccounts.remove(contact);
}
}
}
for (Contact contact : withSystemAccounts) {
boolean needsCacheClean = contact.unsetPhoneContact(JabberIdContact.class);
if (needsCacheClean) {
service.getAvatarService().clear(contact);
}
}
}
});
}
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 { public interface OnVerificationRequested {

View file

@ -53,8 +53,12 @@ public class PhoneNumberUtilWrapper {
return getInstance(context).parse(jid.getEscapedLocal(), "de"); return getInstance(context).parse(jid.getEscapedLocal(), "de");
} }
public static String normalize(Context context, String number) throws NumberParseException { public static String normalize(Context context, String input) throws IllegalArgumentException, NumberParseException {
return normalize(context, getInstance(context).parse(number, getUserCountry(context))); final Phonenumber.PhoneNumber number = getInstance(context).parse(input, getUserCountry(context));
if (!getInstance(context).isValidNumber(number)) {
throw new IllegalArgumentException("Not a valid phone number");
}
return normalize(context, number);
} }
public static String normalize(Context context, Phonenumber.PhoneNumber phoneNumber) { public static String normalize(Context context, Phonenumber.PhoneNumber phoneNumber) {