diff options
author | Andrei Popescu <andreip@google.com> | 2009-07-03 08:20:53 +0100 |
---|---|---|
committer | Andrei Popescu <andreip@google.com> | 2009-07-08 17:01:59 +0100 |
commit | 3c946a1a9637e85e4256f40b68f3b9d4b9f40c27 (patch) | |
tree | 4623bfde7be98dedc64e2a1264354efc3c5d20b6 | |
parent | 6e2863ec16828b77c494592410e6b01281596e87 (diff) | |
download | frameworks_base-3c946a1a9637e85e4256f40b68f3b9d4b9f40c27.zip frameworks_base-3c946a1a9637e85e4256f40b68f3b9d4b9f40c27.tar.gz frameworks_base-3c946a1a9637e85e4256f40b68f3b9d4b9f40c27.tar.bz2 |
Add fullscreen support back in.
-rw-r--r-- | core/java/android/webkit/HTML5VideoViewProxy.java | 185 | ||||
-rw-r--r-- | core/java/android/webkit/ViewManager.java | 16 | ||||
-rw-r--r-- | core/java/android/webkit/WebChromeClient.java | 18 |
3 files changed, 169 insertions, 50 deletions
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 @@ 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<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 @@ class HTML5VideoViewProxy extends Handler { * @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; - } - ChildView child = w.mViewManager.createView(); - sendMessage(obtainMessage(INIT, child)); - return child; + public void createView() { + mChildView = mWebView.mViewManager.createView(); + sendMessage(obtainMessage(INIT)); } - public void attachView(ChildView child, int x, int y, int width, - int height) { - child.attachView(x, y, width, height); + public void attachView(int x, int y, int width, int height) { + if (mChildView == null) { + return; + } + mChildView.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 @@ class ViewManager { 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 class WebChromeClient { public void onReceivedIcon(WebView view, Bitmap icon) {} /** + * 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 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) {} + public void onShowCustomView(View view, CustomViewCallback callback) {}; /** * Notify the host application that the current page would |