| |
| package android.webkit; |
| |
| import android.content.Context; |
| import android.media.MediaPlayer; |
| import android.media.Metadata; |
| import android.util.Log; |
| import android.view.Gravity; |
| import android.view.MotionEvent; |
| import android.view.SurfaceHolder; |
| import android.view.SurfaceView; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.webkit.HTML5VideoView; |
| import android.webkit.HTML5VideoViewProxy; |
| import android.widget.FrameLayout; |
| import android.widget.MediaController; |
| import android.widget.MediaController.MediaPlayerControl; |
| |
| |
| /** |
| * @hide This is only used by the browser |
| */ |
| public class HTML5VideoFullScreen extends HTML5VideoView |
| implements MediaPlayerControl, MediaPlayer.OnPreparedListener, |
| View.OnTouchListener { |
| |
| // Add this sub-class to handle the resizing when rotating screen. |
| private class VideoSurfaceView extends SurfaceView { |
| |
| public VideoSurfaceView(Context context) { |
| super(context); |
| } |
| |
| @Override |
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| int width = getDefaultSize(mVideoWidth, widthMeasureSpec); |
| int height = getDefaultSize(mVideoHeight, heightMeasureSpec); |
| if (mVideoWidth > 0 && mVideoHeight > 0) { |
| if ( mVideoWidth * height > width * mVideoHeight ) { |
| height = width * mVideoHeight / mVideoWidth; |
| } else if ( mVideoWidth * height < width * mVideoHeight ) { |
| width = height * mVideoWidth / mVideoHeight; |
| } |
| } |
| setMeasuredDimension(width, height); |
| } |
| } |
| |
| // This view will contain the video. |
| private VideoSurfaceView mVideoSurfaceView; |
| |
| // We need the full screen state to decide which surface to render to and |
| // when to create the MediaPlayer accordingly. |
| static final int FULLSCREEN_OFF = 0; |
| static final int FULLSCREEN_SURFACECREATING = 1; |
| static final int FULLSCREEN_SURFACECREATED = 2; |
| |
| private int mFullScreenMode; |
| // The Media Controller only used for full screen mode |
| private MediaController mMediaController; |
| |
| // SurfaceHolder for full screen |
| private SurfaceHolder mSurfaceHolder = null; |
| |
| // Data only for MediaController |
| private boolean mCanSeekBack; |
| private boolean mCanSeekForward; |
| private boolean mCanPause; |
| private int mCurrentBufferPercentage; |
| |
| // The progress view. |
| private static View mProgressView; |
| // The container for the progress view and video view |
| private static FrameLayout mLayout; |
| |
| // The video size will be ready when prepared. Used to make sure the aspect |
| // ratio is correct. |
| private int mVideoWidth; |
| private int mVideoHeight; |
| |
| SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() |
| { |
| public void surfaceChanged(SurfaceHolder holder, int format, |
| int w, int h) |
| { |
| if (mPlayer != null && mMediaController != null |
| && mCurrentState == STATE_PREPARED) { |
| if (mMediaController.isShowing()) { |
| // ensure the controller will get repositioned later |
| mMediaController.hide(); |
| } |
| mMediaController.show(); |
| } |
| } |
| |
| public void surfaceCreated(SurfaceHolder holder) |
| { |
| mSurfaceHolder = holder; |
| mFullScreenMode = FULLSCREEN_SURFACECREATED; |
| |
| prepareForFullScreen(); |
| } |
| |
| public void surfaceDestroyed(SurfaceHolder holder) |
| { |
| // after we return from this we can't use the surface any more |
| mSurfaceHolder = null; |
| // The current Video View will be destroy when we play a new video. |
| } |
| }; |
| |
| private SurfaceView getSurfaceView() { |
| return mVideoSurfaceView; |
| } |
| |
| HTML5VideoFullScreen(Context context, int videoLayerId, int position, |
| boolean autoStart) { |
| mVideoSurfaceView = new VideoSurfaceView(context); |
| mFullScreenMode = FULLSCREEN_OFF; |
| mVideoWidth = 0; |
| mVideoHeight = 0; |
| init(videoLayerId, position, autoStart); |
| } |
| |
| private void setMediaController(MediaController m) { |
| mMediaController = m; |
| attachMediaController(); |
| } |
| |
| private void attachMediaController() { |
| if (mPlayer != null && mMediaController != null) { |
| mMediaController.setMediaPlayer(this); |
| mMediaController.setAnchorView(mVideoSurfaceView); |
| //Will be enabled when prepared |
| mMediaController.setEnabled(false); |
| } |
| } |
| |
| @Override |
| public void decideDisplayMode() { |
| mPlayer.setDisplay(mSurfaceHolder); |
| } |
| |
| private void prepareForFullScreen() { |
| // So in full screen, we reset the MediaPlayer |
| mPlayer.reset(); |
| setMediaController(new MediaController(mProxy.getContext())); |
| mPlayer.setScreenOnWhilePlaying(true); |
| prepareDataAndDisplayMode(mProxy); |
| } |
| |
| |
| private void toggleMediaControlsVisiblity() { |
| if (mMediaController.isShowing()) { |
| mMediaController.hide(); |
| } else { |
| mMediaController.show(); |
| } |
| } |
| |
| @Override |
| public void onPrepared(MediaPlayer mp) { |
| super.onPrepared(mp); |
| |
| mVideoSurfaceView.setOnTouchListener(this); |
| // Get the capabilities of the player for this stream |
| Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL, |
| MediaPlayer.BYPASS_METADATA_FILTER); |
| if (data != null) { |
| mCanPause = !data.has(Metadata.PAUSE_AVAILABLE) |
| || data.getBoolean(Metadata.PAUSE_AVAILABLE); |
| mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE) |
| || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE); |
| mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE) |
| || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE); |
| } else { |
| mCanPause = mCanSeekBack = mCanSeekForward = true; |
| } |
| |
| // mMediaController status depends on the Metadata result, so put it |
| // after reading the MetaData |
| if (mMediaController != null) { |
| mMediaController.setEnabled(true); |
| // If paused , should show the controller for ever! |
| if (getAutostart()) |
| mMediaController.show(); |
| else |
| mMediaController.show(0); |
| } |
| |
| if (mProgressView != null) { |
| mProgressView.setVisibility(View.GONE); |
| } |
| |
| mVideoWidth = mp.getVideoWidth(); |
| mVideoHeight = mp.getVideoHeight(); |
| // This will trigger the onMeasure to get the display size right. |
| mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight); |
| } |
| |
| public boolean fullScreenExited() { |
| return (mLayout == null); |
| } |
| |
| private final WebChromeClient.CustomViewCallback mCallback = |
| new WebChromeClient.CustomViewCallback() { |
| public void onCustomViewHidden() { |
| // It listens to SurfaceHolder.Callback.SurfaceDestroyed event |
| // which happens when the video view is detached from its parent |
| // view. This happens in the WebChromeClient before this method |
| // is invoked. |
| pauseAndDispatch(mProxy); |
| mProxy.dispatchOnStopFullScreen(); |
| mLayout.removeView(getSurfaceView()); |
| |
| if (mProgressView != null) { |
| mLayout.removeView(mProgressView); |
| mProgressView = null; |
| } |
| mLayout = null; |
| // Re enable plugin views. |
| mProxy.getWebView().getViewManager().showAll(); |
| |
| mProxy = null; |
| } |
| }; |
| |
| @Override |
| public void enterFullScreenVideoState(int layerId, |
| HTML5VideoViewProxy proxy, WebView webView) { |
| mFullScreenMode = FULLSCREEN_SURFACECREATING; |
| mCurrentBufferPercentage = 0; |
| mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); |
| mProxy = proxy; |
| |
| mVideoSurfaceView.getHolder().addCallback(mSHCallback); |
| mVideoSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); |
| mVideoSurfaceView.setFocusable(true); |
| mVideoSurfaceView.setFocusableInTouchMode(true); |
| mVideoSurfaceView.requestFocus(); |
| |
| // Create a FrameLayout that will contain the VideoView and the |
| // progress view (if any). |
| mLayout = new FrameLayout(mProxy.getContext()); |
| FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( |
| ViewGroup.LayoutParams.WRAP_CONTENT, |
| ViewGroup.LayoutParams.WRAP_CONTENT, |
| Gravity.CENTER); |
| |
| mLayout.addView(getSurfaceView(), layoutParams); |
| |
| mLayout.setVisibility(View.VISIBLE); |
| |
| WebChromeClient client = webView.getWebChromeClient(); |
| client.onShowCustomView(mLayout, mCallback); |
| // Plugins like Flash will draw over the video so hide |
| // them while we're playing. |
| if (webView.getViewManager() != null) |
| webView.getViewManager().hideAll(); |
| |
| mProgressView = client.getVideoLoadingProgressView(); |
| if (mProgressView != null) { |
| mLayout.addView(mProgressView, layoutParams); |
| mProgressView.setVisibility(View.VISIBLE); |
| } |
| |
| } |
| |
| /** |
| * @return true when we are in full screen mode, even the surface not fully |
| * created. |
| */ |
| public boolean isFullScreenMode() { |
| return true; |
| } |
| |
| // MediaController FUNCTIONS: |
| @Override |
| public boolean canPause() { |
| return mCanPause; |
| } |
| |
| @Override |
| public boolean canSeekBackward() { |
| return mCanSeekBack; |
| } |
| |
| @Override |
| public boolean canSeekForward() { |
| return mCanSeekForward; |
| } |
| |
| @Override |
| public int getBufferPercentage() { |
| if (mPlayer != null) { |
| return mCurrentBufferPercentage; |
| } |
| return 0; |
| } |
| |
| // Other listeners functions: |
| private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = |
| new MediaPlayer.OnBufferingUpdateListener() { |
| public void onBufferingUpdate(MediaPlayer mp, int percent) { |
| mCurrentBufferPercentage = percent; |
| } |
| }; |
| |
| @Override |
| public boolean onTouch(View v, MotionEvent event) { |
| if (mFullScreenMode >= FULLSCREEN_SURFACECREATED |
| && mMediaController != null) { |
| toggleMediaControlsVisiblity(); |
| } |
| return false; |
| } |
| |
| @Override |
| protected void switchProgressView(boolean playerBuffering) { |
| if (mProgressView != null) { |
| if (playerBuffering) { |
| mProgressView.setVisibility(View.VISIBLE); |
| } else { |
| mProgressView.setVisibility(View.GONE); |
| } |
| } |
| return; |
| } |
| } |