- * 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/widgets/RingtonePreferenceDialogFragmentCompat.java b/src/org/thoughtcrime/securesms/preferences/widgets/RingtonePreferenceDialogFragmentCompat.java
deleted file mode 100644
index f9d9ef5a70..0000000000
--- a/src/org/thoughtcrime/securesms/preferences/widgets/RingtonePreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,593 +0,0 @@
-package org.thoughtcrime.securesms.preferences.widgets;
-
-
-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.ArrayAdapter;
-import android.widget.CursorAdapter;
-import android.widget.HeaderViewListAdapter;
-import android.widget.ListAdapter;
-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.LinkedList;
-import java.util.List;
-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 String[] data;
-
- 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();
-
- createRingtoneList(ringtonePreference.getRingtone());
-
- 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(data, selectedIndex, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- if (i < data.length) {
- 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 String[] createRingtoneList(Uri ringtoneUri) {
- RingtonePreference ringtonePreference = getRingtonePreference();
- List
- * 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