diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java index 5b2c0e2c2fc05..5a164f8af57be 100644 --- a/core/java/android/webkit/HTML5VideoViewProxy.java +++ b/core/java/android/webkit/HTML5VideoViewProxy.java @@ -17,49 +17,148 @@ package android.webkit; import android.content.Context; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnPreparedListener; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; +import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.webkit.ViewManager.ChildView; +import android.widget.AbsoluteLayout; import android.widget.MediaController; import android.widget.VideoView; import java.util.HashMap; /** - *
A View that displays Videos. Instances of this class - * are created on the WebCore thread. However, their code - * executes on the UI thread. Right now there is only one - * such view for fullscreen video rendering. - * + *
Proxy for HTML5 video views.
*/
class HTML5VideoViewProxy extends Handler {
// Logging tag.
private static final String LOGTAG = "HTML5VideoViewProxy";
- // Message Ids
+ // Message Ids for WebCore thread -> UI thread communication.
private static final int INIT = 100;
private static final int PLAY = 101;
- // The singleton instance.
- private static HTML5VideoViewProxy sInstance;
- // The context object used to initialize the VideoView and the
- // MediaController.
- private Context mContext;
+ // The WebView instance that created this view.
+ private WebView mWebView;
+ // The ChildView instance used by the ViewManager.
+ private ChildView mChildView;
+ // The VideoView instance. Note that we could
+ // also access this via mChildView.mView but it would
+ // always require cast, so it is more convenient to store
+ // it here as well.
+ private HTML5VideoView mVideoView;
+
+ // A VideoView subclass that responds to double-tap
+ // events by going fullscreen.
+ class HTML5VideoView extends VideoView {
+ // Used to save the layout parameters if the view
+ // is changed to fullscreen.
+ private AbsoluteLayout.LayoutParams mEmbeddedLayoutParams;
+ // Flag that denotes whether the view is fullscreen or not.
+ private boolean mIsFullscreen;
+ // Used to save the current playback position when
+ // transitioning to/from fullscreen.
+ private int mPlaybackPosition;
+ // The callback object passed to the host application. This callback
+ // is invoked when the host application dismisses our VideoView
+ // (e.g. the user presses the back key).
+ private WebChromeClient.CustomViewCallback mCallback =
+ new WebChromeClient.CustomViewCallback() {
+ public void onCustomViewHidden() {
+ playEmbedded();
+ }
+ };
+
+ // The OnPreparedListener, used to automatically resume
+ // playback when transitioning to/from fullscreen.
+ private MediaPlayer.OnPreparedListener mPreparedListener =
+ new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer mp) {
+ resumePlayback();
+ }
+ };
+
+ HTML5VideoView(Context context) {
+ super(context);
+ }
+
+ void savePlaybackPosition() {
+ if (isPlaying()) {
+ mPlaybackPosition = getCurrentPosition();
+ }
+ }
+
+ void resumePlayback() {
+ seekTo(mPlaybackPosition);
+ start();
+ setOnPreparedListener(null);
+ }
+
+ void playEmbedded() {
+ // Attach to the WebView.
+ mChildView.attachViewOnUIThread(mEmbeddedLayoutParams);
+ // Make sure we're visible
+ setVisibility(View.VISIBLE);
+ // Set the onPrepared listener so we start
+ // playing when the video view is reattached
+ // and its surface is recreated.
+ setOnPreparedListener(mPreparedListener);
+ mIsFullscreen = false;
+ }
+
+ void playFullScreen() {
+ WebChromeClient client = mWebView.getWebChromeClient();
+ if (client == null) {
+ return;
+ }
+ // Save the current layout params.
+ mEmbeddedLayoutParams =
+ (AbsoluteLayout.LayoutParams) getLayoutParams();
+ // Detach from the WebView.
+ mChildView.removeViewOnUIThread();
+ // Attach to the browser UI.
+ client.onShowCustomView(this, mCallback);
+ // Set the onPrepared listener so we start
+ // playing when after the video view is reattached
+ // and its surface is recreated.
+ setOnPreparedListener(mPreparedListener);
+ mIsFullscreen = true;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // TODO: implement properly (i.e. detect double tap)
+ if (mIsFullscreen || !isPlaying()) {
+ return super.onTouchEvent(ev);
+ }
+ playFullScreen();
+ return true;
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ savePlaybackPosition();
+ }
+ }
/**
* Private constructor.
* @param context is the application context.
*/
- private HTML5VideoViewProxy(Context context) {
+ private HTML5VideoViewProxy(WebView webView) {
// This handler is for the main (UI) thread.
super(Looper.getMainLooper());
- // Save the context object.
- mContext = context;
+ // Save the WebView object.
+ mWebView = webView;
}
@Override
@@ -67,22 +166,23 @@ class HTML5VideoViewProxy extends Handler {
// This executes on the UI thread.
switch (msg.what) {
case INIT:
- ChildView child = (ChildView) msg.obj;
// Create the video view and set a default controller.
- VideoView v = new VideoView(mContext);
+ mVideoView = new HTML5VideoView(mWebView.getContext());
// This is needed because otherwise there will be a black square
// stuck on the screen.
- v.setWillNotDraw(false);
- v.setMediaController(new MediaController(mContext));
- child.mView = v;
+ mVideoView.setWillNotDraw(false);
+ mVideoView.setMediaController(new MediaController(mWebView.getContext()));
+ mChildView.mView = mVideoView;
break;
case PLAY:
+ if (mVideoView == null) {
+ return;
+ }
HashMap