您的位置:首页 > 移动开发 > Android开发

Android 多媒体控制 来电监听-耳机插拔监听-耳机按钮监听-MediaSession-MediaStyle

2016-11-10 09:04 1306 查看
这个系列仅仅包含控制部分 , 不包含音频操作代码 , 如 pauseAudio(); 我不会说这个方法里是怎么操作的 , 大家需要结合自己的音频播放处理来实现.

Android多媒体控制

一个完整的多媒体播放器应该有的基础功能:

通过耳机按钮来控制歌曲 播放/暂停 上/下一首歌曲

当有线耳机/蓝牙耳机 断开连接和重新连接的时候 我们应该对应做出 暂停音频 恢复音频

使用系统提供的Notification.MediaStyle来控制歌曲 , 可以顺利兼容 android 4.x ~ android 7.x 现在的系统样式

[[广告]] https://github.com/ocwvar/DarkPurple

需要代码样例的同学可以在我的这个项目中查看 这个项目是个完整的音频播放器 这里的代码均是从里面提取 , 同时还有均衡器调节 频谱动画显示等.. 目前正在不断完善中 , 但由于上班 , 代码更新可能不及时.

###1. 创建 MediaSessionCompat 对象

MediaSessionCompat sessionCompat = new MediaSessionCompat();


在这个构造方法中 , 我们使用这个:

MediaSessionCompat(Context context, String tag, ComponentName mbrComponent, PendingIntent mbrIntent)

//第一个参数 context: 这个没有什么好讲的,大家都懂的
//第二个参数 tag: 这个是用于调试用的,随便填写即可
//第三个参数 mbrComponent: 这个是用于API21以下的时候传递耳机按钮事件用的MediaSessionCompat.
//第四个参数 mbrIntent: 这个是给API21以下传递的时候携带的,一般设为 NULL即可

//例如:
//其中的HeadsetButtonReceiver是我们的API21以下实用的监听器 , 我们稍候再讲
ComponentName cn = new ComponentName(this.context.getApplicationContext().getPackageName(),
HeadsetButtonReceiver.class.getName())
sessionCompat = new MediaSessionCompat(this.context.getApplicationContext(), "test", cn, null);


参数设置

sessionCompat = new MediaSessionCompat(...);

//设置MediaSession回调监听,主要用于设置API21+的耳机按钮监听
sessionCompat.setCallback(new MediaSessionCallback());

//设置FLAG,FLAG的用途一看名字就知道了
sessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);

//设置MediaSession启动 (很重要,不启动则无法接受到数据)
sessionCompat.setActive(true);


2. 耳机多媒体按钮监听

API 21+ 的方式 MediaSessionCompat.Callback

//创建完成后用MediaSessionCompat.setCallback设置上即可使用

private class MediaSessionCallback extends MediaSessionCompat.Callback {

@Override
public boolean onMediaButtonEvent(Intent mediaButtonEvent) {

//接收到监听事件

}

}


API21- 的方式 HeadsetButtonReceiver

//在代码中创建一个单独的类文件,而不能作为一个内部类

public class HeadsetButtonReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {

//接收到监听事件

}

}

//然后在注册文件中注册这个接收器

<receiver
android:name=".HeadsetButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>

//最后在创建MediaSessionCompat对象的时候使用即可


3. Notification.MediaStyle 的创建

MediaStyle是什么样子的Notification呢?

在**Android 4.x**的是这样的
![这里写图片描述](http://img.blog.csdn.net/20161107152026146)

在**Android 5.x ~ 6.x** 的是这样的
![这里写图片描述](http://img.blog.csdn.net/20161107152048581)

在**Android 7.0** 的是这样的
![这里写图片描述](http://img.blog.csdn.net/20161107152312833)

MediaStyle使用的都是系统资源 , 除了布局之外 , 可以自定义的有:
> 封面图片 标题 副标题 按钮样式 背景颜色

使用MediaStyle的优点是不用担心因系统变化而导致布局的变化 , 以及可以使用MediaSession作为我们的控制中介 , 虽然这部分不能自定义但个人认为还是挺值得的 .

对应的一个还有一个是Style是 **DecoratedMediaCustomViewStyle** 这个是可以提供部分自定义 , 但是修改部分仅是标题文字那部分的布局而已 , 并没有什么太大的作用 .

####**Notification.MediaStyle 创建代码**

PS:我们分开一段段讲 ( 代码太长了不好排版…. )

####**Part.1 基础部分**

//创建 Notification 构建器
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

//创建 MediaStyle 对象
final NotificationCompat.MediaStyle mediaStyle = new NotificationCompat.MediaStyle(builder);

//在折叠的视图中显示的按钮序号  根据下面设置的ACTION顺序相关
mediaStyle.setShowActionsInCompactView(0,1);

//设置上面创建的MediaSession
mediaStyle.setMediaSession(sessionCompat.getSessionToken());

//设置 MediaStyle 风格
builder.setStyle(mediaStyle);

//不显示默认的通知开始时间
builder.setShowWhen(false);

//仅通知一次
builder.setOnlyAlertOnce(true);

//设置为当前正在运行状态,不能清除
builder.setOngoing(true);

//设置点击时不隐藏Notification
builder.setAutoCancel(false);

//设置状态栏上面显示的小图标
builder.setSmallIcon(R.drawable.ic_action_small_icon);

//设置锁屏是否显示 在 Android5.0+ 的锁屏界面可以隐藏Notification内容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
}

//设置通知优先度,让我们的 Notification 显示在最上面
builder.setPriority(Notification.PRIORITY_MAX);

//设置通知类别为服务
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setCategory(Notification.CATEGORY_SERVICE);
}

//点击通知操作 使得用户点击Notification空白区域的时候打开指定的Activity
Intent intent = new Intent(context, SelectMusicActivity.class);
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
builder.setContentIntent(pendingIntent);


Part.2 创建和设置Action按钮

/**
* 生成按钮Action
*
* @param icon  图标资源
* @param intentAction  按钮产生的广播Action
* @return 按钮Action
*/
private NotificationCompat.Action generateAction(int icon, String intentAction ) {
return new NotificationCompat.Action( icon, intentAction, PendingIntent.getBroadcast(context, 0, new Intent(intentAction), PendingIntent.FLAG_CANCEL_CURRENT) );
}

//根据播放状态不同 , 设置不同主按钮样式 , 这里的设置顺序影响到折叠界面显示的顺序

//上一首 按钮ACTION
builder.addAction(generateAction(android.R.drawable.ic_media_previous,MediaNotificationReceiver.BUTTON_PREV));
switch (audioStatus) {
case Paused:
//播放 按钮ACTION
builder.addAction(generateAction(android.R.drawable.ic_media_play,MediaNotificationReceiver.BUTTON_PLAY));
break;
case Playing:
//暂停 按钮ACTION
builder.addAction(generateAction(android.R.drawable.ic_media_pause,MediaNotificationReceiver.BUTTON_PAUSE));
break;
}
//下一首 按钮ACTION
builder.addAction(generateAction(android.R.drawable.ic_media_next,MediaNotificationReceiver.BUTTON_NEXT));


####**Part.3 设置显示歌曲信息**

//更新标题
builder.setContentTitle(songItem.getTitle());
//更新作者
builder.setContentText(songItem.getArtist());
//更新封面
builder.setLargeIcon( 歌曲封面的Bitmap对象 );


最后就 builder.build(); 得到最终的成品 Notification

4.监听耳机插拔事件

我们需要在耳机拔出的时候 暂停音乐 在耳机重新插入的时候 恢复音乐

耳机插入广播接收器

/**
* 耳机插入广播接收器
*/
public class HeadsetPlugInReceiver extends BroadcastReceiver {

final IntentFilter filter;

public HeadsetPlugInReceiver() {
filter = new IntentFilter();

if (Build.VERSION.SDK_INT >= 21) {
filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
} else {
filter.addAction(Intent.ACTION_HEADSET_PLUG);
}
}

@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.hasExtra("state") && AppConfigs.isResumeAudioWhenPlugin) {

//通过判断 "state" 来知道状态
final boolean isPlugIn = intent.getExtras().getInt("state") == 1;

}
}

}


####耳机拔出/断开连接 广播接收器

/**
* 耳机拔出广播接收器
*/
private class HeadsetReceiver extends BroadcastReceiver {

final IntentFilter filter;
final BluetoothAdapter bluetoothAdapter;

public HeadsetReceiver() {
filter = new IntentFilter();
filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY); //有线耳机拔出变化
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); //蓝牙耳机连接变化

bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}

@Override
public void onReceive(Context context, Intent intent) {
if (isRunningForeground) {
//当前是正在运行的时候才能通过媒体按键来操作音频
switch (intent.getAction()) {
case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
if (
bluetoothAdapter != null &&
BluetoothProfile.STATE_DISCONNECTED == bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET) &&
core.getCurrectStatus() == AudioCore.AudioStatus.Playing
)
{
//蓝牙耳机断开连接 同时当前音乐正在播放 则将其暂停
pause();
}
break;
case AudioManager.ACTION_AUDIO_BECOMING_NOISY:
if (core.getCurrectStatus() == AudioCore.AudioStatus.Playing) {
//有线耳机断开连接 同时当前音乐正在播放 则将其暂停
pause();
}
break;
}
}
}

}


最后用 registerReceiver() 来注册广播监听器即可

5.监听电话状态

我们需要在有电话来的时候和拨通电话的时候 暂停音频 , 在通话结束之后 恢复音频

要使得能接受到通话状态 , 我们需要注册一个权限 android.permission.READ_PHONE_STATE 这个权限在Android 6.0+上是需要用户授予的

电话状态广播接收器

/**
*电话状态广播接收器
*/
public class PhoneStatusReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {

if (intent != null && intent.getExtras() != null){

final String status = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

switch (status){
// "IDLE" 则代表通话结束或振铃结束
case "IDLE":
resume();
break;
//其他状态包括了拨通电话和新来电振铃
default:
pause();
break;
}
}

}

}


在代码端写完之后 , 我们还需要在注册清单中注册一下:

<receiver android:name=".PhoneStatusReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>


教程完毕!!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息