1. 程式人生 > >Android 4.1 Music 通知欄的音樂控制

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. privatevoid 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.     private
    void gotoIdleState(boolean isStopForeground) {  
  14.         mDelayedStopHandler.removeCallbacksAndMessages(null);  
  15.         Message msg = mDelayedStopHandler.obtainMessage();  
  16.         mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);  
  17.        //此方法控制消除通知
  18.         stopForeground(isStopForeground);  
  19.     }  
1.2 接收暫停廣播的時候將pause()方法換為:pause(false)
  1. private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {  
  2.         @Override
  3.         publicvoid 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.             } elseif (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {  
  10.                 prev();  
  11.             } elseif (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.             } elseif (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {  
  22.                 /*Begin: modified  */
  23.                 pause(false);  
  24.                 updateNotification();  
  25.                 /*End:*/
  26.                 mPausedByTransientLossOfFocus = false;  
  27.             } elseif (CMDPLAY.equals(cmd)) {  
  28.                               ...  

2 Music 音樂通知控制 

2.1 通知的佈局檔案 /packages/apps/Music/res/layout/statusbar.xml

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


2.2  通知欄控制:

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

 updateNotification()為傳送的通知程式碼,在這裡面修改

  1. privatevoid 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.             views.setTextViewText(R.id.artistalbum,  
  20.                     getString(R.string.notification_artist_album, artist, album)  
  21.                     );  
  22.         }  
  23.         Notification status = new Notification();  
  24.         /*Begin: modified 新增修改程式碼*/
  25.         //status_icon 為狀態列圖示,R.id.statusbar_pause 為通知 的 暫停/開始按鈕
  26.         int status_icon = R.drawable.ic_appwidget_music_play;  
  27.         //根據播放狀態,來決定 暫停/開始 圖示
  28.         if(isPlaying()){  
  29.             views.setImageViewResource(R.id.statusbar_pause, R.drawable.ic_appwidget_music_pause);   
  30.         }else{  
  31.             views.setImageViewResource(R.id.statusbar_pause, R.drawable.ic_appwidget_music_play);  
  32.             status_icon = R.drawable.ic_appwidget_music_pause;  
  33.          }  
  34.          // 給通知欄 上一首 按鈕 新增點選事件  
  35.         views.setOnClickPendingIntent(R.id.statusbar_prev, pre_PendingIntent());  
  36.         //給通知欄 暫停/開始 按鈕 新增點選事件 
  37.         views.setOnClickPendingIntent(R.id.statusbar_pause, pause_PendingIntent());  
  38.         //給通知欄 下一首 按鈕 新增點選事件
  39.         views.setOnClickPendingIntent(R.id.statusbar_next, next_PendingIntent());  
  40.         /*End:  */
  41.         status.contentView = views;  
  42.         status.flags |= Notification.FLAG_ONGOING_EVENT;  
  43.         status.icon = status_icon;  
  44.         status.contentIntent = PendingIntent.getActivity(this0,  
  45.                 new Intent("com.android.music.PLAYBACK_VIEWER")  
  46.                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0);  
  47.         startForeground(PLAYBACKSERVICE_STATUS, status);  
  48.     }  

新增的點選事件函式:

  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.     /*end:  */

為了實現暫停/開始按鈕通知欄圖示顯示與音樂播放 的一致 改了以下兩個地方:

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

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

之後

  1. publicvoid 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.        publicvoid 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.            } elseif (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {  
  11.                prev();  
  12.            } elseif (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.                                ...