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

这个系列仅仅包含控制部分 , 不包含音频操作代码 , 如 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>



教程完毕!!

你可能感兴趣的:(android,监听,音乐,耳机,多媒体)