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

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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 -->

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

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

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