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.graphics.Bitmap; |
8 import android.media.AudioManager; | 9 import android.media.AudioManager; |
| 10 import android.os.Build; |
9 import android.text.TextUtils; | 11 import android.text.TextUtils; |
10 | 12 |
11 import org.chromium.base.ApplicationStatus; | 13 import org.chromium.base.ApplicationStatus; |
12 import org.chromium.base.Log; | 14 import org.chromium.base.Log; |
13 import org.chromium.chrome.R; | 15 import org.chromium.chrome.R; |
| 16 import org.chromium.chrome.browser.ChromeFeatureList; |
14 import org.chromium.chrome.browser.metrics.MediaSessionUMA; | 17 import org.chromium.chrome.browser.metrics.MediaSessionUMA; |
15 import org.chromium.chrome.browser.tab.EmptyTabObserver; | 18 import org.chromium.chrome.browser.tab.EmptyTabObserver; |
16 import org.chromium.chrome.browser.tab.Tab; | 19 import org.chromium.chrome.browser.tab.Tab; |
17 import org.chromium.chrome.browser.tab.TabObserver; | 20 import org.chromium.chrome.browser.tab.TabObserver; |
18 import org.chromium.chrome.browser.util.UrlUtilities; | 21 import org.chromium.chrome.browser.util.UrlUtilities; |
19 import org.chromium.content_public.browser.WebContents; | 22 import org.chromium.content_public.browser.WebContents; |
20 import org.chromium.content_public.browser.WebContentsObserver; | 23 import org.chromium.content_public.browser.WebContentsObserver; |
21 import org.chromium.content_public.common.MediaMetadata; | 24 import org.chromium.content_public.common.MediaMetadata; |
22 import org.chromium.ui.base.WindowAndroid; | 25 import org.chromium.ui.base.WindowAndroid; |
23 | 26 |
24 import java.net.URI; | 27 import java.net.URI; |
25 import java.net.URISyntaxException; | 28 import java.net.URISyntaxException; |
26 | 29 |
27 /** | 30 /** |
28 * A tab helper responsible for enabling/disabling media controls and passing | 31 * A tab helper responsible for enabling/disabling media controls and passing |
29 * media actions from the controls to the {@link org.chromium.content.browser.Me
diaSession} | 32 * media actions from the controls to the {@link org.chromium.content.browser.Me
diaSession} |
30 */ | 33 */ |
31 public class MediaSessionTabHelper { | 34 public class MediaSessionTabHelper { |
32 private static final String TAG = "MediaSession"; | 35 private static final String TAG = "MediaSession"; |
33 | 36 |
34 private static final String UNICODE_PLAY_CHARACTER = "\u25B6"; | 37 private static final String UNICODE_PLAY_CHARACTER = "\u25B6"; |
35 | 38 |
36 private Tab mTab; | 39 private Tab mTab; |
| 40 private Bitmap mFavicon = null; |
| 41 private String mOrigin = null; |
37 private WebContents mWebContents; | 42 private WebContents mWebContents; |
38 private WebContentsObserver mWebContentsObserver; | 43 private WebContentsObserver mWebContentsObserver; |
39 private int mPreviousVolumeControlStream = AudioManager.USE_DEFAULT_STREAM_T
YPE; | 44 private int mPreviousVolumeControlStream = AudioManager.USE_DEFAULT_STREAM_T
YPE; |
40 private MediaNotificationInfo.Builder mNotificationInfoBuilder = null; | 45 private MediaNotificationInfo.Builder mNotificationInfoBuilder = null; |
41 private MediaMetadata mFallbackMetadata; | 46 private MediaMetadata mFallbackMetadata; |
42 | 47 |
43 private MediaNotificationListener mControlsListener = new MediaNotificationL
istener() { | 48 private MediaNotificationListener mControlsListener = new MediaNotificationL
istener() { |
44 @Override | 49 @Override |
45 public void onPlay(int actionSource) { | 50 public void onPlay(int actionSource) { |
46 MediaSessionUMA | 51 MediaSessionUMA |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 super.destroy(); | 91 super.destroy(); |
87 } | 92 } |
88 | 93 |
89 @Override | 94 @Override |
90 public void mediaSessionStateChanged(boolean isControllable, boolean
isPaused, | 95 public void mediaSessionStateChanged(boolean isControllable, boolean
isPaused, |
91 MediaMetadata metadata) { | 96 MediaMetadata metadata) { |
92 if (!isControllable) { | 97 if (!isControllable) { |
93 hideNotification(); | 98 hideNotification(); |
94 return; | 99 return; |
95 } | 100 } |
96 String origin = mTab.getUrl(); | |
97 try { | |
98 origin = UrlUtilities.formatUrlForSecurityDisplay(new URI(or
igin), true); | |
99 } catch (URISyntaxException e) { | |
100 Log.e(TAG, "Unable to parse the origin from the URL. " | |
101 + "Showing the full URL instead."); | |
102 } | |
103 | 101 |
104 mFallbackMetadata = null; | 102 mFallbackMetadata = null; |
105 | 103 |
106 // The page's title is used as a placeholder if no title is spec
ified in the | 104 // The page's title is used as a placeholder if no title is spec
ified in the |
107 // metadata. | 105 // metadata. |
108 if (TextUtils.isEmpty(metadata.getTitle())) { | 106 if (TextUtils.isEmpty(metadata.getTitle())) { |
109 mFallbackMetadata = new MediaMetadata( | 107 mFallbackMetadata = new MediaMetadata( |
110 sanitizeMediaTitle(mTab.getTitle()), | 108 sanitizeMediaTitle(mTab.getTitle()), |
111 metadata.getArtist(), | 109 metadata.getArtist(), |
112 metadata.getAlbum()); | 110 metadata.getAlbum()); |
113 metadata = mFallbackMetadata; | 111 metadata = mFallbackMetadata; |
114 } | 112 } |
115 | 113 |
116 mNotificationInfoBuilder = new MediaNotificationInfo.Builder() | 114 mNotificationInfoBuilder = |
117 .setMetadata(metadata) | 115 new MediaNotificationInfo.Builder() |
118 .setPaused(isPaused) | 116 .setMetadata(metadata) |
119 .setOrigin(origin) | 117 .setPaused(isPaused) |
120 .setTabId(mTab.getId()) | 118 .setOrigin(mOrigin) |
121 .setPrivate(mTab.isIncognito()) | 119 .setTabId(mTab.getId()) |
122 .setIcon(R.drawable.audio_playing) | 120 .setPrivate(mTab.isIncognito()) |
123 .setActions(MediaNotificationInfo.ACTION_PLAY_PAUSE | 121 .setIcon(R.drawable.audio_playing) |
124 | MediaNotificationInfo.ACTION_SWIPEAWAY) | 122 .setLargeIcon(mFavicon) |
125 .setContentIntent(Tab.createBringTabToFrontIntent(mTab.g
etId())) | 123 .setActions(MediaNotificationInfo.ACTION_PLAY_PA
USE |
126 .setId(R.id.media_playback_notification) | 124 | MediaNotificationInfo.ACTION_SWIPEAWAY
) |
127 .setListener(mControlsListener); | 125 .setContentIntent(Tab.createBringTabToFrontInten
t(mTab.getId())) |
| 126 .setId(R.id.media_playback_notification) |
| 127 .setListener(mControlsListener); |
128 | 128 |
129 MediaNotificationManager.show(ApplicationStatus.getApplicationCo
ntext(), | 129 MediaNotificationManager.show(ApplicationStatus.getApplicationCo
ntext(), |
130 mNotificationInfoBuilder.build()); | 130 mNotificationInfoBuilder.build()); |
131 | 131 |
132 Activity activity = getActivityFromTab(mTab); | 132 Activity activity = getActivityFromTab(mTab); |
133 if (activity != null) { | 133 if (activity != null) { |
134 activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); | 134 activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); |
135 } | 135 } |
136 } | 136 } |
137 }; | 137 }; |
(...skipping 14 matching lines...) Expand all Loading... |
152 } | 152 } |
153 | 153 |
154 private final TabObserver mTabObserver = new EmptyTabObserver() { | 154 private final TabObserver mTabObserver = new EmptyTabObserver() { |
155 @Override | 155 @Override |
156 public void onContentChanged(Tab tab) { | 156 public void onContentChanged(Tab tab) { |
157 assert tab == mTab; | 157 assert tab == mTab; |
158 setWebContents(tab.getWebContents()); | 158 setWebContents(tab.getWebContents()); |
159 } | 159 } |
160 | 160 |
161 @Override | 161 @Override |
| 162 public void onFaviconUpdated(Tab tab, Bitmap icon) { |
| 163 assert tab == mTab; |
| 164 // Don't update the large icon if using customized notification. Oth
erwise, the |
| 165 // lockscreen art will be the favicon. |
| 166 if (!ChromeFeatureList.isEnabled(ChromeFeatureList.MEDIA_STYLE_NOTIF
ICATION)) return; |
| 167 |
| 168 if (!updateFavicon(icon)) return; |
| 169 |
| 170 if (mNotificationInfoBuilder == null) return; |
| 171 |
| 172 mNotificationInfoBuilder.setLargeIcon(mFavicon); |
| 173 MediaNotificationManager.show( |
| 174 ApplicationStatus.getApplicationContext(), mNotificationInfo
Builder.build()); |
| 175 } |
| 176 |
| 177 @Override |
| 178 public void onUrlUpdated(Tab tab) { |
| 179 assert tab == mTab; |
| 180 |
| 181 String origin = mTab.getUrl(); |
| 182 try { |
| 183 origin = UrlUtilities.formatUrlForSecurityDisplay(new URI(origin
), true); |
| 184 } catch (URISyntaxException e) { |
| 185 Log.e(TAG, "Unable to parse the origin from the URL. " |
| 186 + "Using the full URL instead."); |
| 187 } |
| 188 |
| 189 if (mOrigin != null && mOrigin.equals(origin)) return; |
| 190 mOrigin = origin; |
| 191 mFavicon = null; |
| 192 |
| 193 if (mNotificationInfoBuilder == null) return; |
| 194 |
| 195 mNotificationInfoBuilder.setOrigin(mOrigin); |
| 196 mNotificationInfoBuilder.setLargeIcon(mFavicon); |
| 197 MediaNotificationManager.show( |
| 198 ApplicationStatus.getApplicationContext(), mNotificationInfo
Builder.build()); |
| 199 } |
| 200 |
| 201 @Override |
162 public void onTitleUpdated(Tab tab) { | 202 public void onTitleUpdated(Tab tab) { |
163 assert tab == mTab; | 203 assert tab == mTab; |
164 if (mNotificationInfoBuilder == null || mFallbackMetadata == null) r
eturn; | 204 if (mNotificationInfoBuilder == null || mFallbackMetadata == null) r
eturn; |
165 | 205 |
166 mFallbackMetadata = new MediaMetadata(mFallbackMetadata); | 206 mFallbackMetadata = new MediaMetadata(mFallbackMetadata); |
167 mFallbackMetadata.setTitle(sanitizeMediaTitle(mTab.getTitle())); | 207 mFallbackMetadata.setTitle(sanitizeMediaTitle(mTab.getTitle())); |
168 mNotificationInfoBuilder.setMetadata(mFallbackMetadata); | 208 mNotificationInfoBuilder.setMetadata(mFallbackMetadata); |
169 | 209 |
170 MediaNotificationManager.show(ApplicationStatus.getApplicationContex
t(), | 210 MediaNotificationManager.show(ApplicationStatus.getApplicationContex
t(), |
171 mNotificationInfoBuilder.build()); | 211 mNotificationInfoBuilder.build()); |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
233 assert false; | 273 assert false; |
234 return MediaSessionUMA.MEDIA_SESSION_ACTION_SOURCE_MAX; | 274 return MediaSessionUMA.MEDIA_SESSION_ACTION_SOURCE_MAX; |
235 } | 275 } |
236 | 276 |
237 private Activity getActivityFromTab(Tab tab) { | 277 private Activity getActivityFromTab(Tab tab) { |
238 WindowAndroid windowAndroid = tab.getWindowAndroid(); | 278 WindowAndroid windowAndroid = tab.getWindowAndroid(); |
239 if (windowAndroid == null) return null; | 279 if (windowAndroid == null) return null; |
240 | 280 |
241 return windowAndroid.getActivity().get(); | 281 return windowAndroid.getActivity().get(); |
242 } | 282 } |
| 283 |
| 284 /** |
| 285 * Updates the best favicon if the given icon is better. |
| 286 * @return whether the best favicon is updated. |
| 287 */ |
| 288 private boolean updateFavicon(Bitmap icon) { |
| 289 if (icon == null) return false; |
| 290 |
| 291 int largeIconSizeInDp = 0; |
| 292 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { |
| 293 largeIconSizeInDp = 128; |
| 294 } else { |
| 295 // TODO(zqzhang): Get this value via Resource.getDimension() if N ha
s a resource id. |
| 296 largeIconSizeInDp = 96; |
| 297 } |
| 298 int minimalIconSizeInPx = Math.round(largeIconSizeInDp * 0.75f); |
| 299 |
| 300 if (icon.getWidth() < minimalIconSizeInPx || icon.getHeight() < minimalI
conSizeInPx) { |
| 301 return false; |
| 302 } |
| 303 if (mFavicon != null && (icon.getWidth() < mFavicon.getWidth() |
| 304 || icon.getHeight() < mFavicon.getHeight
())) { |
| 305 return false; |
| 306 } |
| 307 mFavicon = icon; |
| 308 return true; |
| 309 } |
243 } | 310 } |
OLD | NEW |