Index: chrome/android/java/src/org/chromium/chrome/browser/media/remote/CastNotificationControl.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/CastNotificationControl.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/CastNotificationControl.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9864d7b4a7c49e0d91c2a2c06916b5eeba3eebe8 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/CastNotificationControl.java |
@@ -0,0 +1,211 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.media.remote; |
+ |
+import android.content.Context; |
+import android.content.Intent; |
+import android.graphics.Bitmap; |
+import android.media.AudioManager; |
+ |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.chrome.R; |
+import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; |
+import org.chromium.chrome.browser.media.ui.MediaNotificationInfo; |
+import org.chromium.chrome.browser.media.ui.MediaNotificationListener; |
+import org.chromium.chrome.browser.media.ui.MediaNotificationManager; |
+import org.chromium.chrome.browser.tab.Tab; |
+ |
+import javax.annotation.Nullable; |
+ |
+/** |
+ * Notification control controls the cast notification and lock screen, using |
+ * {@link MediaNotificationManager} |
+ */ |
+public class CastNotificationControl implements MediaRouteController.UiListener, |
+ MediaNotificationListener, AudioManager.OnAudioFocusChangeListener { |
+ |
+ private static CastNotificationControl sInstance; |
+ |
+ private Bitmap mPosterBitmap; |
+ protected MediaRouteController mMediaRouteController = null; |
+ private MediaNotificationInfo.Builder mNotificationBuilder; |
+ private Context mContext; |
+ private PlayerState mState; |
+ private String mTitle = ""; |
+ private AudioManager mAudioManager; |
+ |
+ private boolean mIsShowing = false; |
+ |
+ private static final Object LOCK = new Object(); |
+ |
+ |
+ private CastNotificationControl(Context context) { |
+ mContext = context; |
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); |
+ } |
+ /** |
+ * Get the unique NotificationControl, creating it if necessary. |
+ * @param context The context of the activity |
+ * @param mediaRouteController The current mediaRouteController, if any. |
+ * @return a {@code LockScreenTransportControl} based on the platform's SDK API or null if the |
+ * current platform's SDK API is not supported. |
+ */ |
+ public static CastNotificationControl getOrCreate(Context context, |
+ @Nullable MediaRouteController mediaRouteController) { |
+ synchronized (LOCK) { |
+ if (sInstance == null) { |
+ sInstance = new CastNotificationControl(context); |
+ } |
+ sInstance.mMediaRouteController = mediaRouteController; |
+ return sInstance; |
+ } |
+ } |
+ |
+ /** |
+ * @return the poster bitmap previously assigned with {@link #setPosterBitmap(Bitmap)}, or |
+ * {@code null} if the poster bitmap has not yet been assigned. |
+ */ |
+ public final Bitmap getPosterBitmap() { |
+ return mPosterBitmap; |
+ } |
+ |
+ /** |
+ * Sets the poster bitmap to display on the TransportControl. |
+ */ |
+ public final void setPosterBitmap(Bitmap posterBitmap) { |
+ if (mPosterBitmap == posterBitmap |
+ || (mPosterBitmap != null && mPosterBitmap.sameAs(posterBitmap))) { |
+ return; |
+ } |
+ mPosterBitmap = posterBitmap; |
+ if (mNotificationBuilder == null || mMediaRouteController == null) return; |
+ mNotificationBuilder.setImage(mMediaRouteController.getPoster()); |
+ updateNotification(); |
+ } |
+ |
+ public void hide() { |
+ mIsShowing = false; |
+ MediaNotificationManager.hide(Tab.INVALID_TAB_ID, R.id.remote_notification); |
+ mAudioManager.abandonAudioFocus(this); |
+ mMediaRouteController.removeUiListener(this); |
+ } |
+ |
+ public void show(PlayerState initialState) { |
+ mMediaRouteController.addUiListener(this); |
+ // TODO(aberent): investigate why this is necessary, and whether we are handling |
+ // it correctly. Also add code to restore it when Chrome is resumed. |
+ mAudioManager.requestAudioFocus(this, AudioManager.USE_DEFAULT_STREAM_TYPE, |
+ AudioManager.AUDIOFOCUS_GAIN); |
+ Intent contentIntent = new Intent(mContext, ExpandedControllerActivity.class); |
+ mNotificationBuilder = new MediaNotificationInfo.Builder() |
+ .setPaused(false) |
+ .setPrivate(false) |
+ .setIcon(R.drawable.ic_notification_media_route) |
+ .setContentIntent(contentIntent) |
+ .setImage(mMediaRouteController.getPoster()) |
+ .setId(R.id.remote_notification) |
+ .setListener(this); |
+ mState = initialState; |
+ updateNotification(); |
+ mIsShowing = true; |
+ } |
+ |
+ public void setRouteController(MediaRouteController controller) { |
+ mMediaRouteController = controller; |
+ } |
+ |
+ private void updateNotification() { |
+ mNotificationBuilder.setTitle(mTitle); |
+ if (mState == PlayerState.PAUSED || mState == PlayerState.PLAYING) { |
+ mNotificationBuilder.setPaused(mState != PlayerState.PLAYING); |
+ mNotificationBuilder.setActions(MediaNotificationInfo.ACTION_STOP |
+ | MediaNotificationInfo.ACTION_PLAY_PAUSE); |
+ MediaNotificationManager.show(mContext, mNotificationBuilder.build()); |
+ } else if (mState == PlayerState.LOADING) { |
+ mNotificationBuilder.setActions(MediaNotificationInfo.ACTION_STOP); |
+ MediaNotificationManager.show(mContext, mNotificationBuilder.build()); |
+ } else { |
+ hide(); |
+ } |
+ } |
+ |
+ // TODO(aberent) at the moment this is only called from a test, but it should be called if the |
+ // poster changes. |
+ public void onPosterBitmapChanged() { |
+ if (mNotificationBuilder == null || mMediaRouteController == null) return; |
+ mNotificationBuilder.setImage(mMediaRouteController.getPoster()); |
+ updateNotification(); |
+ } |
+ |
+ // MediaRouteController.UiListener implementation. |
+ @Override |
+ public void onPlaybackStateChanged(PlayerState oldState, PlayerState newState) { |
+ mState = newState; |
+ updateNotification(); |
+ } |
+ |
+ @Override |
+ public void onRouteSelected(String name, MediaRouteController mediaRouteController) { |
+ } |
+ |
+ @Override |
+ public void onRouteUnselected(MediaRouteController mediaRouteController) { |
+ hide(); |
+ } |
+ |
+ @Override |
+ public void onPrepared(MediaRouteController mediaRouteController) { |
+ } |
+ |
+ @Override |
+ public void onError(int errorType, String message) { |
+ } |
+ |
+ @Override |
+ public void onDurationUpdated(long durationMillis) { |
+ } |
+ |
+ @Override |
+ public void onPositionChanged(long positionMillis) { |
+ } |
+ |
+ @Override |
+ public void onTitleChanged(String title) { |
+ mTitle = (title == null) ? "" : title; |
+ updateNotification(); |
+ } |
+ |
+ // MediaNotificationListener methods |
+ @Override |
+ public void onPlay(int actionSource) { |
+ mMediaRouteController.resume(); |
+ } |
+ |
+ @Override |
+ public void onPause(int actionSource) { |
+ mMediaRouteController.pause(); |
+ } |
+ |
+ @Override |
+ public void onStop(int actionSource) { |
+ mMediaRouteController.release(); |
+ } |
+ |
+ // AudioManager.OnAudioFocusChangeListener methods |
+ @Override |
+ public void onAudioFocusChange(int focusChange) { |
+ // Do nothing. The remote player should keep playing. |
+ } |
+ |
+ @VisibleForTesting |
+ static CastNotificationControl getForTests() { |
+ return sInstance; |
+ } |
+ |
+ @VisibleForTesting |
+ boolean isShowingForTests() { |
+ return mIsShowing; |
+ } |
+} |