From 31e20ca87e369a25ecdb006101e560f400e18ba8 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 29 Sep 2015 14:26:37 -0700 Subject: [PATCH] Distinguish between secure and insecure call in conversation menu. // FREEBIE --- .../ic_call_secure_white_24dp.png | Bin 0 -> 1494 bytes .../ic_call_secure_white_24dp.png | Bin 0 -> 958 bytes .../ic_call_secure_white_24dp.png | Bin 0 -> 1987 bytes .../ic_call_secure_white_24dp.png | Bin 0 -> 925 bytes ...xml => conversation_callable_insecure.xml} | 2 +- res/menu/conversation_callable_secure.xml | 10 ++ ...secure_sms.xml => conversation_secure.xml} | 0 res/menu/conversation_secure_identity.xml | 12 -- res/values/strings.xml | 5 +- res/xml/recipient_preferences.xml | 6 +- .../securesms/ConversationActivity.java | 123 ++++++++---------- .../securesms/GroupCreateActivity.java | 2 +- .../RecipientPreferenceActivity.java | 30 ++++- .../database/TextSecureDirectory.java | 2 +- .../securesms/jobs/PushReceivedJob.java | 2 +- .../securesms/sms/MessageSender.java | 2 +- .../securesms/util/DirectoryHelper.java | 60 ++++++--- 17 files changed, 142 insertions(+), 114 deletions(-) create mode 100644 res/drawable-hdpi/ic_call_secure_white_24dp.png create mode 100644 res/drawable-mdpi/ic_call_secure_white_24dp.png create mode 100644 res/drawable-xhdpi/ic_call_secure_white_24dp.png create mode 100644 res/drawable-xxhdpi/ic_call_secure_white_24dp.png rename res/menu/{conversation_callable.xml => conversation_callable_insecure.xml} (86%) create mode 100644 res/menu/conversation_callable_secure.xml rename res/menu/{conversation_secure_sms.xml => conversation_secure.xml} (100%) delete mode 100644 res/menu/conversation_secure_identity.xml diff --git a/res/drawable-hdpi/ic_call_secure_white_24dp.png b/res/drawable-hdpi/ic_call_secure_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..26050a12e80f485647d66defe73fd03c000f8761 GIT binary patch literal 1494 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 VoQh0d6CVHo002ovPDHLkV1i*cvHSo4 literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_call_secure_white_24dp.png b/res/drawable-xxhdpi/ic_call_secure_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..a5dee3398d5e76c98957c5c613c25a783af524c5 GIT binary patch literal 925 zcmV;O17iG%P)%SsK575ozHgni&hMvnw%t~OgNx6i%+E{TjTag!kM&z z*$_altzPpy%e9$z>Q!2q!F5(fY4y^{=HNZF5}S^Bcu(V@r|&~*f@bK6m3g^`t-{hN z8C3=a>d*%Z&@5^Trzo#?0@_`G40>SA(cF>RNmH(~_pE-eqXoKU@w%v%Xn|Ti7su<` zVu%#Tpz9tun@pb+ZHA-^@!%8&y6alB5yvSAWO6za63564$DS%^ITbQHpVZ@N@6ktZ z-oi`j4Xye+_@Qdk<#R7wuYHE_i*#quaKCBr2~?&kl@~Df>$Ay)Jvka4 z4BDl?P#ymrcA4Yga3+2r!)*uB()f|)@N*DQ8sARw1!`KNY*T2n9oM0jug`Fyer>J zrg=YDadqI6PdLIlCi_`30<0<+9oAmQ)O&3(_Y1zCUD1jmN0V2|>b<_ISD)2vI)=ZM zoXpG84|cl!ZqnSeUdX#yIQ+7uh9JRt#)iNFcwC$f_vnzoSyqI=fnUXH?_GKSIDm6Z z3Wx&~a}oIB(h~<69|VVDUN@P}Q#$K$65L7SWVwIRi(5>t6Qg4Khn=%i4rAB^dIOqLnsA|>i~(*00000NkvXXu0mjfQ-i5A literal 0 HcmV?d00001 diff --git a/res/menu/conversation_callable.xml b/res/menu/conversation_callable_insecure.xml similarity index 86% rename from res/menu/conversation_callable.xml rename to res/menu/conversation_callable_insecure.xml index fc027c8f47..009d7212b1 100644 --- a/res/menu/conversation_callable.xml +++ b/res/menu/conversation_callable_insecure.xml @@ -2,7 +2,7 @@ diff --git a/res/menu/conversation_callable_secure.xml b/res/menu/conversation_callable_secure.xml new file mode 100644 index 0000000000..3c5627e272 --- /dev/null +++ b/res/menu/conversation_callable_secure.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/res/menu/conversation_secure_sms.xml b/res/menu/conversation_secure.xml similarity index 100% rename from res/menu/conversation_secure_sms.xml rename to res/menu/conversation_secure.xml diff --git a/res/menu/conversation_secure_identity.xml b/res/menu/conversation_secure_identity.xml deleted file mode 100644 index a1623b622e..0000000000 --- a/res/menu/conversation_secure_identity.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/res/values/strings.xml b/res/values/strings.xml index 2694460863..f749215e28 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -659,6 +659,7 @@ Block Color Color for this contact + Verify identity Signal Call @@ -937,7 +938,7 @@ Refresh contact list - + Call @@ -993,7 +994,7 @@ Expand popup - + Add to contacts diff --git a/res/xml/recipient_preferences.xml b/res/xml/recipient_preferences.xml index 80f8423d71..7fcff5d3bc 100644 --- a/res/xml/recipient_preferences.xml +++ b/res/xml/recipient_preferences.xml @@ -36,9 +36,13 @@ android:persistent="false" app:numColumns="5" /> + + android:title="@string/recipient_preferences__block" + android:persistent="false"/> \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 6cbc25d1a5..c37ad74dcd 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -39,6 +39,7 @@ import android.support.v4.view.WindowCompat; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; +import android.util.Pair; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; @@ -84,8 +85,6 @@ 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; @@ -111,7 +110,8 @@ import org.thoughtcrime.securesms.sms.OutgoingTextMessage; 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.DirectoryHelper.UserCapabilities; +import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.GroupUtil; @@ -121,7 +121,6 @@ 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; @@ -188,7 +187,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private Recipients recipients; private long threadId; private int distributionType; - private boolean isEncryptedConversation; + private boolean isSecureText; + private boolean isSecureVoice; private boolean isMmsEnabled = true; private DynamicTheme dynamicTheme = new DynamicTheme(); @@ -215,7 +215,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity initializeActionBar(); initializeViews(); initializeResources(); - initializeSecurity(false); + initializeSecurity(false, false); initializeDraft(); } @@ -231,7 +231,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity setIntent(intent); initializeResources(); - initializeSecurity(false); + initializeSecurity(false, false); initializeDraft(); if (fragment != null) { @@ -326,15 +326,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity MenuInflater inflater = this.getMenuInflater(); menu.clear(); - if (isSingleConversation() && isEncryptedConversation) { - inflater.inflate(R.menu.conversation_secure_identity, menu); - inflater.inflate(R.menu.conversation_secure_sms, menu.findItem(R.id.menu_security).getSubMenu()); - } else if (isSingleConversation()) { - inflater.inflate(R.menu.conversation_insecure, menu); - } - if (isSingleConversation()) { - inflater.inflate(R.menu.conversation_callable, menu); + if (isSecureVoice) inflater.inflate(R.menu.conversation_callable_secure, menu); + else inflater.inflate(R.menu.conversation_callable_insecure, menu); } else if (isGroupConversation()) { inflater.inflate(R.menu.conversation_group_options, menu); @@ -352,6 +346,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity inflater.inflate(R.menu.conversation, menu); + if (isSingleConversation() && isSecureText) { + inflater.inflate(R.menu.conversation_secure, menu); + } else if (isSingleConversation()) { + inflater.inflate(R.menu.conversation_insecure, menu); + } + if (recipients != null && recipients.isMuted()) inflater.inflate(R.menu.conversation_muted, menu); else inflater.inflate(R.menu.conversation_unmuted, menu); @@ -367,13 +367,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); switch (item.getItemId()) { - case R.id.menu_call: handleDial(getRecipients().getPrimaryRecipient()); return true; + case R.id.menu_call_secure: + case R.id.menu_call_insecure: handleDial(getRecipients().getPrimaryRecipient()); return true; case R.id.menu_delete_thread: handleDeleteThread(); return true; case R.id.menu_add_attachment: handleAddAttachment(); return true; case R.id.menu_view_media: handleViewMedia(); return true; case R.id.menu_add_to_contacts: handleAddToContacts(); return true; case R.id.menu_abort_session: handleAbortSecureSession(); return true; - case R.id.menu_verify_identity: handleVerifyIdentity(); return true; case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true; case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true; case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true; @@ -479,12 +479,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } } - private void handleVerifyIdentity() { - Intent verifyIdentityIntent = new Intent(this, VerifyIdentityActivity.class); - verifyIdentityIntent.putExtra("recipient", getRecipients().getPrimaryRecipient().getRecipientId()); - startActivity(verifyIdentityIntent); - } - private void handleAbortSecureSession() { AlertDialogWrapper.Builder builder = new AlertDialogWrapper.Builder(this); builder.setTitle(R.string.ConversationActivity_abort_secure_session_confirmation); @@ -607,30 +601,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity 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); - } - - private void handleDial(Recipient recipient, boolean secureVoice) { - if (secureVoice) { + if (isSecureVoice) { Intent intent = new Intent(this, RedPhoneService.class); intent.setAction(RedPhoneService.ACTION_OUTGOING_CALL); intent.putExtra(RedPhoneService.EXTRA_REMOTE_NUMBER, recipient.getNumber()); @@ -687,7 +658,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void handleAddAttachment() { - if (this.isMmsEnabled || isEncryptedConversation) { + if (this.isMmsEnabled || isSecureText) { new AlertDialogWrapper.Builder(this).setAdapter(attachmentAdapter, new AttachmentTypeListener()) .show(); } else { @@ -703,17 +674,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity startActivity(intent); } - private void handleSecurityChange(boolean encryptedConversation) { - boolean isMediaMessage = !recipients.isSingleRecipient() || attachmentManager.isAttachmentPresent(); - this.isEncryptedConversation = encryptedConversation; + private void handleSecurityChange(boolean isSecureText, boolean isSecureVoice) { + this.isSecureText = isSecureText; + this.isSecureVoice = isSecureVoice; + + boolean isMediaMessage = !recipients.isSingleRecipient() || attachmentManager.isAttachmentPresent(); sendButton.resetAvailableTransports(isMediaMessage); - if (!isEncryptedConversation) sendButton.disableTransport(Type.TEXTSECURE); + if (!isSecureText) sendButton.disableTransport(Type.TEXTSECURE); if (recipients.isGroupRecipient()) sendButton.disableTransport(Type.SMS); - if (isEncryptedConversation) sendButton.setDefaultTransport(Type.TEXTSECURE); - else sendButton.setDefaultTransport(Type.SMS); + if (isSecureText) sendButton.setDefaultTransport(Type.TEXTSECURE); + else sendButton.setDefaultTransport(Type.SMS); calculateCharactersRemaining(); supportInvalidateOptionsMenu(); @@ -778,30 +751,38 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity }.execute(); } - private void initializeSecurity(final boolean current) { - handleSecurityChange(current || isGroupConversation()); + private void initializeSecurity(final boolean currentSecureText, + final boolean currentSecureVoice) + { + handleSecurityChange(currentSecureText || isGroupConversation(), + currentSecureVoice && !isGroupConversation()); - new AsyncTask() { + new AsyncTask>() { @Override - protected Boolean doInBackground(Recipients... params) { + protected Pair doInBackground(Recipients... params) { try { - Context context = ConversationActivity.this; - Recipients recipients = params[0]; - RegistrationState registrationState = DirectoryHelper.isTextSecureEnabledRecipient(context, recipients); + Context context = ConversationActivity.this; + Recipients recipients = params[0]; + UserCapabilities capabilities = DirectoryHelper.getUserCapabilities(context, recipients); - if (registrationState == RegistrationState.NOT_REGISTERED) return false; - else if (registrationState == RegistrationState.REGISTERED) return true; - else return DirectoryHelper.refreshDirectoryFor(context, recipients) == RegistrationState.REGISTERED; + if (capabilities.getTextCapability() == Capability.UNKNOWN || + capabilities.getVoiceCapability() == Capability.UNKNOWN) + { + capabilities = DirectoryHelper.refreshDirectoryFor(context, recipients); + } + + return new Pair<>(capabilities.getTextCapability() == Capability.SUPPORTED, + capabilities.getVoiceCapability() == Capability.SUPPORTED); } catch (IOException e) { Log.w(TAG, e); - return false; + return new Pair<>(false, false); } } @Override - protected void onPostExecute(Boolean result) { - if (current != result) { - handleSecurityChange(result); + protected void onPostExecute(Pair result) { + if (result.first != currentSecureText || result.second != currentSecureVoice) { + handleSecurityChange(result.first, result.second); } } }.execute(recipients); @@ -944,7 +925,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity securityUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - initializeSecurity(isEncryptedConversation); + initializeSecurity(isSecureText, isSecureVoice); calculateCharactersRemaining(); } }; @@ -1249,7 +1230,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity OutgoingMediaMessage outgoingMessage = new OutgoingMediaMessage(this, recipients, slideDeck, getMessage(), distributionType); - if (isEncryptedConversation && !forceSms) { + if (isSecureText && !forceSms) { outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessage); } @@ -1275,7 +1256,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity final Context context = getApplicationContext(); OutgoingTextMessage message; - if (isEncryptedConversation && !forceSms) { + if (isSecureText && !forceSms) { message = new OutgoingEncryptedMessage(recipients, getMessage()); } else { message = new OutgoingTextMessage(recipients, getMessage()); diff --git a/src/org/thoughtcrime/securesms/GroupCreateActivity.java b/src/org/thoughtcrime/securesms/GroupCreateActivity.java index 38faea84ba..31aa8dbcf3 100644 --- a/src/org/thoughtcrime/securesms/GroupCreateActivity.java +++ b/src/org/thoughtcrime/securesms/GroupCreateActivity.java @@ -173,7 +173,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity { private static boolean isActiveInDirectory(Context context, Recipient recipient) { try { - if (!TextSecureDirectory.getInstance(context).isActiveNumber(Util.canonicalizeNumber(context, recipient.getNumber()))) { + if (!TextSecureDirectory.getInstance(context).isSecureTextSupported(Util.canonicalizeNumber(context, recipient.getNumber()))) { return false; } } catch (NotInDirectoryException e) { diff --git a/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java b/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java index 932ecb15ae..2f039aa352 100644 --- a/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java +++ b/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java @@ -44,11 +44,12 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi public static final String RECIPIENTS_EXTRA = "recipient_ids"; - private static final String PREFERENCE_MUTED = "pref_key_recipient_mute"; - private static final String PREFERENCE_TONE = "pref_key_recipient_ringtone"; - private static final String PREFERENCE_VIBRATE = "pref_key_recipient_vibrate"; - private static final String PREFERENCE_BLOCK = "pref_key_recipient_block"; - private static final String PREFERENCE_COLOR = "pref_key_recipient_color"; + private static final String PREFERENCE_MUTED = "pref_key_recipient_mute"; + private static final String PREFERENCE_TONE = "pref_key_recipient_ringtone"; + private static final String PREFERENCE_VIBRATE = "pref_key_recipient_vibrate"; + private static final String PREFERENCE_BLOCK = "pref_key_recipient_block"; + private static final String PREFERENCE_COLOR = "pref_key_recipient_color"; + private static final String PREFERENCE_IDENTITY = "pref_key_recipient_identity"; private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); @@ -171,7 +172,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi .setOnPreferenceClickListener(new BlockClickedListener()); this.findPreference(PREFERENCE_COLOR) .setOnPreferenceChangeListener(new ColorChangeListener()); - } + this.findPreference(PREFERENCE_IDENTITY) + .setOnPreferenceClickListener(new IdentityClickedListener()); + } @Override public void onResume() { @@ -191,6 +194,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi ListPreference vibratePreference = (ListPreference) this.findPreference(PREFERENCE_VIBRATE); ColorPreference colorPreference = (ColorPreference) this.findPreference(PREFERENCE_COLOR); Preference blockPreference = this.findPreference(PREFERENCE_BLOCK); + Preference identityPreference = this.findPreference(PREFERENCE_IDENTITY); mutePreference.setChecked(recipients.isMuted()); @@ -222,8 +226,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi if (!recipients.isSingleRecipient() || recipients.isGroupRecipient()) { blockPreference.setEnabled(false); + identityPreference.setEnabled(false); } else { blockPreference.setEnabled(true); + identityPreference.setEnabled(true); + if (recipients.isBlocked()) blockPreference.setTitle(R.string.RecipientPreferenceActivity_unblock); else blockPreference.setTitle(R.string.RecipientPreferenceActivity_block); } @@ -352,6 +359,17 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi } } + private class IdentityClickedListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + Intent verifyIdentityIntent = new Intent(getActivity(), VerifyIdentityActivity.class); + verifyIdentityIntent.putExtra("recipient", recipients.getPrimaryRecipient().getRecipientId()); + startActivity(verifyIdentityIntent); + + return true; + } + } + private class BlockClickedListener implements Preference.OnPreferenceClickListener { @Override public boolean onPreferenceClick(Preference preference) { diff --git a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java b/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java index 7b41630535..5370fc0bd7 100644 --- a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java +++ b/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java @@ -66,7 +66,7 @@ public class TextSecureDirectory { this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION); } - public boolean isActiveNumber(String e164number) throws NotInDirectoryException { + public boolean isSecureTextSupported(String e164number) throws NotInDirectoryException { if (e164number == null || e164number.length() == 0) { return false; } diff --git a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java index 517989ee5e..966c2f5739 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java @@ -63,7 +63,7 @@ public abstract class PushReceivedJob extends ContextJob { boolean isActiveNumber; try { - isActiveNumber = TextSecureDirectory.getInstance(context).isActiveNumber(e164number); + isActiveNumber = TextSecureDirectory.getInstance(context).isSecureTextSupported(e164number); } catch (NotInDirectoryException e) { isActiveNumber = false; } diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index a44ec759dd..0c3879c235 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -279,7 +279,7 @@ public class MessageSender { TextSecureDirectory directory = TextSecureDirectory.getInstance(context); try { - return directory.isActiveNumber(destination); + return directory.isSecureTextSupported(destination); } catch (NotInDirectoryException e) { try { TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(context); diff --git a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java index 8617c4d94f..e8a09b2567 100644 --- a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java +++ b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java @@ -17,6 +17,7 @@ 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.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability; import org.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.textsecure.api.TextSecureAccountManager; import org.whispersystems.textsecure.api.push.ContactTokenDetails; @@ -29,8 +30,30 @@ import java.util.Set; public class DirectoryHelper { - public enum RegistrationState { - REGISTERED, NOT_REGISTERED, UNKNOWN + public static class UserCapabilities { + + public static final UserCapabilities UNKNOWN = new UserCapabilities(Capability.UNKNOWN, Capability.UNKNOWN); + public static final UserCapabilities UNSUPPORTED = new UserCapabilities(Capability.UNSUPPORTED, Capability.UNSUPPORTED); + + public enum Capability { + UNKNOWN, SUPPORTED, UNSUPPORTED + } + + private final Capability text; + private final Capability voice; + + public UserCapabilities(Capability text, Capability voice) { + this.text = text; + this.voice = voice; + } + + public Capability getTextCapability() { + return text; + } + + public Capability getVoiceCapability() { + return voice; + } } private static final String TAG = DirectoryHelper.class.getSimpleName(); @@ -77,7 +100,7 @@ public class DirectoryHelper { } } - public static RegistrationState refreshDirectoryFor(Context context, Recipients recipients) + public static UserCapabilities refreshDirectoryFor(Context context, Recipients recipients) throws IOException { try { @@ -90,52 +113,55 @@ public class DirectoryHelper { if (details.isPresent()) { directory.setNumber(details.get(), true); ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context)); - return RegistrationState.REGISTERED; + return new UserCapabilities(Capability.SUPPORTED, details.get().isVoice() ? Capability.SUPPORTED : Capability.UNSUPPORTED); } else { ContactTokenDetails absent = new ContactTokenDetails(); absent.setNumber(number); directory.setNumber(absent, false); - return RegistrationState.NOT_REGISTERED; + return UserCapabilities.UNSUPPORTED; } } catch (InvalidNumberException e) { Log.w(TAG, e); - return RegistrationState.NOT_REGISTERED; + return UserCapabilities.UNSUPPORTED; } } - public static RegistrationState isTextSecureEnabledRecipient(Context context, Recipients recipients) { + public static UserCapabilities getUserCapabilities(Context context, Recipients recipients) { try { if (recipients == null) { - return RegistrationState.NOT_REGISTERED; + return UserCapabilities.UNSUPPORTED; } if (!TextSecurePreferences.isPushRegistered(context)) { - return RegistrationState.NOT_REGISTERED; + return UserCapabilities.UNSUPPORTED; } if (!recipients.isSingleRecipient()) { - return RegistrationState.NOT_REGISTERED; + return UserCapabilities.UNSUPPORTED; } if (recipients.isGroupRecipient()) { - return RegistrationState.REGISTERED; + return new UserCapabilities(Capability.SUPPORTED, Capability.UNSUPPORTED); } final String number = recipients.getPrimaryRecipient().getNumber(); if (number == null) { - return RegistrationState.NOT_REGISTERED; + return UserCapabilities.UNSUPPORTED; } - final String e164number = Util.canonicalizeNumber(context, number); + String e164number = Util.canonicalizeNumber(context, number); + boolean secureText = TextSecureDirectory.getInstance(context).isSecureTextSupported(e164number); + boolean secureVoice = TextSecureDirectory.getInstance(context).isSecureVoiceSupported(e164number); + + return new UserCapabilities(secureText ? Capability.SUPPORTED : Capability.UNSUPPORTED, + secureVoice ? Capability.SUPPORTED : Capability.UNSUPPORTED); - return TextSecureDirectory.getInstance(context).isActiveNumber(e164number) ? - RegistrationState.REGISTERED : RegistrationState.NOT_REGISTERED; } catch (InvalidNumberException e) { Log.w(TAG, e); - return RegistrationState.NOT_REGISTERED; + return UserCapabilities.UNSUPPORTED; } catch (NotInDirectoryException e) { - return RegistrationState.UNKNOWN; + return UserCapabilities.UNKNOWN; } }