En las preferencias, selecciona mi sonido como con RingtonePreference

Tengo sonidos en mi / carpeta cruda y quisiera que mi usuario pudiera elegir un sonido en preferencias exactamente como RingtonePreference hace pero solamente con mis sonidos.

Aquí mi reemplazo RingtonePreference. Todos los tonos del sistema y sus tonos personalizados (definidos en xml, almacenados en res / raw) se enumeran:

ExtraRingtonePreference.java

package de.almisoft.test; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; import de.almisoft.test.R; import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.res.TypedArray; import android.database.Cursor; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.preference.DialogPreference; import android.util.AttributeSet; public class ExtraRingtonePreference extends DialogPreference { private Context mContext; private String mValue; private Ringtone ringtone; private int mRingtoneType; private boolean mShowSilent; private boolean mShowDefault; private CharSequence[] mExtraRingtones; private CharSequence[] mExtraRingtoneTitles; public ExtraRingtonePreference(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ExtraRingtonePreference, 0, 0); mRingtoneType = a.getInt(R.styleable.ExtraRingtonePreference_ringtoneType, RingtoneManager.TYPE_RINGTONE); mShowDefault = a.getBoolean(R.styleable.ExtraRingtonePreference_showDefault, true); mShowSilent = a.getBoolean(R.styleable.ExtraRingtonePreference_showSilent, true); mExtraRingtones = a.getTextArray(R.styleable.ExtraRingtonePreference_extraRingtones); mExtraRingtoneTitles = a.getTextArray(R.styleable.ExtraRingtonePreference_extraRingtoneTitles); a.recycle(); } public ExtraRingtonePreference(Context context) { this(context, null); } public String getValue() { return mValue; } private Map<String, Uri> getSounds(int type) { RingtoneManager ringtoneManager = new RingtoneManager(mContext); ringtoneManager.setType(type); Cursor cursor = ringtoneManager.getCursor(); Map<String, Uri> list = new TreeMap<String, Uri>(); while (cursor.moveToNext()) { String notificationTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); Uri notificationUri = ringtoneManager.getRingtoneUri(cursor.getPosition()); list.put(notificationTitle, notificationUri); } return list; } private Uri uriFromRaw(String name) { int resId = mContext.getResources().getIdentifier(name, "raw", mContext.getPackageName()); return Uri.parse("android.resource://" + mContext.getPackageName() + "/" + resId); } private String getExtraRingtoneTitle(CharSequence name) { if (mExtraRingtones != null && mExtraRingtoneTitles != null) { int index = Arrays.asList(mExtraRingtones).indexOf(name); return mExtraRingtoneTitles[index].toString(); } return null; } @Override public CharSequence getSummary() { String ringtoneTitle = null; if (mValue != null) { if (mValue.length() == 0) ringtoneTitle = mContext.getString(R.string.silent); if (ringtoneTitle == null && mExtraRingtones != null && mExtraRingtoneTitles != null) { for (int i = 0; i < mExtraRingtones.length; i++) { Uri uriExtra = uriFromRaw(mExtraRingtones[i].toString()); if (uriExtra.equals(Uri.parse(mValue))) { ringtoneTitle = mExtraRingtoneTitles[i].toString(); break; } } } if (ringtoneTitle == null) { Ringtone ringtone = RingtoneManager.getRingtone(mContext, Uri.parse(mValue)); String title = ringtone.getTitle(mContext); if (title != null && title.length() > 0) ringtoneTitle = title; } } CharSequence summary = super.getSummary(); if (ringtoneTitle != null) { if (summary != null) return String.format(summary.toString(), ringtoneTitle); else return ringtoneTitle; } else return summary; } @Override protected void onPrepareDialogBuilder(Builder builder) { final Map<String, Uri> sounds = new LinkedHashMap<String, Uri>(); if (mExtraRingtones != null) { for (CharSequence extraRingtone : mExtraRingtones) { Uri uri = uriFromRaw(extraRingtone.toString()); String title = getExtraRingtoneTitle(extraRingtone); sounds.put(title, uri); } } if (mShowDefault) { Uri uriDefault = RingtoneManager.getDefaultUri(mRingtoneType); if (uriDefault != null) { Ringtone ringtoneDefault = RingtoneManager.getRingtone(mContext, uriDefault); if (ringtoneDefault != null) { sounds.put(ringtoneDefault.getTitle(mContext), uriDefault); } } } if (mShowSilent) sounds.put(mContext.getString(R.string.silent), Uri.parse("")); sounds.putAll(getSounds(RingtoneManager.TYPE_NOTIFICATION)); final String[] titleArray = sounds.keySet().toArray(new String[0]); final Uri[] uriArray = sounds.values().toArray(new Uri[0]); int index = mValue != null ? Arrays.asList(uriArray).indexOf(Uri.parse(mValue)) : -1; builder.setSingleChoiceItems(titleArray, index, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (ringtone != null) ringtone.stop(); String title = titleArray[which]; Uri uri = uriArray[which]; if (uri != null) { if (uri.toString().length() > 0) { ringtone = RingtoneManager.getRingtone(mContext, uri); ringtone.play(); } mValue = uri.toString(); } else mValue = null; } }); builder.setPositiveButton(R.string.ok, this); builder.setNegativeButton(R.string.cancel, this); } @Override protected void onDialogClosed(boolean positiveResult) { super.onDialogClosed(positiveResult); if (ringtone != null) ringtone.stop(); if (positiveResult && callChangeListener(mValue)) { persistString(mValue); notifyChanged(); } } @Override protected Object onGetDefaultValue(TypedArray a, int index) { return a.getString(index); } @Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { if (restoreValue) mValue = getPersistedString(""); else { if (mExtraRingtones != null && defaultValue != null && defaultValue.toString().length() > 0) { int index = Arrays.asList(mExtraRingtones).indexOf((CharSequence) defaultValue); if (index >= 0) mValue = uriFromRaw(defaultValue.toString()).toString(); else mValue = (String) defaultValue; } else mValue = (String) defaultValue; persistString(mValue); } } } 

Res / values ​​/ attrs.xml

 <?xml version="1.0" encoding="UTF-8"?> <resources> <declare-styleable name="ExtraRingtonePreference"> <attr name="ringtoneType"> <!-- Ringtones. --> <flag name="ringtone" value="1" /> <!-- Notification sounds. --> <flag name="notification" value="2" /> <!-- Alarm sounds. --> <flag name="alarm" value="4" /> <!-- All available ringtone sounds. --> <flag name="all" value="7" /> </attr> <attr name="showSilent" format="boolean"/> <attr name="showDefault" format="boolean"/> <attr name="extraRingtones" format="reference"/> <attr name="extraRingtoneTitles" format="reference"/> </declare-styleable> </resources> 

Res / values ​​/ strings.xml

 <?xml version="1.0" encoding="utf-8"?> <resources> <string name="silent">Silent</string> <string name="ok">OK</string> <string name="cancel">Cancel</string> <string name="ringtoneTitle">Ringtone</string> <string name="ringtoneSummary">Ringtone: %s</string> <string-array name="extraRingtones"> <item>deichkind_sone_musik</item> <item>madonna_like_a_virgin</item> </string-array> <string-array name="extraRingtoneTitles"> <item>Sone Musik</item> <item>Like A Virgin</item> </string-array> </resources> 

Res / crudo

 res ↳ raw ↳ deichkind_sone_musik.mp3 ↳ madonna_like_a_virgin.mp3 

Res / xml / preferences.xml

 <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:auto="http://schemas.android.com/apk/res-auto"> <de.almisoft.test.ExtraRingtonePreference android:key="ringtone" android:title="@string/ringtoneTitle" android:summary="@string/ringtoneSummary" android:defaultValue="deichkind_sone_musik" auto:ringtoneType="notification" auto:showSilent="true" auto:showDefault="true" auto:extraRingtones="@array/extraRingtones" auto:extraRingtoneTitles="@array/extraRingtoneTitles"/> <!-- set android:defaultValue to "deichkind_sone_musik" for your custom mp3 to "" for silent to "content://settings/system/notification_sound" for system default ringtone --> </PreferenceScreen> 

Así que finalmente miré en el código fuente de ListPreference e hizo lo mismo con algunas modificaciones. Como no puedo usar com.android.internal.R.styleable.ListPreference tuve que crear mi propio styleable en attrs.xml:

 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ListPreference"> <attr name="entries" format="string"></attr> <attr name="entryValues" format="string"></attr> </declare-styleable> <declare-styleable name="Preference"> <attr name="summary" format="string"></attr> </declare-styleable> </resources> 

Y luego importarlo en mi archivo preferences.xml como este:

  xmlns:foo="http://schemas.android.com/apk/res/com.abe.abemoto" 

Y lo utiliza:

  <com.abe.abemoto.preference.CustomSoundListPreference android:defaultValue="@string/pref_alert_ring_value_1" android:key="@string/pref_alert_sound_choice_for_notif_key" android:title="Sonnerie de notification" foo:entries="@array/pref_alert_ring_entries" foo:entryValues="@array/pref_alert_ring_values" foo:summary="Choisissez la sonnerie pour les notifications" /> 

En mi clase CustomSoundListPreference he modificado el método onPrepareDialogBuilder para reproducir mi sonido en el elemento pulsado.

  @Override protected void onPrepareDialogBuilder(Builder builder) { super.onPrepareDialogBuilder(builder); mMediaPlayer = new MediaPlayer(); if (mEntries == null || mEntryValues == null) { throw new IllegalStateException( "ListPreference requires an entries array and an entryValues array."); } mClickedDialogEntryIndex = getValueIndex(); builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mClickedDialogEntryIndex = which; String value = mEntryValues[which].toString(); Resources res = getContext().getResources(); int resId = res.getIdentifier(value, "raw", getContext().getPackageName()); Uri uri = Uri.parse(String.format(getContext() .getString(R.string.resource_sound), getContext().getPackageName(), resId)); Log.d(TAG, "uri sound = " + uri); try { mMediaPlayer.reset(); mMediaPlayer.setDataSource(getContext(), uri); mMediaPlayer.prepare(); mMediaPlayer.start(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }); builder.setPositiveButton("Ok", this); builder.setNegativeButton("Annuler", this); } 

Aquí está el código completo para una ListPreference personalizada como una preferencia de ringtone:

 import java.io.IOException; import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.Cursor; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.preference.ListPreference; import android.provider.MediaStore; import android.util.AttributeSet; import android.util.Log; public class CustomListPreference extends ListPreference{ private MediaPlayer mMediaPlayer; private Context mContext; CharSequence[] mEntries; CharSequence[] mEntryValues; private int mClickedDialogEntryIndex; private String mValue; public CustomListPreference(Context context) { super(context); mContext = context; } /** * Sets the value of the key. This should be one of the entries in * {@link #getEntryValues()}. * * @param value The value to set for the key. */ public void setValue(String value) { mValue = value; persistString(value); } /** * Sets the value to the given index from the entry values. * * @param index The index of the value to set. */ public void setValueIndex(int index) { if (mEntryValues != null) { setValue(mEntryValues[index].toString()); } } /** * Returns the value of the key. This should be one of the entries in * {@link #getEntryValues()}. * * @return The value of the key. */ public String getValue() { return mValue; } /** * Returns the entry corresponding to the current value. * * @return The entry corresponding to the current value, or null. */ public CharSequence getEntry() { int index = getValueIndex(); return index >= 0 && mEntries != null ? mEntries[index] : null; } public int findIndexOfValue(String value) { if (value != null && mEntryValues != null) { for (int i = mEntryValues.length - 1; i >= 0; i--) { if (mEntryValues[i].equals(value)) { return i; } } } return -1; } private int getValueIndex() { return findIndexOfValue(mValue); } @Override protected void onPrepareDialogBuilder(Builder builder) { super.onPrepareDialogBuilder(builder); mMediaPlayer = new MediaPlayer(); mEntries = getEntries(); mEntryValues = getEntryValues(); if (mEntries == null || mEntryValues == null) { throw new IllegalStateException( "ListPreference requires an entries array and an entryValues array."); } mClickedDialogEntryIndex = getValueIndex(); builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mClickedDialogEntryIndex = which; String value = mEntryValues[which].toString(); String path = findPathFromName(value); try { playSong(path); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }); builder.setPositiveButton("Ok", this); builder.setNegativeButton("Cancel", this); } private void playSong(String path) throws IllegalArgumentException, IllegalStateException, IOException { Log.d("ringtone", "playSong :: " + path); mMediaPlayer.reset(); mMediaPlayer.setDataSource(path); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_RING); // mMediaPlayer.setLooping(true); mMediaPlayer.prepare(); mMediaPlayer.start(); } public String findPathFromName(String name) { Cursor mCursor = getContext().getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Audio.Media.TITLE + "='" + name + "'", null, null ); String path = ""; if(mCursor.moveToFirst()){ path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.DATA)); } mCursor.close(); mCursor = null; return path; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state == null || !state.getClass().equals(SavedState.class)) { // Didn't save state for us in onSaveInstanceState super.onRestoreInstanceState(state); return; } SavedState myState = (SavedState) state; super.onRestoreInstanceState(myState.getSuperState()); setValue(myState.value); } private static class SavedState extends BaseSavedState { String value; public SavedState(Parcel source) { super(source); value = source.readString(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(value); } public SavedState(Parcelable superState) { super(superState); } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } @Override protected void onDialogClosed(boolean positiveResult) { super.onDialogClosed(positiveResult); if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) { String value = mEntryValues[mClickedDialogEntryIndex].toString(); if (callChangeListener(value)) { setValue(value); } } mMediaPlayer.stop(); mMediaPlayer.release(); } @Override protected Object onGetDefaultValue(TypedArray a, int index) { return a.getString(index); } @Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue); } @Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); if (isPersistent()) { // No need to save instance state since it's persistent return superState; } final SavedState myState = new SavedState(superState); myState.value = getValue(); return myState; } } 

Espero que esto sea útil para alguien.

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.