more precise sms controls

// FREEBIE
This commit is contained in:
Jake McGinty
2014-03-01 14:17:55 -08:00
parent 4701e59197
commit d827ab1b36
27 changed files with 458 additions and 95 deletions

View File

@@ -47,6 +47,7 @@ import android.widget.Toast;
import com.actionbarsherlock.view.MenuItem;
import com.google.android.gcm.GCMRegistrar;
import org.thoughtcrime.securesms.components.OutgoingSmsPreference;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
@@ -77,6 +78,7 @@ import java.io.IOException;
public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener
{
private static final String TAG = "Preferences";
private static final int PICK_IDENTITY_CONTACT = 1;
private static final int ENABLE_PASSPHRASE_ACTIVITY = 2;
@@ -87,6 +89,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
private static final String KITKAT_DEFAULT_PREF = "pref_set_default";
private static final String UPDATE_DIRECTORY_PREF = "pref_update_directory";
private static final String SUBMIT_DEBUG_LOG_PREF = "pref_submit_debug_logs";
private static final String OUTGOING_SMS_PREF = "pref_outgoing_sms";
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@@ -103,7 +106,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
addPreferencesFromResource(R.xml.preferences);
initializeIdentitySelection();
initializeSmsFallbackOption();
initializePushMessagingToggle();
this.findPreference(TextSecurePreferences.CHANGE_PASSPHRASE_PREF)
@@ -126,7 +128,10 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
.setOnPreferenceClickListener(new DirectoryUpdateListener());
this.findPreference(SUBMIT_DEBUG_LOG_PREF)
.setOnPreferenceClickListener(new SubmitDebugLogListener());
this.findPreference(OUTGOING_SMS_PREF)
.setOnPreferenceChangeListener(new OutgoingSmsPreferenceListener());
initializeOutgoingSmsSummary((OutgoingSmsPreference) findPreference(OUTGOING_SMS_PREF));
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_COLOR_PREF));
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_BLINK_PREF));
initializeRingtoneSummary((RingtonePreference) findPreference(TextSecurePreferences.RINGTONE_PREF));
@@ -188,14 +193,14 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
}
private void initializePlatformSpecificOptions() {
PreferenceGroup generalCategory = (PreferenceGroup) findPreference("general_category");
PreferenceGroup pushSmsCategory = (PreferenceGroup) findPreference("push_sms_category");
Preference defaultPreference = findPreference(KITKAT_DEFAULT_PREF);
Preference allSmsPreference = findPreference(TextSecurePreferences.ALL_SMS_PREF);
Preference allMmsPreference = findPreference(TextSecurePreferences.ALL_MMS_PREF);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (allSmsPreference != null) generalCategory.removePreference(allSmsPreference);
if (allMmsPreference != null) generalCategory.removePreference(allMmsPreference);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && pushSmsCategory != null) {
if (allSmsPreference != null) pushSmsCategory.removePreference(allSmsPreference);
if (allMmsPreference != null) pushSmsCategory.removePreference(allMmsPreference);
if (Util.isDefaultSmsProvider(this)) {
defaultPreference.setIntent(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
@@ -208,36 +213,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
defaultPreference.setTitle(getString(R.string.ApplicationPreferencesActivity_sms_disabled));
defaultPreference.setSummary(getString(R.string.ApplicationPreferencesActivity_touch_to_make_textsecure_your_default_sms_app));
}
} else {
if (defaultPreference != null) generalCategory.removePreference(defaultPreference);
}
}
private void initializeSmsFallbackOption() {
CheckBoxPreference allowSmsPreference =
(CheckBoxPreference) findPreference(TextSecurePreferences.ALLOW_SMS_FALLBACK_PREF);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (Util.isDefaultSmsProvider(this) || !TextSecurePreferences.isPushRegistered(this)) {
allowSmsPreference.setEnabled(false);
allowSmsPreference.setChecked(true);
allowSmsPreference.setSummary(R.string.preferences__allow_sms_fallback_disabled_reason);
} else {
allowSmsPreference.setEnabled(true);
allowSmsPreference.setSummary(R.string.preferences__send_and_receive_sms_messages_when_push_is_not_available);
}
} else {
if (TextSecurePreferences.isInterceptAllMmsEnabled(this) ||
TextSecurePreferences.isInterceptAllSmsEnabled(this) ||
!TextSecurePreferences.isPushRegistered(this))
{
allowSmsPreference.setEnabled(false);
allowSmsPreference.setChecked(true);
allowSmsPreference.setSummary(R.string.preferences__allow_sms_fallback_disabled_reason);
} else {
allowSmsPreference.setEnabled(true);
allowSmsPreference.setSummary(R.string.preferences__send_and_receive_sms_messages_when_push_is_not_available);
}
} else if (pushSmsCategory != null && defaultPreference != null) {
pushSmsCategory.removePreference(defaultPreference);
}
}
@@ -296,6 +273,10 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
listener.onPreferenceChange(pref, sharedPreferences.getString(pref.getKey(), ""));
}
private void initializeOutgoingSmsSummary(OutgoingSmsPreference pref) {
pref.setSummary(buildOutgoingSmsDescription());
}
private void handleIdentitySelection(Intent data) {
Uri contactUri = data.getData();
@@ -311,11 +292,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
dynamicTheme.onResume(this);
} else if (key.equals(TextSecurePreferences.LANGUAGE_PREF)) {
dynamicLanguage.onResume(this);
} else if (key.equals(TextSecurePreferences.ALL_MMS_PREF) ||
key.equals(TextSecurePreferences.ALL_SMS_PREF) ||
key.equals(TextSecurePreferences.REGISTERED_GCM_PREF))
{
initializeSmsFallbackOption();
}
}
@@ -614,6 +590,36 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
}
}
private class OutgoingSmsPreferenceListener implements Preference.OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(final Preference preference, Object newValue) {
preference.setSummary(buildOutgoingSmsDescription());
return false;
}
}
private String buildOutgoingSmsDescription() {
final StringBuilder builder = new StringBuilder();
final boolean dataFallback = TextSecurePreferences.isSmsFallbackEnabled(this);
final boolean dataFallbackAsk = TextSecurePreferences.isSmsFallbackAskEnabled(this);
final boolean nonData = TextSecurePreferences.isSmsNonDataOutEnabled(this);
if (dataFallback) {
builder.append(getString(R.string.preferences__sms_outgoing_push_users));
if (dataFallbackAsk) builder.append(" ").append(getString(R.string.preferences__sms_fallback_push_users_ask));
}
if (nonData) {
if (dataFallback) builder.append(", ");
builder.append(getString(R.string.preferences__sms_fallback_non_push_users));
}
if (!dataFallback && !nonData) {
builder.append(getString(R.string.preferences__sms_fallback_nobody));
}
return builder.toString();
}
/* http://code.google.com/p/android/issues/detail?id=4611#c35 */
@SuppressWarnings("deprecation")
@Override

View File

@@ -44,7 +44,9 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord;
@@ -107,12 +109,15 @@ public class ConversationItem extends LinearLayout {
private View conversationParent;
private TextView bodyText;
private TextView dateText;
private TextView indicatorText;
private TextView groupStatusText;
private ImageView secureImage;
private ImageView failedImage;
private ImageView keyImage;
private ImageView contactPhoto;
private ImageView deliveredImage;
private View triangleTick;
private ImageView pendingIndicator;
private View mmsContainer;
private ImageView mmsThumbnail;
@@ -144,6 +149,7 @@ public class ConversationItem extends LinearLayout {
this.bodyText = (TextView) findViewById(R.id.conversation_item_body);
this.dateText = (TextView) findViewById(R.id.conversation_item_date);
this.indicatorText = (TextView) findViewById(R.id.indicator_text);
this.groupStatusText = (TextView) findViewById(R.id.group_message_status);
this.secureImage = (ImageView)findViewById(R.id.sms_secure_indicator);
this.failedImage = (ImageView)findViewById(R.id.sms_failed_indicator);
@@ -155,6 +161,8 @@ public class ConversationItem extends LinearLayout {
this.contactPhoto = (ImageView)findViewById(R.id.contact_photo);
this.deliveredImage = (ImageView)findViewById(R.id.delivered_indicator);
this.conversationParent = (View) findViewById(R.id.conversation_item_parent);
this.triangleTick = findViewById(R.id.triangle_tick);
this.pendingIndicator = (ImageView)findViewById(R.id.pending_approval_indicator);
this.backgroundDrawables = context.obtainStyledAttributes(STYLE_ATTRIBUTES);
setOnClickListener(clickListener);
@@ -180,6 +188,7 @@ public class ConversationItem extends LinearLayout {
setContactPhoto(messageRecord);
setGroupMessageStatus(messageRecord);
setEvents(messageRecord);
setMinimumWidth();
if (messageRecord instanceof NotificationMmsMessageRecord) {
setNotificationMmsAttributes((NotificationMmsMessageRecord)messageRecord);
@@ -218,10 +227,10 @@ public class ConversationItem extends LinearLayout {
if (messageRecord.isOutgoing()) {
final int background;
final int triangleBackground;
if (messageRecord.isPending() && pushDestination) {
if (messageRecord.isPending() && pushDestination && !messageRecord.isForcedSms()) {
background = SENT_PUSH_PENDING;
triangleBackground = SENT_PUSH_PENDING_TRIANGLE;
} else if (messageRecord.isPending()) {
} else if (messageRecord.isPending() || messageRecord.isPendingFallbackApproval()) {
background = SENT_SMS_PENDING;
triangleBackground = SENT_SMS_PENDING_TRIANGLE;
} else if (messageRecord.isPush()) {
@@ -232,7 +241,7 @@ public class ConversationItem extends LinearLayout {
triangleBackground = SENT_SMS_TRIANGLE;
}
setViewBackgroundWithoutResettingPadding(conversationParent, backgroundDrawables.getResourceId(background, -1));
setViewBackgroundWithoutResettingPadding(findViewById(R.id.triangle_tick), backgroundDrawables.getResourceId(triangleBackground, -1));
setViewBackgroundWithoutResettingPadding(triangleTick, backgroundDrawables.getResourceId(triangleBackground, -1));
}
}
}
@@ -255,6 +264,10 @@ public class ConversationItem extends LinearLayout {
private void setStatusIcons(MessageRecord messageRecord) {
failedImage.setVisibility(messageRecord.isFailed() ? View.VISIBLE : View.GONE);
if (messageRecord.isOutgoing()) {
pendingIndicator.setVisibility(messageRecord.isPendingFallbackApproval() ? View.VISIBLE : View.GONE);
indicatorText.setVisibility(messageRecord.isPendingFallbackApproval() ? View.VISIBLE : View.GONE);
}
secureImage.setVisibility(messageRecord.isSecure() ? View.VISIBLE : View.GONE);
keyImage.setVisibility(messageRecord.isKeyExchange() ? View.VISIBLE : View.GONE);
deliveredImage.setVisibility(!messageRecord.isKeyExchange() && messageRecord.isDelivered() ? View.VISIBLE : View.GONE);
@@ -265,6 +278,9 @@ public class ConversationItem extends LinearLayout {
if (messageRecord.isFailed()) {
dateText.setText(R.string.ConversationItem_error_sending_message);
} else if (messageRecord.isPendingFallbackApproval() && indicatorText != null) {
dateText.setText("");
indicatorText.setText(R.string.ConversationItem_click_to_approve);
} else if (messageRecord.isPending()) {
dateText.setText(" ··· ");
} else {
@@ -276,10 +292,19 @@ public class ConversationItem extends LinearLayout {
}
}
private void setMinimumWidth() {
if (indicatorText != null && indicatorText.getVisibility() == View.VISIBLE && indicatorText.getText() != null) {
conversationParent.setMinimumWidth(indicatorText.getText().length() * 20);
} else {
conversationParent.setMinimumWidth(0);
}
}
private void setEvents(MessageRecord messageRecord) {
setClickable(messageRecord.isKeyExchange() &&
!messageRecord.isCorruptedKeyExchange() &&
!messageRecord.isOutgoing());
setClickable(messageRecord.isPendingFallbackApproval() ||
(messageRecord.isKeyExchange() &&
!messageRecord.isCorruptedKeyExchange() &&
!messageRecord.isOutgoing()));
if (!messageRecord.isOutgoing() &&
messageRecord.getRecipients().isSingleRecipient() &&
@@ -615,9 +640,42 @@ public class ConversationItem extends LinearLayout {
!messageRecord.isProcessedKeyExchange() &&
!messageRecord.isStaleKeyExchange())
handleKeyExchangeClicked();
else if (messageRecord.isPendingFallbackApproval())
handleMessageApproval();
}
}
private void handleMessageApproval() {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.ConversationItem_click_to_approve_dialog_title);
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if (messageRecord.isMms()) {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
database.markAsOutbox(messageRecord.getId());
database.markAsForcedSms(messageRecord.getId());
} else {
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
database.markAsOutbox(messageRecord.getId());
database.markAsForcedSms(messageRecord.getId());
}
Intent intent = new Intent(context, SendReceiveService.class);
intent.setAction(SendReceiveService.SEND_SMS_ACTION);
intent.putExtra(SendReceiveService.MASTER_SECRET_EXTRA, masterSecret);
context.startService(intent);
}
});
builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if (messageRecord.isMms()) DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageRecord.getId());
else DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageRecord.getId());
}
});
builder.show();
}
private void handleAbortSecureSession() {
if (!messageRecord.isSecure()) return;

View File

@@ -159,7 +159,7 @@ public class PushContactSelectionListFragment extends SherlockListFragment
}
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.ContactSelectionlistFragment_select_for + " " + contactData.name);
builder.setTitle(getString(R.string.ContactSelectionlistFragment_select_for) + " " + contactData.name);
builder.setMultiChoiceItems(options, null, new DiscriminatorClickedListener(contactData));
builder.setPositiveButton(android.R.string.ok, new DiscriminatorFinishedListener(contactData, textView, checkBox));
builder.setOnCancelListener(new DiscriminatorFinishedListener(contactData, textView, checkBox));

View File

@@ -0,0 +1,54 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class OutgoingSmsPreference extends DialogPreference {
private CheckBox dataUsers;
private CheckBox askForFallback;
private CheckBox nonDataUsers;
public OutgoingSmsPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setPersistent(false);
setDialogLayoutResource(R.layout.outgoing_sms_preference);
}
@Override
protected void onBindDialogView(final View view) {
super.onBindDialogView(view);
dataUsers = (CheckBox) view.findViewById(R.id.data_users);
askForFallback = (CheckBox) view.findViewById(R.id.ask_before_fallback_data);
nonDataUsers = (CheckBox) view.findViewById(R.id.non_data_users);
dataUsers.setChecked(TextSecurePreferences.isSmsFallbackEnabled(getContext()));
askForFallback.setChecked(TextSecurePreferences.isSmsFallbackAskEnabled(getContext()));
nonDataUsers.setChecked(TextSecurePreferences.isSmsNonDataOutEnabled(getContext()));
dataUsers.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
askForFallback.setEnabled(dataUsers.isChecked());
}
});
askForFallback.setEnabled(dataUsers.isChecked());
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
TextSecurePreferences.setSmsFallbackEnabled(getContext(), dataUsers.isChecked());
TextSecurePreferences.setSmsFallbackAskEnabled(getContext(), askForFallback.isChecked());
TextSecurePreferences.setSmsNonDataOutEnabled(getContext(), nonDataUsers.isChecked());
if (getOnPreferenceChangeListener() != null) getOnPreferenceChangeListener().onPreferenceChange(this, null);
}
}
}

View File

@@ -281,6 +281,15 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
notifyConversationListeners(getThreadIdForMessage(messageId));
}
public void markAsForcedSms(long id) {
updateMailboxBitmask(id, 0, Types.MESSAGE_FORCE_SMS_BIT);
}
public void markAsPendingApproval(long messageId) {
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_PENDING_FALLBACK_APPROVAL);
notifyConversationListeners(getThreadIdForMessage(messageId));
}
public void markAsSending(long messageId) {
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE);
notifyConversationListeners(getThreadIdForMessage(messageId));

View File

@@ -15,16 +15,22 @@ public interface MmsSmsColumns {
protected static final long TOTAL_MASK = 0xFFFFFFFF;
// Base Types
protected static final long BASE_TYPE_MASK = 0xFF;
protected static final long BASE_TYPE_MASK = 0x1F;
protected static final long BASE_INBOX_TYPE = 20;
protected static final long BASE_OUTBOX_TYPE = 21;
protected static final long BASE_SENDING_TYPE = 22;
protected static final long BASE_SENT_TYPE = 23;
protected static final long BASE_SENT_FAILED_TYPE = 24;
protected static final long BASE_INBOX_TYPE = 20;
protected static final long BASE_OUTBOX_TYPE = 21;
protected static final long BASE_SENDING_TYPE = 22;
protected static final long BASE_SENT_TYPE = 23;
protected static final long BASE_SENT_FAILED_TYPE = 24;
protected static final long BASE_PENDING_FALLBACK_APPROVAL = 25;
protected static final long[] OUTGOING_MESSAGE_TYPES = {BASE_OUTBOX_TYPE, BASE_SENT_TYPE,
BASE_SENDING_TYPE, BASE_SENT_FAILED_TYPE};
BASE_SENDING_TYPE, BASE_SENT_FAILED_TYPE,
BASE_PENDING_FALLBACK_APPROVAL};
// Message attributes
protected static final long MESSAGE_ATTRIBUTE_MASK = 0xE0;
protected static final long MESSAGE_FORCE_SMS_BIT = 0x40;
// Key Exchange Information
protected static final long KEY_EXCHANGE_BIT = 0x8000;
@@ -65,12 +71,20 @@ public interface MmsSmsColumns {
return false;
}
public static boolean isForcedSms(long type) {
return (type & MESSAGE_FORCE_SMS_BIT) != 0;
}
public static boolean isPendingMessageType(long type) {
return
(type & BASE_TYPE_MASK) == BASE_OUTBOX_TYPE ||
(type & BASE_TYPE_MASK) == BASE_SENDING_TYPE;
}
public static boolean isPendingApprovalType(long type) {
return (type & BASE_TYPE_MASK) == BASE_PENDING_FALLBACK_APPROVAL;
}
public static boolean isInboxType(long type) {
return (type & BASE_TYPE_MASK) == BASE_INBOX_TYPE;
}

View File

@@ -178,6 +178,10 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
updateTypeBitmask(id, 0, Types.PUSH_MESSAGE_BIT);
}
public void markAsForcedSms(long id) {
updateTypeBitmask(id, 0, Types.MESSAGE_FORCE_SMS_BIT);
}
public void markAsDecryptFailed(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT);
}
@@ -194,6 +198,10 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_OUTBOX_TYPE);
}
public void markAsPendingApproval(long id) {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_PENDING_FALLBACK_APPROVAL);
}
public void markAsSending(long id) {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE);
}

View File

@@ -113,6 +113,10 @@ public abstract class MessageRecord extends DisplayRecord {
return SmsDatabase.Types.isPushType(type);
}
public boolean isForcedSms() {
return SmsDatabase.Types.isForcedSms(type);
}
public boolean isStaleKeyExchange() {
return SmsDatabase.Types.isStaleKeyExchange(type);
}
@@ -121,6 +125,10 @@ public abstract class MessageRecord extends DisplayRecord {
return SmsDatabase.Types.isProcessedKeyExchange(type);
}
public boolean isPendingFallbackApproval() {
return SmsDatabase.Types.isPendingApprovalType(type);
}
public boolean isBundleKeyExchange() {
return SmsDatabase.Types.isBundleKeyExchange(type);
}

View File

@@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.transport.UniversalTransport;
import org.thoughtcrime.securesms.transport.UntrustedIdentityException;
import org.thoughtcrime.securesms.transport.UserInterventionRequiredException;
import org.whispersystems.textsecure.crypto.MasterSecret;
import ws.com.google.android.mms.MmsException;
@@ -78,11 +79,16 @@ public class MmsSender {
if (result.isUpgradedSecure()) database.markAsSecure(message.getDatabaseMessageId());
if (result.isPush()) database.markAsPush(message.getDatabaseMessageId());
database.markAsSent(message.getDatabaseMessageId(), result.getMessageId(),
result.getResponseStatus());
systemStateListener.unregisterForConnectivityChange();
} catch (UserInterventionRequiredException uire) {
Log.w("MmsSender", uire);
database.markAsPendingApproval(message.getDatabaseMessageId());
Recipients recipients = threads.getRecipientsForThreadId(threadId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
} catch (UndeliverableMessageException e) {
Log.w("MmsSender", e);
database.markAsSentFailed(message.getDatabaseMessageId());

View File

@@ -62,6 +62,8 @@ public class SendReceiveService extends Service {
public static final String DOWNLOAD_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_PUSH_ACTION";
public static final String DOWNLOAD_AVATAR_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_AVATAR_ACTION";
public static final String MASTER_SECRET_EXTRA = "master_secret";
private static final int SEND_SMS = 0;
private static final int RECEIVE_SMS = 1;
private static final int SEND_MMS = 2;
@@ -307,7 +309,7 @@ public class SendReceiveService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
Log.w("SendReceiveService", "Got a MasterSecret broadcast...");
initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret"));
initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra(MASTER_SECRET_EXTRA));
}
}

View File

@@ -40,6 +40,8 @@ import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.transport.UniversalTransport;
import org.thoughtcrime.securesms.transport.UntrustedIdentityException;
import org.thoughtcrime.securesms.transport.UserInterventionRequiredException;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.storage.Session;
@@ -84,6 +86,10 @@ public class SmsSender {
database.markAsSending(record.getId());
transport.deliver(record);
} catch (UserInterventionRequiredException uire) {
Log.w("SmsSender", uire);
DatabaseFactory.getSmsDatabase(context).markAsPendingApproval(record.getId());
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipients(), record.getThreadId());
} catch (UntrustedIdentityException e) {
Log.w("SmsSender", e);
IncomingIdentityUpdateMessage identityUpdateMessage = IncomingIdentityUpdateMessage.createFor(e.getE164Number(), e.getIdentityKey());
@@ -92,6 +98,7 @@ public class SmsSender {
} catch (UndeliverableMessageException ude) {
Log.w("SmsSender", ude);
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(record.getId());
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipients(), record.getThreadId());
} catch (RetryLaterException rle) {
Log.w("SmsSender", rle);
DatabaseFactory.getSmsDatabase(context).markAsOutbox(record.getId());

View File

@@ -48,9 +48,7 @@ public class SmsTransport extends BaseTransport {
}
public void deliver(SmsMessageRecord message) throws UndeliverableMessageException {
if (TextSecurePreferences.isPushRegistered(context) &&
!TextSecurePreferences.isSmsFallbackEnabled(context))
{
if (!TextSecurePreferences.isSmsNonDataOutEnabled(context) && !TextSecurePreferences.isSmsFallbackEnabled(context)) {
throw new UndeliverableMessageException("SMS Transport is not enabled!");
}

View File

@@ -60,7 +60,7 @@ public class UniversalTransport {
}
public void deliver(SmsMessageRecord message)
throws UndeliverableMessageException, UntrustedIdentityException, RetryLaterException
throws UndeliverableMessageException, UntrustedIdentityException, RetryLaterException, UserInterventionRequiredException
{
if (!TextSecurePreferences.isPushRegistered(context)) {
smsTransport.deliver(message);
@@ -71,23 +71,25 @@ public class UniversalTransport {
Recipient recipient = message.getIndividualRecipient();
String number = Util.canonicalizeNumber(context, recipient.getNumber());
if (isPushTransport(number) && !message.isKeyExchange()) {
if (isPushTransport(number) && !message.isKeyExchange() && !message.isForcedSms()) {
boolean isSmsFallbackSupported = isSmsFallbackSupported(number);
try {
Log.w("UniversalTransport", "Delivering with GCM...");
Log.w("UniversalTransport", "Using GCM as transport...");
pushTransport.deliver(message);
} catch (UnregisteredUserException uue) {
Log.w("UnviersalTransport", uue);
if (isSmsFallbackSupported) smsTransport.deliver(message);
Log.w("UniversalTransport", uue);
if (isSmsFallbackSupported) fallbackOrAskApproval(message, number);
else throw new UndeliverableMessageException(uue);
} catch (IOException ioe) {
Log.w("UniversalTransport", ioe);
if (isSmsFallbackSupported) smsTransport.deliver(message);
if (isSmsFallbackSupported) fallbackOrAskApproval(message, number);
else throw new RetryLaterException(ioe);
}
} else if (!message.isForcedSms() && !TextSecurePreferences.isSmsNonDataOutEnabled(context)) {
throw new UndeliverableMessageException("User disallows non-push outgoing SMS");
} else {
Log.w("UniversalTransport", "Delivering with SMS...");
Log.w("UniversalTransport", "Using SMS as transport...");
smsTransport.deliver(message);
}
} catch (InvalidNumberException e) {
@@ -97,7 +99,7 @@ public class UniversalTransport {
}
public MmsSendResult deliver(SendReq mediaMessage, long threadId)
throws UndeliverableMessageException, RetryLaterException, UntrustedIdentityException
throws UndeliverableMessageException, RetryLaterException, UntrustedIdentityException, UserInterventionRequiredException
{
if (Util.isEmpty(mediaMessage.getTo())) {
return mmsTransport.deliver(mediaMessage);
@@ -122,16 +124,16 @@ public class UniversalTransport {
boolean isSmsFallbackSupported = isSmsFallbackSupported(destination);
try {
Log.w("UniversalTransport", "Delivering media message with GCM...");
Log.w("UniversalTransport", "Using GCM as transport...");
pushTransport.deliver(mediaMessage, threadId);
return new MmsSendResult("push".getBytes("UTF-8"), 0, true, true);
} catch (IOException ioe) {
Log.w("UniversalTransport", ioe);
if (isSmsFallbackSupported) return mmsTransport.deliver(mediaMessage);
if (isSmsFallbackSupported) return fallbackOrAskApproval(mediaMessage, destination);
else throw new RetryLaterException(ioe);
} catch (RecipientFormattingException e) {
Log.w("UniversalTransport", e);
if (isSmsFallbackSupported) return mmsTransport.deliver(mediaMessage);
if (isSmsFallbackSupported) return fallbackOrAskApproval(mediaMessage, destination);
else throw new UndeliverableMessageException(e);
} catch (EncapsulatedExceptions ee) {
Log.w("UniversalTransport", ee);
@@ -152,6 +154,32 @@ public class UniversalTransport {
}
}
private MmsSendResult fallbackOrAskApproval(SendReq mediaMessage, String destination)
throws UserInterventionRequiredException, UndeliverableMessageException
{
boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination);
if (!isSmsFallbackApprovalRequired) {
Log.i("UniversalTransport", "Falling back to MMS without user intervention");
return mmsTransport.deliver(mediaMessage);
} else {
Log.i("UniversalTransport", "Marking message as pending user approval per their settings");
throw new UserInterventionRequiredException("Pending user approval for fallback to SMS");
}
}
private void fallbackOrAskApproval(SmsMessageRecord smsMessage, String destination)
throws UserInterventionRequiredException, UndeliverableMessageException
{
boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination);
if (!isSmsFallbackApprovalRequired) {
Log.i("UniversalTransport", "Falling back to SMS without user intervention");
smsTransport.deliver(smsMessage);
} else {
Log.i("UniversalTransport", "Marking message as pending user approval per their settings");
throw new UserInterventionRequiredException("Pending user approval for fallback to SMS");
}
}
private MmsSendResult deliverGroupMessage(SendReq mediaMessage, long threadId)
throws RetryLaterException, UndeliverableMessageException
{
@@ -209,6 +237,10 @@ public class UniversalTransport {
return recipientCount > 1;
}
private boolean isSmsFallbackApprovalRequired(String destination) {
return (isSmsFallbackSupported(destination) && TextSecurePreferences.isSmsFallbackAskEnabled(context));
}
private boolean isSmsFallbackSupported(String destination) {
if (GroupUtil.isEncodedGroup(destination)) {
return false;

View File

@@ -0,0 +1,7 @@
package org.thoughtcrime.securesms.transport;
public class UserInterventionRequiredException extends Exception {
public UserInterventionRequiredException(String detailMessage) {
super(detailMessage);
}
}

View File

@@ -10,8 +10,8 @@ import org.thoughtcrime.securesms.R;
public class ActionBarUtil {
public static void initializeDefaultActionBar(final Context c, final ActionBar actionBar, final int title_resid) {
actionBar.setTitle(title_resid);
public static void initializeDefaultActionBar(final Context c, final ActionBar actionBar, final int titleResId) {
actionBar.setTitle(titleResId);
initializeDefaultActionBar(c, actionBar);
}

View File

@@ -45,12 +45,35 @@ public class TextSecurePreferences {
private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications";
private static final String LOCAL_REGISTRATION_ID_PREF = "pref_local_registration_id";
public static final String ALLOW_SMS_FALLBACK_PREF = "pref_allow_sms_traffic_out";
private static final String ALLOW_SMS_FALLBACK_PREF = "pref_allow_sms_traffic_out";
private static final String SMS_FALLBACK_ASK_PREF = "pref_sms_fallback_ask";
private static final String ALLOW_SMS_NON_DATA_PREF = "pref_sms_non_data_out";
public static boolean isSmsFallbackEnabled(Context context) {
return getBooleanPreference(context, ALLOW_SMS_FALLBACK_PREF, true);
}
public static void setSmsFallbackEnabled(Context context, boolean enabled) {
setBooleanPreference(context, ALLOW_SMS_FALLBACK_PREF, enabled);
}
public static boolean isSmsNonDataOutEnabled(Context context) {
return getBooleanPreference(context, ALLOW_SMS_NON_DATA_PREF, true);
}
public static void setSmsNonDataOutEnabled(Context context, boolean enabled) {
setBooleanPreference(context, ALLOW_SMS_NON_DATA_PREF, enabled);
}
public static boolean isSmsFallbackAskEnabled(Context context) {
return getBooleanPreference(context, SMS_FALLBACK_ASK_PREF, false);
}
public static void setSmsFallbackAskEnabled(Context context, boolean enabled) {
setBooleanPreference(context, SMS_FALLBACK_ASK_PREF, enabled);
}
public static int getLocalRegistrationId(Context context) {
return getIntegerPreference(context, LOCAL_REGISTRATION_ID_PREF, 0);
}