Support granular "custom" MMS preferences.

1) Make each MMS preference an individual choice between custom
   and default.

2) Display default values.

Closes #2487

// FREEBIE
master
Moxie Marlinspike 2015-02-16 20:31:44 -08:00
parent e31ddf0599
commit 534df06794
16 changed files with 543 additions and 221 deletions

View File

@ -101,7 +101,7 @@
</intent-filter>
</activity>
<activity android:name=".MmsPreferencesActivity"
<activity android:name=".preferences.MmsPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ShareActivity"

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Spinner android:id="@+id/default_or_custom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/default_or_custom_entries"
android:entryValues="@array/default_or_custom_values"
android:layout_marginStart="16dip"
android:layout_marginLeft="16dip"
android:spinnerMode="dropdown"/>
<TextView android:id="@+id/default_label"
android:layout_marginLeft="18dip"
android:layout_marginRight="16dip"
android:layout_marginTop="16dip"
android:layout_marginBottom="16dip"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<EditText android:id="@+id/custom_edit"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:layout_marginTop="16dip"
android:layout_marginBottom="16dip"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@ -202,4 +202,15 @@
<item>5</item>
<item>10</item>
</string-array>
<string-array name="default_or_custom_values" translatable="false">
<item>default</item>
<item>custom</item>
</string-array>
<string-array name="default_or_custom_entries">
<item>@string/arrays__use_default</item>
<item>@string/arrays__use_custom</item>
</string-array>
</resources>

View File

@ -101,4 +101,9 @@
<attr name="pref_ic_advanced" format="reference" />
<attr name="app_protect_timeout_picker_color" format="reference"/>
<declare-styleable name="CustomDefaultPreference">
<attr name="custom_pref_toggle" format="string"/>
</declare-styleable>
</resources>

View File

@ -153,6 +153,11 @@
<!-- ConversationListItem -->
<string name="ConversationListItem_key_exchange_message">Key exchange message...</string>
<!-- CustomDefaultPreference -->
<string name="CustomDefaultPreference_using_custom">Using custom: %s</string>
<string name="CustomDefaultPreference_using_default">Using default: %s</string>
<string name="CustomDefaultPreference_none">None</string>
<!-- DateUtils -->
<string name="DateUtils_now">Now</string>
@ -691,6 +696,8 @@
<!-- arrays.xml -->
<string name="arrays__import_export">Import / export</string>
<string name="arrays__my_identity_key">My identity key</string>
<string name="arrays__use_default">Use default</string>
<string name="arrays__use_custom">Use custom</string>
<!-- plurals.xml -->
<plurals name="minutes_ago">
@ -775,11 +782,11 @@
<string name="preferences__advanced_mms_access_point_names">Manual MMS settings</string>
<string name="preferences__enable_manual_mms">Use manual MMS settings</string>
<string name="preferences__override_system_mms_settings">Override system MMS settings with the information below.</string>
<string name="preferences__mmsc_url_required">MMSC URL (Required)</string>
<string name="preferences__mms_proxy_host_optional">MMS Proxy Host (Optional)</string>
<string name="preferences__mms_proxy_port_optional">MMS Proxy Port (Optional)</string>
<string name="preferences__mmsc_username_optional">MMSC Username (Optional)</string>
<string name="preferences__mmsc_password_optional">MMSC Password (Optional)</string>
<string name="preferences__mmsc_url">MMSC URL</string>
<string name="preferences__mms_proxy_host">MMS Proxy Host</string>
<string name="preferences__mms_proxy_port">MMS Proxy Port</string>
<string name="preferences__mmsc_username">MMSC Username</string>
<string name="preferences__mmsc_password">MMSC Password</string>
<string name="preferences__sms_delivery_reports">SMS delivery reports</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Request a delivery report for each SMS message you send</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Automatically delete older messages once a conversation thread exceeds a specified length</string>

View File

@ -1,33 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference android:key="pref_enable_manual_mms"
android:defaultValue="false"
android:title="@string/preferences__enable_manual_mms"
android:summary="@string/preferences__override_system_mms_settings"/>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<EditTextPreference android:key="pref_apn_mmsc_host"
android:title="@string/preferences__mmsc_url_required"
android:dependency="pref_enable_manual_mms"
android:inputType="textUri" />
<org.thoughtcrime.securesms.components.CustomDefaultPreference
app:custom_pref_toggle="pref_apn_mmsc_custom_host"
android:key="pref_apn_mmsc_host"
android:title="@string/preferences__mmsc_url"
android:inputType="textUri" />
<EditTextPreference android:key="pref_apn_mms_proxy"
android:title="@string/preferences__mms_proxy_host_optional"
android:dependency="pref_enable_manual_mms"
android:inputType="textUri" />
<org.thoughtcrime.securesms.components.CustomDefaultPreference
app:custom_pref_toggle="pref_apn_mms_custom_proxy"
android:key="pref_apn_mms_proxy"
android:title="@string/preferences__mms_proxy_host"
android:inputType="textUri" />
<EditTextPreference android:key="pref_apn_mms_proxy_port"
android:title="@string/preferences__mms_proxy_port_optional"
android:dependency="pref_enable_manual_mms"
android:inputType="number" />
<org.thoughtcrime.securesms.components.CustomDefaultPreference
app:custom_pref_toggle="pref_apn_mms_custom_proxy_port"
android:key="pref_apn_mms_proxy_port"
android:title="@string/preferences__mms_proxy_port"
android:inputType="number"/>
<EditTextPreference android:key="pref_apn_mmsc_username"
android:title="@string/preferences__mmsc_username_optional"
android:dependency="pref_enable_manual_mms"
android:inputType="textNoSuggestions" />
<org.thoughtcrime.securesms.components.CustomDefaultPreference
app:custom_pref_toggle="pref_apn_mmsc_custom_username"
android:key="pref_apn_mmsc_username"
android:title="@string/preferences__mmsc_username"
android:inputType="textNoSuggestions"/>
<org.thoughtcrime.securesms.components.CustomDefaultPreference
app:custom_pref_toggle="pref_apn_mmsc_custom_password"
android:key="pref_apn_mmsc_password"
android:title="@string/preferences__mmsc_password"
android:inputType="textVisiblePassword"/>
<EditTextPreference android:key="pref_apn_mmsc_password"
android:title="@string/preferences__mmsc_password_optional"
android:dependency="pref_enable_manual_mms"
android:inputType="textVisiblePassword" />
</PreferenceScreen>

View File

@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.jobs.PushDecryptJob;
import org.thoughtcrime.securesms.jobs.SmsDecryptJob;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.ParcelUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.VersionTracker;
import org.whispersystems.jobqueue.EncryptionKeys;

View File

@ -1,143 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
import android.content.Context;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.support.v4.preference.PreferenceFragment;
import android.text.TextUtils;
import android.widget.Toast;
import org.thoughtcrime.securesms.mms.OutgoingMmsConnection;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.net.URI;
import java.net.URISyntaxException;
public class MmsPreferencesFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
initializePreferences();
initializeEditTextSummaries();
((PassphraseRequiredActionBarActivity) getActivity()).getSupportActionBar()
.setTitle(R.string.preferences__advanced_mms_access_point_names);
}
private void initializePreferences() {
if (!OutgoingMmsConnection.isConnectionPossible(getActivity())) {
TextSecurePreferences.setUseLocalApnsEnabled(getActivity(), true);
addPreferencesFromResource(R.xml.preferences_manual_mms);
this.findPreference(TextSecurePreferences.ENABLE_MANUAL_MMS_PREF)
.setOnPreferenceChangeListener(new OverrideMmsChangeListener());
} else {
addPreferencesFromResource(R.xml.preferences_manual_mms);
}
this.findPreference(TextSecurePreferences.MMSC_HOST_PREF).setOnPreferenceChangeListener(new ValidUriVerificationListener());
this.findPreference(TextSecurePreferences.MMSC_PROXY_HOST_PREF).setOnPreferenceChangeListener(new ValidHostnameVerificationListener());
this.findPreference(TextSecurePreferences.MMSC_PROXY_PORT_PREF).setOnPreferenceChangeListener(new EditTextVerificationListener());
this.findPreference(TextSecurePreferences.MMSC_USERNAME_PREF).setOnPreferenceChangeListener(new EditTextVerificationListener());
this.findPreference(TextSecurePreferences.MMSC_PASSWORD_PREF).setOnPreferenceChangeListener(new EditTextVerificationListener());
}
private void initializeEditTextSummary(final EditTextPreference preference) {
preference.setSummary(TextUtils.isEmpty(preference.getText()) ? getString(R.string.MmsPreferencesFragment__not_set) : preference.getText());
}
private void initializeEditTextSummaries() {
initializeEditTextSummary((EditTextPreference)this.findPreference(TextSecurePreferences.MMSC_HOST_PREF));
initializeEditTextSummary((EditTextPreference)this.findPreference(TextSecurePreferences.MMSC_PROXY_HOST_PREF));
initializeEditTextSummary((EditTextPreference)this.findPreference(TextSecurePreferences.MMSC_PROXY_PORT_PREF));
initializeEditTextSummary((EditTextPreference)this.findPreference(TextSecurePreferences.MMSC_USERNAME_PREF));
initializeEditTextSummary((EditTextPreference)this.findPreference(TextSecurePreferences.MMSC_PASSWORD_PREF));
}
private class OverrideMmsChangeListener implements Preference.OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
TextSecurePreferences.setUseLocalApnsEnabled(getActivity(), true);
Toast.makeText(getActivity(), R.string.MmsPreferencesFragment__manual_mms_settings_are_required,
Toast.LENGTH_SHORT).show();
return false;
}
}
public static CharSequence getSummary(Context context) {
final int enabledResId = R.string.MmsPreferencesFragment__enabled;
final int disabledResId = R.string.MmsPreferencesFragment__disabled;
return context.getString(TextSecurePreferences.isUseLocalApnsEnabled(context) ? enabledResId : disabledResId);
}
private class EditTextVerificationListener implements OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String newString = (String)newValue;
if (isValid(newString)) {
preference.setSummary(TextUtils.isEmpty(newString) ? getString(R.string.MmsPreferencesFragment__not_set) : newString);
return true;
} else {
Toast.makeText(getActivity(), getErrorMessage(), Toast.LENGTH_LONG).show();
return false;
}
}
protected boolean isValid(String newString) { return true; }
protected int getErrorMessage() { return 0; }
}
private class ValidUriVerificationListener extends EditTextVerificationListener {
@Override
protected boolean isValid(String newString) {
if (TextUtils.isEmpty(newString)) return true;
try {
new URI(newString);
return true;
} catch (URISyntaxException mue) {
return false;
}
}
@Override
protected int getErrorMessage() {
return R.string.MmsPreferencesFragment__invalid_uri;
}
}
private class ValidHostnameVerificationListener extends EditTextVerificationListener {
@Override
protected boolean isValid(String newString) {
if (TextUtils.isEmpty(newString)) return true;
try {
URI uri = new URI(null, newString, null, null);
return true;
} catch (URISyntaxException mue) {
return false;
}
}
@Override
protected int getErrorMessage() {
return R.string.MmsPreferencesFragment__invalid_host;
}
}
}

View File

@ -5,6 +5,8 @@ import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import org.thoughtcrime.securesms.preferences.MmsPreferencesActivity;
public class PromptMmsActivity extends PassphraseRequiredActionBarActivity {
private Button okButton;

View File

@ -0,0 +1,227 @@
package org.thoughtcrime.securesms.components;
import android.app.AlertDialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.preference.DialogPreference;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.net.URI;
import java.net.URISyntaxException;
public class CustomDefaultPreference extends DialogPreference {
private static final String TAG = CustomDefaultPreference.class.getSimpleName();
private final int inputType;
private final String customPreference;
private final String customToggle;
private CustomPreferenceValidator validator;
private String defaultValue;
private Spinner spinner;
private EditText customText;
private TextView defaultLabel;
private Button positiveButton;
public CustomDefaultPreference(Context context, AttributeSet attrs) {
super(context, attrs);
int[] attributeNames = new int[]{android.R.attr.inputType, R.attr.custom_pref_toggle};
TypedArray attributes = context.obtainStyledAttributes(attrs, attributeNames);
this.inputType = attributes.getInt(0, 0);
this.customPreference = getKey();
this.customToggle = attributes.getString(1);
this.validator = new NullValidator();
attributes.recycle();
setPersistent(false);
setDialogLayoutResource(R.layout.custom_default_preference_dialog);
}
public CustomDefaultPreference setValidator(CustomPreferenceValidator validator) {
this.validator = validator;
return this;
}
public CustomDefaultPreference setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
this.setSummary(getSummary());
return this;
}
@Override
public String getSummary() {
if (isCustom()) {
return getContext().getString(R.string.CustomDefaultPreference_using_custom,
getPrettyPrintValue(getCustomValue()));
} else {
return getContext().getString(R.string.CustomDefaultPreference_using_default,
getPrettyPrintValue(getDefaultValue()));
}
}
@Override
protected void onBindDialogView(@NonNull View view) {
super.onBindDialogView(view);
this.spinner = (Spinner) view.findViewById(R.id.default_or_custom);
this.defaultLabel = (TextView) view.findViewById(R.id.default_label);
this.customText = (EditText) view.findViewById(R.id.custom_edit);
this.customText.setInputType(inputType);
this.customText.addTextChangedListener(new TextValidator());
this.customText.setText(getCustomValue());
this.spinner.setOnItemSelectedListener(new SelectionLister());
this.defaultLabel.setText(getPrettyPrintValue(defaultValue));
}
@Override
protected void showDialog(Bundle instanceState) {
super.showDialog(instanceState);
positiveButton = ((AlertDialog)getDialog()).getButton(AlertDialog.BUTTON_POSITIVE);
if (isCustom()) spinner.setSelection(1, true);
else spinner.setSelection(0, true);
}
@Override
protected void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
if (spinner != null) setCustom(spinner.getSelectedItemPosition() == 1);
if (customText != null) setCustomValue(customText.getText().toString());
setSummary(getSummary());
}
}
private String getPrettyPrintValue(String value) {
if (TextUtils.isEmpty(value)) return getContext().getString(R.string.CustomDefaultPreference_none);
else return value;
}
private boolean isCustom() {
return TextSecurePreferences.getBooleanPreference(getContext(), customToggle, false);
}
private void setCustom(boolean custom) {
TextSecurePreferences.setBooleanPreference(getContext(), customToggle, custom);
}
private String getCustomValue() {
return TextSecurePreferences.getStringPreference(getContext(), customPreference, "");
}
private void setCustomValue(String value) {
TextSecurePreferences.setStringPreference(getContext(), customPreference, value);
}
private String getDefaultValue() {
return defaultValue;
}
private class SelectionLister implements AdapterView.OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
defaultLabel.setVisibility(position == 0 ? View.VISIBLE : View.GONE);
customText.setVisibility(position == 0 ? View.GONE : View.VISIBLE);
positiveButton.setEnabled(position == 0 || validator.isValid(customText.getText().toString()));
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
defaultLabel.setVisibility(View.VISIBLE);
customText.setVisibility(View.GONE);
}
}
private class TextValidator implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
if (spinner.getSelectedItemPosition() == 1) {
positiveButton.setEnabled(validator.isValid(s.toString()));
}
}
}
protected interface CustomPreferenceValidator {
public boolean isValid(String value);
}
private static class NullValidator implements CustomPreferenceValidator {
@Override
public boolean isValid(String value) {
return true;
}
}
public static class UriValidator implements CustomPreferenceValidator {
@Override
public boolean isValid(String value) {
if (TextUtils.isEmpty(value)) return true;
try {
new URI(value);
return true;
} catch (URISyntaxException mue) {
return false;
}
}
}
public static class HostnameValidator implements CustomPreferenceValidator {
@Override
public boolean isValid(String value) {
if (TextUtils.isEmpty(value)) return true;
try {
URI uri = new URI(null, value, null, null);
return true;
} catch (URISyntaxException mue) {
return false;
}
}
}
public static class PortValidator implements CustomPreferenceValidator {
@Override
public boolean isValid(String value) {
try {
Integer.parseInt(value);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}
}

View File

@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.mms.ApnUnavailableException;
import org.thoughtcrime.securesms.mms.MmsConnection.Apn;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.io.File;
import java.io.FileOutputStream;
@ -93,13 +94,11 @@ public class ApnDatabase {
null,
SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
}
protected Apn getLocallyConfiguredMmsConnectionParameters() throws ApnUnavailableException {
if (TextSecurePreferences.isUseLocalApnsEnabled(context)) {
String mmsc = TextSecurePreferences.getMmscUrl(context).trim();
if (TextUtils.isEmpty(mmsc))
throw new ApnUnavailableException("Malformed locally configured MMSC.");
if (!mmsc.startsWith("http"))
private Apn getCustomApnParameters() {
String mmsc = TextSecurePreferences.getMmscUrl(context).trim();
if (!TextUtils.isEmpty(mmsc) && !mmsc.startsWith("http"))
mmsc = "http://" + mmsc;
String proxy = TextSecurePreferences.getMmscProxy(context);
@ -108,26 +107,12 @@ public class ApnDatabase {
String pass = TextSecurePreferences.getMmscPassword(context);
return new Apn(mmsc, proxy, port, user, pass);
}
throw new ApnUnavailableException("No locally configured parameters available");
}
public Apn getMmsConnectionParameters(final String mccmnc, final String apn) {
if (TextSecurePreferences.isUseLocalApnsEnabled(context)) {
Log.w(TAG, "Choosing locally-overridden MMS settings");
try {
return getLocallyConfiguredMmsConnectionParameters();
} catch (ApnUnavailableException aue) {
Log.w(TAG, "preference to use local apn set, but no parameters avaiable. falling back.");
}
}
public Apn getDefaultApnParameters(String mccmnc, String apn) {
if (mccmnc == null) {
Log.w(TAG, "mccmnc was null, returning null");
return null;
return Apn.EMPTY;
}
Cursor cursor = null;
@ -161,9 +146,25 @@ public class ApnDatabase {
}
Log.w(TAG, "No matching APNs found, returning null");
return null;
return Apn.EMPTY;
} finally {
if (cursor != null) cursor.close();
}
}
public Optional<Apn> getMmsConnectionParameters(String mccmnc, String apn) {
Apn customApn = getCustomApnParameters();
Apn defaultApn = getDefaultApnParameters(mccmnc, apn);
Apn result = new Apn(customApn, defaultApn,
TextSecurePreferences.getUseCustomMmsc(context),
TextSecurePreferences.getUseCustomMmscProxy(context),
TextSecurePreferences.getUseCustomMmscProxyPort(context),
TextSecurePreferences.getUseCustomMmscUsername(context),
TextSecurePreferences.getUseCustomMmscPassword(context));
if (TextUtils.isEmpty(result.getMmsc())) return Optional.absent();
else return Optional.of(result);
}
}

View File

@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.database.ApnDatabase;
import org.thoughtcrime.securesms.util.TelephonyUtil;
import org.thoughtcrime.securesms.util.Conversions;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
@ -56,27 +57,24 @@ public abstract class MmsConnection {
this.apn = apn;
}
protected static Apn getLocalApn(Context context) throws ApnUnavailableException {
try {
Apn params = ApnDatabase.getInstance(context)
.getMmsConnectionParameters(TelephonyUtil.getMccMnc(context),
TelephonyUtil.getApn(context));
public static Apn getApn(Context context, String apnName) throws ApnUnavailableException {
Log.w(TAG, "Getting MMSC params for apn " + apnName);
if (params == null) {
try {
Optional<Apn> params = ApnDatabase.getInstance(context)
.getMmsConnectionParameters(TelephonyUtil.getMccMnc(context),
TelephonyUtil.getApn(context));
if (!params.isPresent()) {
throw new ApnUnavailableException("No parameters available from ApnDefaults.");
}
return params;
return params.get();
} catch (IOException ioe) {
throw new ApnUnavailableException("ApnDatabase threw an IOException", ioe);
}
}
public static Apn getApn(Context context, String apnName) throws ApnUnavailableException {
Log.w(TAG, "Getting MMSC params for apn " + apnName);
return getLocalApn(context);
}
protected static boolean checkRouteToHost(Context context, String host, boolean usingMmsRadio)
throws IOException
{
@ -171,6 +169,9 @@ public abstract class MmsConnection {
protected abstract HttpUriRequest constructRequest(boolean useProxy) throws IOException;
public static class Apn {
public static Apn EMPTY = new Apn("", "", "", "", "");
private final String mmsc;
private final String proxy;
private final String port;
@ -185,6 +186,20 @@ public abstract class MmsConnection {
this.password = password;
}
public Apn(Apn customApn, Apn defaultApn,
boolean useCustomMmsc,
boolean useCustomProxy,
boolean useCustomProxyPort,
boolean useCustomUsername,
boolean useCustomPassword)
{
this.mmsc = useCustomMmsc ? customApn.mmsc : defaultApn.mmsc;
this.proxy = useCustomProxy ? customApn.proxy : defaultApn.proxy;
this.port = useCustomProxyPort ? customApn.port : defaultApn.port;
this.username = useCustomUsername ? customApn.username : defaultApn.username;
this.password = useCustomPassword ? customApn.password : defaultApn.password;
}
public boolean hasProxy() {
return !TextUtils.isEmpty(proxy);
}

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
package org.thoughtcrime.securesms.preferences;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@ -22,6 +22,7 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.MenuItem;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;

View File

@ -0,0 +1,96 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.preferences;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.preference.PreferenceFragment;
import android.util.Log;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.CustomDefaultPreference;
import org.thoughtcrime.securesms.database.ApnDatabase;
import org.thoughtcrime.securesms.mms.MmsConnection;
import org.thoughtcrime.securesms.util.TelephonyUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.IOException;
public class MmsPreferencesFragment extends PreferenceFragment {
private static final String TAG = MmsPreferencesFragment.class.getSimpleName();
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
addPreferencesFromResource(R.xml.preferences_manual_mms);
((PassphraseRequiredActionBarActivity) getActivity()).getSupportActionBar()
.setTitle(R.string.preferences__advanced_mms_access_point_names);
}
@Override
public void onResume() {
super.onResume();
new LoadApnDefaultsTask().execute();
}
private class LoadApnDefaultsTask extends AsyncTask<Void, Void, MmsConnection.Apn> {
@Override
protected MmsConnection.Apn doInBackground(Void... params) {
try {
Context context = getActivity();
if (context != null) {
return ApnDatabase.getInstance(context)
.getDefaultApnParameters(TelephonyUtil.getMccMnc(context),
TelephonyUtil.getApn(context));
}
} catch (IOException e) {
Log.w(TAG, e);
}
return null;
}
@Override
protected void onPostExecute(MmsConnection.Apn apnDefaults) {
((CustomDefaultPreference)findPreference(TextSecurePreferences.MMSC_HOST_PREF))
.setValidator(new CustomDefaultPreference.UriValidator())
.setDefaultValue(apnDefaults.getMmsc());
((CustomDefaultPreference)findPreference(TextSecurePreferences.MMSC_PROXY_HOST_PREF))
.setValidator(new CustomDefaultPreference.HostnameValidator())
.setDefaultValue(apnDefaults.getProxy());
((CustomDefaultPreference)findPreference(TextSecurePreferences.MMSC_PROXY_PORT_PREF))
.setValidator(new CustomDefaultPreference.PortValidator())
.setDefaultValue(apnDefaults.getPort());
((CustomDefaultPreference)findPreference(TextSecurePreferences.MMSC_USERNAME_PREF))
.setDefaultValue(apnDefaults.getPort());
((CustomDefaultPreference)findPreference(TextSecurePreferences.MMSC_PASSWORD_PREF))
.setDefaultValue(apnDefaults.getPassword());
}
}
}

View File

@ -15,7 +15,6 @@ import android.support.v4.preference.PreferenceFragment;
import android.text.TextUtils;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.MmsPreferencesFragment;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.OutgoingSmsPreference;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -46,8 +45,6 @@ public class SmsMmsPreferenceFragment extends PreferenceFragment {
public void onResume() {
super.onResume();
((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.preferences__sms_mms);
this.findPreference(MMS_PREF)
.setSummary(MmsPreferencesFragment.getSummary(getActivity()));
initializePlatformSpecificOptions();
}

View File

@ -15,10 +15,15 @@ public class TextSecurePreferences {
public static final String DISABLE_PASSPHRASE_PREF = "pref_disable_passphrase";
public static final String THEME_PREF = "pref_theme";
public static final String LANGUAGE_PREF = "pref_language";
private static final String MMSC_CUSTOM_HOST_PREF = "pref_apn_mmsc_custom_host";
public static final String MMSC_HOST_PREF = "pref_apn_mmsc_host";
private static final String MMSC_CUSTOM_PROXY_PREF = "pref_apn_mms_custom_proxy";
public static final String MMSC_PROXY_HOST_PREF = "pref_apn_mms_proxy";
private static final String MMSC_CUSTOM_PROXY_PORT_PREF = "pref_apn_mms_custom_proxy_port";
public static final String MMSC_PROXY_PORT_PREF = "pref_apn_mms_proxy_port";
private static final String MMSC_CUSTOM_USERNAME_PREF = "pref_apn_mmsc_custom_username";
public static final String MMSC_USERNAME_PREF = "pref_apn_mmsc_username";
private static final String MMSC_CUSTOM_PASSWORD_PREF = "pref_apn_mmsc_custom_password";
public static final String MMSC_PASSWORD_PREF = "pref_apn_mmsc_password";
public static final String THREAD_TRIM_LENGTH = "pref_trim_length";
public static final String THREAD_TRIM_NOW = "pref_trim_now";
@ -206,26 +211,91 @@ public class TextSecurePreferences {
setBooleanPreference(context, DISABLE_PASSPHRASE_PREF, disabled);
}
public static boolean getUseCustomMmsc(Context context) {
boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context);
return getBooleanPreference(context, MMSC_CUSTOM_HOST_PREF, legacy);
}
public static void setUseCustomMmsc(Context context, boolean value) {
setBooleanPreference(context, MMSC_CUSTOM_HOST_PREF, value);
}
public static String getMmscUrl(Context context) {
return getStringPreference(context, MMSC_HOST_PREF, "");
}
public static void setMmscUrl(Context context, String mmsc) {
setStringPreference(context, MMSC_HOST_PREF, mmsc);
}
public static boolean getUseCustomMmscProxy(Context context) {
boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context);
return getBooleanPreference(context, MMSC_CUSTOM_PROXY_PREF, legacy);
}
public static void setUseCustomMmscProxy(Context context, boolean value) {
setBooleanPreference(context, MMSC_CUSTOM_PROXY_PREF, value);
}
public static String getMmscProxy(Context context) {
return getStringPreference(context, MMSC_PROXY_HOST_PREF, "");
}
public static void setMmscProxy(Context context, String value) {
setStringPreference(context, MMSC_PROXY_HOST_PREF, value);
}
public static boolean getUseCustomMmscProxyPort(Context context) {
boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context);
return getBooleanPreference(context, MMSC_CUSTOM_PROXY_PORT_PREF, legacy);
}
public static void setUseCustomMmscProxyPort(Context context, boolean value) {
setBooleanPreference(context, MMSC_CUSTOM_PROXY_PORT_PREF, value);
}
public static String getMmscProxyPort(Context context) {
return getStringPreference(context, MMSC_PROXY_PORT_PREF, "");
}
public static void setMmscProxyPort(Context context, String value) {
setStringPreference(context, MMSC_PROXY_PORT_PREF, value);
}
public static boolean getUseCustomMmscUsername(Context context) {
boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context);
return getBooleanPreference(context, MMSC_CUSTOM_USERNAME_PREF, legacy);
}
public static void setUseCustomMmscUsername(Context context, boolean value) {
setBooleanPreference(context, MMSC_CUSTOM_USERNAME_PREF, value);
}
public static String getMmscUsername(Context context) {
return getStringPreference(context, MMSC_USERNAME_PREF, "");
}
public static void setMmscUsername(Context context, String value) {
setStringPreference(context, MMSC_USERNAME_PREF, value);
}
public static boolean getUseCustomMmscPassword(Context context) {
boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context);
return getBooleanPreference(context, MMSC_CUSTOM_PASSWORD_PREF, legacy);
}
public static void setUseCustomMmscPassword(Context context, boolean value) {
setBooleanPreference(context, MMSC_CUSTOM_PASSWORD_PREF, value);
}
public static String getMmscPassword(Context context) {
return getStringPreference(context, MMSC_PASSWORD_PREF, "");
}
public static void setMmscPassword(Context context, String value) {
setStringPreference(context, MMSC_PASSWORD_PREF, value);
}
public static String getIdentityContactUri(Context context) {
return getStringPreference(context, IDENTITY_PREF, null);
}
@ -242,14 +312,10 @@ public class TextSecurePreferences {
return getBooleanPreference(context, SCREEN_SECURITY_PREF, true);
}
public static boolean isUseLocalApnsEnabled(Context context) {
public static boolean isLegacyUseLocalApnsEnabled(Context context) {
return getBooleanPreference(context, ENABLE_MANUAL_MMS_PREF, false);
}
public static void setUseLocalApnsEnabled(Context context, boolean useLocal) {
setBooleanPreference(context, ENABLE_MANUAL_MMS_PREF, useLocal);
}
public static int getLastVersionCode(Context context) {
return getIntegerPreference(context, LAST_VERSION_CODE_PREF, 0);
}
@ -373,11 +439,11 @@ public class TextSecurePreferences {
setLongPreference(context, PUSH_REGISTRATION_REMINDER_PREF, time);
}
private static void setBooleanPreference(Context context, String key, boolean value) {
public static void setBooleanPreference(Context context, String key, boolean value) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply();
}
private static boolean getBooleanPreference(Context context, String key, boolean defaultValue) {
public static boolean getBooleanPreference(Context context, String key, boolean defaultValue) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(key, defaultValue);
}
@ -385,7 +451,7 @@ public class TextSecurePreferences {
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(key, value).apply();
}
private static String getStringPreference(Context context, String key, String defaultValue) {
public static String getStringPreference(Context context, String key, String defaultValue) {
return PreferenceManager.getDefaultSharedPreferences(context).getString(key, defaultValue);
}