Merge change 6356
* changes:
Add fullscreen support back in.
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index 5b2c0e2..5a164f8 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;
/**
- * <p>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.
- *
+ * <p>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 @@
// 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<String, Object> map =
(HashMap<String, Object>) msg.obj;
String url = (String) map.get("url");
- VideoView view = (VideoView) map.get("view");
- view.setVideoURI(Uri.parse(url));
- view.start();
+ mVideoView.setVideoURI(Uri.parse(url));
+ mVideoView.start();
break;
}
}
@@ -92,46 +192,41 @@
* @param url is the URL of the video stream.
* @param webview is the WebViewCore that is requesting the playback.
*/
- public void play(String url, ChildView child) {
- // We need to know the webview that is requesting the playback.
+ public void play(String url) {
+ // We need to know the webview that is requesting the playback.
Message message = obtainMessage(PLAY);
HashMap<String, Object> map = new HashMap();
map.put("url", url);
- map.put("view", child.mView);
message.obj = map;
sendMessage(message);
}
- public ChildView createView(WebViewCore core) {
- WebView w = core.getWebView();
- if (w == null) {
- return null;
+ public void createView() {
+ mChildView = mWebView.mViewManager.createView();
+ sendMessage(obtainMessage(INIT));
+ }
+
+ public void attachView(int x, int y, int width, int height) {
+ if (mChildView == null) {
+ return;
}
- ChildView child = w.mViewManager.createView();
- sendMessage(obtainMessage(INIT, child));
- return child;
+ mChildView.attachView(x, y, width, height);
}
- public void attachView(ChildView child, int x, int y, int width,
- int height) {
- child.attachView(x, y, width, height);
- }
-
- public void removeView(ChildView child) {
- child.removeView();
+ public void removeView() {
+ if (mChildView == null) {
+ return;
+ }
+ mChildView.removeView();
}
/**
- * The factory for HTML5VideoViewProxy instances. Right now,
- * it only produces a singleton.
+ * The factory for HTML5VideoViewProxy instances.
* @param webViewCore is the WebViewCore that is requesting the proxy.
*
- * @return the HTML5VideoViewProxy singleton.
+ * @return a new HTML5VideoViewProxy object.
*/
public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore) {
- if (sInstance == null) {
- sInstance = new HTML5VideoViewProxy(webViewCore.getWebView().getContext());
- }
- return sInstance;
+ return new HTML5VideoViewProxy(webViewCore.getWebView());
}
}
diff --git a/core/java/android/webkit/ViewManager.java b/core/java/android/webkit/ViewManager.java
index 476e85c..af33b4f 100644
--- a/core/java/android/webkit/ViewManager.java
+++ b/core/java/android/webkit/ViewManager.java
@@ -61,24 +61,32 @@
if (mView.getParent() != null) {
mView.setLayoutParams(lp);
} else {
- mWebView.addView(mView, lp);
- mChildren.add(ChildView.this);
+ attachViewOnUIThread(lp);
}
}
});
}
+ void attachViewOnUIThread(AbsoluteLayout.LayoutParams lp) {
+ mWebView.addView(mView, lp);
+ mChildren.add(this);
+ }
+
void removeView() {
if (mView == null) {
return;
}
mWebView.mPrivateHandler.post(new Runnable() {
public void run() {
- mWebView.removeView(mView);
- mChildren.remove(ChildView.this);
+ removeViewOnUIThread();
}
});
}
+
+ void removeViewOnUIThread() {
+ mWebView.removeView(mView);
+ mChildren.remove(this);
+ }
}
ViewManager(WebView w) {
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 19e39ed..dd43b8b 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -45,13 +45,29 @@
public void onReceivedIcon(WebView view, Bitmap icon) {}
/**
- * Notify the host application that the current page would
- * like to show a custom View.
- * @param view is the View object to be shown.
+ * A callback interface used by the host application to notify
+ * the current page that its custom view has been dismissed.
*
* @hide pending council approval
*/
- public void onShowCustomView(View view) {}
+ public interface CustomViewCallback {
+ /**
+ * Invoked when the host application dismisses the
+ * custom view.
+ */
+ public void onCustomViewHidden();
+ }
+
+ /**
+ * Notify the host application that the current page would
+ * like to show a custom View.
+ * @param view is the View object to be shown.
+ * @param callback is the callback to be invoked if and when the view
+ * is dismissed.
+ *
+ * @hide pending council approval
+ */
+ public void onShowCustomView(View view, CustomViewCallback callback) {};
/**
* Notify the host application that the current page would