【问题标题】:How can I add my app's custom ringtones in res/raw folder to a ringtonepreference如何将我的应用程序在 res/raw 文件夹中的自定义铃声添加到 ringtonepreference
【发布时间】:2014-07-30 20:49:15
【问题描述】:

我有这个 RingtonePreference(来自 Android Studio 的默认 SettingsActivity):

pref_notification.xml:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <RingtonePreference
        android:dependency="notifications_alarm"
        android:key="notifications_alarm_ringtone"
        android:title="@string/pref_title_ringtone"
        android:ringtoneType="notification|all"
        android:defaultValue="content://settings/system/notification_sound" />

SettingsActivity.java:

private void setupSimplePreferencesScreen() {
    if (!isSimplePreferences(this)) {
        return;
    }

    // Add 'general' preferences.
    addPreferencesFromResource(R.xml.pref_general);

    // Add 'notifications' preferences, and a corresponding header.
    PreferenceCategory fakeHeader = new PreferenceCategory(this);
    fakeHeader.setTitle(R.string.pref_header_notifications);
    getPreferenceScreen().addPreference(fakeHeader);
    addPreferencesFromResource(R.xml.pref_notification);

    bindPreferenceSummaryToValue(findPreference("notifications_alarm_ringtone"));
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class NotificationPreferenceFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.pref_notification);

        bindPreferenceSummaryToValue(findPreference("notifications_alarm_ringtone"));
    }
}

我想将我应用的自定义铃声从 res/raw 文件夹添加到列表中。 (我不需要它们可用于其他应用。)

【问题讨论】:

  • 我认为您需要创建自定义首选项,或者将它们复制到设备的共享存储中(然后其他应用也可以使用它们)。
  • 我不希望我的应用程序的 res/raw 文件夹中的铃声可用于其他应用程序作为铃声(尽管如果它们不是问题的话)。我想显示一个包含可用铃声 + 我的自定义铃声的列表。应该有一个标准的方法来做到这一点,因为当我进入 Android 设置 > 声音 > 铃声时,我看到 5 个应用程序可能是 Intents。其中一些只显示他们的自定义铃声,而另一些则显示其他应用的铃声。
  • 因此您可以创建自定义首选项。文档站点中有一个指南。
  • 如果我有我的偏好,那么如何弹出“铃声选择器”操作的意图选择器?我的意思是,我想我需要将我的自定义首选项屏幕添加到清单中,并为这个“铃声选择器”操作过滤它,但它是什么?
  • stackoverflow.com/a/31004356/909406查看我的课程“ExtraRingtonePreference”

标签: android android-custom-view android-preferences ringtone


【解决方案1】:

选项 1:复制到设备存储

将您的铃声文件复制到设备的存储中。我基本上是为 GitHub 上的一个应用程序 here 这样做的,在那里我将警报音从原始资源复制到设备的警报目录(您需要将所有“警报”实例替换为“铃声”)。然后我们使用ContentValues创建元数据告诉系统这些文件是铃声并使用MediaStore.Audio.Media.getContentUriForPath然后context.getContentResolver().insert(contentUri, contentValues)将铃声添加到设备的数据库中,所以它们将包含在RingtonePreference的列表中. 您也可以使用RingtoneManager.setActualDefaultRingtoneUri() 将铃声设置为默认铃声,但您需要WRITE_SETTINGS 权限。

另外,请记住在调用getContentResolver().insert()RingtoneManager.setActualDefaultRingtoneUri() 时使用从getContentUriForPath() 获得的URI。 并且,确保添加到您的 AndroidManifest.xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

选项 2:自定义偏好

创建自定义首选项,如this guide 所示,或者您可以使用(或子类化)ListPreference。您需要使用RingtoneManager.getCursor() (docs) 检索所有设备的铃声并将它们添加到列表中,并包括您的自定义铃声。

然后,在您的preferences.xml 文件中,您将使用RingtonePreference 而不是

<com.yourapp.CustomPreference android:key="your_key"
    ...

在你的情况下,因为如果其他应用程序也可以访问铃声也没关系,我建议使用第一种方法,因为总的来说我觉得最好不要复制系统已经提供的功能,因为它是最好的利用用户已经熟悉的内容。

【讨论】:

    【解决方案2】:

    最后我根据这个答案制作了自己的 ExtraRingtonePreference:In preferences, select my sound just like with RingtonePreference

    我将其包含在此处以供将来参考:

    src/main/java/com/fletech/android/preference/ExtraRingtonePreference.java:

    package com.fletech.android.preference;
    
    import android.app.AlertDialog;
    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;
    
    import com.fletech.android.redalert.R;
    
    import java.util.Arrays;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.TreeMap;
    
    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);
        }
    
        public 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);
    
                Uri mValueUri = Uri.parse(mValue);
                if (ringtoneTitle == null && mExtraRingtones != null && mExtraRingtoneTitles != null) {
    
                    for (int i = 0; i < mExtraRingtones.length; i++) {
                        Uri uriExtra = uriFromRaw(mExtraRingtones[i].toString());
                        if (uriExtra.equals(mValueUri)) {
                            ringtoneTitle = mExtraRingtoneTitles[i].toString();
                            break;
                        }
                    }
                }
    
                if (ringtoneTitle == null) {
                    Ringtone ringtone = RingtoneManager.getRingtone(mContext, mValueUri);
                    if (ringtone != null) {
                        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(AlertDialog.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 (mShowSilent)
                sounds.put(mContext.getString(R.string.silent), Uri.parse(""));
    
            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);
                    }
                }
            }
    
            sounds.putAll(getSounds(mRingtoneType));
    
            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();
    
                    Uri uri = uriArray[which];
    
                    if (uri != null) {
                        if (uri.toString().length() > 0) {
                            ringtone = RingtoneManager.getRingtone(mContext, uri);
                            if (ringtone != null) {
                                ringtone.play();
                            }
                        }
                        mValue = uri.toString();
                    } else mValue = null;
    
                }
            });
    
            builder.setPositiveButton(R.string.dialog_save, this);
            builder.setNegativeButton(R.string.dialog_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);
            }
        }
    }
    

    src/main/res/values.attrs.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <resources>
        <declare-styleable name="ExtraRingtonePreference">
            <attr name="ringtoneType"><!-- Should correspond to RingtoneManager -->
                <!-- TYPE_RINGTONE: Ringtones. -->
                <flag name="ringtone" value="1" />
                <!-- TYPE_NOTIFICATION: Notification sounds. -->
                <flag name="notification" value="2" />
                <!-- TYPE_ALARM: Alarm sounds. -->
                <flag name="alarm" value="4" />
                <!-- TYPE_ALL: 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>
    

    src/res/values/ringtone_preference_strings.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="ringtone_title">Ringtone</string>
        <string name="silent">Silent</string>
        <string name="dialog_save">Save</string>
        <string name="dialog_cancel">Cancel</string>
    
        <string-array name="extra_ringtones">
            <item>beep</item>
            <item>beep_beep</item>
            <item>default</item>
            <item>test</item>
        </string-array>
    
        <string-array name="extra_ringtone_titles">
            <item>Beep</item>
            <item>Beep-Beep</item>
            <item>Default</item>
            <item>Test</item>
        </string-array>
    </resources>
    

    以及src/main/res/xml/pref_alarm.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">
        <com.fletech.android.preference.ExtraRingtonePreference
            android:key="notifications_alarm_ringtone"
            android:title="@string/ringtone_title"
            android:defaultValue="default"
            auto:ringtoneType="alarm"
            auto:showSilent="true"
            auto:showDefault="true"
            auto:extraRingtones="@array/extra_ringtones"
            auto:extraRingtoneTitles="@array/extra_ringtone_titles"/>
    </PreferenceScreen>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 2014-04-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多