refactored phone contact loading in preperation for sync
This commit is contained in:
parent
4df0cc3657
commit
a49a5790c7
|
@ -15,4 +15,8 @@ public class QuickConversationsService {
|
|||
public static boolean isFull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void considerSync() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package eu.siacs.conversations.android;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract;
|
||||
import android.text.TextUtils;
|
||||
|
||||
abstract class AbstractPhoneContact {
|
||||
|
||||
private final Uri lookupUri;
|
||||
private final String displayName;
|
||||
private final String photoUri;
|
||||
|
||||
|
||||
AbstractPhoneContact(Cursor cursor) {
|
||||
int phoneId = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data._ID));
|
||||
String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY));
|
||||
this.lookupUri = ContactsContract.Contacts.getLookupUri(phoneId, lookupKey);
|
||||
this.displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
|
||||
this.photoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.PHOTO_URI));
|
||||
}
|
||||
|
||||
public Uri getLookupUri() {
|
||||
return lookupUri;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getPhotoUri() {
|
||||
return photoUri;
|
||||
}
|
||||
|
||||
|
||||
public int rating() {
|
||||
return (TextUtils.isEmpty(displayName) ? 0 : 2) + (TextUtils.isEmpty(photoUri) ? 0 : 1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package eu.siacs.conversations.android;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import rocks.xmpp.addr.Jid;
|
||||
|
||||
public class JabberIdContact extends AbstractPhoneContact {
|
||||
|
||||
private final Jid jid;
|
||||
|
||||
private JabberIdContact(Cursor cursor) throws IllegalArgumentException {
|
||||
super(cursor);
|
||||
try {
|
||||
this.jid = Jid.of(cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
|
||||
} catch (IllegalArgumentException | NullPointerException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Jid getJid() {
|
||||
return jid;
|
||||
}
|
||||
|
||||
public static void load(Context context, OnPhoneContactsLoaded<JabberIdContact> callback) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
||||
callback.onPhoneContactsLoaded(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
|
||||
ContactsContract.Data.DISPLAY_NAME,
|
||||
ContactsContract.Data.PHOTO_URI,
|
||||
ContactsContract.Data.LOOKUP_KEY,
|
||||
ContactsContract.CommonDataKinds.Im.DATA};
|
||||
|
||||
final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\""
|
||||
+ ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
|
||||
+ "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
|
||||
+ "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
|
||||
+ "\")";
|
||||
final Cursor cursor;
|
||||
try {
|
||||
cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null, null);
|
||||
} catch (Exception e) {
|
||||
callback.onPhoneContactsLoaded(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
final HashMap<Jid, JabberIdContact> contacts = new HashMap<>();
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
try {
|
||||
final JabberIdContact contact = new JabberIdContact(cursor);
|
||||
final JabberIdContact preexisting = contacts.put(contact.getJid(), contact);
|
||||
if (preexisting == null || preexisting.rating() < contact.rating()) {
|
||||
contacts.put(contact.getJid(), contact);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(Config.LOGTAG,"unable to create jabber id contact");
|
||||
}
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
callback.onPhoneContactsLoaded(contacts.values());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package eu.siacs.conversations.android;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface OnPhoneContactsLoaded<T extends AbstractPhoneContact> {
|
||||
|
||||
void onPhoneContactsLoaded(Collection<T> contacts);
|
||||
}
|
|
@ -48,7 +48,7 @@ public class Contact implements ListItem, Blockable {
|
|||
private String commonName;
|
||||
protected Jid jid;
|
||||
private int subscription = 0;
|
||||
private String systemAccount;
|
||||
private Uri systemAccount;
|
||||
private String photoUri;
|
||||
private final JSONObject keys;
|
||||
private JSONArray groups = new JSONArray();
|
||||
|
@ -62,7 +62,7 @@ public class Contact implements ListItem, Blockable {
|
|||
|
||||
public Contact(final String account, final String systemName, final String serverName,
|
||||
final Jid jid, final int subscription, final String photoUri,
|
||||
final String systemAccount, final String keys, final String avatar, final long lastseen,
|
||||
final Uri systemAccount, final String keys, final String avatar, final long lastseen,
|
||||
final String presence, final String groups) {
|
||||
this.accountUuid = account;
|
||||
this.systemName = systemName;
|
||||
|
@ -105,13 +105,19 @@ public class Contact implements ListItem, Blockable {
|
|||
// 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)),
|
||||
jid,
|
||||
cursor.getInt(cursor.getColumnIndex(OPTIONS)),
|
||||
cursor.getString(cursor.getColumnIndex(PHOTOURI)),
|
||||
cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
|
||||
systemAccount,
|
||||
cursor.getString(cursor.getColumnIndex(KEYS)),
|
||||
cursor.getString(cursor.getColumnIndex(AVATAR)),
|
||||
cursor.getLong(cursor.getColumnIndex(LAST_TIME)),
|
||||
|
@ -200,7 +206,7 @@ public class Contact implements ListItem, Blockable {
|
|||
values.put(SERVERNAME, serverName);
|
||||
values.put(JID, jid.toString());
|
||||
values.put(OPTIONS, subscription);
|
||||
values.put(SYSTEMACCOUNT, systemAccount);
|
||||
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());
|
||||
|
@ -270,21 +276,11 @@ public class Contact implements ListItem, Blockable {
|
|||
}
|
||||
|
||||
public Uri getSystemAccount() {
|
||||
if (systemAccount == null) {
|
||||
return null;
|
||||
} else {
|
||||
String[] parts = systemAccount.split("#");
|
||||
if (parts.length != 2) {
|
||||
return null;
|
||||
} else {
|
||||
long id = Long.parseLong(parts[0]);
|
||||
return ContactsContract.Contacts.getLookupUri(id, parts[1]);
|
||||
}
|
||||
}
|
||||
return systemAccount;
|
||||
}
|
||||
|
||||
public void setSystemAccount(String account) {
|
||||
this.systemAccount = account;
|
||||
public void setSystemAccount(Uri lookupUri) {
|
||||
this.systemAccount = lookupUri;
|
||||
}
|
||||
|
||||
private Collection<String> getGroups(final boolean unique) {
|
||||
|
@ -343,7 +339,7 @@ public class Contact implements ListItem, Blockable {
|
|||
}
|
||||
|
||||
public boolean showInPhoneBook() {
|
||||
return systemAccount != null && !systemAccount.trim().isEmpty();
|
||||
return systemAccount != null;
|
||||
}
|
||||
|
||||
public void parseSubscriptionFromElement(Element item) {
|
||||
|
|
|
@ -25,7 +25,7 @@ import rocks.xmpp.addr.Jid;
|
|||
public class ShortcutService {
|
||||
|
||||
private final XmppConnectionService xmppConnectionService;
|
||||
private final ReplacingSerialSingleThreadExecutor replacingSerialSingleThreadExecutor = new ReplacingSerialSingleThreadExecutor(false);
|
||||
private final ReplacingSerialSingleThreadExecutor replacingSerialSingleThreadExecutor = new ReplacingSerialSingleThreadExecutor(ShortcutService.class.getSimpleName());
|
||||
|
||||
public ShortcutService(XmppConnectionService xmppConnectionService) {
|
||||
this.xmppConnectionService = xmppConnectionService;
|
||||
|
|
|
@ -71,6 +71,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.android.JabberIdContact;
|
||||
import eu.siacs.conversations.crypto.OmemoSetting;
|
||||
import eu.siacs.conversations.crypto.PgpDecryptionService;
|
||||
import eu.siacs.conversations.crypto.PgpEngine;
|
||||
|
@ -115,7 +116,6 @@ import eu.siacs.conversations.utils.ConversationsFileObserver;
|
|||
import eu.siacs.conversations.utils.CryptoHelper;
|
||||
import eu.siacs.conversations.utils.ExceptionHelper;
|
||||
import eu.siacs.conversations.utils.MimeUtils;
|
||||
import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
|
||||
import eu.siacs.conversations.utils.PhoneHelper;
|
||||
import eu.siacs.conversations.utils.QuickLoader;
|
||||
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
|
||||
|
@ -192,7 +192,7 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
};
|
||||
public DatabaseBackend databaseBackend;
|
||||
private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true);
|
||||
private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor("ContactMerger");
|
||||
private long mLastActivity = 0;
|
||||
private FileBackend fileBackend = new FileBackend(this);
|
||||
private MemorizingTrustManager mMemorizingTrustManager;
|
||||
|
@ -1519,45 +1519,36 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
public void loadPhoneContacts() {
|
||||
mContactMergerExecutor.execute(() -> PhoneHelper.loadPhoneContacts(XmppConnectionService.this, new OnPhoneContactsLoadedListener() {
|
||||
@Override
|
||||
public void onPhoneContactsLoaded(List<Bundle> phoneContacts) {
|
||||
Log.d(Config.LOGTAG, "start merging phone contacts with roster");
|
||||
for (Account account : accounts) {
|
||||
List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts();
|
||||
for (Bundle phoneContact : phoneContacts) {
|
||||
Jid jid;
|
||||
try {
|
||||
jid = Jid.of(phoneContact.getString("jid"));
|
||||
} catch (final IllegalArgumentException e) {
|
||||
continue;
|
||||
}
|
||||
final Contact contact = account.getRoster().getContact(jid);
|
||||
String systemAccount = phoneContact.getInt("phoneid")
|
||||
+ "#"
|
||||
+ phoneContact.getString("lookup");
|
||||
contact.setSystemAccount(systemAccount);
|
||||
boolean needsCacheClean = contact.setPhotoUri(phoneContact.getString("photouri"));
|
||||
needsCacheClean |= contact.setSystemName(phoneContact.getString("displayname"));
|
||||
if (needsCacheClean) {
|
||||
getAvatarService().clear(contact);
|
||||
}
|
||||
withSystemAccounts.remove(contact);
|
||||
}
|
||||
for (Contact contact : withSystemAccounts) {
|
||||
contact.setSystemAccount(null);
|
||||
boolean needsCacheClean = contact.setPhotoUri(null);
|
||||
needsCacheClean |= contact.setSystemName(null);
|
||||
if (needsCacheClean) {
|
||||
getAvatarService().clear(contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(Config.LOGTAG, "finished merging phone contacts");
|
||||
mShortcutService.refresh(mInitialAddressbookSyncCompleted.compareAndSet(false, true));
|
||||
updateRosterUi();
|
||||
}
|
||||
}));
|
||||
mContactMergerExecutor.execute(() -> {
|
||||
JabberIdContact.load(this, contacts -> {
|
||||
Log.d(Config.LOGTAG, "start merging phone contacts with roster");
|
||||
for (Account account : accounts) {
|
||||
List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts();
|
||||
for (JabberIdContact jidContact : contacts) {
|
||||
final Contact contact = account.getRoster().getContact(jidContact.getJid());
|
||||
contact.setSystemAccount(jidContact.getLookupUri());
|
||||
boolean needsCacheClean = contact.setPhotoUri(jidContact.getPhotoUri());
|
||||
needsCacheClean |= contact.setSystemName(jidContact.getDisplayName());
|
||||
if (needsCacheClean) {
|
||||
getAvatarService().clear(contact);
|
||||
}
|
||||
withSystemAccounts.remove(contact);
|
||||
}
|
||||
for (Contact contact : withSystemAccounts) {
|
||||
contact.setSystemAccount(null);
|
||||
boolean needsCacheClean = contact.setPhotoUri(null);
|
||||
needsCacheClean |= contact.setSystemName(null);
|
||||
if (needsCacheClean) {
|
||||
getAvatarService().clear(contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(Config.LOGTAG, "finished merging phone contacts");
|
||||
mShortcutService.refresh(mInitialAddressbookSyncCompleted.compareAndSet(false, true));
|
||||
updateRosterUi();
|
||||
});
|
||||
mQuickConversationsService.considerSync();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,55 +24,6 @@ public class PhoneHelper {
|
|||
return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
}
|
||||
|
||||
public static void loadPhoneContacts(Context context, final OnPhoneContactsLoadedListener listener) {
|
||||
final List<Bundle> phoneContacts = new ArrayList<>();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
||||
listener.onPhoneContactsLoaded(phoneContacts);
|
||||
return;
|
||||
}
|
||||
final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
|
||||
ContactsContract.Data.DISPLAY_NAME,
|
||||
ContactsContract.Data.PHOTO_URI,
|
||||
ContactsContract.Data.LOOKUP_KEY,
|
||||
ContactsContract.CommonDataKinds.Im.DATA};
|
||||
|
||||
final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\""
|
||||
+ ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
|
||||
+ "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
|
||||
+ "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
|
||||
+ "\")";
|
||||
|
||||
CursorLoader mCursorLoader = new NotThrowCursorLoader(context,
|
||||
ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null,
|
||||
null);
|
||||
mCursorLoader.registerListener(0, (arg0, c) -> {
|
||||
if (c != null) {
|
||||
while (c.moveToNext()) {
|
||||
Bundle contact = new Bundle();
|
||||
contact.putInt("phoneid", c.getInt(c.getColumnIndex(ContactsContract.Data._ID)));
|
||||
contact.putString("displayname", c.getString(c.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)));
|
||||
contact.putString("photouri", c.getString(c.getColumnIndex(ContactsContract.Data.PHOTO_URI)));
|
||||
contact.putString("lookup", c.getString(c.getColumnIndex(ContactsContract.Data.LOOKUP_KEY)));
|
||||
contact.putString("jid", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
|
||||
phoneContacts.add(contact);
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
listener.onPhoneContactsLoaded(phoneContacts);
|
||||
}
|
||||
});
|
||||
try {
|
||||
mCursorLoader.startLoading();
|
||||
} catch (RejectedExecutionException e) {
|
||||
if (listener != null) {
|
||||
listener.onPhoneContactsLoaded(phoneContacts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Uri getProfilePictureUri(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
||||
return null;
|
||||
|
@ -104,22 +55,4 @@ public class PhoneHelper {
|
|||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private static class NotThrowCursorLoader extends CursorLoader {
|
||||
|
||||
private NotThrowCursorLoader(Context c, Uri u, String[] p, String s, String[] sa, String so) {
|
||||
super(c, u, p, s, sa, so);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor loadInBackground() {
|
||||
|
||||
try {
|
||||
return (super.loadInBackground());
|
||||
} catch (Throwable e) {
|
||||
return (null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,17 +3,13 @@ package eu.siacs.conversations.utils;
|
|||
public class ReplacingSerialSingleThreadExecutor extends SerialSingleThreadExecutor {
|
||||
|
||||
public ReplacingSerialSingleThreadExecutor(String name) {
|
||||
super(name, false);
|
||||
}
|
||||
|
||||
public ReplacingSerialSingleThreadExecutor(boolean prepareLooper) {
|
||||
super(ReplacingSerialSingleThreadExecutor.class.getName(), prepareLooper);
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void execute(final Runnable r) {
|
||||
tasks.clear();
|
||||
if (active != null && active instanceof Cancellable) {
|
||||
if (active instanceof Cancellable) {
|
||||
((Cancellable) active).cancel();
|
||||
}
|
||||
super.execute(r);
|
||||
|
@ -21,7 +17,7 @@ public class ReplacingSerialSingleThreadExecutor extends SerialSingleThreadExecu
|
|||
|
||||
public synchronized void cancelRunningTasks() {
|
||||
tasks.clear();
|
||||
if (active != null && active instanceof Cancellable) {
|
||||
if (active instanceof Cancellable) {
|
||||
((Cancellable) active).cancel();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public class ReplacingTaskManager {
|
|||
synchronized (this.executors) {
|
||||
executor = this.executors.get(account);
|
||||
if (executor == null) {
|
||||
executor = new ReplacingSerialSingleThreadExecutor(false);
|
||||
executor = new ReplacingSerialSingleThreadExecutor(ReplacingTaskManager.class.getSimpleName());
|
||||
this.executors.put(account, executor);
|
||||
}
|
||||
executor.execute(runnable);
|
||||
|
|
|
@ -1,73 +1,64 @@
|
|||
package eu.siacs.conversations.utils;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.services.AttachFileToConversationRunnable;
|
||||
|
||||
public class SerialSingleThreadExecutor implements Executor {
|
||||
|
||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||
final ArrayDeque<Runnable> tasks = new ArrayDeque<>();
|
||||
protected Runnable active;
|
||||
private final String name;
|
||||
final ArrayDeque<Runnable> tasks = new ArrayDeque<>();
|
||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||
private final String name;
|
||||
protected Runnable active;
|
||||
|
||||
public SerialSingleThreadExecutor(String name) {
|
||||
this(name, false);
|
||||
}
|
||||
|
||||
SerialSingleThreadExecutor(String name, boolean prepareLooper) {
|
||||
if (prepareLooper) {
|
||||
execute(Looper::prepare);
|
||||
}
|
||||
this.name = name;
|
||||
}
|
||||
public SerialSingleThreadExecutor(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public synchronized void execute(final Runnable r) {
|
||||
tasks.offer(new Runner(r));
|
||||
if (active == null) {
|
||||
scheduleNext();
|
||||
}
|
||||
}
|
||||
public synchronized void execute(final Runnable r) {
|
||||
tasks.offer(new Runner(r));
|
||||
if (active == null) {
|
||||
scheduleNext();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void scheduleNext() {
|
||||
if ((active = tasks.poll()) != null) {
|
||||
executor.execute(active);
|
||||
int remaining = tasks.size();
|
||||
if (remaining > 0) {
|
||||
Log.d(Config.LOGTAG,remaining+" remaining tasks on executor '"+name+"'");
|
||||
}
|
||||
}
|
||||
}
|
||||
private synchronized void scheduleNext() {
|
||||
if ((active = tasks.poll()) != null) {
|
||||
executor.execute(active);
|
||||
int remaining = tasks.size();
|
||||
if (remaining > 0) {
|
||||
Log.d(Config.LOGTAG, remaining + " remaining tasks on executor '" + name + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Runner implements Runnable, Cancellable {
|
||||
private class Runner implements Runnable, Cancellable {
|
||||
|
||||
private final Runnable runnable;
|
||||
private final Runnable runnable;
|
||||
|
||||
private Runner(Runnable runnable) {
|
||||
this.runnable = runnable;
|
||||
}
|
||||
private Runner(Runnable runnable) {
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
if (runnable instanceof Cancellable) {
|
||||
((Cancellable) runnable).cancel();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void cancel() {
|
||||
if (runnable instanceof Cancellable) {
|
||||
((Cancellable) runnable).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
scheduleNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
scheduleNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package eu.siacs.conversations.android;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
|
||||
import io.michaelrocks.libphonenumber.android.NumberParseException;
|
||||
|
||||
public class PhoneNumberContact extends AbstractPhoneContact {
|
||||
|
||||
private String phoneNumber;
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
private PhoneNumberContact(Context context, Cursor cursor) throws IllegalArgumentException {
|
||||
super(cursor);
|
||||
try {
|
||||
this.phoneNumber = PhoneNumberUtilWrapper.normalize(context,cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
|
||||
} catch (NumberParseException | NullPointerException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(Context context, OnPhoneContactsLoaded<PhoneNumberContact> callback) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
||||
callback.onPhoneContactsLoaded(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
|
||||
ContactsContract.Data.DISPLAY_NAME,
|
||||
ContactsContract.Data.PHOTO_URI,
|
||||
ContactsContract.Data.LOOKUP_KEY,
|
||||
ContactsContract.CommonDataKinds.Phone.NUMBER};
|
||||
final Cursor cursor;
|
||||
try {
|
||||
cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null);
|
||||
} catch (Exception e) {
|
||||
callback.onPhoneContactsLoaded(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
final HashMap<String, PhoneNumberContact> contacts = new HashMap<>();
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
try {
|
||||
final PhoneNumberContact contact = new PhoneNumberContact(context, cursor);
|
||||
final PhoneNumberContact preexisting = contacts.get(contact.getPhoneNumber());
|
||||
if (preexisting == null || preexisting.rating() < contact.rating()) {
|
||||
contacts.put(contact.getPhoneNumber(), contact);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(Config.LOGTAG, "unable to create phone contact");
|
||||
}
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
callback.onPhoneContactsLoaded(contacts.values());
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.android.PhoneNumberContact;
|
||||
import eu.siacs.conversations.crypto.sasl.Plain;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
import eu.siacs.conversations.utils.AccountUtils;
|
||||
|
@ -264,6 +265,14 @@ public class QuickConversationsService {
|
|||
return false;
|
||||
}
|
||||
|
||||
public void considerSync() {
|
||||
PhoneNumberContact.load(service, contacts -> {
|
||||
for(PhoneNumberContact c : contacts) {
|
||||
Log.d(Config.LOGTAG, "Display Name=" + c.getDisplayName() + ", number=" + c.getPhoneNumber()+", uri="+c.getLookupUri());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface OnVerificationRequested {
|
||||
void onVerificationRequestFailed(int code);
|
||||
|
||||
|
|
Loading…
Reference in a new issue