Loading core/java/android/webkit/HTML5VideoViewProxy.java +140 −45 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; } } Loading @@ -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) { 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()); } } core/java/android/webkit/ViewManager.java +12 −4 Original line number Diff line number Diff line Loading @@ -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) { Loading core/java/android/webkit/WebChromeClient.java +17 −1 Original line number Diff line number Diff line Loading @@ -44,14 +44,30 @@ 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 Loading Loading
core/java/android/webkit/HTML5VideoViewProxy.java +140 −45 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; } } Loading @@ -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) { 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()); } }
core/java/android/webkit/ViewManager.java +12 −4 Original line number Diff line number Diff line Loading @@ -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) { Loading
core/java/android/webkit/WebChromeClient.java +17 −1 Original line number Diff line number Diff line Loading @@ -44,14 +44,30 @@ 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 Loading