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()));
+ public static class CustomDefaultPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
+
+ private static final String INPUT_TYPE = "input_type";
+
+ private Spinner spinner;
+ private EditText customText;
+ private TextView defaultLabel;
+
+ public static CustomDefaultPreferenceDialogFragmentCompat newInstance(String key) {
+ CustomDefaultPreferenceDialogFragmentCompat fragment = new CustomDefaultPreferenceDialogFragmentCompat();
+ Bundle b = new Bundle(1);
+ b.putString(PreferenceDialogFragmentCompat.ARG_KEY, key);
+ fragment.setArguments(b);
+ return fragment;
}
@Override
- public void onNothingSelected(AdapterView> parent) {
- defaultLabel.setVisibility(View.VISIBLE);
- customText.setVisibility(View.GONE);
+ protected void onBindDialogView(@NonNull View view) {
+ Log.w(TAG, "onBindDialogView");
+ super.onBindDialogView(view);
+
+ CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
+
+ 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(preference.inputType);
+ this.customText.addTextChangedListener(new TextValidator());
+ this.customText.setText(preference.getCustomValue());
+ this.spinner.setOnItemSelectedListener(new SelectionLister());
+ this.defaultLabel.setText(preference.getPrettyPrintValue(preference.defaultValue));
}
- }
- private class TextValidator implements TextWatcher {
@Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ public Dialog onCreateDialog(Bundle instanceState) {
+ Dialog dialog = super.onCreateDialog(instanceState);
+
+ CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
+
+ if (preference.isCustom()) spinner.setSelection(1, true);
+ else spinner.setSelection(0, true);
+
+ return dialog;
+ }
@Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
+ public void onDialogClosed(boolean positiveResult) {
+ CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
- @Override
- public void afterTextChanged(Editable s) {
- if (spinner.getSelectedItemPosition() == 1) {
- positiveButton.setEnabled(validator.isValid(s.toString()));
+ if (positiveResult) {
+ if (spinner != null) preference.setCustom(spinner.getSelectedItemPosition() == 1);
+ if (customText != null) preference.setCustomValue(customText.getText().toString());
+
+ preference.setSummary(preference.getSummary());
}
}
- }
- protected interface CustomPreferenceValidator {
- public boolean isValid(String value);
- }
-
- private static class NullValidator implements CustomPreferenceValidator {
- @Override
- public boolean isValid(String value) {
- return true;
+ interface CustomPreferenceValidator {
+ public boolean isValid(String value);
}
- }
- public static class UriValidator implements CustomPreferenceValidator {
- @Override
- public boolean isValid(String value) {
- if (TextUtils.isEmpty(value)) return true;
-
- try {
- new URI(value);
+ private static class NullValidator implements CustomPreferenceValidator {
+ @Override
+ public boolean isValid(String 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;
+ private class TextValidator implements TextWatcher {
- try {
- URI uri = new URI(null, value, null, null);
- return true;
- } catch (URISyntaxException mue) {
- return false;
+ @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) {
+ CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
+
+ if (spinner.getSelectedItemPosition() == 1) {
+ Button positiveButton = ((AlertDialog)getDialog()).getButton(AlertDialog.BUTTON_POSITIVE);
+ positiveButton.setEnabled(preference.validator.isValid(s.toString()));
+ }
}
}
- }
- public static class PortValidator implements CustomPreferenceValidator {
- @Override
- public boolean isValid(String value) {
- try {
- Integer.parseInt(value);
- return true;
- } catch (NumberFormatException e) {
- return false;
+ 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;
+ }
+ }
+ }
+
+ private class SelectionLister implements AdapterView.OnItemSelectedListener {
+
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
+ Button positiveButton = ((AlertDialog)getDialog()).getButton(AlertDialog.BUTTON_POSITIVE);
+
+ defaultLabel.setVisibility(position == 0 ? View.VISIBLE : View.GONE);
+ customText.setVisibility(position == 0 ? View.GONE : View.VISIBLE);
+ positiveButton.setEnabled(position == 0 || preference.validator.isValid(customText.getText().toString()));
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+ defaultLabel.setVisibility(View.VISIBLE);
+ customText.setVisibility(View.GONE);
+ }
+ }
+
}
diff --git a/src/org/thoughtcrime/securesms/components/SwitchPreferenceCompat.java b/src/org/thoughtcrime/securesms/components/SwitchPreferenceCompat.java
index 26da2811b7..2e7c4539d7 100644
--- a/src/org/thoughtcrime/securesms/components/SwitchPreferenceCompat.java
+++ b/src/org/thoughtcrime/securesms/components/SwitchPreferenceCompat.java
@@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.components;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
-import android.preference.CheckBoxPreference;
+import android.support.v7.preference.CheckBoxPreference;
import android.util.AttributeSet;
import org.thoughtcrime.securesms.R;
diff --git a/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java
index 60c26900f2..ee6ae9a7a0 100644
--- a/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java
@@ -7,13 +7,12 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.preference.PreferenceFragment;
import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.Preference;
import android.util.Log;
import android.widget.Toast;
@@ -35,7 +34,7 @@ import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedE
import java.io.IOException;
-public class AdvancedPreferenceFragment extends PreferenceFragment {
+public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
private static final String TAG = AdvancedPreferenceFragment.class.getSimpleName();
private static final String PUSH_MESSAGING_PREF = "pref_toggle_push_messaging";
@@ -49,7 +48,6 @@ public class AdvancedPreferenceFragment extends PreferenceFragment {
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
masterSecret = getArguments().getParcelable("master_secret");
- addPreferencesFromResource(R.xml.preferences_advanced);
initializeIdentitySelection();
@@ -58,6 +56,11 @@ public class AdvancedPreferenceFragment extends PreferenceFragment {
submitDebugLog.setSummary(getVersion(getActivity()));
}
+ @Override
+ public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_advanced);
+ }
+
@Override
public void onResume() {
super.onResume();
diff --git a/src/org/thoughtcrime/securesms/preferences/AdvancedRingtonePreference.java b/src/org/thoughtcrime/securesms/preferences/AdvancedRingtonePreference.java
index 56072f751e..f83b5cabc4 100644
--- a/src/org/thoughtcrime/securesms/preferences/AdvancedRingtonePreference.java
+++ b/src/org/thoughtcrime/securesms/preferences/AdvancedRingtonePreference.java
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.preferences;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
-import android.preference.RingtonePreference;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
@@ -36,4 +35,6 @@ public class AdvancedRingtonePreference extends RingtonePreference {
public void setCurrentRingtone(Uri uri) {
currentRingtone = uri;
}
+
+
}
diff --git a/src/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java
index 4df78f1405..b9a694f7b3 100644
--- a/src/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java
@@ -6,11 +6,11 @@ import android.content.Intent;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.PreferenceScreen;
-import android.support.v4.preference.PreferenceFragment;
+import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
import android.widget.Toast;
import com.doomonafireball.betterpickers.hmspicker.HmsPickerBuilder;
@@ -37,7 +37,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
- addPreferencesFromResource(R.xml.preferences_app_protection);
masterSecret = getArguments().getParcelable("master_secret");
disablePassphrase = (CheckBoxPreference) this.findPreference("pref_enable_passphrase_temporary");
@@ -52,6 +51,11 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
.setOnPreferenceChangeListener(new DisablePassphraseClickListener());
}
+ @Override
+ public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_app_protection);
+ }
+
@Override
public void onResume() {
super.onResume();
@@ -65,7 +69,7 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
private void initializePlatformSpecificOptions() {
PreferenceScreen preferenceScreen = getPreferenceScreen();
- Preference screenSecurityPreference = findPreference(TextSecurePreferences.SCREEN_SECURITY_PREF);
+ Preference screenSecurityPreference = findPreference(TextSecurePreferences.SCREEN_SECURITY_PREF);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
screenSecurityPreference != null) {
diff --git a/src/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java
index df77d18d02..4da08ec32d 100644
--- a/src/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java
@@ -2,7 +2,8 @@ package org.thoughtcrime.securesms.preferences;
import android.content.Context;
import android.os.Bundle;
-import android.preference.ListPreference;
+import android.support.annotation.Nullable;
+import android.support.v7.preference.ListPreference;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
@@ -15,7 +16,6 @@ public class AppearancePreferenceFragment extends ListSummaryPreferenceFragment
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
- addPreferencesFromResource(R.xml.preferences_appearance);
this.findPreference(TextSecurePreferences.THEME_PREF).setOnPreferenceChangeListener(new ListSummaryListener());
this.findPreference(TextSecurePreferences.LANGUAGE_PREF).setOnPreferenceChangeListener(new ListSummaryListener());
@@ -23,6 +23,11 @@ public class AppearancePreferenceFragment extends ListSummaryPreferenceFragment
initializeListSummary((ListPreference)findPreference(TextSecurePreferences.LANGUAGE_PREF));
}
+ @Override
+ public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_appearance);
+ }
+
@Override
public void onStart() {
super.onStart();
diff --git a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java
index a8bf74b14d..3e890af872 100644
--- a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java
@@ -3,15 +3,13 @@ package org.thoughtcrime.securesms.preferences;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
-import android.preference.EditTextPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.support.v4.preference.PreferenceFragment;
+import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.EditTextPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
import android.text.TextUtils;
import android.util.Log;
-import android.view.View;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
@@ -28,7 +26,6 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
- addPreferencesFromResource(R.xml.preferences_chats);
findPreference(TextSecurePreferences.MEDIA_DOWNLOAD_MOBILE_PREF)
.setOnPreferenceChangeListener(new MediaDownloadChangeListener());
@@ -47,6 +44,11 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.MESSAGE_BODY_TEXT_SIZE_PREF));
}
+ @Override
+ public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_chats);
+ }
+
@Override
public void onResume() {
super.onResume();
@@ -99,7 +101,7 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
}
}
- private class MediaDownloadChangeListener implements OnPreferenceChangeListener {
+ private class MediaDownloadChangeListener implements Preference.OnPreferenceChangeListener {
@SuppressWarnings("unchecked")
@Override public boolean onPreferenceChange(Preference preference, Object newValue) {
Log.w(TAG, "onPreferenceChange");
diff --git a/src/org/thoughtcrime/securesms/preferences/ColorPickerPreference.java b/src/org/thoughtcrime/securesms/preferences/ColorPickerPreference.java
new file mode 100644
index 0000000000..23f101fd65
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/preferences/ColorPickerPreference.java
@@ -0,0 +1,252 @@
+package org.thoughtcrime.securesms.preferences;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.DialogPreference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.ImageView;
+
+import com.takisoft.colorpicker.ColorPickerDialog;
+import com.takisoft.colorpicker.ColorPickerDialog.Size;
+import com.takisoft.colorpicker.ColorStateDrawable;
+
+import org.thoughtcrime.securesms.R;
+
+public class ColorPickerPreference extends DialogPreference {
+
+ private static final String TAG = ColorPickerPreference.class.getSimpleName();
+
+ private int[] colors;
+ private CharSequence[] colorDescriptions;
+ private int color;
+ private int columns;
+ private int size;
+ private boolean sortColors;
+
+ private ImageView colorWidget;
+ private OnPreferenceChangeListener listener;
+
+ public ColorPickerPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ColorPickerPreference, defStyleAttr, 0);
+
+ int colorsId = a.getResourceId(R.styleable.ColorPickerPreference_colors, R.array.color_picker_default_colors);
+
+ if (colorsId != 0) {
+ colors = context.getResources().getIntArray(colorsId);
+ }
+
+ colorDescriptions = a.getTextArray(R.styleable.ColorPickerPreference_colorDescriptions);
+ color = a.getColor(R.styleable.ColorPickerPreference_currentColor, 0);
+ columns = a.getInt(R.styleable.ColorPickerPreference_columns, 4);
+ size = a.getInt(R.styleable.ColorPickerPreference_colorSize, 2);
+ sortColors = a.getBoolean(R.styleable.ColorPickerPreference_sortColors, false);
+
+ a.recycle();
+
+ setWidgetLayoutResource(R.layout.preference_widget_color_swatch);
+ }
+
+ public ColorPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ @SuppressLint("RestrictedApi")
+ public ColorPickerPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
+ android.R.attr.dialogPreferenceStyle));
+ }
+
+ public ColorPickerPreference(Context context) {
+ this(context, null);
+ }
+
+ @Override
+ public void setOnPreferenceChangeListener(OnPreferenceChangeListener listener) {
+ super.setOnPreferenceChangeListener(listener);
+ this.listener = listener;
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ colorWidget = (ImageView) holder.findViewById(R.id.color_picker_widget);
+ setColorOnWidget(color);
+ }
+
+ private void setColorOnWidget(int color) {
+ if (colorWidget == null) {
+ return;
+ }
+
+ Drawable[] colorDrawable = new Drawable[]
+ {ContextCompat.getDrawable(getContext(), R.drawable.colorpickerpreference_pref_swatch)};
+ colorWidget.setImageDrawable(new ColorStateDrawable(colorDrawable, color));
+ }
+
+ /**
+ * Returns the current color.
+ *
+ * @return The current color.
+ */
+ public int getColor() {
+ return color;
+ }
+
+ /**
+ * Sets the current color.
+ *
+ * @param color The current color.
+ */
+ public void setColor(int color) {
+ setInternalColor(color, false);
+ }
+
+ /**
+ * Returns all of the available colors.
+ *
+ * @return The available colors.
+ */
+ public int[] getColors() {
+ return colors;
+ }
+
+ /**
+ * Sets the available colors.
+ *
+ * @param colors The available colors.
+ */
+ public void setColors(int[] colors) {
+ this.colors = colors;
+ }
+
+ /**
+ * Returns whether the available colors should be sorted automatically based on their HSV
+ * values.
+ *
+ * @return Whether the available colors should be sorted automatically based on their HSV
+ * values.
+ */
+ public boolean isSortColors() {
+ return sortColors;
+ }
+
+ /**
+ * Sets whether the available colors should be sorted automatically based on their HSV
+ * values. The sorting does not modify the order of the original colors supplied via
+ * {@link #setColors(int[])} or the XML attribute {@code app:colors}.
+ *
+ * @param sortColors Whether the available colors should be sorted automatically based on their
+ * HSV values.
+ */
+ public void setSortColors(boolean sortColors) {
+ this.sortColors = sortColors;
+ }
+
+ /**
+ * Returns the available colors' descriptions that can be used by accessibility services.
+ *
+ * @return The available colors' descriptions.
+ */
+ public CharSequence[] getColorDescriptions() {
+ return colorDescriptions;
+ }
+
+ /**
+ * Sets the available colors' descriptions that can be used by accessibility services.
+ *
+ * @param colorDescriptions The available colors' descriptions.
+ */
+ public void setColorDescriptions(CharSequence[] colorDescriptions) {
+ this.colorDescriptions = colorDescriptions;
+ }
+
+ /**
+ * Returns the number of columns to be used in the picker dialog for displaying the available
+ * colors. If the value is less than or equals to 0, the number of columns will be determined
+ * automatically by the system using FlexboxLayoutManager.
+ *
+ * @return The number of columns to be used in the picker dialog.
+ * @see com.google.android.flexbox.FlexboxLayoutManager
+ */
+ public int getColumns() {
+ return columns;
+ }
+
+ /**
+ * Sets the number of columns to be used in the picker dialog for displaying the available
+ * colors. If the value is less than or equals to 0, the number of columns will be determined
+ * automatically by the system using FlexboxLayoutManager.
+ *
+ * @param columns The number of columns to be used in the picker dialog. Use 0 to set it to
+ * 'auto' mode.
+ * @see com.google.android.flexbox.FlexboxLayoutManager
+ */
+ public void setColumns(int columns) {
+ this.columns = columns;
+ }
+
+ /**
+ * Returns the size of the color swatches in the dialog. It can be either
+ * {@link ColorPickerDialog#SIZE_SMALL} or {@link ColorPickerDialog#SIZE_LARGE}.
+ *
+ * @return The size of the color swatches in the dialog.
+ * @see ColorPickerDialog#SIZE_SMALL
+ * @see ColorPickerDialog#SIZE_LARGE
+ */
+ @Size
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Sets the size of the color swatches in the dialog. It can be either
+ * {@link ColorPickerDialog#SIZE_SMALL} or {@link ColorPickerDialog#SIZE_LARGE}.
+ *
+ * @param size The size of the color swatches in the dialog. It can be either
+ * {@link ColorPickerDialog#SIZE_SMALL} or {@link ColorPickerDialog#SIZE_LARGE}.
+ * @see ColorPickerDialog#SIZE_SMALL
+ * @see ColorPickerDialog#SIZE_LARGE
+ */
+ public void setSize(@Size int size) {
+ this.size = size;
+ }
+
+ private void setInternalColor(int color, boolean force) {
+ int oldColor = getPersistedInt(0);
+
+ boolean changed = oldColor != color;
+
+ if (changed || force) {
+ this.color = color;
+
+ persistInt(color);
+
+ setColorOnWidget(color);
+
+ if (listener != null) listener.onPreferenceChange(this, color);
+ notifyChanged();
+ }
+ }
+
+ @Override
+ protected Object onGetDefaultValue(TypedArray a, int index) {
+ return a.getString(index);
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValueObj) {
+ final String defaultValue = (String) defaultValueObj;
+ setInternalColor(restoreValue ? getPersistedInt(0) : (!TextUtils.isEmpty(defaultValue) ? Color.parseColor(defaultValue) : 0), true);
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/preferences/ColorPickerPreferenceDialogFragmentCompat.java b/src/org/thoughtcrime/securesms/preferences/ColorPickerPreferenceDialogFragmentCompat.java
new file mode 100644
index 0000000000..1ccb9fbc38
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/preferences/ColorPickerPreferenceDialogFragmentCompat.java
@@ -0,0 +1,64 @@
+package org.thoughtcrime.securesms.preferences;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.preference.PreferenceDialogFragmentCompat;
+
+import com.takisoft.colorpicker.ColorPickerDialog;
+import com.takisoft.colorpicker.OnColorSelectedListener;
+
+public class ColorPickerPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat implements OnColorSelectedListener {
+
+ private int pickedColor;
+
+ public static ColorPickerPreferenceDialogFragmentCompat newInstance(String key) {
+ ColorPickerPreferenceDialogFragmentCompat fragment = new ColorPickerPreferenceDialogFragmentCompat();
+ Bundle b = new Bundle(1);
+ b.putString(PreferenceDialogFragmentCompat.ARG_KEY, key);
+ fragment.setArguments(b);
+ return fragment;
+ }
+
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ ColorPickerPreference pref = getColorPickerPreference();
+
+ ColorPickerDialog.Params params = new ColorPickerDialog.Params.Builder(getContext())
+ .setSelectedColor(pref.getColor())
+ .setColors(pref.getColors())
+ .setColorContentDescriptions(pref.getColorDescriptions())
+ .setSize(pref.getSize())
+ .setSortColors(pref.isSortColors())
+ .setColumns(pref.getColumns())
+ .build();
+
+ ColorPickerDialog dialog = new ColorPickerDialog(getActivity(), this, params);
+ dialog.setTitle(pref.getDialogTitle());
+
+ return dialog;
+ }
+
+ @Override
+ public void onDialogClosed(boolean positiveResult) {
+ ColorPickerPreference preference = getColorPickerPreference();
+
+ if (positiveResult) {
+ preference.setColor(pickedColor);
+ }
+ }
+
+ @Override
+ public void onColorSelected(int color) {
+ this.pickedColor = color;
+
+ super.onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
+ }
+
+ ColorPickerPreference getColorPickerPreference() {
+ return (ColorPickerPreference) getPreference();
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/preferences/ColorPreference.java b/src/org/thoughtcrime/securesms/preferences/ColorPreference.java
deleted file mode 100644
index 219908125f..0000000000
--- a/src/org/thoughtcrime/securesms/preferences/ColorPreference.java
+++ /dev/null
@@ -1,294 +0,0 @@
-package org.thoughtcrime.securesms.preferences;
-
-/*
- * Copyright 2013 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.GridLayout;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.thoughtcrime.securesms.R;
-
-/**
- * A preference that allows the user to choose an application or shortcut.
- */
-public class ColorPreference extends Preference {
- private int[] mColorChoices = {};
- private int mValue = 0;
- private int mItemLayoutId = R.layout.color_preference_item;
- private int mNumColumns = 5;
- private View mPreviewView;
-
- public ColorPreference(Context context) {
- super(context);
- initAttrs(null, 0);
- }
-
- public ColorPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- initAttrs(attrs, 0);
- }
-
- public ColorPreference(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initAttrs(attrs, defStyle);
- }
-
- private void initAttrs(AttributeSet attrs, int defStyle) {
- TypedArray a = getContext().getTheme().obtainStyledAttributes(
- attrs, R.styleable.ColorPreference, defStyle, defStyle);
-
- try {
- mItemLayoutId = a.getResourceId(R.styleable.ColorPreference_itemLayout, mItemLayoutId);
- mNumColumns = a.getInteger(R.styleable.ColorPreference_numColumns, mNumColumns);
-// int choicesResId = a.getResourceId(R.styleable.ColorPreference_choices,
-// R.array.default_color_choice_values);
-// if (choicesResId > 0) {
-// String[] choices = a.getResources().getStringArray(choicesResId);
-// mColorChoices = new int[choices.length];
-// for (int i = 0; i < choices.length; i++) {
-// mColorChoices[i] = Color.parseColor(choices[i]);
-// }
-// }
-
- } finally {
- a.recycle();
- }
-
- setWidgetLayoutResource(mItemLayoutId);
- }
-
- @Override
- protected void onBindView(View view) {
- super.onBindView(view);
- mPreviewView = view.findViewById(R.id.color_view);
- setColorViewValue(mPreviewView, mValue, false);
- }
-
- public void setValue(int value) {
- if (callChangeListener(value)) {
- mValue = value;
- persistInt(value);
- notifyChanged();
- }
- }
-
- public void setChoices(int[] values) {
- mColorChoices = values;
- }
-
- @Override
- protected void onClick() {
- super.onClick();
-
- ColorDialogFragment fragment = ColorDialogFragment.newInstance();
- fragment.setPreference(this);
-
- ((AppCompatActivity) getContext()).getSupportFragmentManager().beginTransaction()
- .add(fragment, getFragmentTag())
- .commit();
- }
-
- @Override
- protected void onAttachedToActivity() {
- super.onAttachedToActivity();
-
- AppCompatActivity activity = (AppCompatActivity) getContext();
- ColorDialogFragment fragment = (ColorDialogFragment) activity
- .getSupportFragmentManager().findFragmentByTag(getFragmentTag());
- if (fragment != null) {
- // re-bind preference to fragment
- fragment.setPreference(this);
- }
- }
-
- @Override
- protected Object onGetDefaultValue(TypedArray a, int index) {
- return a.getInt(index, 0);
- }
-
- @Override
- protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
- setValue(restoreValue ? getPersistedInt(0) : (Integer) defaultValue);
- }
-
- public String getFragmentTag() {
- return "color_" + getKey();
- }
-
- public int getValue() {
- return mValue;
- }
-
- public static class ColorDialogFragment extends android.support.v4.app.DialogFragment {
- private ColorPreference mPreference;
- private GridLayout mColorGrid;
-
- public ColorDialogFragment() {
- }
-
- public static ColorDialogFragment newInstance() {
- return new ColorDialogFragment();
- }
-
- public void setPreference(ColorPreference preference) {
- mPreference = preference;
- repopulateItems();
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- repopulateItems();
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
- View rootView = layoutInflater.inflate(R.layout.color_preference_items, null);
-
- mColorGrid = (GridLayout) rootView.findViewById(R.id.color_grid);
- mColorGrid.setColumnCount(mPreference.mNumColumns);
- repopulateItems();
-
- return new AlertDialog.Builder(getActivity())
- .setView(rootView)
- .create();
- }
-
- private void repopulateItems() {
- if (mPreference == null || mColorGrid == null) {
- return;
- }
-
- Context context = mColorGrid.getContext();
- mColorGrid.removeAllViews();
- for (final int color : mPreference.mColorChoices) {
- View itemView = LayoutInflater.from(context)
- .inflate(R.layout.color_preference_item, mColorGrid, false);
-
- setColorViewValue(itemView.findViewById(R.id.color_view), color,
- color == mPreference.getValue());
- itemView.setClickable(true);
- itemView.setFocusable(true);
- itemView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mPreference.setValue(color);
- dismiss();
- }
- });
-
- mColorGrid.addView(itemView);
- }
-
- sizeDialog();
- }
-
- @Override
- public void onStart() {
- super.onStart();
- sizeDialog();
- }
-
- private void sizeDialog() {
-// if (mPreference == null || mColorGrid == null) {
-// return;
-// }
-//
-// Dialog dialog = getDialog();
-// if (dialog == null) {
-// return;
-// }
-//
-// final Resources res = mColorGrid.getContext().getResources();
-// DisplayMetrics dm = res.getDisplayMetrics();
-//
-// // Can't use Integer.MAX_VALUE here (weird issue observed otherwise on 4.2)
-// mColorGrid.measure(
-// View.MeasureSpec.makeMeasureSpec(dm.widthPixels, View.MeasureSpec.AT_MOST),
-// View.MeasureSpec.makeMeasureSpec(dm.heightPixels, View.MeasureSpec.AT_MOST));
-// int width = mColorGrid.getMeasuredWidth();
-// int height = mColorGrid.getMeasuredHeight();
-//
-// int extraPadding = res.getDimensionPixelSize(R.dimen.color_grid_extra_padding);
-//
-// width += extraPadding;
-// height += extraPadding;
-//
-// dialog.getWindow().setLayout(width, height);
- }
- }
-
- private static void setColorViewValue(View view, int color, boolean selected) {
- if (view instanceof ImageView) {
- ImageView imageView = (ImageView) view;
- Resources res = imageView.getContext().getResources();
-
- Drawable currentDrawable = imageView.getDrawable();
- GradientDrawable colorChoiceDrawable;
- if (currentDrawable instanceof GradientDrawable) {
- // Reuse drawable
- colorChoiceDrawable = (GradientDrawable) currentDrawable;
- } else {
- colorChoiceDrawable = new GradientDrawable();
- colorChoiceDrawable.setShape(GradientDrawable.OVAL);
- }
-
- // Set stroke to dark version of color
-// int darkenedColor = Color.rgb(
-// Color.red(color) * 192 / 256,
-// Color.green(color) * 192 / 256,
-// Color.blue(color) * 192 / 256);
-
- colorChoiceDrawable.setColor(color);
-// colorChoiceDrawable.setStroke((int) TypedValue.applyDimension(
-// TypedValue.COMPLEX_UNIT_DIP, 2, res.getDisplayMetrics()), darkenedColor);
-
- Drawable drawable = colorChoiceDrawable;
- if (selected) {
- BitmapDrawable checkmark = (BitmapDrawable) res.getDrawable(R.drawable.check);
- checkmark.setGravity(Gravity.CENTER);
- drawable = new LayerDrawable(new Drawable[]{
- colorChoiceDrawable,
- checkmark});
- }
-
- imageView.setImageDrawable(drawable);
-
- } else if (view instanceof TextView) {
- ((TextView) view).setTextColor(color);
- }
- }
-}
diff --git a/src/org/thoughtcrime/securesms/preferences/CorrectedPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/CorrectedPreferenceFragment.java
index 220aabb757..e25ffa60ab 100644
--- a/src/org/thoughtcrime/securesms/preferences/CorrectedPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/CorrectedPreferenceFragment.java
@@ -2,10 +2,19 @@ package org.thoughtcrime.securesms.preferences;
import android.os.Bundle;
-import android.support.v4.preference.PreferenceFragment;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
import android.view.View;
-public class CorrectedPreferenceFragment extends PreferenceFragment {
+import org.thoughtcrime.securesms.components.CustomDefaultPreference;
+
+public abstract class CorrectedPreferenceFragment extends PreferenceFragmentCompat {
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ }
@Override
public void onActivityCreated(Bundle savedInstanceState) {
@@ -15,4 +24,25 @@ public class CorrectedPreferenceFragment extends PreferenceFragment {
if (lv != null) lv.setPadding(0, 0, 0, 0);
}
+ @Override
+ public void onDisplayPreferenceDialog(Preference preference) {
+ DialogFragment dialogFragment = null;
+
+ if (preference instanceof RingtonePreference) {
+ dialogFragment = RingtonePreferenceDialogFragmentCompat.newInstance(preference.getKey());
+ } else if (preference instanceof ColorPickerPreference) {
+ dialogFragment = ColorPickerPreferenceDialogFragmentCompat.newInstance(preference.getKey());
+ } else if (preference instanceof CustomDefaultPreference) {
+ dialogFragment = CustomDefaultPreference.CustomDefaultPreferenceDialogFragmentCompat.newInstance(preference.getKey());
+ }
+
+ if (dialogFragment != null) {
+ dialogFragment.setTargetFragment(this, 0);
+ dialogFragment.show(getFragmentManager(), "android.support.v7.preference.PreferenceFragment.DIALOG");
+ } else {
+ super.onDisplayPreferenceDialog(preference);
+ }
+ }
+
+
}
diff --git a/src/org/thoughtcrime/securesms/preferences/LEDColorListPreference.java b/src/org/thoughtcrime/securesms/preferences/LEDColorListPreference.java
index 774e8bdb8d..107875ada5 100644
--- a/src/org/thoughtcrime/securesms/preferences/LEDColorListPreference.java
+++ b/src/org/thoughtcrime/securesms/preferences/LEDColorListPreference.java
@@ -18,11 +18,10 @@ package org.thoughtcrime.securesms.preferences;
import android.content.Context;
import android.graphics.drawable.GradientDrawable;
-import android.preference.ListPreference;
import android.support.annotation.NonNull;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
import android.widget.ImageView;
import org.thoughtcrime.securesms.R;
@@ -69,8 +68,8 @@ public class LEDColorListPreference extends ListPreference {
}
@Override
- protected void onBindView(View view) {
- super.onBindView(view);
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
this.colorImageView = (ImageView)view.findViewById(R.id.color_view);
setPreviewColor(getValue());
}
diff --git a/src/org/thoughtcrime/securesms/preferences/LedBlinkPatternListPreference.java b/src/org/thoughtcrime/securesms/preferences/LedBlinkPatternListPreference.java
deleted file mode 100644
index 529274aabd..0000000000
--- a/src/org/thoughtcrime/securesms/preferences/LedBlinkPatternListPreference.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/**
- * Copyright (C) 2011 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 .
- */
-package org.thoughtcrime.securesms.preferences;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Parcelable;
-import android.preference.ListPreference;
-import android.support.v7.app.AlertDialog;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import org.thoughtcrime.securesms.R;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
-
-/**
- * List preference for LED blink pattern notification.
- *
- * @author Moxie Marlinspike
- */
-
-public class LedBlinkPatternListPreference extends SignalListPreference implements OnSeekBarChangeListener {
-
- private Context context;
- private SeekBar seekBarOn;
- private SeekBar seekBarOff;
-
- private TextView seekBarOnLabel;
- private TextView seekBarOffLabel;
-
- private boolean dialogInProgress;
-
- public LedBlinkPatternListPreference(Context context) {
- super(context);
- this.context = context;
- }
-
- public LedBlinkPatternListPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- this.context = context;
- }
-
- @Override
- protected void onDialogClosed(boolean positiveResult) {
- super.onDialogClosed(positiveResult);
-
- if (positiveResult) {
- String blinkPattern = TextSecurePreferences.getNotificationLedPattern(context);
- if (blinkPattern.equals("custom")) showDialog();
- }
- }
-
- private void initializeSeekBarValues() {
- String patternString = TextSecurePreferences.getNotificationLedPatternCustom(context);
- String[] patternArray = patternString.split(",");
- seekBarOn.setProgress(Integer.parseInt(patternArray[0]));
- seekBarOff.setProgress(Integer.parseInt(patternArray[1]));
- }
-
- private void initializeDialog(View view) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setIconAttribute(R.attr.dialog_info_icon);
- builder.setTitle(R.string.preferences__pref_led_blink_custom_pattern_title);
- builder.setView(view);
- builder.setOnCancelListener(new CustomDialogCancelListener());
- builder.setNegativeButton(android.R.string.cancel, new CustomDialogCancelListener());
- builder.setPositiveButton(android.R.string.ok, new CustomDialogClickListener());
- builder.show();
- }
-
- private void showDialog() {
- LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.led_pattern_dialog, null);
-
- this.seekBarOn = (SeekBar)view.findViewById(R.id.SeekBarOn);
- this.seekBarOff = (SeekBar)view.findViewById(R.id.SeekBarOff);
- this.seekBarOnLabel = (TextView)view.findViewById(R.id.SeekBarOnMsLabel);
- this.seekBarOffLabel = (TextView)view.findViewById(R.id.SeekBarOffMsLabel);
-
- this.seekBarOn.setOnSeekBarChangeListener(this);
- this.seekBarOff.setOnSeekBarChangeListener(this);
-
- initializeSeekBarValues();
- initializeDialog(view);
-
- dialogInProgress = true;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- super.onRestoreInstanceState(state);
- if (dialogInProgress) {
- showDialog();
- }
- }
-
- @Override
- protected View onCreateDialogView() {
- dialogInProgress = false;
- return super.onCreateDialogView();
- }
-
- public void onProgressChanged(SeekBar seekbar, int progress, boolean fromTouch) {
- if (seekbar.equals(seekBarOn)) {
- seekBarOnLabel.setText(Integer.toString(progress));
- } else if (seekbar.equals(seekBarOff)) {
- seekBarOffLabel.setText(Integer.toString(progress));
- }
- }
-
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
-
- private class CustomDialogCancelListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
- public void onClick(DialogInterface dialog, int which) {
- dialogInProgress = false;
- }
-
- public void onCancel(DialogInterface dialog) {
- dialogInProgress = false;
- }
- }
-
- private class CustomDialogClickListener implements DialogInterface.OnClickListener {
-
- public void onClick(DialogInterface dialog, int which) {
- String pattern = seekBarOnLabel.getText() + "," + seekBarOffLabel.getText();
- dialogInProgress = false;
-
- TextSecurePreferences.setNotificationLedPatternCustom(context, pattern);
- Toast.makeText(context, R.string.preferences__pref_led_blink_custom_pattern_set, Toast.LENGTH_LONG).show();
- }
-
- }
-
-}
diff --git a/src/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java
index ebb6375933..274fedeae9 100644
--- a/src/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java
@@ -1,8 +1,8 @@
package org.thoughtcrime.securesms.preferences;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.support.v4.preference.PreferenceFragment;
+
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
import org.thoughtcrime.securesms.R;
diff --git a/src/org/thoughtcrime/securesms/preferences/MmsPreferencesFragment.java b/src/org/thoughtcrime/securesms/preferences/MmsPreferencesFragment.java
index e04547a570..a7f86016c6 100644
--- a/src/org/thoughtcrime/securesms/preferences/MmsPreferencesFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/MmsPreferencesFragment.java
@@ -19,7 +19,7 @@ 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.support.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
@@ -33,19 +33,23 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.IOException;
-public class MmsPreferencesFragment extends PreferenceFragment {
+public class MmsPreferencesFragment extends CorrectedPreferenceFragment {
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 onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_manual_mms);
+ }
+
@Override
public void onResume() {
super.onResume();
@@ -74,15 +78,15 @@ public class MmsPreferencesFragment extends PreferenceFragment {
@Override
protected void onPostExecute(LegacyMmsConnection.Apn apnDefaults) {
((CustomDefaultPreference)findPreference(TextSecurePreferences.MMSC_HOST_PREF))
- .setValidator(new CustomDefaultPreference.UriValidator())
+ .setValidator(new CustomDefaultPreference.CustomDefaultPreferenceDialogFragmentCompat.UriValidator())
.setDefaultValue(apnDefaults.getMmsc());
((CustomDefaultPreference)findPreference(TextSecurePreferences.MMSC_PROXY_HOST_PREF))
- .setValidator(new CustomDefaultPreference.HostnameValidator())
+ .setValidator(new CustomDefaultPreference.CustomDefaultPreferenceDialogFragmentCompat.HostnameValidator())
.setDefaultValue(apnDefaults.getProxy());
((CustomDefaultPreference)findPreference(TextSecurePreferences.MMSC_PROXY_PORT_PREF))
- .setValidator(new CustomDefaultPreference.PortValidator())
+ .setValidator(new CustomDefaultPreference.CustomDefaultPreferenceDialogFragmentCompat.PortValidator())
.setDefaultValue(apnDefaults.getPort());
((CustomDefaultPreference)findPreference(TextSecurePreferences.MMSC_USERNAME_PREF))
diff --git a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java
index 45067ee796..65812f820c 100644
--- a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java
@@ -7,11 +7,12 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceManager;
-import android.preference.RingtonePreference;
+import android.support.annotation.Nullable;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceManager;
import android.text.TextUtils;
+import android.util.Log;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
@@ -21,13 +22,14 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragment {
+ private static final String TAG = NotificationsPreferenceFragment.class.getSimpleName();
+
private MasterSecret masterSecret;
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
masterSecret = getArguments().getParcelable("master_secret");
- addPreferencesFromResource(R.xml.preferences_notifications);
this.findPreference(TextSecurePreferences.LED_COLOR_PREF)
.setOnPreferenceChangeListener(new ListSummaryListener());
@@ -47,7 +49,12 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.REPEAT_ALERTS_PREF));
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF));
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF));
- initializeRingtoneSummary((RingtonePreference) findPreference(TextSecurePreferences.RINGTONE_PREF));
+ initializeRingtoneSummary((AdvancedRingtonePreference) findPreference(TextSecurePreferences.RINGTONE_PREF));
+ }
+
+ @Override
+ public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_notifications);
}
@Override
@@ -59,12 +66,12 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
private class RingtoneSummaryListener implements Preference.OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
- String value = (String) newValue;
+ Uri value = (Uri) newValue;
- if (TextUtils.isEmpty(value)) {
+ if (value == null) {
preference.setSummary(R.string.preferences__silent);
} else {
- Ringtone tone = RingtoneManager.getRingtone(getActivity(), Uri.parse(value));
+ Ringtone tone = RingtoneManager.getRingtone(getActivity(), value);
if (tone != null) {
preference.setSummary(tone.getTitle(getActivity()));
}
@@ -74,12 +81,13 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
}
}
- private void initializeRingtoneSummary(RingtonePreference pref) {
- RingtoneSummaryListener listener =
- (RingtoneSummaryListener) pref.getOnPreferenceChangeListener();
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
+ private void initializeRingtoneSummary(AdvancedRingtonePreference pref) {
+ RingtoneSummaryListener listener = (RingtoneSummaryListener) pref.getOnPreferenceChangeListener();
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
+ String encodedUri = sharedPreferences.getString(pref.getKey(), null);
+ Uri uri = !TextUtils.isEmpty(encodedUri) ? Uri.parse(encodedUri) : null;
- listener.onPreferenceChange(pref, sharedPreferences.getString(pref.getKey(), ""));
+ listener.onPreferenceChange(pref, uri);
}
public static CharSequence getSummary(Context context) {
diff --git a/src/org/thoughtcrime/securesms/preferences/ProfilePreference.java b/src/org/thoughtcrime/securesms/preferences/ProfilePreference.java
index 11060d6131..b0ade21558 100644
--- a/src/org/thoughtcrime/securesms/preferences/ProfilePreference.java
+++ b/src/org/thoughtcrime/securesms/preferences/ProfilePreference.java
@@ -5,10 +5,11 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
-import android.preference.Preference;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
@@ -16,7 +17,6 @@ import android.widget.ImageView;
import android.widget.TextView;
import org.thoughtcrime.securesms.R;
-import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
@@ -55,12 +55,11 @@ public class ProfilePreference extends Preference {
}
@Override
- protected void onBindView(View view) {
- super.onBindView(view);
-
- avatarView = ViewUtil.findById(view, R.id.avatar);
- profileNameView = ViewUtil.findById(view, R.id.profile_name);
- profileNumberView = ViewUtil.findById(view, R.id.number);
+ public void onBindViewHolder(PreferenceViewHolder viewHolder) {
+ super.onBindViewHolder(viewHolder);
+ avatarView = (ImageView)viewHolder.findViewById(R.id.avatar);
+ profileNameView = (TextView)viewHolder.findViewById(R.id.profile_name);
+ profileNumberView = (TextView)viewHolder.findViewById(R.id.number);
refresh();
}
diff --git a/src/org/thoughtcrime/securesms/preferences/RingtonePreference.java b/src/org/thoughtcrime/securesms/preferences/RingtonePreference.java
new file mode 100644
index 0000000000..a955196e09
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/preferences/RingtonePreference.java
@@ -0,0 +1,464 @@
+package org.thoughtcrime.securesms.preferences;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.MediaStore;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.DialogPreference;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+
+import org.thoughtcrime.securesms.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A {@link Preference} that displays a ringtone picker as a dialog.
+ *
+ * This preference will save the picked ringtone's URI as a string into the SharedPreferences. The
+ * saved URI can be fed directly into {@link RingtoneManager#getRingtone(Context, Uri)} to get the
+ * {@link Ringtone} instance that can be played.
+ *
+ * @see RingtoneManager
+ * @see Ringtone
+ */
+@SuppressWarnings("WeakerAccess,unused")
+public class RingtonePreference extends DialogPreference {
+ private static final int CUSTOM_RINGTONE_REQUEST_CODE = 0x9000;
+ private static final int WRITE_FILES_PERMISSION_REQUEST_CODE = 0x9001;
+
+ private int ringtoneType;
+ private boolean showDefault;
+ private boolean showSilent;
+ private boolean showAdd;
+
+ private Uri ringtoneUri;
+
+// private CharSequence summaryHasRingtone;
+// private CharSequence summary;
+
+ private int miscCustomRingtoneRequestCode = CUSTOM_RINGTONE_REQUEST_CODE;
+ private int miscPermissionRequestCode = WRITE_FILES_PERMISSION_REQUEST_CODE;
+
+ @IntDef({
+ RingtoneManager.TYPE_ALL,
+ RingtoneManager.TYPE_ALARM,
+ RingtoneManager.TYPE_NOTIFICATION,
+ RingtoneManager.TYPE_RINGTONE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ protected @interface RingtoneType {
+ }
+
+// static {
+// PreferenceFragmentCompat.addDialogPreference(RingtonePreference.class, RingtonePreferenceDialogFragmentCompat.class);
+// }
+
+ public RingtonePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ android.preference.RingtonePreference proxyPreference;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ proxyPreference = new android.preference.RingtonePreference(context, attrs, defStyleAttr, defStyleRes);
+ } else {
+ proxyPreference = new android.preference.RingtonePreference(context, attrs, defStyleAttr);
+ }
+
+ ringtoneType = proxyPreference.getRingtoneType();
+ showDefault = proxyPreference.getShowDefault();
+ showSilent = proxyPreference.getShowSilent();
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RingtonePreference, defStyleAttr, 0);
+ showAdd = a.getBoolean(R.styleable.RingtonePreference_showAdd, true);
+// summaryHasRingtone = a.getText(R.styleable.RingtonePreference_summaryHasRingtone);
+ a.recycle();
+
+// summary = super.getSummary();
+ }
+
+ public RingtonePreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ @SuppressLint("RestrictedApi")
+ public RingtonePreference(Context context, AttributeSet attrs) {
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
+ android.R.attr.dialogPreferenceStyle));
+ }
+
+ public RingtonePreference(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Returns the sound type(s) that are shown in the picker.
+ *
+ * @return The sound type(s) that are shown in the picker.
+ * @see #setRingtoneType(int)
+ */
+ @RingtoneType
+ public int getRingtoneType() {
+ return ringtoneType;
+ }
+
+ /**
+ * Sets the sound type(s) that are shown in the picker. See {@link RingtoneManager} for the
+ * possible values.
+ *
+ * @param ringtoneType The sound type(s) that are shown in the picker.
+ */
+ public void setRingtoneType(@RingtoneType int ringtoneType) {
+ this.ringtoneType = ringtoneType;
+ }
+
+ /**
+ * Returns whether to a show an item for the default sound/ringtone.
+ *
+ * @return Whether to show an item for the default sound/ringtone.
+ */
+ public boolean getShowDefault() {
+ return showDefault;
+ }
+
+ /**
+ * Sets whether to show an item for the default sound/ringtone. The default
+ * to use will be deduced from the sound type(s) being shown.
+ *
+ * @param showDefault Whether to show the default or not.
+ */
+ public void setShowDefault(boolean showDefault) {
+ this.showDefault = showDefault;
+ }
+
+ /**
+ * Returns whether to a show an item for 'None'.
+ *
+ * @return Whether to show an item for 'None'.
+ */
+ public boolean getShowSilent() {
+ return showSilent;
+ }
+
+ /**
+ * Sets whether to show an item for 'None'.
+ *
+ * @param showSilent Whether to show 'None'.
+ */
+ public void setShowSilent(boolean showSilent) {
+ this.showSilent = showSilent;
+ }
+
+ /**
+ * Returns whether to a show an item for 'Add new ringtone'.
+ *
+ * Note that this requires {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}. If it's
+ * not supplied in the manifest, the item won't be displayed.
+ *
+ * @return Whether to show an item for 'Add new ringtone'.
+ */
+ public boolean getShowAdd() {
+ return showAdd;
+ }
+
+ boolean shouldShowAdd() {
+ if (showAdd) {
+ try {
+ PackageInfo pInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), PackageManager.GET_PERMISSIONS);
+ String[] permissions = pInfo.requestedPermissions;
+ for (String permission : permissions) {
+ if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
+ return true;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Sets whether to show an item for 'Add new ringtone'.
+ *
+ * Note that this requires {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}. If it's
+ * not supplied in the manifest, the item won't be displayed.
+ *
+ * @param showAdd Whether to show 'Add new ringtone'.
+ */
+ public void setShowAdd(boolean showAdd) {
+ this.showAdd = showAdd;
+ }
+
+ /**
+ * This request code will be used to start the file picker activity that the user can use
+ * to add new ringtones. The new ringtone will be delivered to
+ * {@link RingtonePreferenceDialogFragmentCompat#onActivityResult(int, int, Intent)}.
+ *
+ * The default value equals to {@link #CUSTOM_RINGTONE_REQUEST_CODE}
+ * ({@value #CUSTOM_RINGTONE_REQUEST_CODE}).
+ */
+ public int getCustomRingtoneRequestCode() {
+ return miscCustomRingtoneRequestCode;
+ }
+
+ /**
+ * Sets the request code that will be used to start the file picker activity that the user can
+ * use to add new ringtones. The new ringtone will be delivered to
+ * {@link RingtonePreferenceDialogFragmentCompat#onActivityResult(int, int, Intent)}.
+ *
+ * The default value equals to {@link #CUSTOM_RINGTONE_REQUEST_CODE}
+ * ({@value #CUSTOM_RINGTONE_REQUEST_CODE}).
+ *
+ * @param customRingtoneRequestCode the request code for the file picker
+ */
+ public void setCustomRingtoneRequestCode(int customRingtoneRequestCode) {
+ this.miscCustomRingtoneRequestCode = customRingtoneRequestCode;
+ }
+
+ /**
+ * This request code will be used to ask for user permission to save (write) new ringtone
+ * to one of the public external storage directories (only applies to API 23+). The result will
+ * be delivered to
+ * {@link RingtonePreferenceDialogFragmentCompat#onRequestPermissionsResult(int, String[], int[])}.
+ *
+ * The default value equals to {@link #WRITE_FILES_PERMISSION_REQUEST_CODE}
+ * ({@value #WRITE_FILES_PERMISSION_REQUEST_CODE}).
+ */
+ public int getPermissionRequestCode() {
+ return miscPermissionRequestCode;
+ }
+
+ /**
+ * Sets the request code that will be used to ask for user permission to save (write) new
+ * ringtone to one of the public external storage directories (only applies to API 23+). The
+ * result will be delivered to
+ * {@link RingtonePreferenceDialogFragmentCompat#onRequestPermissionsResult(int, String[], int[])}.
+ *
+ * The default value equals to {@link #WRITE_FILES_PERMISSION_REQUEST_CODE}
+ * ({@value #WRITE_FILES_PERMISSION_REQUEST_CODE}).
+ *
+ * @param permissionRequestCode the request code for the file picker
+ */
+ public void setPermissionRequestCode(int permissionRequestCode) {
+ this.miscPermissionRequestCode = permissionRequestCode;
+ }
+
+ public Uri getRingtone() {
+ return onRestoreRingtone();
+ }
+
+ public void setRingtone(Uri uri) {
+ setInternalRingtone(uri, false);
+ }
+
+ private void setInternalRingtone(Uri uri, boolean force) {
+ Uri oldUri = onRestoreRingtone();
+
+ final boolean changed = (oldUri != null && !oldUri.equals(uri)) || (uri != null && !uri.equals(oldUri));
+
+ if (changed || force) {
+ final boolean wasBlocking = shouldDisableDependents();
+
+ ringtoneUri = uri;
+ onSaveRingtone(uri);
+
+ final boolean isBlocking = shouldDisableDependents();
+
+ notifyChanged();
+
+ if (isBlocking != wasBlocking) {
+ notifyDependencyChange(isBlocking);
+ }
+ }
+ }
+
+ /**
+ * Called when a ringtone is chosen.
+ *
+ * By default, this saves the ringtone URI to the persistent storage as a
+ * string.
+ *
+ * @param ringtoneUri The chosen ringtone's {@link Uri}. Can be null.
+ */
+ protected void onSaveRingtone(Uri ringtoneUri) {
+ persistString(ringtoneUri != null ? ringtoneUri.toString() : "");
+ }
+
+ /**
+ * Called when the chooser is about to be shown and the current ringtone
+ * should be marked. Can return null to not mark any ringtone.
+ *
+ * By default, this restores the previous ringtone URI from the persistent
+ * storage.
+ *
+ * @return The ringtone to be marked as the current ringtone.
+ */
+ protected Uri onRestoreRingtone() {
+ final String uriString = getPersistedString(ringtoneUri == null ? null : ringtoneUri.toString());
+ return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
+ }
+
+ @Override
+ protected Object onGetDefaultValue(TypedArray a, int index) {
+ return a.getString(index);
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValueObj) {
+ final String defaultValue = (String) defaultValueObj;
+ setInternalRingtone(restoreValue ? onRestoreRingtone() : (!TextUtils.isEmpty(defaultValue) ? Uri.parse(defaultValue) : null), true);
+ }
+
+ @Override
+ public boolean shouldDisableDependents() {
+ return super.shouldDisableDependents() || onRestoreRingtone() == null;
+ }
+
+// /**
+// * Returns the summary of this Preference. If no {@code summaryHasRingtone} is set, this will be
+// * displayed if no ringtone is selected; otherwise the ringtone title will be used.
+// *
+// * @return The summary.
+// */
+// @Override
+// public CharSequence getSummary() {
+// if (ringtoneUri == null) {
+// return summary;
+// } else {
+// String ringtoneTitle = getRingtoneTitle();
+// if (summaryHasRingtone != null && ringtoneTitle != null) {
+// return String.format(summaryHasRingtone.toString(), ringtoneTitle);
+// } else if (ringtoneTitle != null) {
+// return ringtoneTitle;
+// } else {
+// return summary;
+// }
+// }
+// }
+
+// /**
+// * Sets the summary for this Preference with a CharSequence. If no {@code summaryHasRingtone} is
+// * set, this will be displayed if no ringtone is selected; otherwise the ringtone title will be
+// * used.
+// *
+// * @param summary The summary for the preference.
+// */
+// @Override
+// public void setSummary(CharSequence summary) {
+// super.setSummary(summary);
+// if (summary == null && this.summary != null) {
+// this.summary = null;
+// } else if (summary != null && !summary.equals(this.summary)) {
+// this.summary = summary.toString();
+// }
+// }
+
+// /**
+// * Returns the picked summary for this Preference. This will be displayed if the preference
+// * has a persisted value or the default value is set. If the summary
+// * has a {@linkplain java.lang.String#format String formatting}
+// * marker in it (i.e. "%s" or "%1$s"), then the current ringtone's title
+// * will be substituted in its place.
+// *
+// * @return The picked summary.
+// */
+// @Nullable
+// public CharSequence getSummaryHasRingtone() {
+// return summaryHasRingtone;
+// }
+
+// /**
+// * Sets the picked summary for this Preference with a resource ID. This will be displayed if the
+// * preference has a persisted value or the default value is set. If the summary
+// * has a {@linkplain java.lang.String#format String formatting}
+// * marker in it (i.e. "%s" or "%1$s"), then the current ringtone's title
+// * will be substituted in its place.
+// *
+// * @param resId The summary as a resource.
+// * @see #setSummaryHasRingtone(CharSequence)
+// */
+// public void setSummaryHasRingtone(@StringRes int resId) {
+// setSummaryHasRingtone(getContext().getString(resId));
+// }
+
+// /**
+// * Sets the picked summary for this Preference with a CharSequence. This will be displayed if
+// * the preference has a persisted value or the default value is set. If the summary
+// * has a {@linkplain java.lang.String#format String formatting}
+// * marker in it (i.e. "%s" or "%1$s"), then the current ringtone's title
+// * will be substituted in its place.
+// *
+// * @param summaryHasRingtone The summary for the preference.
+// */
+// public void setSummaryHasRingtone(@Nullable CharSequence summaryHasRingtone) {
+// if (summaryHasRingtone == null && this.summaryHasRingtone != null) {
+// this.summaryHasRingtone = null;
+// } else if (summaryHasRingtone != null && !summaryHasRingtone.equals(this.summaryHasRingtone)) {
+// this.summaryHasRingtone = summaryHasRingtone.toString();
+// }
+//
+// notifyChanged();
+// }
+
+ /**
+ * Returns the selected ringtone's title, or {@code null} if no ringtone is picked.
+ *
+ * @return The selected ringtone's title, or {@code null} if no ringtone is picked.
+ */
+ public String getRingtoneTitle() {
+ Context context = getContext();
+ ContentResolver cr = context.getContentResolver();
+ String[] projection = {MediaStore.MediaColumns.TITLE};
+
+ String ringtoneTitle = null;
+
+ if (ringtoneUri != null) {
+ int type = RingtoneManager.getDefaultType(ringtoneUri);
+
+ switch (type) {
+ case RingtoneManager.TYPE_ALL:
+ case RingtoneManager.TYPE_RINGTONE:
+ ringtoneTitle = context.getString(R.string.RingtonePreference_ringtone_default);
+ break;
+ case RingtoneManager.TYPE_ALARM:
+ ringtoneTitle = context.getString(R.string.RingtonePreference_alarm_sound_default);
+ break;
+ case RingtoneManager.TYPE_NOTIFICATION:
+ ringtoneTitle = context.getString(R.string.RingtonePreference_notification_sound_default);
+ break;
+ default:
+ try {
+ Cursor cursor = cr.query(ringtoneUri, projection, null, null, null);
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ ringtoneTitle = cursor.getString(0);
+ }
+
+ cursor.close();
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ return ringtoneTitle;
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/preferences/RingtonePreferenceDialogFragmentCompat.java b/src/org/thoughtcrime/securesms/preferences/RingtonePreferenceDialogFragmentCompat.java
new file mode 100644
index 0000000000..57f703c075
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/preferences/RingtonePreferenceDialogFragmentCompat.java
@@ -0,0 +1,583 @@
+package org.thoughtcrime.securesms.preferences;
+
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MergeCursor;
+import android.media.MediaScannerConnection;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.OpenableColumns;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.PreferenceDialogFragmentCompat;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.webkit.MimeTypeMap;
+import android.widget.CursorAdapter;
+import android.widget.HeaderViewListAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import org.thoughtcrime.securesms.R;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import static android.app.Activity.RESULT_OK;
+
+public class RingtonePreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
+ private static final String TAG = "RingtonePrefDialog";
+
+ private static final String CURSOR_DEFAULT_ID = "-2";
+ private static final String CURSOR_NONE_ID = "-1";
+
+ private int selectedIndex = -1;
+ private Cursor cursor;
+
+ private RingtoneManager ringtoneManager;
+ private Ringtone defaultRingtone;
+
+ public static RingtonePreferenceDialogFragmentCompat newInstance(String key) {
+ RingtonePreferenceDialogFragmentCompat fragment = new RingtonePreferenceDialogFragmentCompat();
+ Bundle b = new Bundle(1);
+ b.putString(PreferenceDialogFragmentCompat.ARG_KEY, key);
+ fragment.setArguments(b);
+ return fragment;
+ }
+
+ private RingtonePreference getRingtonePreference() {
+ return (RingtonePreference) getPreference();
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ stopPlaying();
+ }
+
+
+ private void stopPlaying() {
+ if (defaultRingtone != null && defaultRingtone.isPlaying()) {
+ defaultRingtone.stop();
+ }
+
+ if (ringtoneManager != null) {
+ ringtoneManager.stopPreviousRingtone();
+ }
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+
+ RingtonePreference ringtonePreference = getRingtonePreference();
+
+ createCursor(ringtonePreference.getRingtone());
+
+ String colTitle = cursor.getColumnName(RingtoneManager.TITLE_COLUMN_INDEX);
+
+ final Context context = getContext();
+
+ final int ringtoneType = ringtonePreference.getRingtoneType();
+ final boolean showDefault = ringtonePreference.getShowDefault();
+ final boolean showSilent = ringtonePreference.getShowSilent();
+ final Uri defaultUri;
+
+ if (showDefault) {
+ defaultUri = RingtoneManager.getDefaultUri(ringtoneType);
+ } else {
+ defaultUri = null;
+ }
+
+ builder
+ .setSingleChoiceItems(cursor, selectedIndex, colTitle, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ if (i < cursor.getCount()) {
+ selectedIndex = i;
+
+ int realIdx = i - (showDefault ? 1 : 0) - (showSilent ? 1 : 0);
+
+ stopPlaying();
+
+ if (showDefault && i == 0) {
+ if (defaultRingtone != null) {
+ defaultRingtone.play();
+ } else {
+ defaultRingtone = RingtoneManager.getRingtone(context, defaultUri);
+ if (defaultRingtone != null) {
+ defaultRingtone.play();
+ }
+ }
+ } else if (((showDefault && i == 1) || (!showDefault && i == 0)) && showSilent) {
+ ringtoneManager.stopPreviousRingtone(); // "playing" silence
+ } else {
+ Ringtone ringtone = ringtoneManager.getRingtone(realIdx);
+ ringtone.play();
+ }
+ } else {
+ newRingtone();
+ }
+ }
+ })
+ .setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ if (defaultRingtone != null) {
+ defaultRingtone.stop();
+ }
+
+ RingtonePreferenceDialogFragmentCompat.this.onDismiss(dialogInterface);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, this)
+ .setPositiveButton(android.R.string.ok, this);
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog dialog = (AlertDialog) super.onCreateDialog(savedInstanceState);
+
+ if (getRingtonePreference().shouldShowAdd()) {
+ ListView listView = dialog.getListView();
+ View addRingtoneView = LayoutInflater.from(listView.getContext()).inflate(R.layout.add_ringtone_item, listView, false);
+ listView.addFooterView(addRingtoneView);
+ }
+
+ return dialog;
+ }
+
+ @Override
+ public void onDialogClosed(boolean positiveResult) {
+ stopPlaying();
+
+ defaultRingtone = null;
+
+ final RingtonePreference preference = getRingtonePreference();
+ final boolean showDefault = preference.getShowDefault();
+ final boolean showSilent = preference.getShowSilent();
+
+ if (positiveResult && selectedIndex >= 0) {
+ final Uri uri;
+
+ if (showDefault && selectedIndex == 0) {
+ uri = RingtoneManager.getDefaultUri(preference.getRingtoneType());
+ } else if (((showDefault && selectedIndex == 1) || (!showDefault && selectedIndex == 0)) && showSilent) {
+ uri = null;
+ } else {
+ uri = ringtoneManager.getRingtoneUri(selectedIndex - (showDefault ? 1 : 0) - (showSilent ? 1 : 0));
+ }
+
+ if (preference.callChangeListener(uri)) {
+ preference.setRingtone(uri);
+ }
+ }
+ }
+
+ @NonNull
+ private Cursor createCursor(Uri ringtoneUri) {
+ RingtonePreference ringtonePreference = getRingtonePreference();
+ ringtoneManager = new RingtoneManager(getContext());
+
+ ringtoneManager.setType(ringtonePreference.getRingtoneType());
+ ringtoneManager.setStopPreviousRingtone(true);
+
+ Cursor ringtoneCursor = ringtoneManager.getCursor();
+
+ String colId = ringtoneCursor.getColumnName(RingtoneManager.ID_COLUMN_INDEX);
+ String colTitle = ringtoneCursor.getColumnName(RingtoneManager.TITLE_COLUMN_INDEX);
+
+ MatrixCursor extras = new MatrixCursor(new String[]{colId, colTitle});
+
+ final int ringtoneType = ringtonePreference.getRingtoneType();
+ final boolean showDefault = ringtonePreference.getShowDefault();
+ final boolean showSilent = ringtonePreference.getShowSilent();
+
+ if (showDefault) {
+ switch (ringtoneType) {
+ case RingtoneManager.TYPE_ALARM:
+ extras.addRow(new String[]{CURSOR_DEFAULT_ID, getString(R.string.RingtonePreference_alarm_sound_default)});
+ break;
+ case RingtoneManager.TYPE_NOTIFICATION:
+ extras.addRow(new String[]{CURSOR_DEFAULT_ID, getString(R.string.RingtonePreference_notification_sound_default)});
+ break;
+ case RingtoneManager.TYPE_RINGTONE:
+ case RingtoneManager.TYPE_ALL:
+ default:
+ extras.addRow(new String[]{CURSOR_DEFAULT_ID, getString(R.string.RingtonePreference_ringtone_default)});
+ break;
+ }
+ }
+
+ if (showSilent) {
+ extras.addRow(new String[]{CURSOR_NONE_ID, getString(R.string.RingtonePreference_ringtone_silent)});
+ }
+
+ selectedIndex = ringtoneManager.getRingtonePosition(ringtoneUri);
+ if (selectedIndex >= 0) {
+ selectedIndex += (showDefault ? 1 : 0) + (showSilent ? 1 : 0);
+ }
+
+ if (selectedIndex < 0 && showDefault) {
+ if (RingtoneManager.getDefaultType(ringtoneUri) != -1) {
+ selectedIndex = 0;
+ }
+ }
+
+ if (selectedIndex < 0 && showSilent) {
+ selectedIndex = showDefault ? 1 : 0;
+ }
+
+ Cursor[] cursors = {extras, ringtoneCursor};
+ return this.cursor = new MergeCursor(cursors);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == getRingtonePreference().getCustomRingtoneRequestCode()) {
+ if (resultCode == RESULT_OK) {
+ final Uri fileUri = data.getData();
+ final Context context = getContext();
+
+ final RingtonePreference ringtonePreference = getRingtonePreference();
+ final int ringtoneType = ringtonePreference.getRingtoneType();
+
+ // FIXME static field leak
+ @SuppressLint("StaticFieldLeak") final AsyncTask installTask = new AsyncTask() {
+ @Override
+ protected Cursor doInBackground(Uri... params) {
+ try {
+ Uri newUri = addCustomExternalRingtone(context, params[0], ringtoneType);
+
+ return createCursor(newUri);
+ } catch (IOException | IllegalArgumentException e) {
+ Log.e(TAG, "Unable to add new ringtone: ", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(final Cursor newCursor) {
+ if (newCursor != null) {
+ final ListView listView = ((AlertDialog) getDialog()).getListView();
+ final CursorAdapter adapter = ((CursorAdapter) ((HeaderViewListAdapter) listView.getAdapter()).getWrappedAdapter());
+ adapter.changeCursor(newCursor);
+
+ listView.setItemChecked(selectedIndex, true);
+ listView.setSelection(selectedIndex);
+ listView.clearFocus();
+ } else {
+ Toast.makeText(context, getString(R.string.RingtonePreference_unable_to_add_ringtone), Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+ installTask.execute(fileUri);
+ } else {
+ ListView listView = ((AlertDialog) getDialog()).getListView();
+ listView.setItemChecked(selectedIndex, true);
+ }
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (requestCode == getRingtonePreference().getPermissionRequestCode() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ newRingtone();
+ } else {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+
+ private void newRingtone() {
+ boolean hasPerm = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
+ if (hasPerm) {
+ final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
+ chooseFile.setType("audio/*");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ chooseFile.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"audio/*", "application/ogg"});
+ }
+ startActivityForResult(chooseFile, getRingtonePreference().getCustomRingtoneRequestCode());
+ } else {
+ requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, getRingtonePreference().getPermissionRequestCode());
+ }
+ }
+
+ @WorkerThread
+ public static Uri addCustomExternalRingtone(Context context, @NonNull Uri fileUri, final int type)
+ throws IOException {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new IOException("External storage is not mounted. Unable to install ringtones.");
+ }
+
+ if (ContentResolver.SCHEME_FILE.equals(fileUri.getScheme())) {
+ fileUri = Uri.fromFile(new File(fileUri.getPath()));
+ }
+
+ String mimeType = context.getContentResolver().getType(fileUri);
+
+ if (mimeType == null) {
+ String fileExtension = MimeTypeMap.getFileExtensionFromUrl(fileUri
+ .toString());
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ fileExtension.toLowerCase());
+ }
+
+ if (mimeType == null || !(mimeType.startsWith("audio/") || mimeType.equals("application/ogg"))) {
+ throw new IllegalArgumentException("Ringtone file must have MIME type \"audio/*\"."
+ + " Given file has MIME type \"" + mimeType + "\"");
+ }
+
+ final String subdirectory = getDirForType(type);
+
+ final File outFile = getUniqueExternalFile(context, subdirectory, getFileDisplayNameFromUri(context, fileUri), mimeType);
+
+ if (outFile != null) {
+ final InputStream input = context.getContentResolver().openInputStream(fileUri);
+ final OutputStream output = new FileOutputStream(outFile);
+
+ if (input != null) {
+ byte[] buffer = new byte[10240];
+
+ for (int len; (len = input.read(buffer)) != -1; ) {
+ output.write(buffer, 0, len);
+ }
+
+ input.close();
+ }
+
+ output.close();
+
+ NewRingtoneScanner scanner = null;
+ try {
+ scanner = new NewRingtoneScanner(context, outFile);
+ return scanner.take();
+ } catch (InterruptedException e) {
+ throw new IOException("Audio file failed to scan as a ringtone", e);
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private static String getDirForType(int type) {
+ switch (type) {
+ case RingtoneManager.TYPE_ALL:
+ case RingtoneManager.TYPE_RINGTONE:
+ return Environment.DIRECTORY_RINGTONES;
+ case RingtoneManager.TYPE_NOTIFICATION:
+ return Environment.DIRECTORY_NOTIFICATIONS;
+ case RingtoneManager.TYPE_ALARM:
+ return Environment.DIRECTORY_ALARMS;
+ default:
+ throw new IllegalArgumentException("Unsupported ringtone type: " + type);
+ }
+ }
+
+ private static String getFileDisplayNameFromUri(Context context, Uri uri) {
+ String scheme = uri.getScheme();
+
+ if (ContentResolver.SCHEME_FILE.equals(scheme)) {
+ return uri.getLastPathSegment();
+ } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
+
+ String[] projection = {OpenableColumns.DISPLAY_NAME};
+
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(uri, projection, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ // This will only happen if the Uri isn't either SCHEME_CONTENT or SCHEME_FILE, so we assume
+ // it already represents the file's name.
+ return uri.toString();
+ }
+
+ /**
+ * Creates a unique file in the specified external storage with the desired name. If the name is
+ * taken, the new file's name will have '(%d)' to avoid overwriting files.
+ *
+ * @param context {@link Context} to query the file name from.
+ * @param subdirectory One of the directories specified in {@link android.os.Environment}
+ * @param fileName desired name for the file.
+ * @param mimeType MIME type of the file to create.
+ * @return the File object in the storage, or null if an error occurs.
+ */
+ @Nullable
+ private static File getUniqueExternalFile(Context context, String subdirectory, String fileName,
+ String mimeType) {
+ File externalStorage = Environment.getExternalStoragePublicDirectory(subdirectory);
+ // Make sure the storage subdirectory exists
+ //noinspection ResultOfMethodCallIgnored
+ externalStorage.mkdirs();
+
+ File outFile;
+ try {
+ // Ensure the file has a unique name, as to not override any existing file
+ outFile = buildUniqueFile(externalStorage, mimeType, fileName);
+ } catch (FileNotFoundException e) {
+ // This might also be reached if the number of repeated files gets too high
+ Log.e(TAG, "Unable to get a unique file name: " + e);
+ return null;
+ }
+ return outFile;
+ }
+
+ @NonNull
+ private static File buildUniqueFile(File externalStorage, String mimeType, String fileName) throws FileNotFoundException {
+ final String[] parts = splitFileName(mimeType, fileName);
+
+ String name = parts[0];
+ String ext = (parts[1] != null) ? "." + parts[1] : "";
+
+ File file = new File(externalStorage, name + ext);
+ SecureRandom random = new SecureRandom();
+
+ int n = 0;
+ while (file.exists()) {
+ if (n++ >= 32) {
+ n = random.nextInt();
+ }
+ file = new File(externalStorage, name + " (" + n + ")" + ext);
+ }
+
+ return file;
+ }
+
+ @NonNull
+ public static String[] splitFileName(String mimeType, String displayName) {
+ String name;
+ String ext;
+
+ String mimeTypeFromExt;
+
+ // Extract requested extension from display name
+ final int lastDot = displayName.lastIndexOf('.');
+ if (lastDot >= 0) {
+ name = displayName.substring(0, lastDot);
+ ext = displayName.substring(lastDot + 1);
+ mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ ext.toLowerCase());
+ } else {
+ name = displayName;
+ ext = null;
+ mimeTypeFromExt = null;
+ }
+
+ if (mimeTypeFromExt == null) {
+ mimeTypeFromExt = "application/octet-stream";
+ }
+
+ final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
+ mimeType);
+ //noinspection StatementWithEmptyBody
+ if (TextUtils.equals(mimeType, mimeTypeFromExt) || TextUtils.equals(ext, extFromMimeType)) {
+ // Extension maps back to requested MIME type; allow it
+ } else {
+ // No match; insist that create file matches requested MIME
+ name = displayName;
+ ext = extFromMimeType;
+ }
+
+
+ if (ext == null) {
+ ext = "";
+ }
+
+ return new String[]{name, ext};
+ }
+
+ /**
+ * Creates a {@link android.media.MediaScannerConnection} to scan a ringtone file and add its
+ * information to the internal database.
+ *
+ * It uses a {@link java.util.concurrent.LinkedBlockingQueue} so that the caller can block until
+ * the scan is completed.
+ */
+ private static class NewRingtoneScanner implements Closeable, MediaScannerConnection.MediaScannerConnectionClient {
+ private MediaScannerConnection mMediaScannerConnection;
+ private File mFile;
+ private LinkedBlockingQueue mQueue = new LinkedBlockingQueue<>(1);
+
+ private NewRingtoneScanner(Context context, File file) {
+ mFile = file;
+ mMediaScannerConnection = new MediaScannerConnection(context, this);
+ mMediaScannerConnection.connect();
+ }
+
+ @Override
+ public void close() {
+ mMediaScannerConnection.disconnect();
+ }
+
+ @Override
+ public void onMediaScannerConnected() {
+ mMediaScannerConnection.scanFile(mFile.getAbsolutePath(), null);
+ }
+
+ @Override
+ public void onScanCompleted(String path, Uri uri) {
+ if (uri == null) {
+ // There was some issue with scanning. Delete the copied file so it is not oprhaned.
+ //noinspection ResultOfMethodCallIgnored
+ mFile.delete();
+ return;
+ }
+ try {
+ mQueue.put(uri);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Unable to put new ringtone Uri in queue", e);
+ }
+ }
+
+ private Uri take() throws InterruptedException {
+ return mQueue.take();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/preferences/SignalListPreference.java b/src/org/thoughtcrime/securesms/preferences/SignalListPreference.java
index 179589ff8c..5b5f07414e 100644
--- a/src/org/thoughtcrime/securesms/preferences/SignalListPreference.java
+++ b/src/org/thoughtcrime/securesms/preferences/SignalListPreference.java
@@ -3,8 +3,9 @@ package org.thoughtcrime.securesms.preferences;
import android.content.Context;
import android.os.Build;
-import android.preference.ListPreference;
import android.support.annotation.RequiresApi;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
@@ -45,8 +46,8 @@ public class SignalListPreference extends ListPreference {
}
@Override
- protected void onBindView(View view) {
- super.onBindView(view);
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
this.rightSummary = (TextView)view.findViewById(R.id.right_summary);
setSummary(this.summary);
diff --git a/src/org/thoughtcrime/securesms/preferences/SignalRingtonePreference.java b/src/org/thoughtcrime/securesms/preferences/SignalRingtonePreference.java
index 51aa0a4943..33257ee02c 100644
--- a/src/org/thoughtcrime/securesms/preferences/SignalRingtonePreference.java
+++ b/src/org/thoughtcrime/securesms/preferences/SignalRingtonePreference.java
@@ -5,6 +5,7 @@ import android.content.Context;
import android.os.Build;
import android.preference.RingtonePreference;
import android.support.annotation.RequiresApi;
+import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
@@ -42,8 +43,8 @@ public class SignalRingtonePreference extends AdvancedRingtonePreference {
}
@Override
- protected void onBindView(View view) {
- super.onBindView(view);
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
this.rightSummary = (TextView)view.findViewById(R.id.right_summary);
setSummary(summary);
diff --git a/src/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragment.java
index 5d1e317633..a3a4b2bd33 100644
--- a/src/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragment.java
@@ -6,14 +6,14 @@ import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.provider.Telephony;
+import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
-import android.support.v4.preference.PreferenceFragment;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
@@ -27,7 +27,7 @@ public class SmsMmsPreferenceFragment extends CorrectedPreferenceFragment {
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
- addPreferencesFromResource(R.xml.preferences_sms_mms);
+
this.findPreference(MMS_PREF)
.setOnPreferenceClickListener(new ApnPreferencesClickListener());
@@ -35,6 +35,11 @@ public class SmsMmsPreferenceFragment extends CorrectedPreferenceFragment {
initializePlatformSpecificOptions();
}
+ @Override
+ public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_sms_mms);
+ }
+
@Override
public void onResume() {
super.onResume();