Chromium Code Reviews| OLD | NEW |
|---|---|
| 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.Activity; | 7 import android.app.Activity; |
| 8 import android.content.Intent; | 8 import android.content.Intent; |
| 9 import android.graphics.Bitmap; | 9 import android.graphics.Bitmap; |
| 10 import android.media.AudioManager; | 10 import android.media.AudioManager; |
| 11 import android.text.TextUtils; | 11 import android.text.TextUtils; |
| 12 | 12 |
| 13 import org.chromium.base.ContextUtils; | 13 import org.chromium.base.ContextUtils; |
| 14 import org.chromium.base.Log; | 14 import org.chromium.base.Log; |
| 15 import org.chromium.base.VisibleForTesting; | |
| 15 import org.chromium.chrome.R; | 16 import org.chromium.chrome.R; |
| 16 import org.chromium.chrome.browser.metrics.MediaNotificationUma; | 17 import org.chromium.chrome.browser.metrics.MediaNotificationUma; |
| 17 import org.chromium.chrome.browser.metrics.MediaSessionUMA; | 18 import org.chromium.chrome.browser.metrics.MediaSessionUMA; |
| 18 import org.chromium.chrome.browser.tab.EmptyTabObserver; | 19 import org.chromium.chrome.browser.tab.EmptyTabObserver; |
| 19 import org.chromium.chrome.browser.tab.Tab; | 20 import org.chromium.chrome.browser.tab.Tab; |
| 20 import org.chromium.chrome.browser.tab.TabObserver; | 21 import org.chromium.chrome.browser.tab.TabObserver; |
| 21 import org.chromium.components.url_formatter.UrlFormatter; | 22 import org.chromium.components.url_formatter.UrlFormatter; |
| 23 import org.chromium.content_public.browser.MediaSessionDelegate; | |
| 22 import org.chromium.content_public.browser.WebContents; | 24 import org.chromium.content_public.browser.WebContents; |
| 23 import org.chromium.content_public.browser.WebContentsObserver; | |
| 24 import org.chromium.content_public.common.MediaMetadata; | 25 import org.chromium.content_public.common.MediaMetadata; |
| 25 import org.chromium.ui.base.WindowAndroid; | 26 import org.chromium.ui.base.WindowAndroid; |
| 26 | 27 |
| 27 import java.net.URI; | 28 import java.net.URI; |
| 28 import java.net.URISyntaxException; | 29 import java.net.URISyntaxException; |
| 30 import java.util.HashMap; | |
| 31 | |
| 32 import javax.annotation.Nullable; | |
| 29 | 33 |
| 30 /** | 34 /** |
| 31 * A tab helper responsible for enabling/disabling media controls and passing | 35 * A tab helper responsible for enabling/disabling media controls and passing |
| 32 * media actions from the controls to the {@link org.chromium.content.browser.Me diaSession} | 36 * media actions from the controls to the {@link org.chromium.content.browser.Me diaSession} |
| 33 */ | 37 */ |
| 34 public class MediaSessionTabHelper implements MediaImageCallback { | 38 public class MediaSessionTabHelper implements MediaImageCallback { |
| 35 private static final String TAG = "MediaSession"; | 39 private static final String TAG = "MediaSession"; |
| 36 | 40 |
| 37 private static final String UNICODE_PLAY_CHARACTER = "\u25B6"; | 41 private static final String UNICODE_PLAY_CHARACTER = "\u25B6"; |
| 38 private static final int MINIMAL_FAVICON_SIZE = 114; | 42 private static final int MINIMAL_FAVICON_SIZE = 114; |
| 39 | 43 |
| 40 private Tab mTab; | 44 private Tab mTab; |
| 41 private Bitmap mPageMediaImage = null; | 45 private Bitmap mPageMediaImage = null; |
| 42 private Bitmap mFavicon = null; | 46 private Bitmap mFavicon = null; |
| 43 private Bitmap mCurrentMediaImage = null; | 47 private Bitmap mCurrentMediaImage = null; |
| 44 private String mOrigin = null; | 48 private String mOrigin = null; |
| 45 private WebContents mWebContents; | 49 private WebContents mWebContents; |
| 46 private WebContentsObserver mWebContentsObserver; | 50 private MediaSessionDelegate mMediaSessionDelegate; |
| 47 private int mPreviousVolumeControlStream = AudioManager.USE_DEFAULT_STREAM_T YPE; | 51 private int mPreviousVolumeControlStream = AudioManager.USE_DEFAULT_STREAM_T YPE; |
| 48 private MediaNotificationInfo.Builder mNotificationInfoBuilder = null; | 52 private MediaNotificationInfo.Builder mNotificationInfoBuilder = null; |
| 49 // The fallback title if |mPageMetadata| is null or its title is empty. | 53 // The fallback title if |mPageMetadata| is null or its title is empty. |
| 50 private String mFallbackTitle = null; | 54 private String mFallbackTitle = null; |
| 51 // The metadata set by the page. | 55 // The metadata set by the page. |
| 52 private MediaMetadata mPageMetadata = null; | 56 private MediaMetadata mPageMetadata = null; |
| 53 // The currently showing metadata. | 57 // The currently showing metadata. |
| 54 private MediaMetadata mCurrentMetadata = null; | 58 private MediaMetadata mCurrentMetadata = null; |
| 55 private MediaImageManager mMediaImageManager = null; | 59 private MediaImageManager mMediaImageManager = null; |
| 56 | 60 |
| 61 private static HashMap<Tab, MediaSessionTabHelper> sTabToHelperMap = | |
|
whywhat
2016/10/20 22:56:53
having an extra method used just for testing is fi
Zhiqiang Zhang (Slow)
2016/10/21 13:43:52
Ahh, using SparseArray<> now, with TabId as key.
| |
| 62 new HashMap<Tab, MediaSessionTabHelper>(); | |
| 63 | |
| 64 @VisibleForTesting | |
| 65 @Nullable | |
| 66 static MediaSessionTabHelper getHelperFromTabForTesting(Tab tab) { | |
| 67 return sTabToHelperMap.get(tab); | |
| 68 } | |
| 69 | |
| 70 @VisibleForTesting | |
| 71 @Nullable | |
| 72 MediaSessionDelegate getMediaSessionDelegateForTesting() { | |
| 73 return mMediaSessionDelegate; | |
| 74 } | |
| 75 | |
| 57 private MediaNotificationListener mControlsListener = new MediaNotificationL istener() { | 76 private MediaNotificationListener mControlsListener = new MediaNotificationL istener() { |
| 58 @Override | 77 @Override |
| 59 public void onPlay(int actionSource) { | 78 public void onPlay(int actionSource) { |
| 60 MediaSessionUMA | 79 MediaSessionUMA |
| 61 .recordPlay(MediaSessionTabHelper.convertMediaActionSourceTo UMA(actionSource)); | 80 .recordPlay(MediaSessionTabHelper.convertMediaActionSourceTo UMA(actionSource)); |
| 62 | 81 if (mMediaSessionDelegate != null) mMediaSessionDelegate.resumeMedia Session(); |
| 63 mWebContents.resumeMediaSession(); | |
| 64 } | 82 } |
| 65 | 83 |
| 66 @Override | 84 @Override |
| 67 public void onPause(int actionSource) { | 85 public void onPause(int actionSource) { |
| 68 MediaSessionUMA.recordPause( | 86 MediaSessionUMA.recordPause( |
| 69 MediaSessionTabHelper.convertMediaActionSourceToUMA(actionSo urce)); | 87 MediaSessionTabHelper.convertMediaActionSourceToUMA(actionSo urce)); |
| 70 | 88 if (mMediaSessionDelegate != null) mMediaSessionDelegate.suspendMedi aSession(); |
| 71 mWebContents.suspendMediaSession(); | |
| 72 } | 89 } |
| 73 | 90 |
| 74 @Override | 91 @Override |
| 75 public void onStop(int actionSource) { | 92 public void onStop(int actionSource) { |
| 76 MediaSessionUMA | 93 MediaSessionUMA |
| 77 .recordStop(MediaSessionTabHelper.convertMediaActionSourceTo UMA(actionSource)); | 94 .recordStop(MediaSessionTabHelper.convertMediaActionSourceTo UMA(actionSource)); |
| 78 | 95 if (mMediaSessionDelegate != null) mMediaSessionDelegate.stopMediaSe ssion(); |
| 79 mWebContents.stopMediaSession(); | |
| 80 } | 96 } |
| 81 }; | 97 }; |
| 82 | 98 |
| 83 void hideNotification() { | 99 void hideNotification() { |
| 84 if (mTab == null) { | 100 if (mTab == null) { |
| 85 return; | 101 return; |
| 86 } | 102 } |
| 87 MediaNotificationManager.hide(mTab.getId(), R.id.media_playback_notifica tion); | 103 MediaNotificationManager.hide(mTab.getId(), R.id.media_playback_notifica tion); |
| 88 Activity activity = getActivityFromTab(mTab); | 104 Activity activity = getActivityFromTab(mTab); |
| 89 if (activity != null) { | 105 if (activity != null) { |
| 90 activity.setVolumeControlStream(mPreviousVolumeControlStream); | 106 activity.setVolumeControlStream(mPreviousVolumeControlStream); |
| 91 } | 107 } |
| 92 mNotificationInfoBuilder = null; | 108 mNotificationInfoBuilder = null; |
| 93 } | 109 } |
| 94 | 110 |
| 95 private WebContentsObserver createWebContentsObserver(WebContents webContent s) { | 111 private MediaSessionDelegate createMediaSessionDelegate(WebContents webConte nts) { |
| 96 return new WebContentsObserver(webContents) { | 112 MediaSessionDelegate delegate = new MediaSessionDelegate() { |
| 97 @Override | 113 @Override |
| 98 public void destroy() { | 114 public void mediaSessionDisconnected() { |
| 99 hideNotification(); | 115 hideNotification(); |
| 100 super.destroy(); | |
| 101 } | 116 } |
| 102 | 117 |
| 103 @Override | 118 @Override |
| 104 public void mediaSessionStateChanged(boolean isControllable, boolean isPaused) { | 119 public void mediaSessionStateChanged(boolean isControllable, boolean isPaused) { |
| 105 if (!isControllable) { | 120 if (!isControllable) { |
| 106 hideNotification(); | 121 hideNotification(); |
| 107 return; | 122 return; |
| 108 } | 123 } |
| 109 | 124 |
| 110 Intent contentIntent = Tab.createBringTabToFrontIntent(mTab.getI d()); | 125 Intent contentIntent = Tab.createBringTabToFrontIntent(mTab.getI d()); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 143 @Override | 158 @Override |
| 144 public void mediaSessionMetadataChanged(MediaMetadata metadata) { | 159 public void mediaSessionMetadataChanged(MediaMetadata metadata) { |
| 145 mPageMetadata = metadata; | 160 mPageMetadata = metadata; |
| 146 if (mPageMetadata != null) { | 161 if (mPageMetadata != null) { |
| 147 mMediaImageManager.downloadImage(mPageMetadata.getArtwork(), | 162 mMediaImageManager.downloadImage(mPageMetadata.getArtwork(), |
| 148 MediaSessionTabHelper.this); | 163 MediaSessionTabHelper.this); |
| 149 } | 164 } |
| 150 updateNotificationMetadata(); | 165 updateNotificationMetadata(); |
| 151 } | 166 } |
| 152 }; | 167 }; |
| 168 webContents.addMediaSessionDelegate(delegate); | |
|
whywhat
2016/10/20 22:56:53
nit: why "add" can WC have more than one delegate?
Zhiqiang Zhang (Slow)
2016/10/21 13:43:52
Currently not. Using "set" might make content::Med
| |
| 169 return delegate; | |
| 153 } | 170 } |
| 154 | 171 |
| 155 private void setWebContents(WebContents webContents) { | 172 private void setWebContents(WebContents webContents) { |
| 156 if (mWebContents == webContents) return; | 173 if (mWebContents == webContents) return; |
|
whywhat
2016/10/20 22:56:53
seems like we don't need mWebContents here, we cou
Zhiqiang Zhang (Slow)
2016/10/21 13:43:52
Maybe we can do it in a separate CL?
| |
| 157 | 174 |
| 158 cleanupWebContents(); | 175 cleanupWebContents(); |
| 159 mWebContents = webContents; | 176 mWebContents = webContents; |
| 160 if (mWebContents != null) mWebContentsObserver = createWebContentsObserv er(mWebContents); | 177 if (mWebContents != null) { |
| 178 if (mMediaSessionDelegate != null) mMediaSessionDelegate.disconnectM ediaSession(); | |
|
whywhat
2016/10/20 22:56:53
this will always be null after cleanupWebContents.
Zhiqiang Zhang (Slow)
2016/10/21 13:43:52
Done.
| |
| 179 | |
| 180 mMediaSessionDelegate = createMediaSessionDelegate(mWebContents); | |
| 181 } | |
| 161 mMediaImageManager.setWebContents(mWebContents); | 182 mMediaImageManager.setWebContents(mWebContents); |
| 162 } | 183 } |
| 163 | 184 |
| 164 private void cleanupWebContents() { | 185 private void cleanupWebContents() { |
| 165 if (mWebContentsObserver != null) mWebContentsObserver.destroy(); | 186 if (mMediaSessionDelegate != null) mMediaSessionDelegate.disconnectMedia Session(); |
| 166 mWebContentsObserver = null; | 187 mMediaSessionDelegate = null; |
| 167 mWebContents = null; | 188 mWebContents = null; |
| 168 } | 189 } |
| 169 | 190 |
| 170 private final TabObserver mTabObserver = new EmptyTabObserver() { | 191 private final TabObserver mTabObserver = new EmptyTabObserver() { |
| 171 @Override | 192 @Override |
| 172 public void onContentChanged(Tab tab) { | 193 public void onContentChanged(Tab tab) { |
| 173 assert tab == mTab; | 194 assert tab == mTab; |
| 174 setWebContents(tab.getWebContents()); | 195 setWebContents(tab.getWebContents()); |
| 175 } | 196 } |
| 176 | 197 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 220 | 241 |
| 221 @Override | 242 @Override |
| 222 public void onDestroyed(Tab tab) { | 243 public void onDestroyed(Tab tab) { |
| 223 assert mTab == tab; | 244 assert mTab == tab; |
| 224 | 245 |
| 225 cleanupWebContents(); | 246 cleanupWebContents(); |
| 226 | 247 |
| 227 hideNotification(); | 248 hideNotification(); |
| 228 mTab.removeObserver(this); | 249 mTab.removeObserver(this); |
| 229 mTab = null; | 250 mTab = null; |
| 251 sTabToHelperMap.remove(tab); | |
| 230 } | 252 } |
| 231 }; | 253 }; |
| 232 | 254 |
| 233 private MediaSessionTabHelper(Tab tab) { | 255 private MediaSessionTabHelper(Tab tab) { |
| 234 mTab = tab; | 256 mTab = tab; |
| 235 mTab.addObserver(mTabObserver); | 257 mTab.addObserver(mTabObserver); |
| 236 mMediaImageManager = new MediaImageManager( | 258 mMediaImageManager = new MediaImageManager( |
| 237 MINIMAL_FAVICON_SIZE, MediaNotificationManager.getMaximumLargeIconSi ze()); | 259 MINIMAL_FAVICON_SIZE, MediaNotificationManager.getMaximumLargeIconSi ze()); |
| 238 if (mTab.getWebContents() != null) setWebContents(tab.getWebContents()); | 260 if (mTab.getWebContents() != null) setWebContents(tab.getWebContents()); |
| 239 | 261 |
| 240 Activity activity = getActivityFromTab(mTab); | 262 Activity activity = getActivityFromTab(mTab); |
| 241 if (activity != null) { | 263 if (activity != null) { |
| 242 mPreviousVolumeControlStream = activity.getVolumeControlStream(); | 264 mPreviousVolumeControlStream = activity.getVolumeControlStream(); |
| 243 } | 265 } |
| 266 sTabToHelperMap.put(tab, this); | |
| 244 } | 267 } |
| 245 | 268 |
| 246 /** | 269 /** |
| 247 * Creates the {@link MediaSessionTabHelper} for the given {@link Tab}. | 270 * Creates the {@link MediaSessionTabHelper} for the given {@link Tab}. |
| 248 * @param tab the tab to attach the helper to. | 271 * @param tab the tab to attach the helper to. |
| 249 */ | 272 */ |
| 250 public static void createForTab(Tab tab) { | 273 public static void createForTab(Tab tab) { |
| 251 new MediaSessionTabHelper(tab); | 274 new MediaSessionTabHelper(tab); |
| 252 } | 275 } |
| 253 | 276 |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 364 if (mNotificationInfoBuilder == null) return; | 387 if (mNotificationInfoBuilder == null) return; |
| 365 mNotificationInfoBuilder.setLargeIcon(mCurrentMediaImage); | 388 mNotificationInfoBuilder.setLargeIcon(mCurrentMediaImage); |
| 366 MediaNotificationManager.show( | 389 MediaNotificationManager.show( |
| 367 ContextUtils.getApplicationContext(), mNotificationInfoBuilder.b uild()); | 390 ContextUtils.getApplicationContext(), mNotificationInfoBuilder.b uild()); |
| 368 } | 391 } |
| 369 | 392 |
| 370 private Bitmap getNotificationImage() { | 393 private Bitmap getNotificationImage() { |
| 371 return (mPageMediaImage != null) ? mPageMediaImage : mFavicon; | 394 return (mPageMediaImage != null) ? mPageMediaImage : mFavicon; |
| 372 } | 395 } |
| 373 } | 396 } |
| OLD | NEW |