diff --git a/build.gradle b/build.gradle index 3f81d256ac..ae795074bc 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,7 @@ dependencies { compile 'org.whispersystems:jobmanager:0.11.0' compile 'org.whispersystems:libpastelog:1.0.6' compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' - compile 'org.whispersystems:textsecure-android:1.7.2' + compile 'org.whispersystems:textsecure-android:1.8.0' compile 'com.h6ah4i.android.compat:mulsellistprefcompat:1.0.0' testCompile 'junit:junit:4.12' @@ -129,23 +129,23 @@ dependencyVerification { 'org.whispersystems:jobmanager:ea9cb943c4892fb90c1eea1be30efeb85cefca213d52c788419553b58d0ed70d', 'org.whispersystems:libpastelog:550d33c565380d90f4c671e7b8ed5f3a6da55a9fda468373177106b2eb5220b2', 'com.amulyakhare:com.amulyakhare.textdrawable:54c92b5fba38cfd316a07e5a30528068f45ce8515a6890f1297df4c401af5dcb', - 'org.whispersystems:textsecure-android:0bc637dcfd1ef25888f07f05120fe0360c182e012a5151fcf6d96464ba850e8f', + 'org.whispersystems:textsecure-android:226214454003de852d856509ce9ca90a0865f7ad85a45f7a769916c6362fd4d7', 'com.h6ah4i.android.compat:mulsellistprefcompat:47167c5cb796de1a854788e9ff318358e36c8fb88123baaa6e38fb78511dfabe', 'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a', 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', + 'org.whispersystems:textsecure-java:97ef37e2ad88c6d7a896680bbab051ea894dc45409bc9b4683f75b55718fbeb5', 'org.whispersystems:axolotl-android:40d3db5004a84749a73f68d2f0d01b2ae35a73c54df96d8c6c6723b96efb6fc0', - 'org.whispersystems:textsecure-java:137ef598a3bb77bf53930f76e3881190d12a91a597c2a17bf48835611fcf2f66', - 'org.whispersystems:axolotl-java:6daee739b89d8d7101de6d98f77132fee48495c6ea647d880e77def842f999ea', - 'org.whispersystems:curve25519-android:3c29a4131a69b0d16baaa3d707678deb39602c3a3ffd75805ce7f9db252e5d0d', 'com.googlecode.libphonenumber:libphonenumber:eba17eae81dd622ea89a00a3a8c025b2f25d342e0d9644c5b62e16f15687c3ab', 'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74', 'com.squareup.okhttp:okhttp:89b7f63e2e5b6c410266abc14f50fe52ea8d2d8a57260829e499b1cd9f0e61af', 'com.fasterxml.jackson.core:jackson-databind:835097bcdd11f5bc8a08378c70d4c8054dfa4b911691cc2752063c75534d198d', - 'org.whispersystems:curve25519-java:9ccef8f5aba05d9942336f023c589d6278b4f9135bdc34a7bade1f4e7ad65fa3', + 'org.whispersystems:axolotl-java:6daee739b89d8d7101de6d98f77132fee48495c6ea647d880e77def842f999ea', + 'org.whispersystems:curve25519-android:3c29a4131a69b0d16baaa3d707678deb39602c3a3ffd75805ce7f9db252e5d0d', 'com.squareup.okio:okio:5e1098bd3fdee4c3347f5ab815b40ba851e4ab1b348c5e49a5b0362f0ce6e978', 'com.fasterxml.jackson.core:jackson-annotations:0ca408c24202a7626ec8b861e99d85eca5e38b73311dd6dd12e3e9deecc3fe94', 'com.fasterxml.jackson.core:jackson-core:cbf4604784b4de226262845447a1ad3bb38a6728cebe86562e2c5afada8be2c0', + 'org.whispersystems:curve25519-java:9ccef8f5aba05d9942336f023c589d6278b4f9135bdc34a7bade1f4e7ad65fa3', 'com.android.support:support-v4:1e2e4d35ac7fd30db5ce3bc177b92e4d5af86acef2ef93e9221599d733346f56', 'com.android.support:support-annotations:7bc07519aa613b186001160403bcfd68260fa82c61cc7e83adeedc9b862b94ae', ] diff --git a/src/org/thoughtcrime/securesms/BindableConversationItem.java b/src/org/thoughtcrime/securesms/BindableConversationItem.java index 971907c8a3..553b9f5ac9 100644 --- a/src/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/src/org/thoughtcrime/securesms/BindableConversationItem.java @@ -13,5 +13,5 @@ public interface BindableConversationItem extends Unbindable { @NonNull MessageRecord messageRecord, @NonNull Locale locale, @NonNull Set batchSelected, - boolean groupThread, boolean pushDestination); + boolean groupThread); } diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 41daef6ebb..5bf71af398 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -35,7 +35,6 @@ import android.os.Build; import android.os.Bundle; import android.provider.ContactsContract; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v4.view.WindowCompat; import android.text.Editable; import android.text.TextWatcher; @@ -64,19 +63,18 @@ import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener; import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.components.AnimatingToggle; import org.thoughtcrime.securesms.components.ComposeText; -import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout; +import org.thoughtcrime.securesms.components.InputAwareLayout; import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener; import org.thoughtcrime.securesms.components.SendButton; +import org.thoughtcrime.securesms.components.camera.HidingImageButton; +import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer; +import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer.AttachmentDrawerListener; import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer.DrawerState; -import org.thoughtcrime.securesms.components.InputAwareLayout; -import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiEventListener; 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.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; -import org.thoughtcrime.securesms.components.camera.HidingImageButton; -import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer.AttachmentDrawerListener; -import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer; import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.SecurityEvent; @@ -86,13 +84,13 @@ import org.thoughtcrime.securesms.database.DraftDatabase.Draft; import org.thoughtcrime.securesms.database.DraftDatabase.Drafts; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.MmsSmsColumns.Types; +import org.thoughtcrime.securesms.database.NotInDirectoryException; +import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.mms.AttachmentManager; import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType; import org.thoughtcrime.securesms.mms.AttachmentTypeSelectorAdapter; import org.thoughtcrime.securesms.mms.MediaConstraints; -import org.thoughtcrime.securesms.mms.MediaTooLargeException; -import org.thoughtcrime.securesms.mms.MmsMediaConstraints; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; @@ -110,10 +108,10 @@ 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.BitmapDecodingException; import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState; import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.DirectoryHelper; +import org.thoughtcrime.securesms.util.DirectoryHelper.RegistrationState; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.GroupUtil; @@ -123,6 +121,7 @@ import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; import org.thoughtcrime.securesms.util.concurrent.SettableFuture; import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.textsecure.api.util.InvalidNumberException; import java.io.IOException; import java.security.NoSuchAlgorithmException; @@ -606,28 +605,53 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } } - private void handleDial(Recipient recipient) { - Intent intent = new Intent(this, RedPhoneService.class); - intent.setAction(RedPhoneService.ACTION_OUTGOING_CALL); - intent.putExtra(RedPhoneService.EXTRA_REMOTE_NUMBER, recipient.getNumber()); - startService(intent); + private void handleDial(final Recipient recipient) { + if (recipient == null) return; + + new AsyncTask() { + @Override + protected Boolean doInBackground(Recipient... params) { + try { + Context context = ConversationActivity.this; + Recipient recipient = params[0]; + String e164number = Util.canonicalizeNumber(context, recipient.getNumber()); + + return TextSecureDirectory.getInstance(context).isSecureVoiceSupported(e164number); + } catch (InvalidNumberException | NotInDirectoryException e) { + Log.w(TAG, e); + return false; + } + } + + @Override + protected void onPostExecute(Boolean secureVoiceSupported) { + handleDial(recipient, secureVoiceSupported); + } + }.execute(recipient); + } - Intent activityIntent = new Intent(this, RedPhone.class); - activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(activityIntent); + private void handleDial(Recipient recipient, boolean secureVoice) { + if (secureVoice) { + Intent intent = new Intent(this, RedPhoneService.class); + intent.setAction(RedPhoneService.ACTION_OUTGOING_CALL); + intent.putExtra(RedPhoneService.EXTRA_REMOTE_NUMBER, recipient.getNumber()); + startService(intent); -// try { -// if (recipient == null) return; -// -// Intent dialIntent = new Intent(Intent.ACTION_DIAL, -// Uri.parse("tel:" + recipient.getNumber())); -// startActivity(dialIntent); -// } catch (ActivityNotFoundException anfe) { -// Log.w(TAG, anfe); -// Dialogs.showAlertDialog(this, -// getString(R.string.ConversationActivity_calls_not_supported), -// getString(R.string.ConversationActivity_this_device_does_not_appear_to_support_dial_actions)); -// } + Intent activityIntent = new Intent(this, RedPhone.class); + activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(activityIntent); + } else { + try { + Intent dialIntent = new Intent(Intent.ACTION_DIAL, + Uri.parse("tel:" + recipient.getNumber())); + startActivity(dialIntent); + } catch (ActivityNotFoundException anfe) { + Log.w(TAG, anfe); + Dialogs.showAlertDialog(this, + getString(R.string.ConversationActivity_calls_not_supported), + getString(R.string.ConversationActivity_this_device_does_not_appear_to_support_dial_actions)); + } + } } private void handleDisplayGroupRecipients() { @@ -664,7 +688,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void handleAddAttachment() { - if (this.isMmsEnabled || DirectoryHelper.isPushDestination(this, getRecipients())) { + if (this.isMmsEnabled || isEncryptedConversation) { new AlertDialogWrapper.Builder(this).setAdapter(attachmentAdapter, new AttachmentTypeListener()) .show(); } else { @@ -740,8 +764,37 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void initializeSecurity() { - boolean isMediaMessage = !recipients.isSingleRecipient() || attachmentManager.isAttachmentPresent(); - this.isEncryptedConversation = DirectoryHelper.isPushDestination(this, getRecipients()); + initializeSecurity(false); + + new AsyncTask() { + @Override + protected Boolean doInBackground(Recipients... params) { + try { + Context context = ConversationActivity.this; + Recipients recipients = params[0]; + RegistrationState registrationState = DirectoryHelper.isTextSecureEnabledRecipient(context, recipients); + + if (registrationState == RegistrationState.NOT_REGISTERED) return false; + else if (registrationState == RegistrationState.REGISTERED) return true; + else return DirectoryHelper.refreshDirectoryFor(context, recipients) == RegistrationState.REGISTERED; + } catch (IOException e) { + Log.w(TAG, e); + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + if (result != isEncryptedConversation) { + initializeSecurity(result); + } + } + }.execute(recipients); + } + + private void initializeSecurity(boolean encryptedConversation) { + boolean isMediaMessage = !recipients.isSingleRecipient() || attachmentManager.isAttachmentPresent(); + this.isEncryptedConversation = encryptedConversation; sendButton.resetAvailableTransports(isMediaMessage); diff --git a/src/org/thoughtcrime/securesms/ConversationAdapter.java b/src/org/thoughtcrime/securesms/ConversationAdapter.java index 370cfc723d..730c1499f8 100644 --- a/src/org/thoughtcrime/securesms/ConversationAdapter.java +++ b/src/org/thoughtcrime/securesms/ConversationAdapter.java @@ -72,7 +72,6 @@ public class ConversationAdapter private final MasterSecret masterSecret; private final Locale locale; private final boolean groupThread; - private final boolean pushDestination; private final MmsSmsDatabase db; private final LayoutInflater inflater; @@ -97,15 +96,13 @@ public class ConversationAdapter @NonNull Locale locale, @Nullable ItemClickListener clickListener, @Nullable Cursor cursor, - boolean groupThread, - boolean pushDestination) + boolean groupThread) { super(context, cursor); this.masterSecret = masterSecret; this.locale = locale; this.clickListener = clickListener; this.groupThread = groupThread; - this.pushDestination = pushDestination; this.inflater = LayoutInflater.from(context); this.db = DatabaseFactory.getMmsSmsDatabase(context); } @@ -121,7 +118,7 @@ public class ConversationAdapter String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT)); MessageRecord messageRecord = getMessageRecord(id, cursor, type); - viewHolder.getView().bind(masterSecret, messageRecord, locale, batchSelected, groupThread, pushDestination); + viewHolder.getView().bind(masterSecret, messageRecord, locale, batchSelected, groupThread); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java index 43c27040e1..b9de105cf3 100644 --- a/src/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationFragment.java @@ -40,7 +40,6 @@ import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.MessageSender; -import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.FutureTaskListener; import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.SaveAttachmentTask; @@ -136,8 +135,7 @@ public class ConversationFragment extends Fragment private void initializeListAdapter() { if (this.recipients != null && this.threadId != -1) { list.setAdapter(new ConversationAdapter(getActivity(), masterSecret, locale, selectionClickListener, null, - (!this.recipients.isSingleRecipient()) || this.recipients.isGroupRecipient(), - DirectoryHelper.isPushDestination(getActivity(), this.recipients))); + (!this.recipients.isSingleRecipient()) || this.recipients.isGroupRecipient())); getLoaderManager().restartLoader(0, null, this); } } diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java index 35ddd42a45..006f3b16b5 100644 --- a/src/org/thoughtcrime/securesms/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/ConversationItem.java @@ -83,7 +83,6 @@ public class ConversationItem extends LinearLayout private MasterSecret masterSecret; private Locale locale; private boolean groupThread; - private boolean pushDestination; private Recipient recipient; private View bodyBubble; @@ -171,14 +170,13 @@ public class ConversationItem extends LinearLayout @NonNull MessageRecord messageRecord, @NonNull Locale locale, @NonNull Set batchSelected, - boolean groupThread, boolean pushDestination) + boolean groupThread) { this.masterSecret = masterSecret; this.messageRecord = messageRecord; this.locale = locale; this.batchSelected = batchSelected; this.groupThread = groupThread; - this.pushDestination = pushDestination; this.recipient = messageRecord.getIndividualRecipient(); this.recipient.addListener(this); @@ -503,7 +501,7 @@ public class ConversationItem extends LinearLayout intent.putExtra(MessageDetailsActivity.MASTER_SECRET_EXTRA, masterSecret); intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, messageRecord.getId()); intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); - intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && pushDestination); + intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && messageRecord.isPush()); context.startActivity(intent); } else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) { handleApproveIdentity(); diff --git a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java index b41d36b5c8..b42def811d 100644 --- a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java +++ b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java @@ -56,7 +56,7 @@ public class ConversationUpdateItem extends LinearLayout @NonNull MessageRecord messageRecord, @NonNull Locale locale, @NonNull Set batchSelected, - boolean groupThread, boolean pushDestination) + boolean groupThread) { bind(messageRecord, locale); } diff --git a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java index 132e989220..bd59046fec 100644 --- a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java +++ b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java @@ -43,7 +43,6 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.GroupUtil; @@ -174,8 +173,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity toFrom.setText(toFromRes); conversationItem.bind(masterSecret, messageRecord, dynamicLanguage.getCurrentLocale(), new HashSet(), - recipients != messageRecord.getRecipients(), - DirectoryHelper.isPushDestination(this, recipients)); + recipients != messageRecord.getRecipients()); recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, masterSecret, messageRecord, recipients, isPushGroup)); } diff --git a/src/org/thoughtcrime/securesms/RegistrationProgressActivity.java b/src/org/thoughtcrime/securesms/RegistrationProgressActivity.java index f988126439..90f71d83d0 100644 --- a/src/org/thoughtcrime/securesms/RegistrationProgressActivity.java +++ b/src/org/thoughtcrime/securesms/RegistrationProgressActivity.java @@ -520,9 +520,9 @@ public class RegistrationProgressActivity extends BaseActionBarActivity { protected Integer doInBackground(Void... params) { try { TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(context, e164number, password); - int registrationId = TextSecurePreferences.getLocalRegistrationId(context); + int registrationId = TextSecurePreferences.getLocalRegistrationId(context); - accountManager.verifyAccountWithCode(code, signalingKey, registrationId); + accountManager.verifyAccountWithCode(code, signalingKey, registrationId, true); return SUCCESS; } catch (ExpectationFailedException e) { diff --git a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java b/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java index ac8a733ca7..7b41630535 100644 --- a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java +++ b/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java @@ -7,6 +7,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.text.TextUtils; import android.util.Log; import org.whispersystems.textsecure.api.push.ContactTokenDetails; @@ -22,9 +23,10 @@ import java.util.Set; public class TextSecureDirectory { private static final int INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER = 2; + private static final int INTRODUCED_VOICE_COLUMN = 4; private static final String DATABASE_NAME = "whisper_directory.db"; - private static final int DATABASE_VERSION = 3; + private static final int DATABASE_VERSION = 4; private static final String TABLE_NAME = "directory"; private static final String ID = "_id"; @@ -32,11 +34,14 @@ public class TextSecureDirectory { private static final String REGISTERED = "registered"; private static final String RELAY = "relay"; private static final String TIMESTAMP = "timestamp"; + private static final String VOICE = "voice"; + private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY, " + NUMBER + " TEXT UNIQUE, " + REGISTERED + " INTEGER, " + RELAY + " TEXT, " + - TIMESTAMP + " INTEGER);"; + TIMESTAMP + " INTEGER, " + + VOICE + " INTEGER);"; private static final Object instanceLock = new Object(); private static volatile TextSecureDirectory instance; @@ -86,6 +91,31 @@ public class TextSecureDirectory { } } + public boolean isSecureVoiceSupported(String e164number) throws NotInDirectoryException { + if (TextUtils.isEmpty(e164number)) { + return false; + } + + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + + try { + cursor = db.query(TABLE_NAME, + new String[]{VOICE}, NUMBER + " = ?", + new String[] {e164number}, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + return cursor.getInt(0) == 1; + } else { + throw new NotInDirectoryException(); + } + + } finally { + if (cursor != null) + cursor.close(); + } + } + public String getRelay(String e164number) { SQLiteDatabase database = databaseHelper.getReadableDatabase(); Cursor cursor = null; @@ -127,6 +157,7 @@ public class TextSecureDirectory { values.put(REGISTERED, 1); values.put(TIMESTAMP, timestamp); values.put(RELAY, token.getRelay()); + values.put(VOICE, token.isVoice()); db.replace(TABLE_NAME, null, values); } @@ -226,6 +257,10 @@ public class TextSecureDirectory { "supports_sms INTEGER, " + "timestamp INTEGER);"); } + + if (oldVersion < INTRODUCED_VOICE_COLUMN) { + db.execSQL("ALTER TABLE directory ADD COLUMN voice INTEGER;"); + } } } diff --git a/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java index 45601cc5a2..3c6ece846b 100644 --- a/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java +++ b/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java @@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.jobs.PushGroupSendJob; import org.thoughtcrime.securesms.jobs.PushMediaSendJob; import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; import org.thoughtcrime.securesms.jobs.PushTextSendJob; +import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; import org.thoughtcrime.securesms.push.SecurityEventListener; import org.thoughtcrime.securesms.push.TextSecurePushTrustStore; @@ -41,7 +42,8 @@ import dagger.Provides; PushNotificationReceiveJob.class, MultiDeviceContactUpdateJob.class, MultiDeviceGroupUpdateJob.class, - DeviceListActivity.DeviceListFragment.class}) + DeviceListActivity.DeviceListFragment.class, + RefreshAttributesJob.class}) public class TextSecureCommunicationModule { private final Context context; diff --git a/src/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java b/src/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java new file mode 100644 index 0000000000..f50f5068a6 --- /dev/null +++ b/src/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java @@ -0,0 +1,50 @@ +package org.thoughtcrime.securesms.jobs; + +import android.content.Context; +import android.util.Log; + +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.whispersystems.jobqueue.JobParameters; +import org.whispersystems.jobqueue.requirements.NetworkRequirement; +import org.whispersystems.textsecure.api.TextSecureAccountManager; +import org.whispersystems.textsecure.api.push.exceptions.NetworkFailureException; + +import java.io.IOException; + +import javax.inject.Inject; + +public class RefreshAttributesJob extends ContextJob { + + private static final String TAG = RefreshAttributesJob.class.getSimpleName(); + + @Inject TextSecureAccountManager accountManager; + + public RefreshAttributesJob(Context context) { + super(context, JobParameters.newBuilder() + .withPersistence() + .withRequirement(new NetworkRequirement(context)) + .withWakeLock(true) + .create()); + } + + @Override + public void onAdded() {} + + @Override + public void onRun() throws IOException { + String signalingKey = TextSecurePreferences.getSignalingKey(context); + int registrationId = TextSecurePreferences.getLocalRegistrationId(context); + + accountManager.setAccountAttributes(signalingKey, registrationId, true); + } + + @Override + public boolean onShouldRetry(Exception e) { + return e instanceof NetworkFailureException; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to update account attributes!"); + } +} diff --git a/src/org/thoughtcrime/securesms/service/RegistrationService.java b/src/org/thoughtcrime/securesms/service/RegistrationService.java index 80678e99d9..aa20b39e45 100644 --- a/src/org/thoughtcrime/securesms/service/RegistrationService.java +++ b/src/org/thoughtcrime/securesms/service/RegistrationService.java @@ -203,7 +203,7 @@ public class RegistrationService extends Service { setState(new RegistrationState(RegistrationState.STATE_VERIFYING, number)); String challenge = waitForChallenge(); - accountManager.verifyAccountWithCode(challenge, signalingKey, registrationId); + accountManager.verifyAccountWithCode(challenge, signalingKey, registrationId, true); handleCommonRegistration(accountManager, number, password, signalingKey); markAsVerified(number, password, signalingKey); diff --git a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java index 1078d0e310..8617c4d94f 100644 --- a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java +++ b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java @@ -8,13 +8,13 @@ import android.content.OperationApplicationException; import android.os.RemoteException; import android.provider.ContactsContract; import android.util.Log; -import android.widget.Toast; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.contacts.ContactsDatabase; 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.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.whispersystems.libaxolotl.util.guava.Optional; @@ -28,6 +28,11 @@ import java.util.List; import java.util.Set; public class DirectoryHelper { + + public enum RegistrationState { + REGISTERED, NOT_REGISTERED, UNKNOWN + } + private static final String TAG = DirectoryHelper.class.getSimpleName(); public static void refreshDirectory(final Context context) throws IOException { @@ -72,38 +77,65 @@ public class DirectoryHelper { } } - public static boolean isPushDestination(Context context, Recipients recipients) { + public static RegistrationState refreshDirectoryFor(Context context, Recipients recipients) + 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); + + if (details.isPresent()) { + directory.setNumber(details.get(), true); + ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context)); + return RegistrationState.REGISTERED; + } else { + ContactTokenDetails absent = new ContactTokenDetails(); + absent.setNumber(number); + directory.setNumber(absent, false); + return RegistrationState.NOT_REGISTERED; + } + } catch (InvalidNumberException e) { + Log.w(TAG, e); + return RegistrationState.NOT_REGISTERED; + } + } + + public static RegistrationState isTextSecureEnabledRecipient(Context context, Recipients recipients) { try { if (recipients == null) { - return false; + return RegistrationState.NOT_REGISTERED; } if (!TextSecurePreferences.isPushRegistered(context)) { - return false; + return RegistrationState.NOT_REGISTERED; } if (!recipients.isSingleRecipient()) { - return false; + return RegistrationState.NOT_REGISTERED; } if (recipients.isGroupRecipient()) { - return true; + return RegistrationState.REGISTERED; } final String number = recipients.getPrimaryRecipient().getNumber(); if (number == null) { - return false; + return RegistrationState.NOT_REGISTERED; } final String e164number = Util.canonicalizeNumber(context, number); - return TextSecureDirectory.getInstance(context).isActiveNumber(e164number); + return TextSecureDirectory.getInstance(context).isActiveNumber(e164number) ? + RegistrationState.REGISTERED : RegistrationState.NOT_REGISTERED; } catch (InvalidNumberException e) { Log.w(TAG, e); - return false; + return RegistrationState.NOT_REGISTERED; } catch (NotInDirectoryException e) { - return false; + return RegistrationState.UNKNOWN; } } @@ -128,8 +160,4 @@ public class DirectoryHelper { return Optional.absent(); } } - - public static interface DirectoryUpdateFinishedListener { - public void onUpdateFinished(); - } }