more precise sms controls

// FREEBIE
pull/1/head
Jake McGinty 11 years ago
parent 4701e59197
commit d827ab1b36

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#09000000" />
<corners android:radius="@dimen/conversation_item_corner_radius" />
</shape>
</item>
<item android:bottom="@dimen/conversation_item_drop_shadow_dist">
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/conversation_item_received_background_light" />
<corners android:bottomLeftRadius="@dimen/conversation_item_corner_radius" android:bottomRightRadius="@dimen/conversation_item_corner_radius" />
</shape>
</item>
</layer-list>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#09000000" />
<corners android:radius="@dimen/conversation_item_corner_radius" />
</shape>
</item>
<item android:bottom="@dimen/conversation_item_drop_shadow_dist">
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/conversation_item_received_background_dark" />
<corners android:bottomLeftRadius="@dimen/conversation_item_corner_radius" android:bottomRightRadius="@dimen/conversation_item_corner_radius" />
</shape>
</item>
</layer-list>

@ -18,9 +18,10 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:gravity="right" android:gravity="left|center_vertical"
android:layout_marginLeft="6dp" android:layout_marginLeft="6dp"
android:layout_alignParentLeft="true"> android:layout_alignParentLeft="true"
android:layout_centerVertical="true">
<ImageView <ImageView
android:id="@+id/sms_failed_indicator" android:id="@+id/sms_failed_indicator"
@ -29,6 +30,15 @@
android:src="@drawable/ic_list_alert_sms_failed" android:src="@drawable/ic_list_alert_sms_failed"
android:visibility="gone" android:visibility="gone"
android:contentDescription="Send Failed Indicator"/> android:contentDescription="Send Failed Indicator"/>
<ImageView
android:id="@+id/pending_approval_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_dialog_info_holo_light"
android:visibility="gone"
android:layout_gravity="center_vertical"
android:contentDescription="Pending Approval"/>
</LinearLayout> </LinearLayout>
<LinearLayout android:id="@+id/conversation_item_parent" <LinearLayout android:id="@+id/conversation_item_parent"
@ -190,5 +200,22 @@
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:visibility="gone" /> android:visibility="gone" />
<TextView android:id="@+id/indicator_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/conversation_item_parent"
android:layout_alignParentRight="true"
android:paddingRight="5dip"
android:paddingLeft="5dip"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:layout_marginLeft="50dp"
android:layout_marginRight="15dp"
android:layout_marginTop="-2dp"
android:textSize="12sp"
android:textColor="?conversation_sent_text_secondary_color"
android:background="?conversation_item_sent_indicator_text_background"
android:visibility="gone" />
</RelativeLayout> </RelativeLayout>
</org.thoughtcrime.securesms.ConversationItem> </org.thoughtcrime.securesms.ConversationItem>

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:paddingTop="15dp"
android:paddingLeft="15dp"
android:paddingBottom="15dp"
android:paddingRight="10dp">
<CheckBox android:id="@+id/data_users"
style="@style/TextSecureDialogPrimaryText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:singleLine="true"
android:text="@string/preferences__sms_outgoing_push_users" />
<TextView
style="@style/TextSecureDialogSecondaryText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-5dp"
android:layout_marginLeft="32dp"
android:textSize="12sp"
android:text="@string/preferences__sms_outgoing_push_users_description" />
<CheckBox android:id="@+id/ask_before_fallback_data"
style="@style/TextSecureDialogPrimaryText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:text="@string/preferences__sms_fallback_ask_fallback"
android:layout_marginLeft="25dp" />
<CheckBox android:id="@+id/non_data_users"
style="@style/TextSecureDialogPrimaryText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:text="@string/preferences__sms_fallback_non_push_users" />
</LinearLayout>

@ -4,4 +4,12 @@
<style name="Widget.ProgressBar.Horizontal" parent="@android:style/Widget.Holo.ProgressBar.Horizontal"> <style name="Widget.ProgressBar.Horizontal" parent="@android:style/Widget.Holo.ProgressBar.Horizontal">
</style> </style>
<style name="TextSecureDialogPrimaryText">
<item name="android:textColor">?conversation_list_item_contact_color</item>
</style>
<style name="TextSecureDialogSecondaryText">
<item name="android:textColor">?conversation_list_item_date_color</item>
</style>
</resources> </resources>

@ -37,6 +37,7 @@
<attr name="conversation_item_sent_pending_triangle_background" format="reference" /> <attr name="conversation_item_sent_pending_triangle_background" format="reference" />
<attr name="conversation_item_sent_push_background" format="reference" /> <attr name="conversation_item_sent_push_background" format="reference" />
<attr name="conversation_item_sent_push_triangle_background" format="reference" /> <attr name="conversation_item_sent_push_triangle_background" format="reference" />
<attr name="conversation_item_sent_indicator_text_background" format="reference" />
<attr name="dialog_info_icon" format="reference" /> <attr name="dialog_info_icon" format="reference" />
<attr name="dialog_alert_icon" format="reference" /> <attr name="dialog_alert_icon" format="reference" />
<attr name="conversation_item_sent_push_pending_background" format="reference" /> <attr name="conversation_item_sent_push_pending_background" format="reference" />

@ -30,4 +30,4 @@
<color name="conversation_item_received_background_dark">#ff333333</color> <color name="conversation_item_received_background_dark">#ff333333</color>
<color name="conversation_item_received_shadow_light">#ffd5d5d5</color> <color name="conversation_item_received_shadow_light">#ffd5d5d5</color>
<color name="conversation_item_received_shadow_dark">#ff222222</color> <color name="conversation_item_received_shadow_dark">#ff222222</color>
</resources> </resources>

@ -32,9 +32,9 @@
<string name="ApplicationPreferencesActivity_you_are_not_registered_with_the_push_service">You are not registered with the push service...</string> <string name="ApplicationPreferencesActivity_you_are_not_registered_with_the_push_service">You are not registered with the push service...</string>
<string name="ApplicationPreferencesActivity_updating_directory">Updating directory</string> <string name="ApplicationPreferencesActivity_updating_directory">Updating directory</string>
<string name="ApplicationPreferencesActivity_updating_push_directory">Updating push directory...</string> <string name="ApplicationPreferencesActivity_updating_push_directory">Updating push directory...</string>
<string name="ApplicationPreferencesActivity_sms_enabled">SMS Enabled</string> <string name="ApplicationPreferencesActivity_sms_enabled">Incoming SMS Enabled</string>
<string name="ApplicationPreferencesActivity_touch_to_change_your_default_sms_app">Touch to change your default SMS app</string> <string name="ApplicationPreferencesActivity_touch_to_change_your_default_sms_app">Touch to change your default SMS app</string>
<string name="ApplicationPreferencesActivity_sms_disabled">SMS Disabled</string> <string name="ApplicationPreferencesActivity_sms_disabled">Incoming SMS Disabled</string>
<string name="ApplicationPreferencesActivity_touch_to_make_textsecure_your_default_sms_app">Touch to make TextSecure your default SMS app</string> <string name="ApplicationPreferencesActivity_touch_to_make_textsecure_your_default_sms_app">Touch to make TextSecure your default SMS app</string>
<!-- AttachmentTypeSelectorAdapter --> <!-- AttachmentTypeSelectorAdapter -->
@ -63,6 +63,8 @@
<string name="ConversationItem_group_action_left">%1$s has left the group.</string> <string name="ConversationItem_group_action_left">%1$s has left the group.</string>
<string name="ConversationItem_group_action_joined">%1$s have joined the group.</string> <string name="ConversationItem_group_action_joined">%1$s have joined the group.</string>
<string name="ConversationItem_group_action_modify">%1$s has updated the group.</string> <string name="ConversationItem_group_action_modify">%1$s has updated the group.</string>
<string name="ConversationItem_click_to_approve">Tap for SMS fallback</string>
<string name="ConversationItem_click_to_approve_dialog_title">Fallback to SMS?</string>
<!-- ConversationActivity --> <!-- ConversationActivity -->
<string name="ConversationActivity_initiate_secure_session_question">Initiate Secure Session?</string> <string name="ConversationActivity_initiate_secure_session_question">Initiate Secure Session?</string>
@ -642,8 +644,9 @@
<!-- preferences.xml --> <!-- preferences.xml -->
<string name="preferences__general">General</string> <string name="preferences__general">General</string>
<string name="preferences__pref_all_sms_title">Use for all SMS</string> <string name="preferences__push_sms_category">Push and SMS</string>
<string name="preferences__pref_all_mms_title">Use for all MMS</string> <string name="preferences__pref_all_sms_title">Receive all SMS</string>
<string name="preferences__pref_all_mms_title">Receive all MMS</string>
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">Use TextSecure for viewing and storing all incoming text messages</string> <string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">Use TextSecure for viewing and storing all incoming text messages</string>
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">Use TextSecure for viewing and storing all incoming multimedia messages</string> <string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">Use TextSecure for viewing and storing all incoming multimedia messages</string>
<string name="preferences__enable_enter_key_title">Enable Enter key</string> <string name="preferences__enable_enter_key_title">Enable Enter key</string>
@ -726,11 +729,16 @@
<string name="preferences__use_the_data_channel_for_communication_with_other_textsecure_users"> <string name="preferences__use_the_data_channel_for_communication_with_other_textsecure_users">
Increase privacy and avoid SMS fees by using the data channel for communication with other TextSecure users Increase privacy and avoid SMS fees by using the data channel for communication with other TextSecure users
</string> </string>
<string name="preferences__allow_sms_fallback">SMS Fallback</string> <string name="preferences__allow_sms_fallback">Allow outgoing SMS to</string>
<string name="preferences__allow_sms_fallback_disabled_reason">TextSecure is currently your default SMS app. Please set another default SMS app first to change this preference.</string> <string name="preferences__allow_sms_fallback_disabled_reason">TextSecure is currently your default SMS app. Please set another default SMS app first to change this preference.</string>
<string name="preferences__send_and_receive_sms_messages_when_push_is_not_available">Send and receive SMS messages when push is not available</string>
<string name="preferences__refresh_push_directory">Refresh Push Directory</string> <string name="preferences__refresh_push_directory">Refresh Push Directory</string>
<string name="preferences__submit_debug_log">Submit debug log</string> <string name="preferences__submit_debug_log">Submit debug log</string>
<string name="preferences__sms_outgoing_push_users">TextSecure users</string>
<string name="preferences__sms_fallback_push_users_ask">(ask first)</string>
<string name="preferences__sms_outgoing_push_users_description">Send secure SMS if data connectivity is lost</string>
<string name="preferences__sms_fallback_ask_fallback">Ask before sending SMS</string>
<string name="preferences__sms_fallback_non_push_users">Non-TextSecure users</string>
<string name="preferences__sms_fallback_nobody">Nobody</string>
<!-- **************************************** --> <!-- **************************************** -->
<!-- menus --> <!-- menus -->

@ -107,4 +107,13 @@
<style name="Widget.ProgressBar.Horizontal" parent="@android:style/Widget.ProgressBar.Horizontal"> <style name="Widget.ProgressBar.Horizontal" parent="@android:style/Widget.ProgressBar.Horizontal">
</style> </style>
<style name="TextSecureDialogPrimaryText">
<item name="android:textColor">@color/white</item>
</style>
<style name="TextSecureDialogSecondaryText">
<item name="android:textColor">#ff999999</item>
</style>
</resources> </resources>

@ -39,6 +39,7 @@
<item name="conversation_item_sent_triangle_background">@drawable/conversation_item_sent_triangle_shape</item> <item name="conversation_item_sent_triangle_background">@drawable/conversation_item_sent_triangle_shape</item>
<item name="conversation_item_sent_push_background">@drawable/conversation_item_sent_push_shape</item> <item name="conversation_item_sent_push_background">@drawable/conversation_item_sent_push_shape</item>
<item name="conversation_item_sent_push_triangle_background">@drawable/conversation_item_sent_push_triangle_shape</item> <item name="conversation_item_sent_push_triangle_background">@drawable/conversation_item_sent_push_triangle_shape</item>
<item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape</item>
<item name="dialog_info_icon">@drawable/ic_dialog_info_light</item> <item name="dialog_info_icon">@drawable/ic_dialog_info_light</item>
<item name="dialog_alert_icon">@drawable/ic_dialog_alert_light</item> <item name="dialog_alert_icon">@drawable/ic_dialog_alert_light</item>
<item name="conversation_item_sent_pending_background">@drawable/conversation_item_sent_pending_shape</item> <item name="conversation_item_sent_pending_background">@drawable/conversation_item_sent_pending_shape</item>
@ -59,7 +60,6 @@
<item name="menu_split_icon">@drawable/ic_menu_split_holo_light</item> <item name="menu_split_icon">@drawable/ic_menu_split_holo_light</item>
<item name="menu_accept_icon">@drawable/ic_menu_accept_holo_light</item> <item name="menu_accept_icon">@drawable/ic_menu_accept_holo_light</item>
<item name="menu_refresh_directory">@drawable/ic_menu_refresh_holo_light</item> <item name="menu_refresh_directory">@drawable/ic_menu_refresh_holo_light</item>
</style> </style>
<style name="TextSecure.LightTheme.NavigationDrawer" <style name="TextSecure.LightTheme.NavigationDrawer"
@ -100,6 +100,7 @@
<item name="conversation_item_sent_triangle_background">@drawable/conversation_item_sent_triangle_shape_dark</item> <item name="conversation_item_sent_triangle_background">@drawable/conversation_item_sent_triangle_shape_dark</item>
<item name="conversation_item_sent_push_background">@drawable/conversation_item_sent_push_shape_dark</item> <item name="conversation_item_sent_push_background">@drawable/conversation_item_sent_push_shape_dark</item>
<item name="conversation_item_sent_push_triangle_background">@drawable/conversation_item_sent_push_triangle_shape_dark</item> <item name="conversation_item_sent_push_triangle_background">@drawable/conversation_item_sent_push_triangle_shape_dark</item>
<item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape_dark</item>
<item name="dialog_info_icon">@drawable/ic_dialog_info_dark</item> <item name="dialog_info_icon">@drawable/ic_dialog_info_dark</item>
<item name="dialog_alert_icon">@drawable/ic_dialog_alert_dark</item> <item name="dialog_alert_icon">@drawable/ic_dialog_alert_dark</item>
<item name="conversation_item_sent_pending_background">@drawable/conversation_item_sent_pending_shape_dark</item> <item name="conversation_item_sent_pending_background">@drawable/conversation_item_sent_pending_shape_dark</item>

@ -1,7 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:key="general_category" android:title="@string/preferences__general"> <PreferenceCategory android:key="push_sms_category" android:title="@string/preferences__push_sms_category">
<CheckBoxPreference android:defaultValue="false"
android:key="pref_toggle_push_messaging"
android:title="@string/preferences__use_data_channel"
android:summary="@string/preferences__use_the_data_channel_for_communication_with_other_textsecure_users"/>
<CheckBoxPreference android:defaultValue="true" <CheckBoxPreference android:defaultValue="true"
android:key="pref_all_sms" android:key="pref_all_sms"
android:summary="@string/preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages" android:summary="@string/preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages"
@ -16,22 +21,14 @@
android:title="@string/preferences__make_default_sms_app" android:title="@string/preferences__make_default_sms_app"
android:summary="@string/preferences__make_textsecure_the_default_sms_mms_app" /> android:summary="@string/preferences__make_textsecure_the_default_sms_mms_app" />
<CheckBoxPreference android:defaultValue="false" <org.thoughtcrime.securesms.components.OutgoingSmsPreference
android:key="pref_toggle_push_messaging" android:key="pref_outgoing_sms"
android:title="@string/preferences__use_data_channel" android:title="@string/preferences__allow_sms_fallback" />
android:summary="@string/preferences__use_the_data_channel_for_communication_with_other_textsecure_users"/>
<CheckBoxPreference android:defaultValue="true"
android:enabled="false"
android:key="pref_allow_sms_traffic_out"
android:title="@string/preferences__allow_sms_fallback"
android:summary="@string/preferences__send_and_receive_sms_messages_when_push_is_not_available"/>
<CheckBoxPreference android:defaultValue="false" <CheckBoxPreference android:defaultValue="false"
android:key="pref_delivery_report_sms" android:key="pref_delivery_report_sms"
android:summary="@string/preferences__request_a_delivery_report_for_each_sms_message_you_send" android:summary="@string/preferences__request_a_delivery_report_for_each_sms_message_you_send"
android:title="@string/preferences__sms_delivery_reports" /> android:title="@string/preferences__sms_delivery_reports" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/preferences__notifications"> <PreferenceCategory android:title="@string/preferences__notifications">
<CheckBoxPreference android:key="pref_key_enable_notifications" <CheckBoxPreference android:key="pref_key_enable_notifications"

@ -47,6 +47,7 @@ import android.widget.Toast;
import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.MenuItem;
import com.google.android.gcm.GCMRegistrar; import com.google.android.gcm.GCMRegistrar;
import org.thoughtcrime.securesms.components.OutgoingSmsPreference;
import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactIdentityManager; import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
@ -77,6 +78,7 @@ import java.io.IOException;
public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPreferenceActivity public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener implements SharedPreferences.OnSharedPreferenceChangeListener
{ {
private static final String TAG = "Preferences";
private static final int PICK_IDENTITY_CONTACT = 1; private static final int PICK_IDENTITY_CONTACT = 1;
private static final int ENABLE_PASSPHRASE_ACTIVITY = 2; 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 KITKAT_DEFAULT_PREF = "pref_set_default";
private static final String UPDATE_DIRECTORY_PREF = "pref_update_directory"; 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 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 DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -103,7 +106,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
addPreferencesFromResource(R.xml.preferences); addPreferencesFromResource(R.xml.preferences);
initializeIdentitySelection(); initializeIdentitySelection();
initializeSmsFallbackOption();
initializePushMessagingToggle(); initializePushMessagingToggle();
this.findPreference(TextSecurePreferences.CHANGE_PASSPHRASE_PREF) this.findPreference(TextSecurePreferences.CHANGE_PASSPHRASE_PREF)
@ -126,7 +128,10 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
.setOnPreferenceClickListener(new DirectoryUpdateListener()); .setOnPreferenceClickListener(new DirectoryUpdateListener());
this.findPreference(SUBMIT_DEBUG_LOG_PREF) this.findPreference(SUBMIT_DEBUG_LOG_PREF)
.setOnPreferenceClickListener(new SubmitDebugLogListener()); .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_COLOR_PREF));
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_BLINK_PREF)); initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_BLINK_PREF));
initializeRingtoneSummary((RingtonePreference) findPreference(TextSecurePreferences.RINGTONE_PREF)); initializeRingtoneSummary((RingtonePreference) findPreference(TextSecurePreferences.RINGTONE_PREF));
@ -188,14 +193,14 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
} }
private void initializePlatformSpecificOptions() { private void initializePlatformSpecificOptions() {
PreferenceGroup generalCategory = (PreferenceGroup) findPreference("general_category"); PreferenceGroup pushSmsCategory = (PreferenceGroup) findPreference("push_sms_category");
Preference defaultPreference = findPreference(KITKAT_DEFAULT_PREF); Preference defaultPreference = findPreference(KITKAT_DEFAULT_PREF);
Preference allSmsPreference = findPreference(TextSecurePreferences.ALL_SMS_PREF); Preference allSmsPreference = findPreference(TextSecurePreferences.ALL_SMS_PREF);
Preference allMmsPreference = findPreference(TextSecurePreferences.ALL_MMS_PREF); Preference allMmsPreference = findPreference(TextSecurePreferences.ALL_MMS_PREF);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && pushSmsCategory != null) {
if (allSmsPreference != null) generalCategory.removePreference(allSmsPreference); if (allSmsPreference != null) pushSmsCategory.removePreference(allSmsPreference);
if (allMmsPreference != null) generalCategory.removePreference(allMmsPreference); if (allMmsPreference != null) pushSmsCategory.removePreference(allMmsPreference);
if (Util.isDefaultSmsProvider(this)) { if (Util.isDefaultSmsProvider(this)) {
defaultPreference.setIntent(new Intent(Settings.ACTION_WIRELESS_SETTINGS)); 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.setTitle(getString(R.string.ApplicationPreferencesActivity_sms_disabled));
defaultPreference.setSummary(getString(R.string.ApplicationPreferencesActivity_touch_to_make_textsecure_your_default_sms_app)); defaultPreference.setSummary(getString(R.string.ApplicationPreferencesActivity_touch_to_make_textsecure_your_default_sms_app));
} }
} else { } else if (pushSmsCategory != null && defaultPreference != null) {
if (defaultPreference != null) generalCategory.removePreference(defaultPreference); pushSmsCategory.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);
}
} }
} }
@ -296,6 +273,10 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
listener.onPreferenceChange(pref, sharedPreferences.getString(pref.getKey(), "")); listener.onPreferenceChange(pref, sharedPreferences.getString(pref.getKey(), ""));
} }
private void initializeOutgoingSmsSummary(OutgoingSmsPreference pref) {
pref.setSummary(buildOutgoingSmsDescription());
}
private void handleIdentitySelection(Intent data) { private void handleIdentitySelection(Intent data) {
Uri contactUri = data.getData(); Uri contactUri = data.getData();
@ -311,11 +292,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
dynamicTheme.onResume(this); dynamicTheme.onResume(this);
} else if (key.equals(TextSecurePreferences.LANGUAGE_PREF)) { } else if (key.equals(TextSecurePreferences.LANGUAGE_PREF)) {
dynamicLanguage.onResume(this); 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 */ /* http://code.google.com/p/android/issues/detail?id=4611#c35 */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override

@ -44,7 +44,9 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; 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.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord; import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord;
@ -107,12 +109,15 @@ public class ConversationItem extends LinearLayout {
private View conversationParent; private View conversationParent;
private TextView bodyText; private TextView bodyText;
private TextView dateText; private TextView dateText;
private TextView indicatorText;
private TextView groupStatusText; private TextView groupStatusText;
private ImageView secureImage; private ImageView secureImage;
private ImageView failedImage; private ImageView failedImage;
private ImageView keyImage; private ImageView keyImage;
private ImageView contactPhoto; private ImageView contactPhoto;
private ImageView deliveredImage; private ImageView deliveredImage;
private View triangleTick;
private ImageView pendingIndicator;
private View mmsContainer; private View mmsContainer;
private ImageView mmsThumbnail; private ImageView mmsThumbnail;
@ -144,6 +149,7 @@ public class ConversationItem extends LinearLayout {
this.bodyText = (TextView) findViewById(R.id.conversation_item_body); this.bodyText = (TextView) findViewById(R.id.conversation_item_body);
this.dateText = (TextView) findViewById(R.id.conversation_item_date); 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.groupStatusText = (TextView) findViewById(R.id.group_message_status);
this.secureImage = (ImageView)findViewById(R.id.sms_secure_indicator); this.secureImage = (ImageView)findViewById(R.id.sms_secure_indicator);
this.failedImage = (ImageView)findViewById(R.id.sms_failed_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.contactPhoto = (ImageView)findViewById(R.id.contact_photo);
this.deliveredImage = (ImageView)findViewById(R.id.delivered_indicator); this.deliveredImage = (ImageView)findViewById(R.id.delivered_indicator);
this.conversationParent = (View) findViewById(R.id.conversation_item_parent); 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); this.backgroundDrawables = context.obtainStyledAttributes(STYLE_ATTRIBUTES);
setOnClickListener(clickListener); setOnClickListener(clickListener);
@ -180,6 +188,7 @@ public class ConversationItem extends LinearLayout {
setContactPhoto(messageRecord); setContactPhoto(messageRecord);
setGroupMessageStatus(messageRecord); setGroupMessageStatus(messageRecord);
setEvents(messageRecord); setEvents(messageRecord);
setMinimumWidth();
if (messageRecord instanceof NotificationMmsMessageRecord) { if (messageRecord instanceof NotificationMmsMessageRecord) {
setNotificationMmsAttributes((NotificationMmsMessageRecord)messageRecord); setNotificationMmsAttributes((NotificationMmsMessageRecord)messageRecord);
@ -218,10 +227,10 @@ public class ConversationItem extends LinearLayout {
if (messageRecord.isOutgoing()) { if (messageRecord.isOutgoing()) {
final int background; final int background;
final int triangleBackground; final int triangleBackground;
if (messageRecord.isPending() && pushDestination) { if (messageRecord.isPending() && pushDestination && !messageRecord.isForcedSms()) {
background = SENT_PUSH_PENDING; background = SENT_PUSH_PENDING;
triangleBackground = SENT_PUSH_PENDING_TRIANGLE; triangleBackground = SENT_PUSH_PENDING_TRIANGLE;
} else if (messageRecord.isPending()) { } else if (messageRecord.isPending() || messageRecord.isPendingFallbackApproval()) {
background = SENT_SMS_PENDING; background = SENT_SMS_PENDING;
triangleBackground = SENT_SMS_PENDING_TRIANGLE; triangleBackground = SENT_SMS_PENDING_TRIANGLE;
} else if (messageRecord.isPush()) { } else if (messageRecord.isPush()) {
@ -232,7 +241,7 @@ public class ConversationItem extends LinearLayout {
triangleBackground = SENT_SMS_TRIANGLE; triangleBackground = SENT_SMS_TRIANGLE;
} }
setViewBackgroundWithoutResettingPadding(conversationParent, backgroundDrawables.getResourceId(background, -1)); 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) { private void setStatusIcons(MessageRecord messageRecord) {
failedImage.setVisibility(messageRecord.isFailed() ? View.VISIBLE : View.GONE); 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); secureImage.setVisibility(messageRecord.isSecure() ? View.VISIBLE : View.GONE);
keyImage.setVisibility(messageRecord.isKeyExchange() ? View.VISIBLE : View.GONE); keyImage.setVisibility(messageRecord.isKeyExchange() ? View.VISIBLE : View.GONE);
deliveredImage.setVisibility(!messageRecord.isKeyExchange() && messageRecord.isDelivered() ? 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()) { if (messageRecord.isFailed()) {
dateText.setText(R.string.ConversationItem_error_sending_message); 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()) { } else if (messageRecord.isPending()) {
dateText.setText(" ··· "); dateText.setText(" ··· ");
} else { } 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) { private void setEvents(MessageRecord messageRecord) {
setClickable(messageRecord.isKeyExchange() && setClickable(messageRecord.isPendingFallbackApproval() ||
!messageRecord.isCorruptedKeyExchange() && (messageRecord.isKeyExchange() &&
!messageRecord.isOutgoing()); !messageRecord.isCorruptedKeyExchange() &&
!messageRecord.isOutgoing()));
if (!messageRecord.isOutgoing() && if (!messageRecord.isOutgoing() &&
messageRecord.getRecipients().isSingleRecipient() && messageRecord.getRecipients().isSingleRecipient() &&
@ -615,9 +640,42 @@ public class ConversationItem extends LinearLayout {
!messageRecord.isProcessedKeyExchange() && !messageRecord.isProcessedKeyExchange() &&
!messageRecord.isStaleKeyExchange()) !messageRecord.isStaleKeyExchange())
handleKeyExchangeClicked(); 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() { private void handleAbortSecureSession() {
if (!messageRecord.isSecure()) return; if (!messageRecord.isSecure()) return;

@ -159,7 +159,7 @@ public class PushContactSelectionListFragment extends SherlockListFragment
} }
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 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.setMultiChoiceItems(options, null, new DiscriminatorClickedListener(contactData));
builder.setPositiveButton(android.R.string.ok, new DiscriminatorFinishedListener(contactData, textView, checkBox)); builder.setPositiveButton(android.R.string.ok, new DiscriminatorFinishedListener(contactData, textView, checkBox));
builder.setOnCancelListener(new DiscriminatorFinishedListener(contactData, textView, checkBox)); builder.setOnCancelListener(new DiscriminatorFinishedListener(contactData, textView, checkBox));

@ -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);
}
}
}

@ -281,6 +281,15 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
notifyConversationListeners(getThreadIdForMessage(messageId)); 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) { public void markAsSending(long messageId) {
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE); updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE);
notifyConversationListeners(getThreadIdForMessage(messageId)); notifyConversationListeners(getThreadIdForMessage(messageId));

@ -15,16 +15,22 @@ public interface MmsSmsColumns {
protected static final long TOTAL_MASK = 0xFFFFFFFF; protected static final long TOTAL_MASK = 0xFFFFFFFF;
// Base Types // 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_INBOX_TYPE = 20;
protected static final long BASE_OUTBOX_TYPE = 21; protected static final long BASE_OUTBOX_TYPE = 21;
protected static final long BASE_SENDING_TYPE = 22; protected static final long BASE_SENDING_TYPE = 22;
protected static final long BASE_SENT_TYPE = 23; protected static final long BASE_SENT_TYPE = 23;
protected static final long BASE_SENT_FAILED_TYPE = 24; 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, 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 // Key Exchange Information
protected static final long KEY_EXCHANGE_BIT = 0x8000; protected static final long KEY_EXCHANGE_BIT = 0x8000;
@ -65,12 +71,20 @@ public interface MmsSmsColumns {
return false; return false;
} }
public static boolean isForcedSms(long type) {
return (type & MESSAGE_FORCE_SMS_BIT) != 0;
}
public static boolean isPendingMessageType(long type) { public static boolean isPendingMessageType(long type) {
return return
(type & BASE_TYPE_MASK) == BASE_OUTBOX_TYPE || (type & BASE_TYPE_MASK) == BASE_OUTBOX_TYPE ||
(type & BASE_TYPE_MASK) == BASE_SENDING_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) { public static boolean isInboxType(long type) {
return (type & BASE_TYPE_MASK) == BASE_INBOX_TYPE; return (type & BASE_TYPE_MASK) == BASE_INBOX_TYPE;
} }

@ -178,6 +178,10 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
updateTypeBitmask(id, 0, Types.PUSH_MESSAGE_BIT); 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) { public void markAsDecryptFailed(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT); 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); 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) { public void markAsSending(long id) {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE); updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE);
} }

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

@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.transport.UniversalTransport; import org.thoughtcrime.securesms.transport.UniversalTransport;
import org.thoughtcrime.securesms.transport.UntrustedIdentityException; import org.thoughtcrime.securesms.transport.UntrustedIdentityException;
import org.thoughtcrime.securesms.transport.UserInterventionRequiredException;
import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.MasterSecret;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
@ -78,11 +79,16 @@ public class MmsSender {
if (result.isUpgradedSecure()) database.markAsSecure(message.getDatabaseMessageId()); if (result.isUpgradedSecure()) database.markAsSecure(message.getDatabaseMessageId());
if (result.isPush()) database.markAsPush(message.getDatabaseMessageId()); if (result.isPush()) database.markAsPush(message.getDatabaseMessageId());
database.markAsSent(message.getDatabaseMessageId(), result.getMessageId(), database.markAsSent(message.getDatabaseMessageId(), result.getMessageId(),
result.getResponseStatus()); result.getResponseStatus());
systemStateListener.unregisterForConnectivityChange(); 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) { } catch (UndeliverableMessageException e) {
Log.w("MmsSender", e); Log.w("MmsSender", e);
database.markAsSentFailed(message.getDatabaseMessageId()); database.markAsSentFailed(message.getDatabaseMessageId());

@ -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_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 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 SEND_SMS = 0;
private static final int RECEIVE_SMS = 1; private static final int RECEIVE_SMS = 1;
private static final int SEND_MMS = 2; private static final int SEND_MMS = 2;
@ -307,7 +309,7 @@ public class SendReceiveService extends Service {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.w("SendReceiveService", "Got a MasterSecret broadcast..."); Log.w("SendReceiveService", "Got a MasterSecret broadcast...");
initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret")); initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra(MASTER_SECRET_EXTRA));
} }
} }

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

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

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

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

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

@ -45,12 +45,35 @@ public class TextSecurePreferences {
private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications"; private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications";
private static final String LOCAL_REGISTRATION_ID_PREF = "pref_local_registration_id"; 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) { public static boolean isSmsFallbackEnabled(Context context) {
return getBooleanPreference(context, ALLOW_SMS_FALLBACK_PREF, true); 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) { public static int getLocalRegistrationId(Context context) {
return getIntegerPreference(context, LOCAL_REGISTRATION_ID_PREF, 0); return getIntegerPreference(context, LOCAL_REGISTRATION_ID_PREF, 0);
} }

Loading…
Cancel
Save