diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index ffcd2e8937..5c3963a860 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -30,7 +30,6 @@ import android.graphics.PorterDuff.Mode; import android.graphics.drawable.ColorDrawable; import android.net.Uri; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; import android.provider.ContactsContract; import android.support.annotation.NonNull; @@ -67,8 +66,6 @@ import org.thoughtcrime.securesms.components.AttachmentTypeSelector; import org.thoughtcrime.securesms.components.ComposeText; import org.thoughtcrime.securesms.components.InputAwareLayout; import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener; -import org.thoughtcrime.securesms.components.reminder.InviteReminder; -import org.thoughtcrime.securesms.components.reminder.ReminderView; import org.thoughtcrime.securesms.components.SendButton; import org.thoughtcrime.securesms.components.camera.HidingImageButton; import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer; @@ -77,6 +74,8 @@ import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer.Drawer import org.thoughtcrime.securesms.components.emoji.EmojiDrawer; import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiEventListener; import org.thoughtcrime.securesms.components.emoji.EmojiToggle; +import org.thoughtcrime.securesms.components.reminder.InviteReminder; +import org.thoughtcrime.securesms.components.reminder.ReminderView; import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; import org.thoughtcrime.securesms.crypto.MasterCipher; @@ -110,7 +109,6 @@ import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage; import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; -import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState; import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.DirectoryHelper; @@ -123,6 +121,7 @@ import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; import org.thoughtcrime.securesms.util.concurrent.SettableFuture; import org.whispersystems.libaxolotl.InvalidMessageException; @@ -785,7 +784,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (capabilities.getTextCapability() == Capability.UNKNOWN || capabilities.getVoiceCapability() == Capability.UNKNOWN) { - capabilities = DirectoryHelper.refreshDirectoryFor(context, recipients); + capabilities = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients, + TextSecurePreferences.getLocalNumber(context)); } return new Pair<>(capabilities.getTextCapability() == Capability.SUPPORTED, diff --git a/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java b/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java index a8f3204d37..8642d5e6e9 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java @@ -77,7 +77,8 @@ public class ContactsDatabase { public synchronized @NonNull List setRegisteredUsers(@NonNull Account account, @NonNull String localNumber, - @NonNull List registeredContacts) + @NonNull List registeredContacts, + boolean remove) throws RemoteException, OperationApplicationException { @@ -107,8 +108,10 @@ public class ContactsDatabase { ContactTokenDetails tokenDetails = registeredNumbers.get(currentContactEntry.getKey()); if (tokenDetails == null) { - Log.w(TAG, "Removing number: " + currentContactEntry.getKey()); - removeTextSecureRawContact(operations, account, currentContactEntry.getValue().getId()); + if (remove) { + Log.w(TAG, "Removing number: " + currentContactEntry.getKey()); + removeTextSecureRawContact(operations, account, currentContactEntry.getValue().getId()); + } } else if (tokenDetails.isVoice() && !currentContactEntry.getValue().isVoiceSupported()) { Log.w(TAG, "Adding voice support: " + currentContactEntry.getKey()); addContactVoiceSupport(operations, currentContactEntry.getKey(), currentContactEntry.getValue().getId()); @@ -298,7 +301,9 @@ public class ContactsDatabase { .build()); } - private @NonNull Map getSignalRawContacts(Account account, String localNumber) { + private @NonNull Map getSignalRawContacts(@NonNull Account account, + @NonNull String localNumber) + { Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon() .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type).build(); diff --git a/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java b/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java index 7e14f84626..8202dc589b 100644 --- a/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java +++ b/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java @@ -2,11 +2,16 @@ package org.thoughtcrime.securesms.jobs; import android.content.Context; import android.os.PowerManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; +import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.SecurityEvent; +import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.DirectoryHelper; +import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.textsecure.api.push.exceptions.PushNetworkException; @@ -15,11 +20,24 @@ import java.io.IOException; public class DirectoryRefreshJob extends ContextJob { - public DirectoryRefreshJob(Context context) { + @Nullable private transient Recipients recipients; + @Nullable private transient MasterSecret masterSecret; + + public DirectoryRefreshJob(@NonNull Context context) { + this(context, null, null); + } + + public DirectoryRefreshJob(@NonNull Context context, + @Nullable MasterSecret masterSecret, + @Nullable Recipients recipients) + { super(context, JobParameters.newBuilder() .withGroupId(DirectoryRefreshJob.class.getSimpleName()) .withRequirement(new NetworkRequirement(context)) .create()); + + this.recipients = recipients; + this.masterSecret = masterSecret; } @Override @@ -33,7 +51,11 @@ public class DirectoryRefreshJob extends ContextJob { try { wakeLock.acquire(); - DirectoryHelper.refreshDirectory(context, KeyCachingService.getMasterSecret(context)); + if (recipients == null) { + DirectoryHelper.refreshDirectory(context, KeyCachingService.getMasterSecret(context)); + } else { + DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients, TextSecurePreferences.getLocalNumber(context)); + } SecurityEvent.broadcastSecurityUpdateEvent(context); } finally { if (wakeLock.isHeld()) wakeLock.release(); diff --git a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java index 9b8852fe4a..2acabb0dca 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java @@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.database.NotInDirectoryException; import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; +import org.thoughtcrime.securesms.service.KeyCachingService; import org.whispersystems.jobqueue.JobManager; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.textsecure.api.messages.TextSecureEnvelope; @@ -29,6 +30,9 @@ public abstract class PushReceivedJob extends ContextJob { contactTokenDetails.setNumber(envelope.getSource()); directory.setNumber(contactTokenDetails, true); + + Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false); + ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, KeyCachingService.getMasterSecret(context), recipients)); } if (envelope.isReceipt()) { diff --git a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java index cd484b35cd..a22b34d342 100644 --- a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java +++ b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java @@ -18,14 +18,11 @@ import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.NotInDirectoryException; import org.thoughtcrime.securesms.database.TextSecureDirectory; -import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; 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.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.textsecure.api.TextSecureAccountManager; @@ -80,11 +77,7 @@ public class DirectoryHelper { .add(new MultiDeviceContactUpdateJob(context)); } - for (String newUser : newUsers) { - IncomingJoinedMessage message = new IncomingJoinedMessage(newUser); - Pair smsAndThreadId = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message); - MessageNotifier.updateNotification(context, masterSecret, smsAndThreadId.second); - } + notifyNewUsers(context, masterSecret, newUsers); } public static @NonNull List refreshDirectory(@NonNull Context context, @@ -93,7 +86,6 @@ public class DirectoryHelper { throws IOException { TextSecureDirectory directory = TextSecureDirectory.getInstance(context); - Optional account = getOrCreateAccount(context); Set eligibleContactNumbers = directory.getPushEligibleContactNumbers(localNumber); List activeTokens = accountManager.getContacts(eligibleContactNumbers); @@ -104,33 +96,35 @@ public class DirectoryHelper { } directory.setNumbers(activeTokens, eligibleContactNumbers); - - if (account.isPresent()) { - try { - return DatabaseFactory.getContactsDatabase(context) - .setRegisteredUsers(account.get(), localNumber, activeTokens); - } catch (RemoteException | OperationApplicationException e) { - Log.w(TAG, e); - } - } + return updateContactsDatabase(context, localNumber, activeTokens, true); } return new LinkedList<>(); } - public static UserCapabilities refreshDirectoryFor(Context context, Recipients recipients) + public static UserCapabilities refreshDirectoryFor(@NonNull Context context, + @Nullable MasterSecret masterSecret, + @NonNull Recipients recipients, + @NonNull String localNumber) throws IOException { try { - TextSecureDirectory directory = TextSecureDirectory.getInstance(context); - TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(context); - String number = Util.canonicalizeNumber(context, recipients.getPrimaryRecipient().getNumber()); - - Optional details = accountManager.getContact(number); + TextSecureDirectory directory = TextSecureDirectory.getInstance(context); + TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(context); + String number = Util.canonicalizeNumber(context, recipients.getPrimaryRecipient().getNumber()); + Optional details = accountManager.getContact(number); if (details.isPresent()) { directory.setNumber(details.get(), true); - ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context)); + + List newUsers = updateContactsDatabase(context, localNumber, details.get()); + + if (!newUsers.isEmpty() && TextSecurePreferences.isMultiDevice(context)) { + ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context)); + } + + notifyNewUsers(context, masterSecret, newUsers); + return new UserCapabilities(Capability.SUPPORTED, details.get().isVoice() ? Capability.SUPPORTED : Capability.UNSUPPORTED); } else { ContactTokenDetails absent = new ContactTokenDetails(); @@ -185,6 +179,45 @@ public class DirectoryHelper { } } + private static @NonNull List updateContactsDatabase(@NonNull Context context, + @NonNull String localNumber, + @NonNull final ContactTokenDetails activeToken) + { + return updateContactsDatabase(context, localNumber, + new LinkedList() {{add(activeToken);}}, + false); + } + + private static @NonNull List updateContactsDatabase(@NonNull Context context, + @NonNull String localNumber, + @NonNull List activeTokens, + boolean removeMissing) + { + Optional account = getOrCreateAccount(context); + + if (account.isPresent()) { + try { + return DatabaseFactory.getContactsDatabase(context) + .setRegisteredUsers(account.get(), localNumber, activeTokens, removeMissing); + } catch (RemoteException | OperationApplicationException e) { + Log.w(TAG, e); + } + } + + return new LinkedList<>(); + } + + private static void notifyNewUsers(@NonNull Context context, + @Nullable MasterSecret masterSecret, + @NonNull List newUsers) + { + for (String newUser : newUsers) { + IncomingJoinedMessage message = new IncomingJoinedMessage(newUser); + Pair smsAndThreadId = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message); + MessageNotifier.updateNotification(context, masterSecret, smsAndThreadId.second); + } + } + private static Optional getOrCreateAccount(Context context) { AccountManager accountManager = AccountManager.get(context); Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms");