最近在做一个需求,要在桌面去控制qq音乐的播放(即,可以播放暂停,上一曲,下一曲显示歌名和歌手名)。接下来一一说下完成这个需求的心路历程。

  • 1.在网上查腾讯有没有针对qq音乐提供SDK接口,结果发现,没有,如果需要控制,得和qq音乐合作,实现qplay 协议。

  • 2.一位前辈和我说,让我试试Android 的MediaSession 是否可行,我也试了下,还是行不通。一般的话只要音乐播放器实现了谷歌提供的MediaSession 框架,然后就可以被第三方应用控制。但是人家qq音乐没实现这个。哈哈哈

  • 3.这下没辙了,另外网上看到了一篇文章,这里也分享下,这里说的就是百度的一个app可以控制很多的第三方音乐播发器,但是这些第三方app基本都是车载的。例如控制qq音乐,需要先在qq音乐里面打开车载功能。这一看,这就是百度和腾讯有合作的莫。具体可以参考这篇文章 我通过了这篇文章中的方法获取我手机中所有实现MediaSession的应用,最后获取到了今日头条,英语流利说等,没有QQ音乐。

  • 4.这下没办法了。然后插上耳机,放了一首音乐冷静冷静。突然,发现耳机线上的两个按钮,咦,这不就能控制qq音乐吗,于是就尝试了下在我的app里面点击模拟发MediaButton 按键,果然,可以正常的控制播放暂停上一首,下一首qq音乐,当然此时系统中必须只有一个音乐播放器。这里就先这样。关于如何模拟发送MediaButton 按键,可以看我这篇文章android 中用代码模拟发送按键

    一般MediaButton 有如下几个按键:而且一般的音乐播放器里面都会实现对MediaButton的接收,具体原理是接收广播,有兴趣的可以自行研究。

      KeyEvent.KEYCODE_MEDIA_NEXT  KeyEvent.KEYCODE_MEDIA_PREVIOUS  KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE  KeyEvent.KEYCODE_MEDIA_PAUSE  KeyEvent.KEYCODE_MEDIA_PLAY

    可以控制了,但是音乐名和歌手名还拿不到。不过可以从音乐的通知中获取音乐名和歌手名,一般主流的音乐播放器,播放音乐时,都会发一个通知,即Notification,如下图,这个通知中就带有音乐的播放信息。音乐我这边有系统的源码,故想要在系统中去截取notification中的音乐的信息。但是后来发现,完全不用。谷歌提供了一个notificationListener,来专门监听通知。下面来说下notification 的具体用法

notificationListener 的具体用法

1. 创建一个服务并且继承 NotificationListenerService(这个核心的代码我下面再具体列出来)
2.在AndroidManifest中添加服务
            <service            android:name=".service.NotificationListener"            android:label="@string/app_name"            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">            <intent-filter>                <action android:name="android.service.notification.NotificationListenerService" />            </intent-filter>        </service>

3.在初次使用时需要申请通知使用的权限

if (!isNotificationServiceEnabled()) { 初次使用时 判断是否获取了通知使用权            enableNotificationListenerAlertDialog = buildNotificationServiceAlertDialog();            enableNotificationListenerAlertDialog.show();        } private boolean isNotificationServiceEnabled() {        String pkgName = mActivityView.getContext().getPackageName();        final String flat = Settings.Secure.getString(mActivityView.getContext().getContentResolver(),                ENABLED_NOTIFICATION_LISTENERS);        if (!TextUtils.isEmpty(flat)) {            final String[] names = flat.split(":");            for (int i = 0; i < names.length; i++) {                final ComponentName cn = ComponentName.unflattenFromString(names[i]);                if (cn != null) {                    if (TextUtils.equals(pkgName, cn.getPackageName())) {                        return true;                    }                }            }        }        return false;    }        private AlertDialog buildNotificationServiceAlertDialog() {        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivityView.getContext());        alertDialogBuilder.setTitle(R.string.notification_listener_service);        alertDialogBuilder.setMessage(R.string.notification_listener_service_explanation);        alertDialogBuilder.setPositiveButton(R.string.yes,                new DialogInterface.OnClickListener() {                    public void onClick(DialogInterface dialog, int id) {                        mActivityView.getContext().startActivity(new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS));  // 如果没有获取,则跳转到setting 去获取                    }                });        alertDialogBuilder.setNegativeButton(R.string.no,                new DialogInterface.OnClickListener() {                    public void onClick(DialogInterface dialog, int id) {                        // If you choose to not enable the notification listener                        // the app. will not work as expected                    }                });        return (alertDialogBuilder.create());    }    

申请完权限,接下来就可以来监听接收通知了,

public class MyNotificationListenerService extends NotificationListenerService {@Overridepublic void onListenerConnected() {    //当连接成功时调用,一般在开启监听后会回调一次该方法}@Overridepublic void onNotificationPosted(StatusBarNotification sbn) {      //当收到一条消息时回调,sbn里面带有这条消息的具体信息}@Overridepublic void onNotificationRemoved(StatusBarNotification sbn) {     //当移除一条消息的时候回调,sbn是被移除的消息}

所以一般就是在 onNotificationPosted 方法里面去监听拿到消息的具体信息,如下,一般有如下信息。但是我基本都打印了返回值为String 类型的,但是基本都是空。最后我发现,qq音乐发送上面图中的通知,其实是发了一个自定义的RemoteViews(关于RemoteViews 这里不再多说)

Bundle extras = sbn.getNotification().extras;String title = extras.getString(Notification.EXTRA_TITLE);String content = extras.getString(Notification.EXTRA_TEXT);String packageName = sbn.getPackageName();

所以,我通过 sbn.getNotification().bigContentView 拿到了上图中的view,我有尝试通过windowManager 去将此view 显示出来,发现是可以正常显示的。那么接下来的问题就是解析这个view了。

一般的话android中去分析一个view的结构,可以使用AndroidStudio 中的 Layout Inspector工具。然后通过此工具我发现我需要的歌手名在此view的第二个子view 中的第一个和第二个子view。于是:

if (sbn.getNotification().bigContentView != null) {     ViewGroup view = (ViewGroup) sbn.getNotification().bigContentView.apply(this, null);     TextView txMusciName = (TextView) ((ViewGroup) view.getChildAt(1)).getChildAt(0);     TextView txMusicSinger = (TextView) ((ViewGroup) view.getChildAt(1)).getChildAt(1);     String  qqMusicName = (String) txMusciName.getText();     String  qqMuiscSinger = (String) txMusicSinger.getText();         // 此处因为这个服务比较特殊,拿到数据之后想要更新UI,则可以通过发广播的方式。          Intent intent = new Intent(ControlPresent.MUSIC_ACTION_CHANGE_BROADCAST);                intent.putExtra("qqMusicName", qqMusicName);                intent.putExtra("qqMuiscSinger", qqMuiscSinger);                sendBroadcast(intent);

以上,则可以正常的控制qq音乐了。

更多相关文章

  1. Android和ROS的通信 消息的传递
  2. 利用NotificationListenerService获取微信通知消息的头像和内容
  3. Android音乐播放器的设计
  4. Android Handler消息队列的实现原理
  5. Android中的通知和自定义通知布局
  6. 【android】手写一套Java的Handler程序,深入理解Android消息机制
  7. android异步线程利用Handler将消息发送至UI线程
  8. Android消息机制原理,仿写Handler Looper源码跨线程通信原理--之
  9. Android日志消息的生成详细步骤

随机推荐

  1. 《Android开发艺术探索》笔记(五)
  2. android学习笔记之十一数据存储(Shared P
  3. androidP 系统集成时发现部分应用初次打
  4. android 实用的开源框架
  5. Android使用WebView和JS互相调用
  6. Android(安卓)指纹识别
  7. Android监测手指上下左右滑动屏幕
  8. Android游戏引擎Rokon宣布停止更新
  9. Android之AlertDialog的基础使用
  10. 如何检查后台服务(Android的Service类)是否