Video player with an online video in landscape orientation |
private void launchVideoPlayer() {
Intent i = new Intent( this, VideoPlayerActivity.class );
i.putExtra( VideoPlayerActivity.EXTRA_VIDEO_URL, "http://www.pocketjourney.com/downloads/pj/video/famous.3gp" );
startActivity( i );
}
The layout for the video player activity consists of a VideoView and an indeterminate ProgressBar spinner. The spinner is shown while the video is buffered.
Loading spinner for the video player |
When the video player is created, MediaPlayer listeners and the URL that was passed through the intent are attached to the VideoView.
mVideoView = (VideoView) findViewById( R.id.video_view );
mVideoView.setOnCompletionListener( onCompletionListener );
mVideoView.setOnErrorListener( onErrorListener );
mVideoView.setOnPreparedListener( onPreparedListener );
if( mVideoView == null ) {
throw new IllegalArgumentException( "Layout must contain a video view with ID video_view" );
}
mUri = Uri.parse( getIntent().getExtras().getString( EXTRA_VIDEO_URL ) );
mVideoView.setVideoURI( mUri );
A MediaController object is then created to add playback controls for the VideoView.
mMediaController = new MediaController( this );
mMediaController.setEnabled( true );
mMediaController.show();
mMediaController.setMediaPlayer( mVideoView );
The OnCompletionListener resets the video to its initial position, pauses it and shows the playback controls.
protected MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mVideoView.seekTo( 0 );
if( mVideoView.isPlaying() )
mVideoView.pause();
if( !mMediaController.isShowing() )
mMediaController.show();
}
};
The OnPreparedListener is triggered when the video URL has been found and buffered. This is where the progress spinner is hidden and the media controller is attached to the VideoView, and the video is started.
protected MediaPlayer.OnPreparedListener onPreparedListener = new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
if( mediaPlayer == null )
return;
mMediaPlayer = mediaPlayer;
mediaPlayer.start();
if( mSpinningProgressBar != null )
mSpinningProgressBar.setVisibility( View.GONE );
mVideoView.setMediaController( mMediaController );
}
};
Finally the OnErrorListener simply shows an AlertDialog that closes the video activity when the dialog is closed. Once the basic infrastructure is put together for playing the remote video, handling rotation and leaving/coming back to the app should be considered. In regards to hitting home and coming back to the app, the onPause and onResume methods are used to save the video position, restore it and set the spinner to visible. Given that the OnPreparedListener is still attached to the video view, that listener will be triggered when the video is ready to be played again and the app can continue as normal.
@Override
protected void onResume() {
super.onResume();
mVideoView.seekTo( mPosition );
mSpinningProgressBar.setVisibility( View.VISIBLE );
}
@Override
protected void onPause() {
super.onPause();
mPosition = mVideoView.getCurrentPosition();
}
For device rotation, we take advantage of the activity lifecycle and onSaveInstanceState and onRestoreInstanceState to save whether the video is playing and its position on rotation.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if( mVideoView == null || mVideoView.getCurrentPosition() == 0 )
return;
outState.putInt( EXTRA_CURRENT_POSITION, mVideoView.getCurrentPosition() );
outState.putBoolean( EXTRA_IS_PLAYING, mVideoView.isPlaying() );
mVideoView.pause();
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if( mVideoView == null || savedInstanceState == null )
return;
if( savedInstanceState.getBoolean( EXTRA_IS_PLAYING, false ) ) {
mVideoView.seekTo(savedInstanceState.getInt(EXTRA_CURRENT_POSITION, 0));
mVideoView.start();
}
}
Video in portrait orientation |