RingtoneManager

本文详细解读了Android系统中铃声管理的核心原理及实现方式,包括如何通过Intent和Action来弹出铃声选择对话框,以及如何利用RingtoneManager获取并设置铃声。文章还介绍了各种铃声相关参数的作用,如类型、标题、音频属性等,并阐述了铃声播放时音量键的控制逻辑。最后,文章探讨了铃声与不同音频流的关系,以及如何在不同场景下正确配置铃声。

铃声学习
RingtoneManager.getRingtone(context, ringtoneUri);获取铃声Ringtone对象
不管哪里用到铃声,都会有弹出一个Dialog 选择铃声 因此我们自己使用的时候应该

通过intent+action 弹出,这个界面在/packages/providers/MediaProvide然后就是com.android.providor.media.RingtonePickerActivity.java
是以对话框的形式因此extend Alertctivity

action是有几种选择:
RingtoneManager.ACTION_RINGTONE_PICKER Activity Action: Shows a ringtone picker.展现铃声选择弹窗
RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT 传递一个boolean值是否弹出默认选项 默认铃声 Settings.System.DEFAULT_RINGTONE_URI;
RingtonrManager.EXTRA_RINGTONE_SHOW_SILENT 是否有静音选项
RingtoneManager.EXTRA_RINGTONE_SHOW_MORE_RINGTONES 是否有更多铃声选项
RingtoneManager.EXTRA_RINGTONE_EXISTING_URI 打开对话框的时候就默认选中上次被选中的音乐
/**
* Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the
* current ringtone, which will be used to show a checkmark next to the item
* for this {@link Uri}. If showing an item for “Default” (@see
* {@link #EXTRA_RINGTONE_SHOW_DEFAULT}), this can also be one of
* {@link System#DEFAULT_RINGTONE_URI},
* {@link System#DEFAULT_NOTIFICATION_URI},
* {@link System#DEFAULT_ALARM_ALERT_URI}, or
* M: { System#DEFAULT_VIDEO_CALL_URI} to have the “Default” item
* checked.
*
* @see #ACTION_RINGTONE_PICKER
*/
看懂大概意思:如有有Default选项,那么 * {@link System#DEFAULT_RINGTONE_URI},* {@link System#DEFAULT_NOTIFICATION_URI},* {@link System#DEFAULT_ALARM_ALERT_URI}, M: { System#DEFAULT_VIDEO_CALL_URI} 那么也要有这四个选项主要是URI
RingtoneManager.EXTRA_RINGTONE_TYPE 根据铃声类型指定是否显示,有type Notification,Alarm,all
RingtoneManager.EXTRA_RINGTONE_TITLE 展现铃声的title
RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS 铃声额外的属性
RingtoneManager.EXTRA_RINGTONE_PICKED_URI 已被选的铃声的URI,DEFAULT_VIDEO_CALL_URI选的是系统默认,选silent,那么是null
RingtoneManager.EXTRA_RINGTONE_PICKED_POSITION 选的位置

RingtonePickerActivity中根据获得的值设置弹框
然后获取type值intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1);
所有属性列出来看一下:
Type that refers to sounds that are used for the phone ringer.
TYPE_RINGTONE = 1;
Type that refers to sounds that are used for notifications.
TYPE_NOTIFICATION = 2;
Type that refers to sounds that are used for the alarm.
TYPE_ALARM = 4;
Type that refers to sounds that are used for the video call phone ringer.
TYPE_VIDEO_CALL = 8;
Type that refers to sounds that are used for the sip call phone ringer.
TYPE_SIP_CALL = 16;
All types of sounds.
TYPE_ALL = TYPE_RINGTONE | TYPE_NOTIFICATION | TYPE_ALARM;

然后 setVolumeControlStream(mRingtoneManager.inferStreamType());这个是表示音量键控制哪种音频的开关
public int inferStreamType() {
switch (mType) {

        case TYPE_ALARM:
            return AudioManager.STREAM_ALARM;

        case TYPE_NOTIFICATION:
            return AudioManager.STREAM_NOTIFICATION;

        default:
            return AudioManager.STREAM_RING;
    }
}

一共有三种进入AudioManager
/* The audio stream for phone calls /
public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
/* The audio stream for system sounds /
public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM;
/* The audio stream for the phone ring /
public static final int STREAM_RING = AudioSystem.STREAM_RING;
/* The audio stream for music playback /
public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC;
/* The audio stream for alarms /
public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;
/* The audio stream for notifications /
public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
/* @hide The audio stream for phone calls when connected to bluetooth /
public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
/* @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) /
public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED;
/* The audio stream for DTMF Tones /
public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF;
/* @hide The audio stream for text to speech (TTS) /
public static final int STREAM_TTS = AudioSystem.STREAM_TTS;
可以看出使用的是AudioSystem.java进入看一下
/* The default audio stream */
public static final int STREAM_DEFAULT = -1;
/* The audio stream for phone calls */
public static final int STREAM_VOICE_CALL = 0;
/* The audio stream for system sounds */
public static final int STREAM_SYSTEM = 1;
/* The audio stream for the phone ring and message alerts */
public static final int STREAM_RING = 2;
/* The audio stream for music playback */
public static final int STREAM_MUSIC = 3;
/* The audio stream for alarms */
public static final int STREAM_ALARM = 4;
/* The audio stream for notifications */
public static final int STREAM_NOTIFICATION = 5;
/* @hide The audio stream for phone calls when connected on bluetooth */
public static final int STREAM_BLUETOOTH_SCO = 6;
/* @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = 7;
/* @hide The audio stream for DTMF tones */
public static final int STREAM_DTMF = 8;
/* @hide The audio stream for text to speech (TTS) */
public static final int STREAM_TTS = 9;
/**

可以看到参数使用

RingtoneManager.getRingtone(context, ringtoneUri);获取铃声Ringtone对象这个方法还没研究透彻
AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
IRingtonePlayer player = mAudioManager.getRingtonePlayer();

package com.dosen.watchtest.activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.dosen.watchtest.R; import com.dosen.watchtest.widget.WatchListView; import java.util.ArrayList; import java.util.List; /** * Created by Daisy */ public class RingtonePickerActivity extends SecondaryActivity { private static final String TAG = "RingtonePickerActivity"; public static void start(Context context) { Intent starter = new Intent(context, RingtonePickerActivity.class); context.startActivity(starter); } private WatchListView watchListView; private RingtoneAdapter adapter; private List<RingtoneItem> ringtoneItems = new ArrayList<>(); private Ringtone currentRingtone; private int selectedPosition = -1; private Uri currentRingtoneUri; private Cursor cursor; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_ringtone_picker); currentRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_RINGTONE); watchListView = findViewById(R.id.ringtone_list); getSystemRingtones(); adapter = new RingtoneAdapter(this, ringtoneItems); watchListView.setAdapter(adapter); watchListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { handleRingtoneSelection(position); } }); setDefaultSelection(); } private class RingtoneAdapter extends BaseAdapter { private final Context context; private final List<RingtoneItem> items; public RingtoneAdapter(Context context, List<RingtoneItem> items) { this.context = context; this.items = items; } @Override public int getCount() { return items.size(); } @Override public Object getItem(int position) { return items.get(position); } @Override public long getItemId(int position) { return position; } class ViewHolder { TextView tvName; ImageView ivSelected; } @Override public View getView(int position, View view, ViewGroup viewGroup) { ViewHolder holder; if (view == null) { view = LayoutInflater.from(context).inflate(R.layout.ringtone_list_item, viewGroup, false); holder = new ViewHolder(); holder.tvName = view.findViewById(R.id.tv_ringtone_name); holder.ivSelected = view.findViewById(R.id.iv_ringtone_selected); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } RingtoneItem item = items.get(position); holder.tvName.setText(item.title); if (item.selected) { holder.tvName.setTextColor(getResources().getColor(R.color.white)); holder.ivSelected.setImageResource(R.drawable.ic_radio_button_checked); } else { holder.tvName.setTextColor(getResources().getColor(R.color.text_hui)); holder.ivSelected.setImageResource(R.drawable.ic_radio_button_unchecked); } return view; } } /** * 获取系统铃声 */ private void getSystemRingtones() { ringtoneItems.clear(); RingtoneManager manager = new RingtoneManager(this); manager.setType(RingtoneManager.TYPE_RINGTONE); cursor = manager.getCursor(); if (cursor != null && cursor.moveToFirst()) { do { String title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); Uri uri = manager.getRingtoneUri(cursor.getPosition()); ringtoneItems.add(new RingtoneItem(title, uri)); } while (cursor.moveToNext()); cursor.close(); } } /** * 查找当前铃声在列表中的位置 */ private void setDefaultSelection() { for (int i = 0; i < ringtoneItems.size(); i++) { if (ringtoneItems.get(i).uri.equals(currentRingtoneUri)) { selectedPosition = i; ringtoneItems.get(i).selected = true; break; } } adapter.notifyDataSetChanged(); } /** * 设置选中的铃声 */ private void handleRingtoneSelection(int position) { RingtoneItem selectedItem = ringtoneItems.get(position); // 停止当前播放的铃声 if (currentRingtone != null && currentRingtone.isPlaying()) { currentRingtone.stop(); } // (重新)播放选中的铃声 currentRingtone = RingtoneManager.getRingtone(this, selectedItem.uri); currentRingtone.play(); // 更新选中状态 if (selectedPosition != -1) { ringtoneItems.get(selectedPosition).selected = false; } selectedItem.selected = true; selectedPosition = position; setAsDefaultRingtone(selectedItem.uri); adapter.notifyDataSetChanged(); } /** * 设置为系统默认铃声 * * @param uri 铃声Uri */ private void setAsDefaultRingtone(Uri uri) { try { RingtoneManager.setActualDefaultRingtoneUri( this, RingtoneManager.TYPE_RINGTONE, uri ); Log.d(TAG, "Ringtone set successfully: " + uri.toString()); } catch (SecurityException e) { Log.e(TAG, "Failed to set ringtone: " + e.getMessage()); // 处理权限问题 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { if (!Settings.System.canWrite(this)) { // 请求写入设置权限 Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent); } } } catch (Exception e) { Log.e(TAG, "Exception setting ringtone: " + e.getMessage()); } } /** * 铃声项数据类 * * @title 铃声名称 * @uri 铃声Uri * @selected 是否选中 */ private static class RingtoneItem { String title; Uri uri; boolean selected; public RingtoneItem(String title, Uri uri) { this.title = title; this.uri = uri; this.selected = false; } } @Override protected void onDestroy() { super.onDestroy(); if (currentRingtone != null && currentRingtone.isPlaying()) { currentRingtone.stop(); } if (cursor != null) { cursor.close(); } } } java Android,上述代码出现了Android StaleDataException: Attempted to access a cursor after it has been closed 的报错,请帮忙优化修改
最新发布
07-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值