Android 4.1 Music 通知栏的音乐控制

本文根据原生Android 4.1.1 Music 源码做修改。

1 原生Music 暂停的时候,会删除通知栏上的通知。

2 原生Music 通知栏不能控制音乐,比如下一首,上一首,暂停/播放。

一 解决思路:

1 接到暂停广播时,只暂停,不去除通知。

/packages/apps/Music/src/com/android/music/MediaPlaybackService.java中

调用的 stopForeground(true)方法控制去除通知

2 自定义音乐通知,添加按钮事件。

   /packages/apps/Music/src/com/android/music/MediaPlaybackService.java中

  updateNotification()方法中自定义Notification,关键对RemoteViews 的理解

二  修改后效果图:


三 详细代码

1 接到暂停广播时,只暂停,不去除通知

源代码 采取的策略是 只要暂停就去除通知,这就会造成一个问题就是,当播放视频的时候音乐播放器接受到暂停的广播,于是消除了通知栏。

1.1 添加暂停 不消除通知的方法 pause(boolean isStopForeground)

  1. private void pause(boolean isStopForeground) {  
  2.         synchronized(this) {  
  3.             mMediaplayerHandler.removeMessages(FADEUP);  
  4.             if (isPlaying()) {  
  5.                 mPlayer.pause();  
  6.                 gotoIdleState(isStopForeground);  
  7.                 mIsSupposedToBePlaying = false;  
  8.                 notifyChange(PLAYSTATE_CHANGED);  
  9.                 saveBookmarkIfNeeded();  
  10.             }  
  11.         }  
  12.     }  
  13.       
  14.     private void gotoIdleState(boolean isStopForeground) {  
  15.         mDelayedStopHandler.removeCallbacksAndMessages(null);  
  16.         Message msg = mDelayedStopHandler.obtainMessage();  
  17.         mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);  
  18.        //此方法控制消除通知  
  19.         stopForeground(isStopForeground);  
  20.     }  
1.2 接收暂停广播的时候将pause()方法换为:pause(false)
  1. private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {  
  2.         @Override  
  3.         public void onReceive(Context context, Intent intent) {  
  4.             String action = intent.getAction();  
  5.             String cmd = intent.getStringExtra("command");  
  6.             MusicUtils.debugLog("mIntentReceiver.onReceive " + action + " / " + cmd);  
  7.             if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {  
  8.                 gotoNext(true);  
  9.             } else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {  
  10.                 prev();  
  11.             } else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {  
  12.                 if (isPlaying()) {  
  13.                     /*Begin: modified  */  
  14.                     pause(false);  
  15.                     updateNotification();  
  16.                     /*End: */  
  17.                     mPausedByTransientLossOfFocus = false;  
  18.                 } else {  
  19.                     play();  
  20.                 }  
  21.             } else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {  
  22.                 /*Begin: modified  */  
  23.                 pause(false);  
  24.                 updateNotification();  
  25.                 /*End:*/  
  26.                 mPausedByTransientLossOfFocus = false;  
  27.             } else if (CMDPLAY.equals(cmd)) {  
  28.                               ...  

2 Music 音乐通知控制 

2.1 通知的布局文件 /packages/apps/Music/res/layout/statusbar.xml

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="wrap_content"  
  4.     android:orientation="horizontal" >  
  5.   
  6.     <LinearLayout  
  7.         xmlns:android="http://schemas.android.com/apk/res/android"  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:orientation="horizontal"   
  11.         android:layout_gravity="center_vertical">  
  12.         <!-- 图标 -->  
  13.         <ImageView  
  14.             android:id="@+id/icon"  
  15.             android:layout_width="wrap_content"  
  16.             android:layout_height="wrap_content"  
  17.             android:gravity="center"  
  18.             android:padding="4dip" >  
  19.         </ImageView>  
  20.     </LinearLayout>  
  21.   
  22.     <LinearLayout  
  23.         android:layout_width="match_parent"  
  24.         android:layout_height="wrap_content"  
  25.         android:orientation="vertical" >  
  26.   
  27.         <LinearLayout  
  28.             android:layout_width="match_parent"  
  29.             android:layout_height="wrap_content"  
  30.             android:orientation="horizontal" >  
  31.               <!-- 音乐名 -->  
  32.             <TextView  
  33.                 android:id="@+id/trackname"  
  34.                 style="@android:style/TextAppearance.StatusBar.EventContent.Title"  
  35.                 android:layout_width="80dip"  
  36.                 android:layout_height="wrap_content"  
  37.                 android:layout_gravity="left"  
  38.                 android:ellipsize="marquee"  
  39.                 android:focusable="true"  
  40.                 android:singleLine="true" />  
  41.   
  42.             <RelativeLayout  
  43.                 android:layout_width="match_parent"  
  44.                 android:layout_height="match_parent" >  
  45.                   <!-- 上一首 -->  
  46.                 <ImageButton  
  47.                     android:id="@+id/statusbar_prev"  
  48.                     style="@android:style/MediaButton.Previous"  
  49.                     android:layout_width="wrap_content"  
  50.                     android:layout_height="wrap_content"  
  51.                     android:layout_alignParentLeft="true" />  
  52.                   <!-- 暂停/播放 -->  
  53.                 <ImageButton  
  54.                     android:id="@+id/statusbar_pause"  
  55.                     style="@android:style/MediaButton.Pause"  
  56.                     android:layout_width="wrap_content"  
  57.                     android:layout_height="wrap_content"  
  58.                     android:layout_centerHorizontal="true" />  
  59.                   <!-- 下一首 -->  
  60.                 <ImageButton  
  61.                     android:id="@+id/statusbar_next"  
  62.                     style="@android:style/MediaButton.Next"  
  63.                     android:layout_width="wrap_content"  
  64.                     android:layout_height="wrap_content"  
  65.                     android:layout_alignParentRight="true"  
  66.                     android:gravity="right" />  
  67.             </RelativeLayout>  
  68.         </LinearLayout>  
  69.          <!-- 专辑信息,歌手 -->  
  70.         <TextView  
  71.             android:id="@+id/artistalbum"  
  72.             style="@android:style/TextAppearance.StatusBar.EventContent"  
  73.             android:layout_width="wrap_content"  
  74.             android:layout_height="wrap_content"  
  75.             android:layout_gravity="left"  
  76.             android:ellipsize="end"  
  77.             android:maxLines="2"  
  78.             android:scrollHorizontally="true" />  
  79.     </LinearLayout>  
  80.   
  81. </LinearLayout>  


2.2  通知栏控制:

/packages/apps/Music/src/com/android/music/MediaPlaybackService.java中

 updateNotification()为发送的通知代码,在这里面修改

  1. private void updateNotification() {  
  2.         //RemoteViews未自定义布局,   R.layout.statusbar 为通知的布局文件  
  3.         RemoteViews views = new RemoteViews(getPackageName(), R.layout.statusbar);  
  4.         views.setImageViewResource(R.id.icon, R.drawable.stat_notify_musicplayer);  
  5.         if (getAudioId() < 0) {  
  6.             // streaming  
  7.             views.setTextViewText(R.id.trackname, getPath());  
  8.             views.setTextViewText(R.id.artistalbum, null);  
  9.         } else {  
  10.             String artist = getArtistName();  
  11.             views.setTextViewText(R.id.trackname, getTrackName());  
  12.             if (artist == null || artist.equals(MediaStore.UNKNOWN_STRING)) {  
  13.                 artist = getString(R.string.unknown_artist_name);  
  14.             }  
  15.             String album = getAlbumName();  
  16.             if (album == null || album.equals(MediaStore.UNKNOWN_STRING)) {  
  17.                 album = getString(R.string.unknown_album_name);  
  18.             }  
  19.   
  20.             views.setTextViewText(R.id.artistalbum,  
  21.                     getString(R.string.notification_artist_album, artist, album)  
  22.                     );  
  23.         }  
  24.         Notification status = new Notification();  
  25.         /*Begin: modified 添加修改代码*/  
  26.         //status_icon 为状态栏图标,R.id.statusbar_pause 为通知 的 暂停/开始按钮  
  27.         int status_icon = R.drawable.ic_appwidget_music_play;  
  28.         //根据播放状态,来决定 暂停/开始 图标  
  29.         if(isPlaying()){  
  30.             views.setImageViewResource(R.id.statusbar_pause, R.drawable.ic_appwidget_music_pause);   
  31.         }else{  
  32.             views.setImageViewResource(R.id.statusbar_pause, R.drawable.ic_appwidget_music_play);  
  33.             status_icon = R.drawable.ic_appwidget_music_pause;  
  34.          }  
  35.          // 给通知栏 上一首 按钮 添加点击事件    
  36.         views.setOnClickPendingIntent(R.id.statusbar_prev, pre_PendingIntent());  
  37.         //给通知栏 暂停/开始 按钮 添加点击事件   
  38.         views.setOnClickPendingIntent(R.id.statusbar_pause, pause_PendingIntent());  
  39.         //给通知栏 下一首 按钮 添加点击事件  
  40.         views.setOnClickPendingIntent(R.id.statusbar_next, next_PendingIntent());  
  41.         /*End:  */  
  42.            
  43.         status.contentView = views;  
  44.         status.flags |= Notification.FLAG_ONGOING_EVENT;  
  45.         status.icon = status_icon;  
  46.         status.contentIntent = PendingIntent.getActivity(this0,  
  47.                 new Intent("com.android.music.PLAYBACK_VIEWER")  
  48.                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0);  
  49.         startForeground(PLAYBACKSERVICE_STATUS, status);  
  50.     }  

添加的点击事件函数:

  1. /*Begin:  modify  利用MediaPlaybackService 现有的广播监听 实现播放控制 */  
  2.     //上一首,  
  3.     private PendingIntent pre_PendingIntent(){  
  4.         Intent intent = new Intent(PREVIOUS_ACTION);  
  5.         PendingIntent pendingIntent = PendingIntent.getBroadcast(this0, intent, 0);  
  6.          return pendingIntent;  
  7.     }  
  8.     //暂停开始   
  9.     private PendingIntent pause_PendingIntent(){  
  10.         Intent intent = new Intent(TOGGLEPAUSE_ACTION);  
  11.         PendingIntent pendingIntent = PendingIntent.getBroadcast(this0, intent, 0);  
  12.          return pendingIntent;  
  13.     }  
  14.     //下一首   
  15.     private PendingIntent next_PendingIntent(){  
  16.         Intent intent = new Intent(NEXT_ACTION);  
  17.         PendingIntent pendingIntent = PendingIntent.getBroadcast(this0, intent, 0);  
  18.          return pendingIntent;  
  19.     }  
  20.       
  21.     /*end:  */  

为了实现暂停/开始按钮通知栏图标显示与音乐播放 的一致 改了以下两个地方:

其一 play()  方法中 将  updateNotification() 放在了

if (!mIsSupposedToBePlaying) {
                mIsSupposedToBePlaying = true;
                notifyChange(PLAYSTATE_CHANGED);
            }

之后

  1. public void play() {  
  2.             ...  
  3. if (!mIsSupposedToBePlaying) {  
  4.                 mIsSupposedToBePlaying = true;  
  5.                 notifyChange(PLAYSTATE_CHANGED);  
  6.             }  
  7. updateNotification();  
  8.           ...  
  9. }  
其二:当接受到 暂停/开始  事件的 广播后,如果 暂停音乐,则调用updateNotification()更新方法
  1. private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {  
  2.        @Override  
  3.        public void onReceive(Context context, Intent intent) {  
  4.            String action = intent.getAction();  
  5.            String cmd = intent.getStringExtra("command");  
  6.            MusicUtils.debugLog("mIntentReceiver.onReceive " + action + " / " + cmd);  
  7.            Log.e(LOGTAG, "  mIntentReceiver   action = "+action+"   cmd ="+cmd+"===");  
  8.            if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {  
  9.                gotoNext(true);  
  10.            } else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {  
  11.                prev();  
  12.            } else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {  
  13.                if (isPlaying()) {  
  14.                    /*Begin: 开始修改*/  
  15.                   // pause(false); 暂停时不消除通知栏  
  16.                    pause(false);  
  17.                    updateNotification();  
  18.                    /*End: modified*/  
  19.                    mPausedByTransientLossOfFocus = false;  
  20.                } else {  
  21.                    play();  
  22.                }  
  23.                                ... 
原文地址:https://www.cnblogs.com/chengliu/p/4130630.html