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

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

Issue 1159113006: [Android] A prototype of the interactive media notification. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed Min's nits Created 5 years, 5 months 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.media.ui;
6
7 import android.app.Notification;
8 import android.app.PendingIntent;
9 import android.app.Service;
10 import android.content.Context;
11 import android.content.Intent;
12 import android.os.IBinder;
13 import android.provider.Browser;
14 import android.support.v4.app.NotificationCompat;
15 import android.support.v4.app.NotificationManagerCompat;
16 import android.widget.RemoteViews;
17
18 import org.chromium.chrome.R;
19 import org.chromium.chrome.browser.IntentHandler.TabOpenType;
20 import org.chromium.chrome.browser.Tab;
21
22 /**
23 * A class for notifications that provide information and optional media control s for a given media.
24 * Internally implements a Service for transforming notification Intents into
25 * {@link MediaPlaybackListener} calls for all registered listeners.
26 */
27 public class NotificationMediaPlaybackControls {
28 private static final Object LOCK = new Object();
29 private static final int MSG_ID_UPDATE_NOTIFICATION = 100;
Ted C 2015/07/06 22:18:28 not used
whywhat 2015/07/07 15:44:58 Done.
30 private static NotificationMediaPlaybackControls sInstance;
31
32 /**
33 * Service used to transform intent requests triggered from the notification into
34 * {@code Listener} callbacks. Ideally this class should be private, but pub lic is required to
35 * create as a service.
36 */
37 public static class ListenerService extends Service {
38 private static final String ACTION_PLAY =
39 "NotificationMediaPlaybackControls.ListenerService.PLAY";
40 private static final String ACTION_PAUSE =
41 "NotificationMediaPlaybackControls.ListenerService.PAUSE";
42
43 private PendingIntent getPendingIntent(String action) {
44 Intent intent = new Intent(this, ListenerService.class);
45 intent.setAction(action);
46 return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_ CANCEL_CURRENT);
47 }
48
49 @Override
50 public IBinder onBind(Intent intent) {
51 return null;
52 }
53
54 @Override
55 public void onCreate() {
56 super.onCreate();
57 onServiceStarted(this);
58 }
59
60 @Override
61 public void onDestroy() {
Ted C 2015/07/06 22:18:29 should you call super.onDestroy() here as well?
whywhat 2015/07/07 15:44:58 Done.
62 onServiceDestroyed();
63 }
64
65 @Override
66 public int onStartCommand(Intent intent, int flags, int startId) {
67 if (intent == null
68 || sInstance == null
69 || sInstance.mMediaInfo == null
70 || sInstance.mMediaInfo.listener == null) {
71 stopSelf();
72 return START_NOT_STICKY;
73 }
74
75 String action = intent.getAction();
76 if (ACTION_PLAY.equals(action)) {
77 sInstance.mMediaInfo.listener.onPlay();
78 sInstance.onPlaybackStateChanged(false);
79 } else if (ACTION_PAUSE.equals(action)) {
80 sInstance.mMediaInfo.listener.onPause();
81 sInstance.onPlaybackStateChanged(true);
82 }
83
84 return START_NOT_STICKY;
85 }
86 }
87
88 /**
89 * Shows the notification with media controls with the specified media info. Replaces/updates
90 * the current notification if already showing. Does nothing if |mediaInfo| hasn't changed from
91 * the last one.
92 *
93 * @param applicationContext context to create the notification with
94 * @param mediaInfo information to show in the notification
95 */
96 public static void show(Context applicationContext, MediaInfo mediaInfo) {
97 synchronized (LOCK) {
98 if (sInstance == null) {
99 sInstance = new NotificationMediaPlaybackControls(applicationCon text);
100 }
101 }
102 sInstance.showNotification(mediaInfo);
103 }
104
105 /**
106 * Hides the notification for the specified tabId. If tabId equals to
107 * {@link Tab#INVALID_TAB_ID}, hides any notification shown.
108 *
109 * @param tabId the id of the tab that showed the notification or invalid ta b id.
110 */
111 public static void hide(int tabId) {
112 if (sInstance == null) return;
113 sInstance.hideNotification(tabId);
114 }
115
116 /**
117 * Registers the started {@link Service} with the singleton and creates the notification.
118 *
119 * @param service the service that was started
120 */
121 private static void onServiceStarted(ListenerService service) {
122 assert sInstance != null;
123 assert sInstance.mService == null;
124 sInstance.mService = service;
125 sInstance.createNotification();
126 }
127
128 /**
129 * Handles the destruction
130 */
131 private static void onServiceDestroyed() {
132 assert sInstance != null;
133 assert sInstance.mService != null;
134 sInstance.destroyNotification();
135 sInstance.mService = null;
136 }
137
138 private final Context mContext;
139
140 // ListenerService running for the notification. Only non-null when showing.
141 private ListenerService mService;
142
143 private final String mPlayDescription;
144
145 private final String mPauseDescription;
146
147 private Notification mNotification;
148
149 private MediaInfo mMediaInfo;
150
151 private NotificationMediaPlaybackControls(Context context) {
152 mContext = context;
153 mPlayDescription = context.getResources().getString(R.string.accessibili ty_play);
154 mPauseDescription = context.getResources().getString(R.string.accessibil ity_pause);
155 }
156
157 private void showNotification(MediaInfo mediaInfo) {
158 mContext.startService(new Intent(mContext, ListenerService.class));
159
160 assert mediaInfo != null;
161
162 if (mediaInfo.equals(mMediaInfo)) return;
163
164 mMediaInfo = mediaInfo;
165 updateNotification();
166 }
167
168 private void hideNotification(int tabId) {
169 if (mMediaInfo == null || (tabId != Tab.INVALID_TAB_ID && tabId != mMedi aInfo.tabId)) {
Ted C 2015/07/06 22:18:28 If you have a problem with a dangling notification
whywhat 2015/07/07 15:44:58 Done.
170 return;
171 }
172
173 mMediaInfo = null;
174 mContext.stopService(new Intent(mContext, ListenerService.class));
175 }
176
177 private void onPlaybackStateChanged(boolean isPaused) {
178 assert mMediaInfo != null;
179 mMediaInfo = new MediaInfo(
180 mMediaInfo.title,
181 isPaused,
182 mMediaInfo.origin,
183 mMediaInfo.tabId,
184 mMediaInfo.listener);
185 updateNotification();
186 }
187
188 private RemoteViews createContentView() {
189 RemoteViews contentView =
190 new RemoteViews(getContext().getPackageName(), R.layout.playback _notification_bar);
191 return contentView;
192 }
193
194 private void createNotification() {
195 NotificationCompat.Builder notificationBuilder =
Ted C 2015/07/06 22:18:28 I would just inline this in updateNotification. Y
whywhat 2015/07/07 15:44:58 Done.
196 new NotificationCompat.Builder(getContext())
197 .setSmallIcon(R.drawable.audio_playing)
198 .setAutoCancel(false)
199 .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
200 .setOngoing(true)
201 .setContent(createContentView())
202 .setContentIntent(createContentIntent());
203 mNotification = notificationBuilder.build();
204 updateNotification();
205 }
206
207 private void destroyNotification() {
208 NotificationManagerCompat manager = NotificationManagerCompat.from(getCo ntext());
209 manager.cancel(R.id.media_playback_notification);
210
211 mNotification = null;
212 }
213
214 private final Context getContext() {
Ted C 2015/07/06 22:18:28 remove this and just inline mContext everywhere.
whywhat 2015/07/07 15:44:57 Done.
215 return mContext;
216 }
217
218 private String getStatus() {
219 Context context = getContext();
220 return context.getString(R.string.media_notification_link_text,
221 mMediaInfo.origin != null ? mMediaInfo.origin : "");
Ted C 2015/07/06 22:18:28 You should probably have some Unknown URL or somet
whywhat 2015/07/07 15:44:57 Done.
222 }
223
224 private String getTitle() {
225 Context context = getContext();
226 String mediaTitle = mMediaInfo.title;
227 if (mMediaInfo.isPaused) {
228 return context.getString(
229 R.string.media_playback_notification_paused_for_media, media Title);
230 }
231 return context.getString(
232 R.string.media_playback_notification_playing_for_media, mediaTit le);
233 }
234
235 private PendingIntent createContentIntent() {
236 int tabId = mMediaInfo.tabId;
237 Intent intent = new Intent(Intent.ACTION_MAIN);
238 intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageNam e());
239 intent.putExtra(TabOpenType.BRING_TAB_TO_FRONT.name(), tabId);
240 intent.setPackage(mContext.getPackageName());
241 return PendingIntent.getActivity(getContext(), tabId, intent, 0);
242 }
243
244 private void updateNotification() {
245 if (mService == null) return;
246
247 if (mMediaInfo == null) {
248 // Notification was hidden before we could update it.
249 assert mNotification == null;
250 return;
251 }
252
253 RemoteViews contentView = createContentView();
254
255 contentView.setTextViewText(R.id.title, getTitle());
256 contentView.setTextViewText(R.id.status, getStatus());
257 contentView.setImageViewResource(R.id.icon, R.drawable.audio_playing);
258
259 if (mMediaInfo.isPaused) {
260 contentView.setImageViewResource(R.id.playpause, R.drawable.ic_vidco ntrol_play);
261 contentView.setContentDescription(R.id.playpause, mPlayDescription);
262 contentView.setOnClickPendingIntent(R.id.playpause,
263 mService.getPendingIntent(ListenerService.ACTION_PLAY));
264 } else {
265 contentView.setImageViewResource(R.id.playpause, R.drawable.ic_vidco ntrol_pause);
266 contentView.setContentDescription(R.id.playpause, mPauseDescription) ;
267 contentView.setOnClickPendingIntent(R.id.playpause,
268 mService.getPendingIntent(ListenerService.ACTION_PAUSE));
269 }
270
271 mNotification.contentView = contentView;
272
273 NotificationManagerCompat manager = NotificationManagerCompat.from(getCo ntext());
274 manager.notify(R.id.media_playback_notification, mNotification);
Ted C 2015/07/06 22:18:28 I would use a notification namespace here (and it'
whywhat 2015/07/07 15:44:58 So we actually don't need NotificationManager at a
275
276 mService.startForeground(R.id.media_playback_notification, mNotification );
277 }
278 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698