Chromium Code Reviews| 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..d591309ad604e78c3ba40cc1b79e65b9c072c721 |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/CastNotificationControl.java |
| @@ -0,0 +1,213 @@ |
| +// 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.setPoster(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 selectionIntent = new Intent(mContext, ExpandedControllerActivity.class); |
| + mNotificationBuilder = new MediaNotificationInfo.Builder() |
| + .setPaused(false) |
| + .setPrivate(false) |
| + .setIcon(R.drawable.ic_notification_media_route) |
| + .setActions(MediaNotificationInfo.ACTION_STOP) |
| + .setActions(MediaNotificationInfo.ACTION_PLAY_PAUSE) |
|
mlamouri (slow - plz ping)
2016/02/03 14:47:18
`.setActions(MediaNotificationInfo.ACTION_PLAY_PAU
aberent
2016/02/04 14:36:47
Removed, since the actions are set (correctly!) by
|
| + .setSelectionIntent(selectionIntent) |
| + .setPoster(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)); |
|
mlamouri (slow - plz ping)
2016/02/03 14:47:18
`mState != PlayerState.PLAYING`
aberent
2016/02/04 14:36:47
Done.
|
| + 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.setPoster(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; |
| + } |
| +} |