• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.widget;
18 
19 import android.app.AlertDialog;
20 import android.content.Context;
21 import android.content.DialogInterface;
22 import android.content.Intent;
23 import android.content.res.Resources;
24 import android.media.AudioManager;
25 import android.media.MediaPlayer;
26 import android.media.Metadata;
27 import android.media.MediaPlayer.OnCompletionListener;
28 import android.media.MediaPlayer.OnErrorListener;
29 import android.media.MediaPlayer.OnInfoListener;
30 import android.net.Uri;
31 import android.util.AttributeSet;
32 import android.util.Log;
33 import android.view.KeyEvent;
34 import android.view.MotionEvent;
35 import android.view.SurfaceHolder;
36 import android.view.SurfaceView;
37 import android.view.View;
38 import android.view.accessibility.AccessibilityEvent;
39 import android.view.accessibility.AccessibilityNodeInfo;
40 import android.widget.MediaController.MediaPlayerControl;
41 
42 import java.io.IOException;
43 import java.util.Map;
44 
45 /**
46  * Displays a video file.  The VideoView class
47  * can load images from various sources (such as resources or content
48  * providers), takes care of computing its measurement from the video so that
49  * it can be used in any layout manager, and provides various display options
50  * such as scaling and tinting.
51  */
52 public class VideoView extends SurfaceView implements MediaPlayerControl {
53     private String TAG = "VideoView";
54     // settable by the client
55     private Uri         mUri;
56     private Map<String, String> mHeaders;
57 
58     // all possible internal states
59     private static final int STATE_ERROR              = -1;
60     private static final int STATE_IDLE               = 0;
61     private static final int STATE_PREPARING          = 1;
62     private static final int STATE_PREPARED           = 2;
63     private static final int STATE_PLAYING            = 3;
64     private static final int STATE_PAUSED             = 4;
65     private static final int STATE_PLAYBACK_COMPLETED = 5;
66 
67     // mCurrentState is a VideoView object's current state.
68     // mTargetState is the state that a method caller intends to reach.
69     // For instance, regardless the VideoView object's current state,
70     // calling pause() intends to bring the object to a target state
71     // of STATE_PAUSED.
72     private int mCurrentState = STATE_IDLE;
73     private int mTargetState  = STATE_IDLE;
74 
75     // All the stuff we need for playing and showing a video
76     private SurfaceHolder mSurfaceHolder = null;
77     private MediaPlayer mMediaPlayer = null;
78     private int         mAudioSession;
79     private int         mVideoWidth;
80     private int         mVideoHeight;
81     private int         mSurfaceWidth;
82     private int         mSurfaceHeight;
83     private MediaController mMediaController;
84     private OnCompletionListener mOnCompletionListener;
85     private MediaPlayer.OnPreparedListener mOnPreparedListener;
86     private int         mCurrentBufferPercentage;
87     private OnErrorListener mOnErrorListener;
88     private OnInfoListener  mOnInfoListener;
89     private int         mSeekWhenPrepared;  // recording the seek position while preparing
90     private boolean     mCanPause;
91     private boolean     mCanSeekBack;
92     private boolean     mCanSeekForward;
93 
VideoView(Context context)94     public VideoView(Context context) {
95         super(context);
96         initVideoView();
97     }
98 
VideoView(Context context, AttributeSet attrs)99     public VideoView(Context context, AttributeSet attrs) {
100         this(context, attrs, 0);
101         initVideoView();
102     }
103 
VideoView(Context context, AttributeSet attrs, int defStyle)104     public VideoView(Context context, AttributeSet attrs, int defStyle) {
105         super(context, attrs, defStyle);
106         initVideoView();
107     }
108 
109     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)110     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
111         //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
112         //        + MeasureSpec.toString(heightMeasureSpec) + ")");
113 
114         int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
115         int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
116         if (mVideoWidth > 0 && mVideoHeight > 0) {
117 
118             int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
119             int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
120             int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
121             int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
122 
123             if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
124                 // the size is fixed
125                 width = widthSpecSize;
126                 height = heightSpecSize;
127 
128                 // for compatibility, we adjust size based on aspect ratio
129                 if ( mVideoWidth * height  < width * mVideoHeight ) {
130                     //Log.i("@@@", "image too wide, correcting");
131                     width = height * mVideoWidth / mVideoHeight;
132                 } else if ( mVideoWidth * height  > width * mVideoHeight ) {
133                     //Log.i("@@@", "image too tall, correcting");
134                     height = width * mVideoHeight / mVideoWidth;
135                 }
136             } else if (widthSpecMode == MeasureSpec.EXACTLY) {
137                 // only the width is fixed, adjust the height to match aspect ratio if possible
138                 width = widthSpecSize;
139                 height = width * mVideoHeight / mVideoWidth;
140                 if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
141                     // couldn't match aspect ratio within the constraints
142                     height = heightSpecSize;
143                 }
144             } else if (heightSpecMode == MeasureSpec.EXACTLY) {
145                 // only the height is fixed, adjust the width to match aspect ratio if possible
146                 height = heightSpecSize;
147                 width = height * mVideoWidth / mVideoHeight;
148                 if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
149                     // couldn't match aspect ratio within the constraints
150                     width = widthSpecSize;
151                 }
152             } else {
153                 // neither the width nor the height are fixed, try to use actual video size
154                 width = mVideoWidth;
155                 height = mVideoHeight;
156                 if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
157                     // too tall, decrease both width and height
158                     height = heightSpecSize;
159                     width = height * mVideoWidth / mVideoHeight;
160                 }
161                 if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
162                     // too wide, decrease both width and height
163                     width = widthSpecSize;
164                     height = width * mVideoHeight / mVideoWidth;
165                 }
166             }
167         } else {
168             // no size yet, just adopt the given spec sizes
169         }
170         setMeasuredDimension(width, height);
171     }
172 
173     @Override
onInitializeAccessibilityEvent(AccessibilityEvent event)174     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
175         super.onInitializeAccessibilityEvent(event);
176         event.setClassName(VideoView.class.getName());
177     }
178 
179     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)180     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
181         super.onInitializeAccessibilityNodeInfo(info);
182         info.setClassName(VideoView.class.getName());
183     }
184 
resolveAdjustedSize(int desiredSize, int measureSpec)185     public int resolveAdjustedSize(int desiredSize, int measureSpec) {
186         return getDefaultSize(desiredSize, measureSpec);
187     }
188 
initVideoView()189     private void initVideoView() {
190         mVideoWidth = 0;
191         mVideoHeight = 0;
192         getHolder().addCallback(mSHCallback);
193         getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
194         setFocusable(true);
195         setFocusableInTouchMode(true);
196         requestFocus();
197         mCurrentState = STATE_IDLE;
198         mTargetState  = STATE_IDLE;
199     }
200 
setVideoPath(String path)201     public void setVideoPath(String path) {
202         setVideoURI(Uri.parse(path));
203     }
204 
setVideoURI(Uri uri)205     public void setVideoURI(Uri uri) {
206         setVideoURI(uri, null);
207     }
208 
209     /**
210      * @hide
211      */
setVideoURI(Uri uri, Map<String, String> headers)212     public void setVideoURI(Uri uri, Map<String, String> headers) {
213         mUri = uri;
214         mHeaders = headers;
215         mSeekWhenPrepared = 0;
216         openVideo();
217         requestLayout();
218         invalidate();
219     }
220 
stopPlayback()221     public void stopPlayback() {
222         if (mMediaPlayer != null) {
223             mMediaPlayer.stop();
224             mMediaPlayer.release();
225             mMediaPlayer = null;
226             mCurrentState = STATE_IDLE;
227             mTargetState  = STATE_IDLE;
228         }
229     }
230 
openVideo()231     private void openVideo() {
232         if (mUri == null || mSurfaceHolder == null) {
233             // not ready for playback just yet, will try again later
234             return;
235         }
236         // Tell the music playback service to pause
237         // TODO: these constants need to be published somewhere in the framework.
238         Intent i = new Intent("com.android.music.musicservicecommand");
239         i.putExtra("command", "pause");
240         mContext.sendBroadcast(i);
241 
242         // we shouldn't clear the target state, because somebody might have
243         // called start() previously
244         release(false);
245         try {
246             mMediaPlayer = new MediaPlayer();
247             if (mAudioSession != 0) {
248                 mMediaPlayer.setAudioSessionId(mAudioSession);
249             } else {
250                 mAudioSession = mMediaPlayer.getAudioSessionId();
251             }
252             mMediaPlayer.setOnPreparedListener(mPreparedListener);
253             mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
254             mMediaPlayer.setOnCompletionListener(mCompletionListener);
255             mMediaPlayer.setOnErrorListener(mErrorListener);
256             mMediaPlayer.setOnInfoListener(mOnInfoListener);
257             mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
258             mCurrentBufferPercentage = 0;
259             mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
260             mMediaPlayer.setDisplay(mSurfaceHolder);
261             mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
262             mMediaPlayer.setScreenOnWhilePlaying(true);
263             mMediaPlayer.prepareAsync();
264             // we don't set the target state here either, but preserve the
265             // target state that was there before.
266             mCurrentState = STATE_PREPARING;
267             attachMediaController();
268         } catch (IOException ex) {
269             Log.w(TAG, "Unable to open content: " + mUri, ex);
270             mCurrentState = STATE_ERROR;
271             mTargetState = STATE_ERROR;
272             mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
273             return;
274         } catch (IllegalArgumentException ex) {
275             Log.w(TAG, "Unable to open content: " + mUri, ex);
276             mCurrentState = STATE_ERROR;
277             mTargetState = STATE_ERROR;
278             mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
279             return;
280         }
281     }
282 
setMediaController(MediaController controller)283     public void setMediaController(MediaController controller) {
284         if (mMediaController != null) {
285             mMediaController.hide();
286         }
287         mMediaController = controller;
288         attachMediaController();
289     }
290 
attachMediaController()291     private void attachMediaController() {
292         if (mMediaPlayer != null && mMediaController != null) {
293             mMediaController.setMediaPlayer(this);
294             View anchorView = this.getParent() instanceof View ?
295                     (View)this.getParent() : this;
296             mMediaController.setAnchorView(anchorView);
297             mMediaController.setEnabled(isInPlaybackState());
298         }
299     }
300 
301     MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
302         new MediaPlayer.OnVideoSizeChangedListener() {
303             public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
304                 mVideoWidth = mp.getVideoWidth();
305                 mVideoHeight = mp.getVideoHeight();
306                 if (mVideoWidth != 0 && mVideoHeight != 0) {
307                     getHolder().setFixedSize(mVideoWidth, mVideoHeight);
308                     requestLayout();
309                 }
310             }
311     };
312 
313     MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
314         public void onPrepared(MediaPlayer mp) {
315             mCurrentState = STATE_PREPARED;
316 
317             // Get the capabilities of the player for this stream
318             Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
319                                       MediaPlayer.BYPASS_METADATA_FILTER);
320 
321             if (data != null) {
322                 mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
323                         || data.getBoolean(Metadata.PAUSE_AVAILABLE);
324                 mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
325                         || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
326                 mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
327                         || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
328             } else {
329                 mCanPause = mCanSeekBack = mCanSeekForward = true;
330             }
331 
332             if (mOnPreparedListener != null) {
333                 mOnPreparedListener.onPrepared(mMediaPlayer);
334             }
335             if (mMediaController != null) {
336                 mMediaController.setEnabled(true);
337             }
338             mVideoWidth = mp.getVideoWidth();
339             mVideoHeight = mp.getVideoHeight();
340 
341             int seekToPosition = mSeekWhenPrepared;  // mSeekWhenPrepared may be changed after seekTo() call
342             if (seekToPosition != 0) {
343                 seekTo(seekToPosition);
344             }
345             if (mVideoWidth != 0 && mVideoHeight != 0) {
346                 //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);
347                 getHolder().setFixedSize(mVideoWidth, mVideoHeight);
348                 if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {
349                     // We didn't actually change the size (it was already at the size
350                     // we need), so we won't get a "surface changed" callback, so
351                     // start the video here instead of in the callback.
352                     if (mTargetState == STATE_PLAYING) {
353                         start();
354                         if (mMediaController != null) {
355                             mMediaController.show();
356                         }
357                     } else if (!isPlaying() &&
358                                (seekToPosition != 0 || getCurrentPosition() > 0)) {
359                        if (mMediaController != null) {
360                            // Show the media controls when we're paused into a video and make 'em stick.
361                            mMediaController.show(0);
362                        }
363                    }
364                 }
365             } else {
366                 // We don't know the video size yet, but should start anyway.
367                 // The video size might be reported to us later.
368                 if (mTargetState == STATE_PLAYING) {
369                     start();
370                 }
371             }
372         }
373     };
374 
375     private MediaPlayer.OnCompletionListener mCompletionListener =
376         new MediaPlayer.OnCompletionListener() {
377         public void onCompletion(MediaPlayer mp) {
378             mCurrentState = STATE_PLAYBACK_COMPLETED;
379             mTargetState = STATE_PLAYBACK_COMPLETED;
380             if (mMediaController != null) {
381                 mMediaController.hide();
382             }
383             if (mOnCompletionListener != null) {
384                 mOnCompletionListener.onCompletion(mMediaPlayer);
385             }
386         }
387     };
388 
389     private MediaPlayer.OnErrorListener mErrorListener =
390         new MediaPlayer.OnErrorListener() {
391         public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
392             Log.d(TAG, "Error: " + framework_err + "," + impl_err);
393             mCurrentState = STATE_ERROR;
394             mTargetState = STATE_ERROR;
395             if (mMediaController != null) {
396                 mMediaController.hide();
397             }
398 
399             /* If an error handler has been supplied, use it and finish. */
400             if (mOnErrorListener != null) {
401                 if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {
402                     return true;
403                 }
404             }
405 
406             /* Otherwise, pop up an error dialog so the user knows that
407              * something bad has happened. Only try and pop up the dialog
408              * if we're attached to a window. When we're going away and no
409              * longer have a window, don't bother showing the user an error.
410              */
411             if (getWindowToken() != null) {
412                 Resources r = mContext.getResources();
413                 int messageId;
414 
415                 if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
416                     messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback;
417                 } else {
418                     messageId = com.android.internal.R.string.VideoView_error_text_unknown;
419                 }
420 
421                 new AlertDialog.Builder(mContext)
422                         .setMessage(messageId)
423                         .setPositiveButton(com.android.internal.R.string.VideoView_error_button,
424                                 new DialogInterface.OnClickListener() {
425                                     public void onClick(DialogInterface dialog, int whichButton) {
426                                         /* If we get here, there is no onError listener, so
427                                          * at least inform them that the video is over.
428                                          */
429                                         if (mOnCompletionListener != null) {
430                                             mOnCompletionListener.onCompletion(mMediaPlayer);
431                                         }
432                                     }
433                                 })
434                         .setCancelable(false)
435                         .show();
436             }
437             return true;
438         }
439     };
440 
441     private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
442         new MediaPlayer.OnBufferingUpdateListener() {
443         public void onBufferingUpdate(MediaPlayer mp, int percent) {
444             mCurrentBufferPercentage = percent;
445         }
446     };
447 
448     /**
449      * Register a callback to be invoked when the media file
450      * is loaded and ready to go.
451      *
452      * @param l The callback that will be run
453      */
setOnPreparedListener(MediaPlayer.OnPreparedListener l)454     public void setOnPreparedListener(MediaPlayer.OnPreparedListener l)
455     {
456         mOnPreparedListener = l;
457     }
458 
459     /**
460      * Register a callback to be invoked when the end of a media file
461      * has been reached during playback.
462      *
463      * @param l The callback that will be run
464      */
setOnCompletionListener(OnCompletionListener l)465     public void setOnCompletionListener(OnCompletionListener l)
466     {
467         mOnCompletionListener = l;
468     }
469 
470     /**
471      * Register a callback to be invoked when an error occurs
472      * during playback or setup.  If no listener is specified,
473      * or if the listener returned false, VideoView will inform
474      * the user of any errors.
475      *
476      * @param l The callback that will be run
477      */
setOnErrorListener(OnErrorListener l)478     public void setOnErrorListener(OnErrorListener l)
479     {
480         mOnErrorListener = l;
481     }
482 
483     /**
484      * Register a callback to be invoked when an informational event
485      * occurs during playback or setup.
486      *
487      * @param l The callback that will be run
488      */
setOnInfoListener(OnInfoListener l)489     public void setOnInfoListener(OnInfoListener l) {
490         mOnInfoListener = l;
491     }
492 
493     SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
494     {
495         public void surfaceChanged(SurfaceHolder holder, int format,
496                                     int w, int h)
497         {
498             mSurfaceWidth = w;
499             mSurfaceHeight = h;
500             boolean isValidState =  (mTargetState == STATE_PLAYING);
501             boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h);
502             if (mMediaPlayer != null && isValidState && hasValidSize) {
503                 if (mSeekWhenPrepared != 0) {
504                     seekTo(mSeekWhenPrepared);
505                 }
506                 start();
507             }
508         }
509 
510         public void surfaceCreated(SurfaceHolder holder)
511         {
512             mSurfaceHolder = holder;
513             openVideo();
514         }
515 
516         public void surfaceDestroyed(SurfaceHolder holder)
517         {
518             // after we return from this we can't use the surface any more
519             mSurfaceHolder = null;
520             if (mMediaController != null) mMediaController.hide();
521             release(true);
522         }
523     };
524 
525     /*
526      * release the media player in any state
527      */
release(boolean cleartargetstate)528     private void release(boolean cleartargetstate) {
529         if (mMediaPlayer != null) {
530             mMediaPlayer.reset();
531             mMediaPlayer.release();
532             mMediaPlayer = null;
533             mCurrentState = STATE_IDLE;
534             if (cleartargetstate) {
535                 mTargetState  = STATE_IDLE;
536             }
537         }
538     }
539 
540     @Override
onTouchEvent(MotionEvent ev)541     public boolean onTouchEvent(MotionEvent ev) {
542         if (isInPlaybackState() && mMediaController != null) {
543             toggleMediaControlsVisiblity();
544         }
545         return false;
546     }
547 
548     @Override
onTrackballEvent(MotionEvent ev)549     public boolean onTrackballEvent(MotionEvent ev) {
550         if (isInPlaybackState() && mMediaController != null) {
551             toggleMediaControlsVisiblity();
552         }
553         return false;
554     }
555 
556     @Override
onKeyDown(int keyCode, KeyEvent event)557     public boolean onKeyDown(int keyCode, KeyEvent event)
558     {
559         boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
560                                      keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
561                                      keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
562                                      keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
563                                      keyCode != KeyEvent.KEYCODE_MENU &&
564                                      keyCode != KeyEvent.KEYCODE_CALL &&
565                                      keyCode != KeyEvent.KEYCODE_ENDCALL;
566         if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
567             if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
568                     keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
569                 if (mMediaPlayer.isPlaying()) {
570                     pause();
571                     mMediaController.show();
572                 } else {
573                     start();
574                     mMediaController.hide();
575                 }
576                 return true;
577             } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
578                 if (!mMediaPlayer.isPlaying()) {
579                     start();
580                     mMediaController.hide();
581                 }
582                 return true;
583             } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
584                     || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
585                 if (mMediaPlayer.isPlaying()) {
586                     pause();
587                     mMediaController.show();
588                 }
589                 return true;
590             } else {
591                 toggleMediaControlsVisiblity();
592             }
593         }
594 
595         return super.onKeyDown(keyCode, event);
596     }
597 
toggleMediaControlsVisiblity()598     private void toggleMediaControlsVisiblity() {
599         if (mMediaController.isShowing()) {
600             mMediaController.hide();
601         } else {
602             mMediaController.show();
603         }
604     }
605 
606     @Override
start()607     public void start() {
608         if (isInPlaybackState()) {
609             mMediaPlayer.start();
610             mCurrentState = STATE_PLAYING;
611         }
612         mTargetState = STATE_PLAYING;
613     }
614 
615     @Override
pause()616     public void pause() {
617         if (isInPlaybackState()) {
618             if (mMediaPlayer.isPlaying()) {
619                 mMediaPlayer.pause();
620                 mCurrentState = STATE_PAUSED;
621             }
622         }
623         mTargetState = STATE_PAUSED;
624     }
625 
suspend()626     public void suspend() {
627         release(false);
628     }
629 
resume()630     public void resume() {
631         openVideo();
632     }
633 
634     @Override
getDuration()635     public int getDuration() {
636         if (isInPlaybackState()) {
637             return mMediaPlayer.getDuration();
638         }
639 
640         return -1;
641     }
642 
643     @Override
getCurrentPosition()644     public int getCurrentPosition() {
645         if (isInPlaybackState()) {
646             return mMediaPlayer.getCurrentPosition();
647         }
648         return 0;
649     }
650 
651     @Override
seekTo(int msec)652     public void seekTo(int msec) {
653         if (isInPlaybackState()) {
654             mMediaPlayer.seekTo(msec);
655             mSeekWhenPrepared = 0;
656         } else {
657             mSeekWhenPrepared = msec;
658         }
659     }
660 
661     @Override
isPlaying()662     public boolean isPlaying() {
663         return isInPlaybackState() && mMediaPlayer.isPlaying();
664     }
665 
666     @Override
getBufferPercentage()667     public int getBufferPercentage() {
668         if (mMediaPlayer != null) {
669             return mCurrentBufferPercentage;
670         }
671         return 0;
672     }
673 
isInPlaybackState()674     private boolean isInPlaybackState() {
675         return (mMediaPlayer != null &&
676                 mCurrentState != STATE_ERROR &&
677                 mCurrentState != STATE_IDLE &&
678                 mCurrentState != STATE_PREPARING);
679     }
680 
681     @Override
canPause()682     public boolean canPause() {
683         return mCanPause;
684     }
685 
686     @Override
canSeekBackward()687     public boolean canSeekBackward() {
688         return mCanSeekBack;
689     }
690 
691     @Override
canSeekForward()692     public boolean canSeekForward() {
693         return mCanSeekForward;
694     }
695 
696     @Override
getAudioSessionId()697     public int getAudioSessionId() {
698         if (mAudioSession == 0) {
699             MediaPlayer foo = new MediaPlayer();
700             mAudioSession = foo.getAudioSessionId();
701             foo.release();
702         }
703         return mAudioSession;
704     }
705 }
706