本文根据原生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. false;
8. notifyChange(PLAYSTATE_CHANGED);
9. saveBookmarkIfNeeded();
10. }
11. }
12. }
13.
14. private void gotoIdleState(boolean isStopForeground) {
15. 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. "command");
6. "mIntentReceiver.onReceive " + action + " / " + cmd);
7. if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
8. 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. false);
15. updateNotification();
16. /*End: */
17. false;
18. else {
19. play();
20. }
21. else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {
22. /*Begin: modified */
23. false);
24. updateNotification();
25. /*End:*/
26. 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. 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. 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. 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. this, 0,
47. new Intent("com.android.music.PLAYBACK_VIEWER")
48. 0);
49. startForeground(PLAYBACKSERVICE_STATUS, status);
50. }
添加的点击事件函数:
1. /*Begin: modify 利用MediaPlaybackService 现有的广播监听 实现播放控制 */
2. //上一首,
3. private PendingIntent pre_PendingIntent(){
4. new Intent(PREVIOUS_ACTION);
5. this, 0, intent, 0);
6. return pendingIntent;
7. }
8. //暂停开始
9. private PendingIntent pause_PendingIntent(){
10. new Intent(TOGGLEPAUSE_ACTION);
11. this, 0, intent, 0);
12. return pendingIntent;
13. }
14. //下一首
15. private PendingIntent next_PendingIntent(){
16. new Intent(NEXT_ACTION);
17. this, 0, 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. 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. "command");
6. "mIntentReceiver.onReceive " + action + " / " + cmd);
7. " mIntentReceiver action = "+action+" cmd ="+cmd+"===");
8. if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
9. 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. false);
17. updateNotification();
18. /*End: modified*/
19. false;
20. else {
21. play();
22. }
23. ...