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