Synchronize with paired devices when a contact changes.

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-10-23 12:51:28 -07:00
parent 05eba8c2f0
commit 11a93fabe5
6 changed files with 46 additions and 21 deletions

View File

@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.api.TextSecureAccountManager; import org.whispersystems.textsecure.api.TextSecureAccountManager;
import org.whispersystems.textsecure.api.messages.multidevice.DeviceInfo; import org.whispersystems.textsecure.api.messages.multidevice.DeviceInfo;
@ -122,8 +123,12 @@ public class DeviceListActivity extends PassphraseRequiredActionBarActivity {
setListAdapter(new DeviceListAdapter(getActivity(), R.layout.device_list_item_view, data)); setListAdapter(new DeviceListAdapter(getActivity(), R.layout.device_list_item_view, data));
if (data.isEmpty()) empty.setVisibility(View.VISIBLE); if (data.isEmpty()) {
else empty.setVisibility(View.GONE); empty.setVisibility(View.VISIBLE);
TextSecurePreferences.setMultiDevice(getActivity(), false);
} else {
empty.setVisibility(View.GONE);
}
} }
@Override @Override

View File

@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.IdentityKeyPair; import org.whispersystems.libaxolotl.IdentityKeyPair;
import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.ecc.Curve; import org.whispersystems.libaxolotl.ecc.Curve;
@ -111,6 +112,7 @@ public class DeviceProvisioningActivity extends PassphraseRequiredActionBarActiv
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context); IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context);
accountManager.addDevice(ephemeralId, publicKey, identityKeyPair, verificationCode); accountManager.addDevice(ephemeralId, publicKey, identityKeyPair, verificationCode);
TextSecurePreferences.setMultiDevice(context, true);
return SUCCESS; return SUCCESS;

View File

@ -70,7 +70,7 @@ public class ContactsDatabase {
this.context = context; this.context = context;
} }
public synchronized void setRegisteredUsers(Account account, List<String> e164numbers) public synchronized boolean setRegisteredUsers(Account account, List<String> e164numbers)
throws RemoteException, OperationApplicationException throws RemoteException, OperationApplicationException
{ {
Map<String, Long> currentContacts = new HashMap<>(); Map<String, Long> currentContacts = new HashMap<>();
@ -111,6 +111,9 @@ 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;
} }
} }

View File

@ -101,17 +101,19 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
private void sendUpdate(TextSecureMessageSender messageSender, File contactsFile) private void sendUpdate(TextSecureMessageSender messageSender, File contactsFile)
throws IOException, UntrustedIdentityException, NetworkException throws IOException, UntrustedIdentityException, NetworkException
{ {
FileInputStream contactsFileStream = new FileInputStream(contactsFile); if (contactsFile.length() > 0) {
TextSecureAttachmentStream attachmentStream = TextSecureAttachment.newStreamBuilder() FileInputStream contactsFileStream = new FileInputStream(contactsFile);
.withStream(contactsFileStream) TextSecureAttachmentStream attachmentStream = TextSecureAttachment.newStreamBuilder()
.withContentType("application/octet-stream") .withStream(contactsFileStream)
.withLength(contactsFile.length()) .withContentType("application/octet-stream")
.build(); .withLength(contactsFile.length())
.build();
try { try {
messageSender.sendMessage(TextSecureSyncMessage.forContacts(attachmentStream)); messageSender.sendMessage(TextSecureSyncMessage.forContacts(attachmentStream));
} catch (IOException ioe) { } catch (IOException ioe) {
throw new NetworkException(ioe); throw new NetworkException(ioe);
}
} }
} }

View File

@ -15,6 +15,7 @@ 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.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability; import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability;
@ -59,13 +60,9 @@ 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(final Context context) throws IOException {
refreshDirectory(context, TextSecureCommunicationFactory.createManager(context)); refreshDirectory(context,
} TextSecureCommunicationFactory.createManager(context),
TextSecurePreferences.getLocalNumber(context));
public static void refreshDirectory(final Context context, final TextSecureAccountManager accountManager)
throws IOException
{
refreshDirectory(context, accountManager, TextSecurePreferences.getLocalNumber(context));
} }
public static void refreshDirectory(final Context context, final TextSecureAccountManager accountManager, final String localNumber) public static void refreshDirectory(final Context context, final TextSecureAccountManager accountManager, final String localNumber)
@ -92,7 +89,14 @@ public class DirectoryHelper {
} }
try { try {
DatabaseFactory.getContactsDatabase(context).setRegisteredUsers(account.get(), e164numbers); boolean modified = DatabaseFactory.getContactsDatabase(context)
.setRegisteredUsers(account.get(), 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);
} }

View File

@ -90,6 +90,15 @@ public class TextSecurePreferences {
public static final String MEDIA_DOWNLOAD_ROAMING_PREF = "pref_media_download_roaming"; public static final String MEDIA_DOWNLOAD_ROAMING_PREF = "pref_media_download_roaming";
public static final String SYSTEM_EMOJI_PREF = "pref_system_emoji"; public static final String SYSTEM_EMOJI_PREF = "pref_system_emoji";
private static final String MULTI_DEVICE_PROVISIONED_PREF = "pref_multi_device";
public static void setMultiDevice(Context context, boolean value) {
setBooleanPreference(context, MULTI_DEVICE_PROVISIONED_PREF, value);
}
public static boolean isMultiDevice(Context context) {
return getBooleanPreference(context, MULTI_DEVICE_PROVISIONED_PREF, false);
}
public static NotificationPrivacyPreference getNotificationPrivacy(Context context) { public static NotificationPrivacyPreference getNotificationPrivacy(Context context) {
return new NotificationPrivacyPreference(getStringPreference(context, NOTIFICATION_PRIVACY_PREF, "all")); return new NotificationPrivacyPreference(getStringPreference(context, NOTIFICATION_PRIVACY_PREF, "all"));