OLD | NEW |
---|---|
(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 } | |
OLD | NEW |