| /* |
| * Copyright (C) 2013 Google Inc. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.leanback; |
| |
| import android.app.Activity; |
| import android.content.Intent; |
| import android.media.MediaPlayer; |
| import android.media.MediaPlayer.OnCompletionListener; |
| import android.media.MediaPlayer.OnErrorListener; |
| import android.media.MediaPlayer.OnPreparedListener; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.util.DisplayMetrics; |
| import android.util.Log; |
| import android.view.KeyEvent; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.widget.ImageView; |
| import android.widget.ProgressBar; |
| import android.widget.RelativeLayout.LayoutParams; |
| import android.widget.SeekBar; |
| import android.widget.TextView; |
| import android.widget.VideoView; |
| |
| import java.util.Locale; |
| import java.util.Timer; |
| import java.util.TimerTask; |
| import java.util.concurrent.TimeUnit; |
| |
| /* |
| * PlayerActivity handles video playback |
| */ |
| public class PlayerActivity extends Activity { |
| |
| private static final String TAG = "PlayerActivity"; |
| |
| private static final int HIDE_CONTROLLER_TIME = 5000; |
| private static final int SEEKBAR_DELAY_TIME = 100; |
| private static final int SEEKBAR_INTERVAL_TIME = 1000; |
| private static final int MIN_SCRUB_TIME = 3000; |
| private static final int SCRUB_SEGMENT_DIVISOR = 30; |
| private static final double MEDIA_BAR_TOP_MARGIN = 0.8; |
| private static final double MEDIA_BAR_RIGHT_MARGIN = 0.2; |
| private static final double MEDIA_BAR_BOTTOM_MARGIN = 0.0; |
| private static final double MEDIA_BAR_LEFT_MARGIN = 0.2; |
| private static final double MEDIA_BAR_HEIGHT = 0.1; |
| private static final double MEDIA_BAR_WIDTH = 0.9; |
| |
| private VideoView mVideoView; |
| private TextView mStartText; |
| private TextView mEndText; |
| private SeekBar mSeekbar; |
| private ImageView mPlayPause; |
| private ProgressBar mLoading; |
| private View mControllers; |
| private View mContainer; |
| private Timer mSeekbarTimer; |
| private Timer mControllersTimer; |
| private PlaybackState mPlaybackState; |
| private final Handler mHandler = new Handler(); |
| private Movie mSelectedMovie; |
| private boolean mShouldStartPlayback; |
| private boolean mControllersVisible; |
| private int mDuration; |
| private DisplayMetrics mMetrics; |
| |
| /* |
| * List of various states that we can be in |
| */ |
| public static enum PlaybackState { |
| PLAYING, PAUSED, BUFFERING, IDLE; |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.player_activity); |
| |
| mMetrics = new DisplayMetrics(); |
| getWindowManager().getDefaultDisplay().getMetrics(mMetrics); |
| |
| loadViews(); |
| setupController(); |
| setupControlsCallbacks(); |
| startVideoPlayer(); |
| updateMetadata(true); |
| } |
| |
| private void startVideoPlayer() { |
| Bundle b = getIntent().getExtras(); |
| mSelectedMovie = (Movie) getIntent().getSerializableExtra( |
| getResources().getString(R.string.movie)); |
| if (null != b) { |
| mShouldStartPlayback = b.getBoolean(getResources().getString(R.string.should_start)); |
| int startPosition = b.getInt(getResources().getString(R.string.start_position), 0); |
| mVideoView.setVideoPath(mSelectedMovie.getVideoUrl()); |
| if (mShouldStartPlayback) { |
| mPlaybackState = PlaybackState.PLAYING; |
| updatePlayButton(mPlaybackState); |
| if (startPosition > 0) { |
| mVideoView.seekTo(startPosition); |
| } |
| mVideoView.start(); |
| mPlayPause.requestFocus(); |
| startControllersTimer(); |
| } else { |
| updatePlaybackLocation(); |
| mPlaybackState = PlaybackState.PAUSED; |
| updatePlayButton(mPlaybackState); |
| } |
| } |
| } |
| |
| private void updatePlaybackLocation() { |
| if (mPlaybackState == PlaybackState.PLAYING || |
| mPlaybackState == PlaybackState.BUFFERING) { |
| startControllersTimer(); |
| } else { |
| stopControllersTimer(); |
| } |
| } |
| |
| private void play(int position) { |
| startControllersTimer(); |
| mVideoView.seekTo(position); |
| mVideoView.start(); |
| restartSeekBarTimer(); |
| } |
| |
| private void stopSeekBarTimer() { |
| if (null != mSeekbarTimer) { |
| mSeekbarTimer.cancel(); |
| } |
| } |
| |
| private void restartSeekBarTimer() { |
| stopSeekBarTimer(); |
| mSeekbarTimer = new Timer(); |
| mSeekbarTimer.scheduleAtFixedRate(new UpdateSeekbarTask(), SEEKBAR_DELAY_TIME, |
| SEEKBAR_INTERVAL_TIME); |
| } |
| |
| private void stopControllersTimer() { |
| if (null != mControllersTimer) { |
| mControllersTimer.cancel(); |
| } |
| } |
| |
| private void startControllersTimer() { |
| if (null != mControllersTimer) { |
| mControllersTimer.cancel(); |
| } |
| mControllersTimer = new Timer(); |
| mControllersTimer.schedule(new HideControllersTask(), HIDE_CONTROLLER_TIME); |
| } |
| |
| private void updateControllersVisibility(boolean show) { |
| if (show) { |
| mControllers.setVisibility(View.VISIBLE); |
| } else { |
| mControllers.setVisibility(View.INVISIBLE); |
| } |
| } |
| |
| @Override |
| protected void onPause() { |
| super.onPause(); |
| Log.d(TAG, "onPause() was called"); |
| if (null != mSeekbarTimer) { |
| mSeekbarTimer.cancel(); |
| mSeekbarTimer = null; |
| } |
| if (null != mControllersTimer) { |
| mControllersTimer.cancel(); |
| } |
| mVideoView.pause(); |
| mPlaybackState = PlaybackState.PAUSED; |
| updatePlayButton(PlaybackState.PAUSED); |
| } |
| |
| @Override |
| protected void onStop() { |
| Log.d(TAG, "onStop() was called"); |
| super.onStop(); |
| } |
| |
| @Override |
| protected void onDestroy() { |
| Log.d(TAG, "onDestroy() is called"); |
| stopControllersTimer(); |
| stopSeekBarTimer(); |
| super.onDestroy(); |
| } |
| |
| @Override |
| protected void onStart() { |
| Log.d(TAG, "onStart() was called"); |
| super.onStart(); |
| } |
| |
| @Override |
| protected void onResume() { |
| Log.d(TAG, "onResume() was called"); |
| super.onResume(); |
| } |
| |
| private class HideControllersTask extends TimerTask { |
| @Override |
| public void run() { |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| updateControllersVisibility(false); |
| mControllersVisible = false; |
| } |
| }); |
| |
| } |
| } |
| |
| private class UpdateSeekbarTask extends TimerTask { |
| |
| @Override |
| public void run() { |
| mHandler.post(new Runnable() { |
| |
| @Override |
| public void run() { |
| int currentPos = 0; |
| currentPos = mVideoView.getCurrentPosition(); |
| updateSeekbar(currentPos, mDuration); |
| } |
| }); |
| } |
| } |
| |
| private class BackToDetailTask extends TimerTask { |
| |
| @Override |
| public void run() { |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| Intent intent = new Intent(PlayerActivity.this, DetailsActivity.class); |
| intent.putExtra(getResources().getString(R.string.movie), mSelectedMovie); |
| startActivity(intent); |
| } |
| }); |
| |
| } |
| } |
| |
| private void setupController() { |
| |
| int w = (int) (mMetrics.widthPixels * MEDIA_BAR_WIDTH); |
| int h = (int) (mMetrics.heightPixels * MEDIA_BAR_HEIGHT); |
| int marginLeft = (int) (mMetrics.widthPixels * MEDIA_BAR_LEFT_MARGIN); |
| int marginTop = (int) (mMetrics.heightPixels * MEDIA_BAR_TOP_MARGIN); |
| int marginRight = (int) (mMetrics.widthPixels * MEDIA_BAR_RIGHT_MARGIN); |
| int marginBottom = (int) (mMetrics.heightPixels * MEDIA_BAR_BOTTOM_MARGIN); |
| LayoutParams lp = new LayoutParams(w, h); |
| lp.setMargins(marginLeft, marginTop, marginRight, marginBottom); |
| mControllers.setLayoutParams(lp); |
| mStartText.setText(getResources().getString(R.string.init_text)); |
| mEndText.setText(getResources().getString(R.string.init_text)); |
| } |
| |
| private void setupControlsCallbacks() { |
| |
| mVideoView.setOnErrorListener(new OnErrorListener() { |
| |
| @Override |
| public boolean onError(MediaPlayer mp, int what, int extra) { |
| String msg = ""; |
| if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) { |
| msg = getString(R.string.video_error_media_load_timeout); |
| } else if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) { |
| msg = getString(R.string.video_error_server_unaccessible); |
| } else { |
| msg = getString(R.string.video_error_unknown_error); |
| } |
| Utils.showErrorDialog(PlayerActivity.this, msg); |
| mVideoView.stopPlayback(); |
| mPlaybackState = PlaybackState.IDLE; |
| return false; |
| } |
| }); |
| |
| mVideoView.setOnPreparedListener(new OnPreparedListener() { |
| |
| @Override |
| public void onPrepared(MediaPlayer mp) { |
| Log.d(TAG, "onPrepared is reached"); |
| mDuration = mp.getDuration(); |
| mEndText.setText(formatTimeSignature(mDuration)); |
| mSeekbar.setMax(mDuration); |
| restartSeekBarTimer(); |
| } |
| }); |
| |
| mVideoView.setOnCompletionListener(new OnCompletionListener() { |
| |
| @Override |
| public void onCompletion(MediaPlayer mp) { |
| stopSeekBarTimer(); |
| mPlaybackState = PlaybackState.IDLE; |
| updatePlayButton(PlaybackState.IDLE); |
| mControllersTimer = new Timer(); |
| mControllersTimer.schedule(new BackToDetailTask(), HIDE_CONTROLLER_TIME); |
| } |
| }); |
| } |
| |
| /* |
| * @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return |
| * super.onKeyDown(keyCode, event); } |
| */ |
| |
| @Override |
| public boolean onKeyDown(int keyCode, KeyEvent event) { |
| int currentPos = 0; |
| int delta = (int) (mDuration / SCRUB_SEGMENT_DIVISOR); |
| if (delta < MIN_SCRUB_TIME) |
| delta = MIN_SCRUB_TIME; |
| |
| Log.v("keycode", "duration " + mDuration + " delta:" + delta); |
| if (!mControllersVisible) { |
| updateControllersVisibility(true); |
| } |
| switch (keyCode) { |
| case KeyEvent.KEYCODE_DPAD_CENTER: |
| return true; |
| case KeyEvent.KEYCODE_DPAD_DOWN: |
| return true; |
| case KeyEvent.KEYCODE_DPAD_LEFT: |
| currentPos = mVideoView.getCurrentPosition(); |
| currentPos -= delta; |
| if (currentPos > 0) |
| play(currentPos); |
| return true; |
| case KeyEvent.KEYCODE_DPAD_RIGHT: |
| currentPos = mVideoView.getCurrentPosition(); |
| currentPos += delta; |
| if (currentPos < mDuration) |
| play(currentPos); |
| return true; |
| case KeyEvent.KEYCODE_DPAD_UP: |
| return true; |
| } |
| |
| return super.onKeyDown(keyCode, event); |
| } |
| |
| private void updateSeekbar(int position, int duration) { |
| mSeekbar.setProgress(position); |
| mSeekbar.setMax(duration); |
| mStartText.setText(formatTimeSignature(mDuration)); |
| } |
| |
| private void updatePlayButton(PlaybackState state) { |
| switch (state) { |
| case PLAYING: |
| mLoading.setVisibility(View.INVISIBLE); |
| mPlayPause.setVisibility(View.VISIBLE); |
| mPlayPause.setImageDrawable( |
| getResources().getDrawable(R.drawable.ic_pause_playcontrol_normal)); |
| break; |
| case PAUSED: |
| case IDLE: |
| mLoading.setVisibility(View.INVISIBLE); |
| mPlayPause.setVisibility(View.VISIBLE); |
| mPlayPause.setImageDrawable( |
| getResources().getDrawable(R.drawable.ic_play_playcontrol_normal)); |
| break; |
| case BUFFERING: |
| mPlayPause.setVisibility(View.INVISIBLE); |
| mLoading.setVisibility(View.VISIBLE); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| private void updateMetadata(boolean visible) { |
| mVideoView.invalidate(); |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| return true; |
| } |
| |
| private void loadViews() { |
| mVideoView = (VideoView) findViewById(R.id.videoView); |
| mStartText = (TextView) findViewById(R.id.startText); |
| mEndText = (TextView) findViewById(R.id.endText); |
| mSeekbar = (SeekBar) findViewById(R.id.seekBar); |
| mPlayPause = (ImageView) findViewById(R.id.playpause); |
| mLoading = (ProgressBar) findViewById(R.id.progressBar); |
| mControllers = findViewById(R.id.controllers); |
| mContainer = findViewById(R.id.container); |
| |
| mVideoView.setOnClickListener(mPlayPauseHandler); |
| } |
| |
| View.OnClickListener mPlayPauseHandler = new View.OnClickListener() { |
| public void onClick(View v) { |
| Log.d(TAG, "clicked play pause button"); |
| |
| if (!mControllersVisible) { |
| updateControllersVisibility(true); |
| } |
| |
| if (mPlaybackState == PlaybackState.PAUSED) { |
| mPlaybackState = PlaybackState.PLAYING; |
| updatePlayButton(mPlaybackState); |
| mVideoView.start(); |
| startControllersTimer(); |
| } else { |
| mVideoView.pause(); |
| mPlaybackState = PlaybackState.PAUSED; |
| updatePlayButton(PlaybackState.PAUSED); |
| stopControllersTimer(); |
| } |
| } |
| }; |
| |
| private String formatTimeSignature(int timeSignature) { |
| return String.format(Locale.US, |
| "%02d:%02d", |
| TimeUnit.MILLISECONDS.toMinutes(timeSignature), |
| TimeUnit.MILLISECONDS.toSeconds(timeSignature) |
| - |
| TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS |
| .toMinutes(timeSignature))); |
| } |
| } |