| Index: chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f452fc1b97e06538e138454635563f4a75a20a65
|
| --- /dev/null
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java
|
| @@ -0,0 +1,353 @@
|
| +// Copyright 2013 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.graphics.Color;
|
| +import android.os.Bundle;
|
| +import android.os.Handler;
|
| +import android.support.v4.app.FragmentActivity;
|
| +import android.support.v4.media.TransportMediator;
|
| +import android.support.v4.media.TransportPerformer;
|
| +import android.text.TextUtils;
|
| +import android.view.KeyEvent;
|
| +import android.view.View;
|
| +import android.view.ViewGroup;
|
| +import android.view.Window;
|
| +import android.view.WindowManager;
|
| +import android.widget.ImageView;
|
| +import android.widget.TextView;
|
| +
|
| +import com.google.android.gms.cast.CastMediaControlIntent;
|
| +
|
| +import org.chromium.base.ApiCompatibilityUtils;
|
| +import org.chromium.chrome.R;
|
| +import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState;
|
| +import org.chromium.third_party.android.media.MediaController;
|
| +
|
| +/**
|
| + * The activity that's opened by clicking the video flinging (casting) notification.
|
| + *
|
| + * TODO(cimamoglu): Refactor to merge some common logic with {@link TransportControl}.
|
| + */
|
| +public class ExpandedControllerActivity
|
| + extends FragmentActivity implements MediaRouteController.UiListener {
|
| + private static final int PROGRESS_UPDATE_PERIOD_IN_MS = 1000;
|
| + // The alpha value for the poster/placeholder image, an integer between 0 and 256 (opaque).
|
| + private static final int POSTER_IMAGE_ALPHA = 200;
|
| +
|
| + private Handler mHandler;
|
| + // We don't use the standard android.media.MediaController, but a custom one.
|
| + // See the class itself for details.
|
| + private MediaController mMediaController;
|
| + private FullscreenMediaRouteButton mMediaRouteButton;
|
| + private MediaRouteController mMediaRouteController;
|
| + private RemoteVideoInfo mVideoInfo;
|
| + private String mScreenName;
|
| + private TransportMediator mTransportMediator;
|
| +
|
| + /**
|
| + * Handle actions from on-screen media controls.
|
| + */
|
| + private TransportPerformer mTransportPerformer = new TransportPerformer() {
|
| + @Override
|
| + public void onStart() {
|
| + mMediaRouteController.resume();
|
| + }
|
| +
|
| + @Override
|
| + public void onStop() {
|
| + onPause();
|
| + mMediaRouteController.release();
|
| + }
|
| +
|
| + @Override
|
| + public void onPause() {
|
| + mMediaRouteController.pause();
|
| + }
|
| +
|
| + @Override
|
| + public long onGetDuration() {
|
| + return mMediaRouteController.getDuration();
|
| + }
|
| +
|
| + @Override
|
| + public long onGetCurrentPosition() {
|
| + return mMediaRouteController.getPosition();
|
| + }
|
| +
|
| + @Override
|
| + public void onSeekTo(long pos) {
|
| + mMediaRouteController.seekTo((int) pos);
|
| + }
|
| +
|
| + @Override
|
| + public boolean onIsPlaying() {
|
| + return mMediaRouteController.isPlaying();
|
| + }
|
| +
|
| + @Override
|
| + public int onGetTransportControlFlags() {
|
| + int flags = TransportMediator.FLAG_KEY_MEDIA_REWIND
|
| + | TransportMediator.FLAG_KEY_MEDIA_FAST_FORWARD;
|
| + if (mMediaRouteController.isPlaying()) {
|
| + flags |= TransportMediator.FLAG_KEY_MEDIA_PAUSE;
|
| + } else {
|
| + flags |= TransportMediator.FLAG_KEY_MEDIA_PLAY;
|
| + }
|
| + return flags;
|
| + }
|
| + };
|
| +
|
| + private Runnable mProgressUpdater = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + if (mMediaRouteController.isPlaying()) {
|
| + mMediaController.updateProgress();
|
| + mHandler.postDelayed(this, PROGRESS_UPDATE_PERIOD_IN_MS);
|
| + } else {
|
| + mHandler.removeCallbacks(this);
|
| + }
|
| + }
|
| + };
|
| +
|
| + @Override
|
| + protected void onCreate(Bundle savedInstanceState) {
|
| + super.onCreate(savedInstanceState);
|
| +
|
| + mMediaRouteController =
|
| + RemoteMediaPlayerController.instance().getCurrentlyPlayingMediaRouteController();
|
| +
|
| + if (mMediaRouteController == null || mMediaRouteController.routeIsDefaultRoute()) {
|
| + // We don't want to do anything for the default (local) route
|
| + finish();
|
| + return;
|
| + }
|
| +
|
| + // Make the activity full screen.
|
| + requestWindowFeature(Window.FEATURE_NO_TITLE);
|
| + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
| + WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
| +
|
| + // requestWindowFeature must be called before adding content.
|
| + setContentView(R.layout.expanded_cast_controller);
|
| + mHandler = new Handler();
|
| +
|
| + ViewGroup rootView = (ViewGroup) findViewById(android.R.id.content);
|
| + rootView.setBackgroundColor(Color.BLACK);
|
| +
|
| + mMediaRouteController.addUiListener(this);
|
| +
|
| + // Create transport controller to control video, giving the callback
|
| + // interface to receive actions from.
|
| + mTransportMediator = new TransportMediator(this, mTransportPerformer);
|
| +
|
| + // Create and initialize the media control UI.
|
| + mMediaController = (MediaController) findViewById(R.id.cast_media_controller);
|
| + mMediaController.setMediaPlayer(mTransportMediator);
|
| +
|
| + View button = getLayoutInflater().inflate(R.layout.cast_controller_media_route_button,
|
| + rootView, false);
|
| +
|
| + if (button instanceof FullscreenMediaRouteButton) {
|
| + mMediaRouteButton = (FullscreenMediaRouteButton) button;
|
| + rootView.addView(mMediaRouteButton);
|
| + mMediaRouteButton.bringToFront();
|
| + mMediaRouteButton.initialize(mMediaRouteController);
|
| + } else {
|
| + mMediaRouteButton = null;
|
| + }
|
| +
|
| + // Initialize the video info.
|
| + setVideoInfo(new RemoteVideoInfo(null, 0, RemoteVideoInfo.PlayerState.STOPPED, 0, null));
|
| +
|
| + mMediaController.refresh();
|
| +
|
| + scheduleProgressUpdate();
|
| + }
|
| +
|
| + @Override
|
| + protected void onResume() {
|
| + super.onResume();
|
| + if (mVideoInfo.state == PlayerState.FINISHED) finish();
|
| + if (mMediaRouteController == null) return;
|
| + mMediaRouteController.prepareMediaRoute();
|
| +
|
| + ImageView iv = (ImageView) findViewById(R.id.cast_background_image);
|
| + if (iv == null) return;
|
| + Bitmap posterBitmap = mMediaRouteController.getPoster();
|
| + if (posterBitmap != null) iv.setImageBitmap(posterBitmap);
|
| + ApiCompatibilityUtils.setImageAlpha(iv, POSTER_IMAGE_ALPHA);
|
| + }
|
| +
|
| + @Override
|
| + protected void onDestroy() {
|
| + cleanup();
|
| + super.onDestroy();
|
| + }
|
| +
|
| + @Override
|
| + public boolean dispatchKeyEvent(KeyEvent event) {
|
| + int keyCode = event.getKeyCode();
|
| + if ((keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_UP)
|
| + || mVideoInfo.state == PlayerState.FINISHED) {
|
| + return super.dispatchKeyEvent(event);
|
| + }
|
| +
|
| + return handleVolumeKeyEvent(mMediaRouteController, event);
|
| + }
|
| +
|
| + private void cleanup() {
|
| + if (mHandler != null) mHandler.removeCallbacks(mProgressUpdater);
|
| + if (mMediaRouteController != null) mMediaRouteController.removeUiListener(this);
|
| + mMediaRouteController = null;
|
| + mProgressUpdater = null;
|
| + }
|
| +
|
| + /**
|
| + * Sets the remote's video information to display.
|
| + */
|
| + private final void setVideoInfo(RemoteVideoInfo videoInfo) {
|
| + if ((mVideoInfo == null) ? (videoInfo == null) : mVideoInfo.equals(videoInfo)) return;
|
| +
|
| + mVideoInfo = videoInfo;
|
| + onVideoInfoChanged();
|
| + }
|
| +
|
| + private void scheduleProgressUpdate() {
|
| + mHandler.removeCallbacks(mProgressUpdater);
|
| + if (mMediaRouteController.isPlaying()) {
|
| + mHandler.post(mProgressUpdater);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Sets the name to display for the device.
|
| + */
|
| + private void setScreenName(String screenName) {
|
| + if (TextUtils.equals(mScreenName, screenName)) return;
|
| +
|
| + mScreenName = screenName;
|
| + onScreenNameChanged();
|
| + }
|
| +
|
| + private void onVideoInfoChanged() {
|
| + updateUi();
|
| + }
|
| +
|
| + private void onScreenNameChanged() {
|
| + updateUi();
|
| + }
|
| +
|
| + private void updateUi() {
|
| + if (mMediaController == null || mMediaRouteController == null) return;
|
| +
|
| + String deviceName = mMediaRouteController.getRouteName();
|
| + String castText = "";
|
| + if (deviceName != null) {
|
| + castText = getResources().getString(R.string.cast_casting_video, deviceName);
|
| + }
|
| + TextView castTextView = (TextView) findViewById(R.id.cast_screen_title);
|
| + castTextView.setText(castText);
|
| +
|
| + mMediaController.refresh();
|
| + }
|
| +
|
| + @Override
|
| + public void onRouteSelected(String name, MediaRouteController mediaRouteController) {
|
| + setScreenName(name);
|
| + }
|
| +
|
| + @Override
|
| + public void onRouteUnselected(MediaRouteController mediaRouteController) {
|
| + finish();
|
| + }
|
| +
|
| + @Override
|
| + public void onPrepared(MediaRouteController mediaRouteController) {
|
| + // No implementation.
|
| + }
|
| +
|
| + @Override
|
| + public void onError(int error, String message) {
|
| + if (error == CastMediaControlIntent.ERROR_CODE_SESSION_START_FAILED) finish();
|
| + }
|
| +
|
| + @Override
|
| + public void onPlaybackStateChanged(PlayerState oldState, PlayerState newState) {
|
| + RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo);
|
| + videoInfo.state = newState;
|
| + setVideoInfo(videoInfo);
|
| +
|
| + scheduleProgressUpdate();
|
| +
|
| + if (newState == PlayerState.FINISHED || newState == PlayerState.INVALIDATED) {
|
| + // If we are switching to a finished state, stop the notifications.
|
| + finish();
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void onDurationUpdated(int durationMillis) {
|
| + RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo);
|
| + videoInfo.durationMillis = durationMillis;
|
| + setVideoInfo(videoInfo);
|
| + }
|
| +
|
| + @Override
|
| + public void onPositionChanged(int positionMillis) {
|
| + RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo);
|
| + videoInfo.currentTimeMillis = positionMillis;
|
| + setVideoInfo(videoInfo);
|
| + }
|
| +
|
| + @Override
|
| + public void onTitleChanged(String title) {
|
| + RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo);
|
| + videoInfo.title = title;
|
| + setVideoInfo(videoInfo);
|
| + }
|
| +
|
| + /**
|
| + * Modify remote volume by handling volume keys.
|
| + *
|
| + * @param controller The remote controller through which the volume will be modified.
|
| + * @param event The key event. Its keycode needs to be either {@code KEYCODE_VOLUME_DOWN} or
|
| + * {@code KEYCODE_VOLUME_UP} otherwise this method will return false.
|
| + * @return True if the event is handled.
|
| + */
|
| + private boolean handleVolumeKeyEvent(MediaRouteController controller, KeyEvent event) {
|
| + if (!controller.isBeingCast()) return false;
|
| +
|
| + int action = event.getAction();
|
| + int keyCode = event.getKeyCode();
|
| + // Intercept the volume keys to affect only remote volume.
|
| + switch (keyCode) {
|
| + case KeyEvent.KEYCODE_VOLUME_DOWN:
|
| + if (action == KeyEvent.ACTION_DOWN) controller.setRemoteVolume(-1);
|
| + return true;
|
| + case KeyEvent.KEYCODE_VOLUME_UP:
|
| + if (action == KeyEvent.ACTION_DOWN) controller.setRemoteVolume(1);
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Launches the ExpandedControllerActivity as a new task.
|
| + *
|
| + * @param context the Context to start this activity within.
|
| + */
|
| + public static void startActivity(Context context) {
|
| + if (context == null) return;
|
| +
|
| + Intent intent = new Intent(context, ExpandedControllerActivity.class);
|
| + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
| + context.startActivity(intent);
|
| + }
|
| +}
|
|
|