Notify when contacts join Signal

Closes #4314
// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-10-27 12:18:02 -07:00
parent 434ce4f9c9
commit 8d82033855
22 changed files with 163 additions and 39 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -291,6 +291,7 @@
<string name="MessageRecord_s_called_you">%s called you</string> <string name="MessageRecord_s_called_you">%s called you</string>
<string name="MessageRecord_called_s">Called %s</string> <string name="MessageRecord_called_s">Called %s</string>
<string name="MessageRecord_missed_call_from">Missed call from %s</string> <string name="MessageRecord_missed_call_from">Missed call from %s</string>
<string name="MessageRecord_s_is_on_signal_say_hey">%s is on Signal, say hey!</string>
<!-- PassphraseChangeActivity --> <!-- PassphraseChangeActivity -->
@ -447,6 +448,7 @@
<string name="ThreadRecord_called_you">Called you</string> <string name="ThreadRecord_called_you">Called you</string>
<string name="ThreadRecord_missed_call">Missed call</string> <string name="ThreadRecord_missed_call">Missed call</string>
<string name="ThreadRecord_media_message">Media message</string> <string name="ThreadRecord_media_message">Media message</string>
<string name="ThreadRecord_s_is_on_signal_say_hey">%s is on Signal, say hey!</string>
<!-- VerifyIdentityActivity --> <!-- VerifyIdentityActivity -->
<string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">You do not have an identity key.</string> <string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">You do not have an identity key.</string>

View File

@ -3,6 +3,6 @@
android:contentAuthority="com.android.contacts" android:contentAuthority="com.android.contacts"
android:accountType="org.thoughtcrime.securesms" android:accountType="org.thoughtcrime.securesms"
android:userVisible="true" android:userVisible="true"
android:supportsUploading="false" android:supportsUploading="true"
android:allowParallelSyncs="false" android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/> android:isAlwaysSyncable="true"/>

View File

@ -61,6 +61,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
protected ContactSelectionListFragment contactsFragment; protected ContactSelectionListFragment contactsFragment;
private MasterSecret masterSecret;
private Toolbar toolbar; private Toolbar toolbar;
private EditText searchText; private EditText searchText;
private AnimatingToggle toggle; private AnimatingToggle toggle;
@ -79,6 +80,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
@Override @Override
protected void onCreate(Bundle icicle, MasterSecret masterSecret) { protected void onCreate(Bundle icicle, MasterSecret masterSecret) {
setContentView(R.layout.contact_selection_activity); setContentView(R.layout.contact_selection_activity);
this.masterSecret = masterSecret;
initializeToolbar(); initializeToolbar();
initializeResources(); initializeResources();
@ -216,16 +218,19 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> { private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> {
private final WeakReference<ContactSelectionActivity> activity; private final WeakReference<ContactSelectionActivity> activity;
private final MasterSecret masterSecret;
private RefreshDirectoryTask(ContactSelectionActivity activity) { private RefreshDirectoryTask(ContactSelectionActivity activity) {
this.activity = new WeakReference<>(activity); this.activity = new WeakReference<>(activity);
this.masterSecret = activity.masterSecret;
} }
@Override @Override
protected Void doInBackground(Context... params) { protected Void doInBackground(Context... params) {
try { try {
DirectoryHelper.refreshDirectory(params[0]); DirectoryHelper.refreshDirectory(params[0], masterSecret);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }

View File

@ -158,13 +158,17 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
@Override @Override
public int getItemViewType(@NonNull Cursor cursor) { public int getItemViewType(@NonNull Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID)); long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID));
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT)); String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
MessageRecord messageRecord = getMessageRecord(id, cursor, type); MessageRecord messageRecord = getMessageRecord(id, cursor, type);
if (messageRecord.isGroupAction() || messageRecord.isCallLog()) return MESSAGE_TYPE_UPDATE; if (messageRecord.isGroupAction() || messageRecord.isCallLog() || messageRecord.isJoined()) {
else if (messageRecord.isOutgoing()) return MESSAGE_TYPE_OUTGOING; return MESSAGE_TYPE_UPDATE;
else return MESSAGE_TYPE_INCOMING; } else if (messageRecord.isOutgoing()) {
return MESSAGE_TYPE_OUTGOING;
} else {
return MESSAGE_TYPE_INCOMING;
}
} }
private MessageRecord getMessageRecord(long messageId, Cursor cursor, String type) { private MessageRecord getMessageRecord(long messageId, Cursor cursor, String type) {

View File

@ -70,7 +70,8 @@ public class ConversationUpdateItem extends LinearLayout
if (messageRecord.isGroupAction()) setGroupRecord(messageRecord); if (messageRecord.isGroupAction()) setGroupRecord(messageRecord);
else if (messageRecord.isCallLog()) setCallRecord(messageRecord); else if (messageRecord.isCallLog()) setCallRecord(messageRecord);
else throw new AssertionError("Neither group no log."); else if (messageRecord.isJoined()) setJoinedRecord(messageRecord);
else throw new AssertionError("Neither group nor log nor joined.");
} }
private void setCallRecord(MessageRecord messageRecord) { private void setCallRecord(MessageRecord messageRecord) {
@ -99,6 +100,12 @@ public class ConversationUpdateItem extends LinearLayout
date.setVisibility(View.GONE); date.setVisibility(View.GONE);
} }
private void setJoinedRecord(MessageRecord messageRecord) {
icon.setImageResource(R.drawable.ic_favorite_grey600_24dp);
body.setText(messageRecord.getDisplayBody());
date.setVisibility(View.GONE);
}
@Override @Override
public void onModified(Recipients recipients) { public void onModified(Recipients recipients) {
onModified(recipients.getPrimaryRecipient()); onModified(recipients.getPrimaryRecipient());

View File

@ -32,14 +32,18 @@ import android.provider.ContactsContract.RawContacts;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.util.Pair; import android.util.Pair;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.util.InvalidNumberException;
import org.whispersystems.textsecure.api.util.PhoneNumberFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -70,11 +74,14 @@ public class ContactsDatabase {
this.context = context; this.context = context;
} }
public synchronized boolean setRegisteredUsers(Account account, List<String> e164numbers) public synchronized @NonNull List<String> setRegisteredUsers(@NonNull Account account,
@NonNull String localNumber,
@NonNull List<String> e164numbers)
throws RemoteException, OperationApplicationException throws RemoteException, OperationApplicationException
{ {
Map<String, Long> currentContacts = new HashMap<>(); Map<String, Long> currentContacts = new HashMap<>();
Set<String> registeredNumbers = new HashSet<>(e164numbers); Set<String> registeredNumbers = new HashSet<>(e164numbers);
List<String> addedNumbers = new LinkedList<>();
ArrayList<ContentProviderOperation> operations = new ArrayList<>(); ArrayList<ContentProviderOperation> operations = new ArrayList<>();
Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon() Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon()
.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
@ -86,7 +93,16 @@ public class ContactsDatabase {
cursor = context.getContentResolver().query(currentContactsUri, new String[] {BaseColumns._ID, RawContacts.SYNC1}, null, null, null); cursor = context.getContentResolver().query(currentContactsUri, new String[] {BaseColumns._ID, RawContacts.SYNC1}, null, null, null);
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
currentContacts.put(cursor.getString(1), cursor.getLong(0)); String currentNumber;
try {
currentNumber = PhoneNumberFormatter.formatNumber(cursor.getString(1), localNumber);
} catch (InvalidNumberException e) {
Log.w(TAG, e);
currentNumber = cursor.getString(1);
}
currentContacts.put(currentNumber, cursor.getLong(0));
} }
} finally { } finally {
if (cursor != null) if (cursor != null)
@ -95,10 +111,11 @@ public class ContactsDatabase {
for (String number : e164numbers) { for (String number : e164numbers) {
if (!currentContacts.containsKey(number)) { if (!currentContacts.containsKey(number)) {
Optional<Pair<String, Long>> systemContactInfo = getSystemContactInfo(number); Optional<SystemContactInfo> systemContactInfo = getSystemContactInfo(number);
if (systemContactInfo.isPresent()) { if (systemContactInfo.isPresent()) {
addTextSecureRawContact(operations, account, systemContactInfo.get().first, systemContactInfo.get().second); addedNumbers.add(number);
addTextSecureRawContact(operations, account, systemContactInfo.get().number, systemContactInfo.get().id);
} }
} }
} }
@ -111,14 +128,15 @@ public class ContactsDatabase {
if (!operations.isEmpty()) { if (!operations.isEmpty()) {
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations); context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
return true;
} else {
return false;
} }
return addedNumbers;
} }
private void addTextSecureRawContact(List<ContentProviderOperation> operations, private void addTextSecureRawContact(List<ContentProviderOperation> operations,
Account account, String e164number, long aggregateId) Account account,
String e164number,
long aggregateId)
{ {
int index = operations.size(); int index = operations.size();
Uri dataUri = ContactsContract.Data.CONTENT_URI.buildUpon() Uri dataUri = ContactsContract.Data.CONTENT_URI.buildUpon()
@ -254,10 +272,11 @@ public class ContactsDatabase {
return newNumberCursor; return newNumberCursor;
} }
private Optional<Pair<String, Long>> getSystemContactInfo(String e164number) { private Optional<SystemContactInfo> getSystemContactInfo(String e164number) {
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(e164number)); Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(e164number));
String[] projection = {ContactsContract.PhoneLookup.NUMBER, String[] projection = {ContactsContract.PhoneLookup.NUMBER,
ContactsContract.PhoneLookup._ID}; ContactsContract.PhoneLookup._ID,
ContactsContract.PhoneLookup.DISPLAY_NAME};
Cursor numberCursor = null; Cursor numberCursor = null;
Cursor idCursor = null; Cursor idCursor = null;
@ -272,7 +291,9 @@ public class ContactsDatabase {
null); null);
if (idCursor != null && idCursor.moveToNext()) { if (idCursor != null && idCursor.moveToNext()) {
return Optional.of(new Pair<>(numberCursor.getString(0), idCursor.getLong(0))); return Optional.of(new SystemContactInfo(numberCursor.getString(2),
numberCursor.getString(0),
idCursor.getLong(0)));
} }
} }
} finally { } finally {
@ -381,4 +402,16 @@ public class ContactsDatabase {
return null; return null;
} }
} }
private static class SystemContactInfo {
private final String name;
private final String number;
private final long id;
private SystemContactInfo(String name, String number, long id) {
this.name = name;
this.number = number;
this.id = id;
}
}
} }

View File

@ -8,6 +8,7 @@ import android.content.SyncResult;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import java.io.IOException; import java.io.IOException;
@ -25,7 +26,7 @@ public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
ContentProviderClient provider, SyncResult syncResult) ContentProviderClient provider, SyncResult syncResult)
{ {
try { try {
DirectoryHelper.refreshDirectory(getContext()); DirectoryHelper.refreshDirectory(getContext(), KeyCachingService.getMasterSecret(getContext()));
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }

View File

@ -22,6 +22,7 @@ public interface MmsSmsColumns {
protected static final long INCOMING_CALL_TYPE = 1; protected static final long INCOMING_CALL_TYPE = 1;
protected static final long OUTGOING_CALL_TYPE = 2; protected static final long OUTGOING_CALL_TYPE = 2;
protected static final long MISSED_CALL_TYPE = 3; protected static final long MISSED_CALL_TYPE = 3;
protected static final long JOINED_TYPE = 4;
protected static final long BASE_INBOX_TYPE = 20; protected static final long BASE_INBOX_TYPE = 20;
protected static final long BASE_OUTBOX_TYPE = 21; protected static final long BASE_OUTBOX_TYPE = 21;
@ -114,6 +115,10 @@ public interface MmsSmsColumns {
return (type & BASE_TYPE_MASK) == BASE_INBOX_TYPE; return (type & BASE_TYPE_MASK) == BASE_INBOX_TYPE;
} }
public static boolean isJoinedType(long type) {
return (type & BASE_TYPE_MASK) == JOINED_TYPE;
}
public static boolean isSecureType(long type) { public static boolean isSecureType(long type) {
return (type & SECURE_MESSAGE_BIT) != 0; return (type & SECURE_MESSAGE_BIT) != 0;
} }

View File

@ -384,7 +384,9 @@ public class SmsDatabase extends MessagingDatabase {
} }
protected Pair<Long, Long> insertMessageInbox(IncomingTextMessage message, long type) { protected Pair<Long, Long> insertMessageInbox(IncomingTextMessage message, long type) {
if (message.isPreKeyBundle()) { if (message.isJoined()) {
type = (type & (Types.TOTAL_MASK - Types.BASE_TYPE_MASK)) | Types.JOINED_TYPE;
} else if (message.isPreKeyBundle()) {
type |= Types.KEY_EXCHANGE_BIT | Types.KEY_EXCHANGE_BUNDLE_BIT; type |= Types.KEY_EXCHANGE_BIT | Types.KEY_EXCHANGE_BUNDLE_BIT;
} else if (message.isSecureMessage()) { } else if (message.isSecureMessage()) {
type |= Types.SECURE_MESSAGE_BIT; type |= Types.SECURE_MESSAGE_BIT;

View File

@ -99,6 +99,10 @@ public abstract class DisplayRecord {
return SmsDatabase.Types.isCallLog(type); return SmsDatabase.Types.isCallLog(type);
} }
public boolean isJoined() {
return SmsDatabase.Types.isJoinedType(type);
}
public boolean isIncomingCall() { public boolean isIncomingCall() {
return SmsDatabase.Types.isIncomingCall(type); return SmsDatabase.Types.isIncomingCall(type);
} }

View File

@ -121,6 +121,8 @@ public abstract class MessageRecord extends DisplayRecord {
return emphasisAdded(context.getString(R.string.MessageRecord_called_s, getIndividualRecipient().toShortString())); return emphasisAdded(context.getString(R.string.MessageRecord_called_s, getIndividualRecipient().toShortString()));
} else if (isMissedCall()) { } else if (isMissedCall()) {
return emphasisAdded(context.getString(R.string.MessageRecord_missed_call_from, getIndividualRecipient().toShortString())); return emphasisAdded(context.getString(R.string.MessageRecord_missed_call_from, getIndividualRecipient().toShortString()));
} else if (isJoined()) {
return emphasisAdded(context.getString(R.string.MessageRecord_s_is_on_signal_say_hey, getIndividualRecipient().toShortString()));
} else if (getBody().getBody().length() > MAX_DISPLAY_LENGTH) { } else if (getBody().getBody().length() > MAX_DISPLAY_LENGTH) {
return new SpannableString(getBody().getBody().substring(0, MAX_DISPLAY_LENGTH)); return new SpannableString(getBody().getBody().substring(0, MAX_DISPLAY_LENGTH));
} }

View File

@ -90,6 +90,8 @@ public class ThreadRecord extends DisplayRecord {
return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_called_you)); return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_called_you));
} else if (SmsDatabase.Types.isMissedCall(type)) { } else if (SmsDatabase.Types.isMissedCall(type)) {
return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_missed_call)); return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_missed_call));
} else if (SmsDatabase.Types.isJoinedType(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal_say_hey, getRecipients().getPrimaryRecipient().toShortString()));
} else { } else {
if (TextUtils.isEmpty(getBody().getBody())) { if (TextUtils.isEmpty(getBody().getBody())) {
return new SpannableString(emphasisAdded(context.getString(R.string.ThreadRecord_media_message))); return new SpannableString(emphasisAdded(context.getString(R.string.ThreadRecord_media_message)));

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.os.PowerManager; import android.os.PowerManager;
import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@ -30,7 +31,7 @@ public class DirectoryRefreshJob extends ContextJob {
try { try {
wakeLock.acquire(); wakeLock.acquire();
DirectoryHelper.refreshDirectory(context); DirectoryHelper.refreshDirectory(context, KeyCachingService.getMasterSecret(context));
SecurityEvent.broadcastSecurityUpdateEvent(context); SecurityEvent.broadcastSecurityUpdateEvent(context);
} finally { } finally {
if (wakeLock.isHeld()) wakeLock.release(); if (wakeLock.isHeld()) wakeLock.release();

View File

@ -14,6 +14,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class DirectoryRefreshListener extends BroadcastReceiver { public class DirectoryRefreshListener extends BroadcastReceiver {
private static final String TAG = DirectoryRefreshListener.class.getSimpleName();
private static final String REFRESH_EVENT = "org.whispersystems.whisperpush.DIRECTORY_REFRESH"; private static final String REFRESH_EVENT = "org.whispersystems.whisperpush.DIRECTORY_REFRESH";
private static final String BOOT_EVENT = "android.intent.action.BOOT_COMPLETED"; private static final String BOOT_EVENT = "android.intent.action.BOOT_COMPLETED";
@ -51,7 +53,7 @@ public class DirectoryRefreshListener extends BroadcastReceiver {
time = System.currentTimeMillis() + INTERVAL; time = System.currentTimeMillis() + INTERVAL;
} }
Log.w("DirectoryRefreshListener", "Scheduling for: " + time); Log.w(TAG, "Scheduling for: " + time);
alarmManager.cancel(pendingIntent); alarmManager.cancel(pendingIntent);
alarmManager.set(AlarmManager.RTC_WAKEUP, time, pendingIntent); alarmManager.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);

View File

@ -0,0 +1,22 @@
package org.thoughtcrime.securesms.sms;
import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
public class IncomingJoinedMessage extends IncomingTextMessage {
public IncomingJoinedMessage(String sender) {
super(sender, 1, System.currentTimeMillis(), null, Optional.<TextSecureGroup>absent());
}
@Override
public boolean isJoined() {
return true;
}
@Override
public boolean isSecureMessage() {
return true;
}
}

View File

@ -188,6 +188,10 @@ public class IncomingTextMessage implements Parcelable {
return false; return false;
} }
public boolean isJoined() {
return false;
}
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;

View File

@ -7,17 +7,25 @@ import android.content.Context;
import android.content.OperationApplicationException; import android.content.OperationApplicationException;
import android.os.RemoteException; import android.os.RemoteException;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.NotInDirectoryException; import org.thoughtcrime.securesms.database.NotInDirectoryException;
import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.database.TextSecureDirectory;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability; import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability;
import org.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.TextSecureAccountManager; import org.whispersystems.textsecure.api.TextSecureAccountManager;
@ -59,13 +67,29 @@ public class DirectoryHelper {
private static final String TAG = DirectoryHelper.class.getSimpleName(); private static final String TAG = DirectoryHelper.class.getSimpleName();
public static void refreshDirectory(final Context context) throws IOException { public static void refreshDirectory(@NonNull Context context, @Nullable MasterSecret masterSecret)
refreshDirectory(context, throws IOException
TextSecureCommunicationFactory.createManager(context), {
TextSecurePreferences.getLocalNumber(context)); List<String> newUsers = refreshDirectory(context,
TextSecureCommunicationFactory.createManager(context),
TextSecurePreferences.getLocalNumber(context));
if (!newUsers.isEmpty() && TextSecurePreferences.isMultiDevice(context)) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new MultiDeviceContactUpdateJob(context));
}
for (String newUser : newUsers) {
IncomingJoinedMessage message = new IncomingJoinedMessage(newUser);
Pair<Long, Long> smsAndThreadId = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message);
MessageNotifier.updateNotification(context, masterSecret, smsAndThreadId.second);
}
} }
public static void refreshDirectory(final Context context, final TextSecureAccountManager accountManager, final String localNumber) public static @NonNull List<String> refreshDirectory(@NonNull Context context,
@NonNull TextSecureAccountManager accountManager,
@NonNull String localNumber)
throws IOException throws IOException
{ {
TextSecureDirectory directory = TextSecureDirectory.getInstance(context); TextSecureDirectory directory = TextSecureDirectory.getInstance(context);
@ -89,19 +113,15 @@ public class DirectoryHelper {
} }
try { try {
boolean modified = DatabaseFactory.getContactsDatabase(context) return DatabaseFactory.getContactsDatabase(context)
.setRegisteredUsers(account.get(), e164numbers); .setRegisteredUsers(account.get(), localNumber, e164numbers);
if (modified && TextSecurePreferences.isMultiDevice(context)) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new MultiDeviceContactUpdateJob(context));
}
} catch (RemoteException | OperationApplicationException e) { } catch (RemoteException | OperationApplicationException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
} }
} }
return new LinkedList<>();
} }
public static UserCapabilities refreshDirectoryFor(Context context, Recipients recipients) public static UserCapabilities refreshDirectoryFor(Context context, Recipients recipients)
@ -173,8 +193,16 @@ public class DirectoryHelper {
AccountManager accountManager = AccountManager.get(context); AccountManager accountManager = AccountManager.get(context);
Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms"); Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms");
if (accounts.length == 0) return createAccount(context); Optional<Account> account;
else return Optional.of(accounts[0]);
if (accounts.length == 0) account = createAccount(context);
else account = Optional.of(accounts[0]);
if (account.isPresent() && !ContentResolver.getSyncAutomatically(account.get(), ContactsContract.AUTHORITY)) {
ContentResolver.setSyncAutomatically(account.get(), ContactsContract.AUTHORITY, true);
}
return account;
} }
private static Optional<Account> createAccount(Context context) { private static Optional<Account> createAccount(Context context) {