package org.tm.archive.contacts; import android.accounts.Account; import android.content.AbstractThreadedSyncAdapter; import android.content.ContentProviderClient; import android.content.Context; import android.content.SyncResult; import android.os.Bundle; import com.annimon.stream.Stream; import org.signal.contacts.SystemContactsRepository; import org.signal.core.util.logging.Log; import org.tm.archive.contacts.sync.ContactDiscovery; import org.tm.archive.database.SignalDatabase; import org.tm.archive.dependencies.ApplicationDependencies; import org.tm.archive.jobs.DirectoryRefreshJob; import org.tm.archive.keyvalue.SignalStore; import org.tm.archive.phonenumbers.PhoneNumberFormatter; import org.tm.archive.recipients.Recipient; import org.signal.core.util.SetUtil; import org.tm.archive.util.Util; import java.io.IOException; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter { private static final String TAG = Log.tag(ContactsSyncAdapter.class); private static final int FULL_SYNC_THRESHOLD = 10; public ContactsSyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); } @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { Log.i(TAG, "onPerformSync(" + authority +")"); Context context = getContext(); if (SignalStore.account().getE164() == null) { Log.i(TAG, "No local number set, skipping all sync operations."); return; } if (!SignalStore.account().isRegistered()) { Log.i(TAG, "Not push registered. Just syncing contact info."); ContactDiscovery.syncRecipientInfoWithSystemContacts(context); return; } Set allSystemE164s = SystemContactsRepository.getAllDisplayNumbers(context) .stream() .map(number -> PhoneNumberFormatter.get(context).format(number)) .collect(Collectors.toSet()); Set knownSystemE164s = SignalDatabase.recipients().getAllE164s(); Set unknownSystemE164s = SetUtil.difference(allSystemE164s, knownSystemE164s); if (unknownSystemE164s.size() > FULL_SYNC_THRESHOLD) { Log.i(TAG, "There are " + unknownSystemE164s.size() + " unknown contacts. Doing a full sync."); try { ContactDiscovery.refreshAll(context, true); } catch (IOException e) { Log.w(TAG, e); } } else if (unknownSystemE164s.size() > 0) { List recipients = Stream.of(unknownSystemE164s) .filter(s -> s.startsWith("+")) .map(s -> Recipient.external(getContext(), s)) .toList(); Log.i(TAG, "There are " + unknownSystemE164s.size() + " unknown E164s, which are now " + recipients.size() + " recipients. Only syncing these specific contacts."); try { ContactDiscovery.refresh(context, recipients, true); } catch (IOException e) { Log.w(TAG, "Failed to refresh! Scheduling for later.", e); ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(true)); } } else { Log.i(TAG, "No new contacts. Just syncing system contact data."); ContactDiscovery.syncRecipientInfoWithSystemContacts(context); } } @Override public void onSyncCanceled() { Log.w(TAG, "onSyncCanceled()"); } @Override public void onSyncCanceled(Thread thread) { Log.w(TAG, "onSyncCanceled(" + thread + ")"); } }