Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(171)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java

Issue 1431023004: [Media, Android] Fix lockscreen and other remote controls on KK- (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Handle invalid intent. Remove an else. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.media.ui; 5 package org.chromium.chrome.browser.media.ui;
6 6
7 import android.app.Notification; 7 import android.app.Notification;
8 import android.app.PendingIntent; 8 import android.app.PendingIntent;
9 import android.app.Service; 9 import android.app.Service;
10 import android.content.ComponentName; 10 import android.content.ComponentName;
11 import android.content.Context; 11 import android.content.Context;
12 import android.content.Intent; 12 import android.content.Intent;
13 import android.graphics.Bitmap; 13 import android.graphics.Bitmap;
14 import android.graphics.drawable.BitmapDrawable; 14 import android.graphics.drawable.BitmapDrawable;
15 import android.graphics.drawable.Drawable; 15 import android.graphics.drawable.Drawable;
16 import android.os.Build; 16 import android.os.Build;
17 import android.os.IBinder; 17 import android.os.IBinder;
18 import android.support.v4.app.NotificationCompat; 18 import android.support.v4.app.NotificationCompat;
19 import android.support.v4.app.NotificationManagerCompat; 19 import android.support.v4.app.NotificationManagerCompat;
20 import android.support.v4.media.MediaMetadataCompat; 20 import android.support.v4.media.MediaMetadataCompat;
21 import android.support.v4.media.session.MediaSessionCompat; 21 import android.support.v4.media.session.MediaSessionCompat;
22 import android.support.v4.media.session.PlaybackStateCompat; 22 import android.support.v4.media.session.PlaybackStateCompat;
23 import android.util.SparseArray; 23 import android.util.SparseArray;
24 import android.view.KeyEvent; 24 import android.view.KeyEvent;
25 import android.view.View; 25 import android.view.View;
26 import android.widget.RemoteViews; 26 import android.widget.RemoteViews;
27 27
28 import org.chromium.base.ApiCompatibilityUtils; 28 import org.chromium.base.ApiCompatibilityUtils;
29 import org.chromium.base.Log;
29 import org.chromium.chrome.R; 30 import org.chromium.chrome.R;
30 import org.chromium.chrome.browser.tab.Tab; 31 import org.chromium.chrome.browser.tab.Tab;
31 32
32 /** 33 /**
33 * A class for notifications that provide information and optional media control s for a given media. 34 * A class for notifications that provide information and optional media control s for a given media.
34 * Internally implements a Service for transforming notification Intents into 35 * Internally implements a Service for transforming notification Intents into
35 * {@link MediaNotificationListener} calls for all registered listeners. 36 * {@link MediaNotificationListener} calls for all registered listeners.
36 * There's one service started for a distinct notification id. 37 * There's one service started for a distinct notification id.
37 */ 38 */
38 public class MediaNotificationManager { 39 public class MediaNotificationManager {
39 40
41 private static final String TAG = "cr_MediaNotification";
42
40 // We're always used on the UI thread but the LOCK is required by lint when creating the 43 // We're always used on the UI thread but the LOCK is required by lint when creating the
41 // singleton. 44 // singleton.
42 private static final Object LOCK = new Object(); 45 private static final Object LOCK = new Object();
43 46
44 // Maps the notification ids to their corresponding notification managers. 47 // Maps the notification ids to their corresponding notification managers.
45 private static SparseArray<MediaNotificationManager> sManagers; 48 private static SparseArray<MediaNotificationManager> sManagers;
46 49
47 /** 50 /**
48 * Service used to transform intent requests triggered from the notification into 51 * Service used to transform intent requests triggered from the notification into
49 * {@code MediaNotificationListener} callbacks. We have to create a separate derived class for 52 * {@code MediaNotificationListener} callbacks. We have to create a separate derived class for
50 * each type of notification since one class corresponds to one instance of the service only. 53 * each type of notification since one class corresponds to one instance of the service only.
51 */ 54 */
52 private abstract static class ListenerService extends Service { 55 private abstract static class ListenerService extends Service {
53 private static final String ACTION_PLAY = 56 private static final String ACTION_PLAY =
54 "MediaNotificationManager.ListenerService.PLAY"; 57 "MediaNotificationManager.ListenerService.PLAY";
55 private static final String ACTION_PAUSE = 58 private static final String ACTION_PAUSE =
56 "MediaNotificationManager.ListenerService.PAUSE"; 59 "MediaNotificationManager.ListenerService.PAUSE";
57 private static final String ACTION_STOP = 60 private static final String ACTION_STOP =
58 "MediaNotificationManager.ListenerService.STOP"; 61 "MediaNotificationManager.ListenerService.STOP";
59 private static final String EXTRA_NOTIFICATION_ID = 62 private static final String EXTRA_NOTIFICATION_ID =
60 "MediaNotificationManager.ListenerService.NOTIFICATION_ID"; 63 MediaButtonReceiver.EXTRA_NOTIFICATION_ID;
61 64
62 // The notification id this service instance corresponds to. 65 // The notification id this service instance corresponds to.
63 private int mNotificationId = MediaNotificationInfo.INVALID_ID; 66 private int mNotificationId = MediaNotificationInfo.INVALID_ID;
64 67
65 private PendingIntent getPendingIntent(String action) { 68 private PendingIntent getPendingIntent(String action) {
66 Intent intent = getIntent(this, mNotificationId).setAction(action); 69 Intent intent = getIntent(this, mNotificationId).setAction(action);
67 return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_ CANCEL_CURRENT); 70 return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_ CANCEL_CURRENT);
68 } 71 }
69 72
70 @Override 73 @Override
(...skipping 14 matching lines...) Expand all
85 @Override 88 @Override
86 public int onStartCommand(Intent intent, int flags, int startId) { 89 public int onStartCommand(Intent intent, int flags, int startId) {
87 if (!processIntent(intent)) stopSelf(); 90 if (!processIntent(intent)) stopSelf();
88 91
89 return START_NOT_STICKY; 92 return START_NOT_STICKY;
90 } 93 }
91 94
92 private boolean processIntent(Intent intent) { 95 private boolean processIntent(Intent intent) {
93 if (intent == null) return false; 96 if (intent == null) return false;
94 97
95 mNotificationId = intent.getIntExtra( 98 int notificationId = intent.getIntExtra(
96 EXTRA_NOTIFICATION_ID, MediaNotificationInfo.INVALID_ID); 99 EXTRA_NOTIFICATION_ID, MediaNotificationInfo.INVALID_ID);
97 if (mNotificationId == MediaNotificationInfo.INVALID_ID) return fals e; 100
101 // The notification id must always be valid and should match the fir st notification id
102 // the service got via the intent.
103 if (notificationId == MediaNotificationInfo.INVALID_ID
104 || (mNotificationId != MediaNotificationInfo.INVALID_ID
105 && mNotificationId != notificationId)) {
106 Log.w(TAG, "The service intent's notification id is invalid: ", notificationId);
107 return false;
108 }
109
110 // Either the notification id matches or it's the first intent we've got.
111 mNotificationId = notificationId;
112
113 assert mNotificationId != MediaNotificationInfo.INVALID_ID;
98 114
99 MediaNotificationManager manager = getManager(mNotificationId); 115 MediaNotificationManager manager = getManager(mNotificationId);
100 if (manager == null || manager.mMediaNotificationInfo == null) retur n false; 116 if (manager == null || manager.mMediaNotificationInfo == null) retur n false;
101 117
102 manager.onServiceStarted(this); 118 manager.onServiceStarted(this);
103 119
104 processAction(intent, manager); 120 processAction(intent, manager);
105 return true; 121 return true;
106 } 122 }
107 123
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 private static final int NOTIFICATION_ID = R.id.media_playback_notificat ion; 173 private static final int NOTIFICATION_ID = R.id.media_playback_notificat ion;
158 } 174 }
159 175
160 /** 176 /**
161 * This class is used internally but have to be public to be able to launch the service. 177 * This class is used internally but have to be public to be able to launch the service.
162 */ 178 */
163 public static final class PresentationListenerService extends ListenerServic e { 179 public static final class PresentationListenerService extends ListenerServic e {
164 private static final int NOTIFICATION_ID = R.id.presentation_notificatio n; 180 private static final int NOTIFICATION_ID = R.id.presentation_notificatio n;
165 } 181 }
166 182
183 // Two classes to specify the right notification id in the intent.
184
185 /**
186 * This class is used internally but have to be public to be able to launch the service.
187 */
188 public static final class PlaybackMediaButtonReceiver extends MediaButtonRec eiver {
189 @Override
190 public int getNotificationId() {
191 return PlaybackListenerService.NOTIFICATION_ID;
192 }
193 }
194
195 /**
196 * This class is used internally but have to be public to be able to launch the service.
197 */
198 public static final class PresentationMediaButtonReceiver extends MediaButto nReceiver {
199 @Override
200 public int getNotificationId() {
201 return PresentationListenerService.NOTIFICATION_ID;
202 }
203 }
204
167 private static Intent getIntent(Context context, int notificationId) { 205 private static Intent getIntent(Context context, int notificationId) {
168 Intent intent = null; 206 Intent intent = null;
169 if (notificationId == PlaybackListenerService.NOTIFICATION_ID) { 207 if (notificationId == PlaybackListenerService.NOTIFICATION_ID) {
170 intent = new Intent(context, PlaybackListenerService.class); 208 intent = new Intent(context, PlaybackListenerService.class);
171 } else if (notificationId == PresentationListenerService.NOTIFICATION_ID ) { 209 } else if (notificationId == PresentationListenerService.NOTIFICATION_ID ) {
172 intent = new Intent(context, PresentationListenerService.class); 210 intent = new Intent(context, PresentationListenerService.class);
173 } else { 211 } else {
174 return null; 212 return null;
175 } 213 }
176 return intent.putExtra(ListenerService.EXTRA_NOTIFICATION_ID, notificati onId); 214 return intent.putExtra(ListenerService.EXTRA_NOTIFICATION_ID, notificati onId);
177 } 215 }
178 216
217 private static String getButtonReceiverClassName(int notificationId) {
218 if (notificationId == PlaybackListenerService.NOTIFICATION_ID) {
219 return PlaybackMediaButtonReceiver.class.getName();
220 }
221
222 if (notificationId == PresentationListenerService.NOTIFICATION_ID) {
223 return PresentationMediaButtonReceiver.class.getName();
224 }
225
226 assert false;
227 return null;
228 }
229
179 /** 230 /**
180 * Shows the notification with media controls with the specified media info. Replaces/updates 231 * Shows the notification with media controls with the specified media info. Replaces/updates
181 * the current notification if already showing. Does nothing if |mediaNotifi cationInfo| hasn't 232 * the current notification if already showing. Does nothing if |mediaNotifi cationInfo| hasn't
182 * changed from the last one. 233 * changed from the last one.
183 * 234 *
184 * @param applicationContext context to create the notification with 235 * @param applicationContext context to create the notification with
185 * @param notificationInfoBuilder information to show in the notification 236 * @param notificationInfoBuilder information to show in the notification
186 */ 237 */
187 public static void show(Context applicationContext, 238 public static void show(Context applicationContext,
188 MediaNotificationInfo.Builder notificationInfoBuilde r) { 239 MediaNotificationInfo.Builder notificationInfoBuilde r) {
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after
528 } else { 579 } else {
529 mService.startForeground(mMediaNotificationInfo.id, notification); 580 mService.startForeground(mMediaNotificationInfo.id, notification);
530 } 581 }
531 } 582 }
532 583
533 private MediaSessionCompat createMediaSession() { 584 private MediaSessionCompat createMediaSession() {
534 MediaSessionCompat mediaSession = new MediaSessionCompat( 585 MediaSessionCompat mediaSession = new MediaSessionCompat(
535 mContext, 586 mContext,
536 mContext.getString(R.string.app_name), 587 mContext.getString(R.string.app_name),
537 new ComponentName(mContext.getPackageName(), 588 new ComponentName(mContext.getPackageName(),
538 MediaButtonReceiver.class.getName()), 589 getButtonReceiverClassName(mMediaNotificationInfo.id)),
539 null); 590 null);
540 mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS 591 mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
541 | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); 592 | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
542 mediaSession.setCallback(mMediaSessionCallback); 593 mediaSession.setCallback(mMediaSessionCallback);
543 594
544 // TODO(mlamouri): the following code is to work around a bug that hopef ully 595 // TODO(mlamouri): the following code is to work around a bug that hopef ully
545 // MediaSessionCompat will handle directly. see b/24051980. 596 // MediaSessionCompat will handle directly. see b/24051980.
546 try { 597 try {
547 mediaSession.setActive(true); 598 mediaSession.setActive(true);
548 } catch (NullPointerException e) { 599 } catch (NullPointerException e) {
549 // Some versions of KitKat do not support AudioManager.registerMedia ButtonIntent 600 // Some versions of KitKat do not support AudioManager.registerMedia ButtonIntent
550 // with a PendingIntent. They will throw a NullPointerException, in which case 601 // with a PendingIntent. They will throw a NullPointerException, in which case
551 // they should be able to activate a MediaSessionCompat with only tr ansport 602 // they should be able to activate a MediaSessionCompat with only tr ansport
552 // controls. 603 // controls.
553 mediaSession.setActive(false); 604 mediaSession.setActive(false);
554 mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONT ROLS); 605 mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONT ROLS);
555 mediaSession.setActive(true); 606 mediaSession.setActive(true);
556 } 607 }
557 return mediaSession; 608 return mediaSession;
558 } 609 }
559 610
560 private Bitmap drawableToBitmap(Drawable drawable) { 611 private Bitmap drawableToBitmap(Drawable drawable) {
561 if (!(drawable instanceof BitmapDrawable)) return null; 612 if (!(drawable instanceof BitmapDrawable)) return null;
562 613
563 BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; 614 BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
564 return bitmapDrawable.getBitmap(); 615 return bitmapDrawable.getBitmap();
565 } 616 }
566 } 617 }
OLDNEW
« no previous file with comments | « chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698