Loading core/java/android/webkit/HTML5VideoViewProxy.java +263 −105 Original line number Original line Diff line number Diff line Loading @@ -17,8 +17,18 @@ package android.webkit; package android.webkit; import android.content.Context; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.MediaPlayer; import android.media.MediaPlayer; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.net.http.EventHandler; import android.net.http.Headers; import android.net.http.RequestHandle; import android.net.http.RequestQueue; import android.net.http.SslCertificate; import android.net.http.SslError; import android.net.Uri; import android.net.Uri; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; Loading @@ -30,9 +40,12 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import android.webkit.ViewManager.ChildView; import android.webkit.ViewManager.ChildView; import android.widget.AbsoluteLayout; import android.widget.AbsoluteLayout; import android.widget.ImageView; import android.widget.MediaController; import android.widget.MediaController; import android.widget.VideoView; import android.widget.VideoView; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.HashMap; /** /** Loading @@ -45,108 +58,216 @@ class HTML5VideoViewProxy extends Handler { // Message Ids for WebCore thread -> UI thread communication. // Message Ids for WebCore thread -> UI thread communication. private static final int INIT = 100; private static final int INIT = 100; private static final int PLAY = 101; private static final int PLAY = 101; private static final int SET_POSTER = 102; // Message Ids to be handled on the WebCore thread // The handler for WebCore thread messages; private Handler mWebCoreHandler; // The WebView instance that created this view. // The WebView instance that created this view. private WebView mWebView; private WebView mWebView; // The ChildView instance used by the ViewManager. // The ChildView instance used by the ViewManager. private ChildView mChildView; private ChildView mChildView; // The VideoView instance. Note that we could // The poster image to be shown when the video is not playing. // also access this via mChildView.mView but it would private ImageView mPosterView; // always require cast, so it is more convenient to store // The poster downloader. // it here as well. private PosterDownloader mPosterDownloader; private HTML5VideoView mVideoView; // A helper class to control the playback. This executes on the UI thread! private static final class VideoPlayer { // A VideoView subclass that responds to double-tap // The proxy that is currently playing (if any). // events by going fullscreen. private static HTML5VideoViewProxy mCurrentProxy; class HTML5VideoView extends VideoView { // The VideoView instance. This is a singleton for now, at least until // Used to save the layout parameters if the view // http://b/issue?id=1973663 is fixed. // is changed to fullscreen. private static VideoView mVideoView; private AbsoluteLayout.LayoutParams mEmbeddedLayoutParams; // Flag that denotes whether the view is fullscreen or not. private static final WebChromeClient.CustomViewCallback mCallback = 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() { new WebChromeClient.CustomViewCallback() { public void onCustomViewHidden() { public void onCustomViewHidden() { playEmbedded(); // At this point the videoview is pretty much destroyed. // 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. mCurrentProxy.playbackEnded(); mCurrentProxy = null; mVideoView = null; } } }; }; // The OnPreparedListener, used to automatically resume public static void play(String url, HTML5VideoViewProxy proxy, WebChromeClient client) { // playback when transitioning to/from fullscreen. if (mCurrentProxy != null) { private MediaPlayer.OnPreparedListener mPreparedListener = // Some other video is already playing. Notify the caller that its playback ended. new MediaPlayer.OnPreparedListener() { proxy.playbackEnded(); public void onPrepared(MediaPlayer mp) { return; resumePlayback(); } mCurrentProxy = proxy; mVideoView = new VideoView(proxy.getContext()); mVideoView.setWillNotDraw(false); mVideoView.setMediaController(new MediaController(proxy.getContext())); mVideoView.setVideoURI(Uri.parse(url)); mVideoView.start(); client.onShowCustomView(mVideoView, mCallback); } } // Handler for the messages from WebCore thread to the UI thread. @Override public void handleMessage(Message msg) { // This executes on the UI thread. switch (msg.what) { case INIT: { mPosterView = new ImageView(mWebView.getContext()); mChildView.mView = mPosterView; break; } case PLAY: { String url = (String) msg.obj; WebChromeClient client = mWebView.getWebChromeClient(); if (client != null) { VideoPlayer.play(url, this, client); } break; } case SET_POSTER: { Bitmap poster = (Bitmap) msg.obj; mPosterView.setImageBitmap(poster); break; } } } } }; HTML5VideoView(Context context) { public void playbackEnded() { super(context); // TODO: notify WebKit } } void savePlaybackPosition() { // Everything below this comment executes on the WebCore thread, except for if (isPlaying()) { // the EventHandler methods, which are called on the network thread. mPlaybackPosition = getCurrentPosition(); // A helper class that knows how to download posters private static final class PosterDownloader implements EventHandler { // The request queue. This is static as we have one queue for all posters. private static RequestQueue mRequestQueue; private static int mQueueRefCount = 0; // The poster URL private String mUrl; // The proxy we're doing this for. private final HTML5VideoViewProxy mProxy; // The poster bytes. We only touch this on the network thread. private ByteArrayOutputStream mPosterBytes; // The request handle. We only touch this on the WebCore thread. private RequestHandle mRequestHandle; // The response status code. private int mStatusCode; // The response headers. private Headers mHeaders; // The handler to handle messages on the WebCore thread. private Handler mHandler; public PosterDownloader(String url, HTML5VideoViewProxy proxy) { mUrl = url; mProxy = proxy; mHandler = new Handler(); } // Start the download. Called on WebCore thread. public void start() { retainQueue(); mRequestHandle = mRequestQueue.queueRequest(mUrl, "GET", null, this, null, 0); } // Cancel the download if active and release the queue. Called on WebCore thread. public void cancelAndReleaseQueue() { if (mRequestHandle != null) { mRequestHandle.cancel(); mRequestHandle = null; } } releaseQueue(); } // EventHandler methods. Executed on the network thread. public void status(int major_version, int minor_version, int code, String reason_phrase) { mStatusCode = code; } } void resumePlayback() { public void headers(Headers headers) { seekTo(mPlaybackPosition); mHeaders = headers; start(); setOnPreparedListener(null); } } void playEmbedded() { public void data(byte[] data, int len) { // Attach to the WebView. if (mPosterBytes == null) { mChildView.attachViewOnUIThread(mEmbeddedLayoutParams); mPosterBytes = new ByteArrayOutputStream(); // Make sure we're visible } setVisibility(View.VISIBLE); mPosterBytes.write(data, 0, len); // 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() { public void endData() { WebChromeClient client = mWebView.getWebChromeClient(); if (mStatusCode == 200) { if (client == null) { if (mPosterBytes.size() > 0) { return; Bitmap poster = BitmapFactory.decodeByteArray( mPosterBytes.toByteArray(), 0, mPosterBytes.size()); if (poster != null) { mProxy.doSetPoster(poster); } } cleanup(); } else if (mStatusCode >= 300 && mStatusCode < 400) { // We have a redirect. mUrl = mHeaders.getLocation(); if (mUrl != null) { mHandler.post(new Runnable() { public void run() { if (mRequestHandle != null) { mRequestHandle.setupRedirect(mUrl, mStatusCode, new HashMap<String, String>()); } } }); } } } // 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 void certificate(SslCertificate certificate) { public boolean onTouchEvent(MotionEvent ev) { // Don't care. // TODO: implement properly (i.e. detect double tap) if (mIsFullscreen || !isPlaying()) { return super.onTouchEvent(ev); } } playFullScreen(); return true; public void error(int id, String description) { cleanup(); } } @Override public boolean handleSslErrorRequest(SslError error) { public void onDetachedFromWindow() { // Don't care. If this happens, data() will never be called so super.onDetachedFromWindow(); // mPosterBytes will never be created, so no need to call cleanup. savePlaybackPosition(); return false; } // Tears down the poster bytes stream. Called on network thread. private void cleanup() { if (mPosterBytes != null) { try { mPosterBytes.close(); } catch (IOException ignored) { // Ignored. } finally { mPosterBytes = null; } } } // Queue management methods. Called on WebCore thread. private void retainQueue() { if (mRequestQueue == null) { mRequestQueue = new RequestQueue(mProxy.getContext()); } mQueueRefCount++; } private void releaseQueue() { if (mQueueRefCount == 0) { return; } if (--mQueueRefCount == 0) { mRequestQueue.shutdown(); mRequestQueue = null; } } } } } Loading @@ -159,53 +280,65 @@ class HTML5VideoViewProxy extends Handler { super(Looper.getMainLooper()); super(Looper.getMainLooper()); // Save the WebView object. // Save the WebView object. mWebView = webView; mWebView = webView; // create the message handler for this thread createWebCoreHandler(); } } private void createWebCoreHandler() { mWebCoreHandler = new Handler() { @Override @Override public void handleMessage(Message msg) { public void handleMessage(Message msg) { // This executes on the UI thread. switch (msg.what) { switch (msg.what) { case INIT: // TODO here we will process the messages from the VideoPlayer // Create the video view and set a default controller. // and will call native WebKit methods. mVideoView = new HTML5VideoView(mWebView.getContext()); } // This is needed because otherwise there will be a black square } // stuck on the screen. }; mVideoView.setWillNotDraw(false); } mVideoView.setMediaController(new MediaController(mWebView.getContext())); mChildView.mView = mVideoView; private void doSetPoster(Bitmap poster) { break; if (poster == null) { case PLAY: if (mVideoView == null) { return; return; } } HashMap<String, Object> map = // Send the bitmap over to the UI thread. (HashMap<String, Object>) msg.obj; Message message = obtainMessage(SET_POSTER); String url = (String) map.get("url"); message.obj = poster; mVideoView.setVideoURI(Uri.parse(url)); sendMessage(message); mVideoView.start(); break; } } public Context getContext() { return mWebView.getContext(); } } // The public methods below are all called from WebKit only. /** /** * Play a video stream. * Play a video stream. * @param url is the URL of the video stream. * @param url is the URL of the video stream. * @param webview is the WebViewCore that is requesting the playback. * @param webview is the WebViewCore that is requesting the playback. */ */ public void play(String url) { public void play(String url) { if (url == null) { return; } // We need to know the webview that is requesting the playback. // We need to know the webview that is requesting the playback. Message message = obtainMessage(PLAY); Message message = obtainMessage(PLAY); HashMap<String, Object> map = new HashMap(); message.obj = url; map.put("url", url); message.obj = map; sendMessage(message); sendMessage(message); } } /** * Create the child view that will cary the poster. */ public void createView() { public void createView() { mChildView = mWebView.mViewManager.createView(); mChildView = mWebView.mViewManager.createView(); sendMessage(obtainMessage(INIT)); sendMessage(obtainMessage(INIT)); } } /** * Attach the poster view. * @param x, y are the screen coordinates where the poster should be hung. * @param width, height denote the size of the poster. */ public void attachView(int x, int y, int width, int height) { public void attachView(int x, int y, int width, int height) { if (mChildView == null) { if (mChildView == null) { return; return; Loading @@ -213,11 +346,36 @@ class HTML5VideoViewProxy extends Handler { mChildView.attachView(x, y, width, height); mChildView.attachView(x, y, width, height); } } /** * Remove the child view and, thus, the poster. */ public void removeView() { public void removeView() { if (mChildView == null) { if (mChildView == null) { return; return; } } mChildView.removeView(); mChildView.removeView(); // This is called by the C++ MediaPlayerPrivate dtor. // Cancel any active poster download. if (mPosterDownloader != null) { mPosterDownloader.cancelAndReleaseQueue(); } } /** * Load the poster image. * @param url is the URL of the poster image. */ public void loadPoster(String url) { if (url == null) { return; } // Cancel any active poster download. if (mPosterDownloader != null) { mPosterDownloader.cancelAndReleaseQueue(); } // Load the poster asynchronously mPosterDownloader = new PosterDownloader(url, this); mPosterDownloader.start(); } } /** /** Loading Loading
core/java/android/webkit/HTML5VideoViewProxy.java +263 −105 Original line number Original line Diff line number Diff line Loading @@ -17,8 +17,18 @@ package android.webkit; package android.webkit; import android.content.Context; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.MediaPlayer; import android.media.MediaPlayer; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.net.http.EventHandler; import android.net.http.Headers; import android.net.http.RequestHandle; import android.net.http.RequestQueue; import android.net.http.SslCertificate; import android.net.http.SslError; import android.net.Uri; import android.net.Uri; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; Loading @@ -30,9 +40,12 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import android.webkit.ViewManager.ChildView; import android.webkit.ViewManager.ChildView; import android.widget.AbsoluteLayout; import android.widget.AbsoluteLayout; import android.widget.ImageView; import android.widget.MediaController; import android.widget.MediaController; import android.widget.VideoView; import android.widget.VideoView; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.HashMap; /** /** Loading @@ -45,108 +58,216 @@ class HTML5VideoViewProxy extends Handler { // Message Ids for WebCore thread -> UI thread communication. // Message Ids for WebCore thread -> UI thread communication. private static final int INIT = 100; private static final int INIT = 100; private static final int PLAY = 101; private static final int PLAY = 101; private static final int SET_POSTER = 102; // Message Ids to be handled on the WebCore thread // The handler for WebCore thread messages; private Handler mWebCoreHandler; // The WebView instance that created this view. // The WebView instance that created this view. private WebView mWebView; private WebView mWebView; // The ChildView instance used by the ViewManager. // The ChildView instance used by the ViewManager. private ChildView mChildView; private ChildView mChildView; // The VideoView instance. Note that we could // The poster image to be shown when the video is not playing. // also access this via mChildView.mView but it would private ImageView mPosterView; // always require cast, so it is more convenient to store // The poster downloader. // it here as well. private PosterDownloader mPosterDownloader; private HTML5VideoView mVideoView; // A helper class to control the playback. This executes on the UI thread! private static final class VideoPlayer { // A VideoView subclass that responds to double-tap // The proxy that is currently playing (if any). // events by going fullscreen. private static HTML5VideoViewProxy mCurrentProxy; class HTML5VideoView extends VideoView { // The VideoView instance. This is a singleton for now, at least until // Used to save the layout parameters if the view // http://b/issue?id=1973663 is fixed. // is changed to fullscreen. private static VideoView mVideoView; private AbsoluteLayout.LayoutParams mEmbeddedLayoutParams; // Flag that denotes whether the view is fullscreen or not. private static final WebChromeClient.CustomViewCallback mCallback = 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() { new WebChromeClient.CustomViewCallback() { public void onCustomViewHidden() { public void onCustomViewHidden() { playEmbedded(); // At this point the videoview is pretty much destroyed. // 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. mCurrentProxy.playbackEnded(); mCurrentProxy = null; mVideoView = null; } } }; }; // The OnPreparedListener, used to automatically resume public static void play(String url, HTML5VideoViewProxy proxy, WebChromeClient client) { // playback when transitioning to/from fullscreen. if (mCurrentProxy != null) { private MediaPlayer.OnPreparedListener mPreparedListener = // Some other video is already playing. Notify the caller that its playback ended. new MediaPlayer.OnPreparedListener() { proxy.playbackEnded(); public void onPrepared(MediaPlayer mp) { return; resumePlayback(); } mCurrentProxy = proxy; mVideoView = new VideoView(proxy.getContext()); mVideoView.setWillNotDraw(false); mVideoView.setMediaController(new MediaController(proxy.getContext())); mVideoView.setVideoURI(Uri.parse(url)); mVideoView.start(); client.onShowCustomView(mVideoView, mCallback); } } // Handler for the messages from WebCore thread to the UI thread. @Override public void handleMessage(Message msg) { // This executes on the UI thread. switch (msg.what) { case INIT: { mPosterView = new ImageView(mWebView.getContext()); mChildView.mView = mPosterView; break; } case PLAY: { String url = (String) msg.obj; WebChromeClient client = mWebView.getWebChromeClient(); if (client != null) { VideoPlayer.play(url, this, client); } break; } case SET_POSTER: { Bitmap poster = (Bitmap) msg.obj; mPosterView.setImageBitmap(poster); break; } } } } }; HTML5VideoView(Context context) { public void playbackEnded() { super(context); // TODO: notify WebKit } } void savePlaybackPosition() { // Everything below this comment executes on the WebCore thread, except for if (isPlaying()) { // the EventHandler methods, which are called on the network thread. mPlaybackPosition = getCurrentPosition(); // A helper class that knows how to download posters private static final class PosterDownloader implements EventHandler { // The request queue. This is static as we have one queue for all posters. private static RequestQueue mRequestQueue; private static int mQueueRefCount = 0; // The poster URL private String mUrl; // The proxy we're doing this for. private final HTML5VideoViewProxy mProxy; // The poster bytes. We only touch this on the network thread. private ByteArrayOutputStream mPosterBytes; // The request handle. We only touch this on the WebCore thread. private RequestHandle mRequestHandle; // The response status code. private int mStatusCode; // The response headers. private Headers mHeaders; // The handler to handle messages on the WebCore thread. private Handler mHandler; public PosterDownloader(String url, HTML5VideoViewProxy proxy) { mUrl = url; mProxy = proxy; mHandler = new Handler(); } // Start the download. Called on WebCore thread. public void start() { retainQueue(); mRequestHandle = mRequestQueue.queueRequest(mUrl, "GET", null, this, null, 0); } // Cancel the download if active and release the queue. Called on WebCore thread. public void cancelAndReleaseQueue() { if (mRequestHandle != null) { mRequestHandle.cancel(); mRequestHandle = null; } } releaseQueue(); } // EventHandler methods. Executed on the network thread. public void status(int major_version, int minor_version, int code, String reason_phrase) { mStatusCode = code; } } void resumePlayback() { public void headers(Headers headers) { seekTo(mPlaybackPosition); mHeaders = headers; start(); setOnPreparedListener(null); } } void playEmbedded() { public void data(byte[] data, int len) { // Attach to the WebView. if (mPosterBytes == null) { mChildView.attachViewOnUIThread(mEmbeddedLayoutParams); mPosterBytes = new ByteArrayOutputStream(); // Make sure we're visible } setVisibility(View.VISIBLE); mPosterBytes.write(data, 0, len); // 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() { public void endData() { WebChromeClient client = mWebView.getWebChromeClient(); if (mStatusCode == 200) { if (client == null) { if (mPosterBytes.size() > 0) { return; Bitmap poster = BitmapFactory.decodeByteArray( mPosterBytes.toByteArray(), 0, mPosterBytes.size()); if (poster != null) { mProxy.doSetPoster(poster); } } cleanup(); } else if (mStatusCode >= 300 && mStatusCode < 400) { // We have a redirect. mUrl = mHeaders.getLocation(); if (mUrl != null) { mHandler.post(new Runnable() { public void run() { if (mRequestHandle != null) { mRequestHandle.setupRedirect(mUrl, mStatusCode, new HashMap<String, String>()); } } }); } } } // 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 void certificate(SslCertificate certificate) { public boolean onTouchEvent(MotionEvent ev) { // Don't care. // TODO: implement properly (i.e. detect double tap) if (mIsFullscreen || !isPlaying()) { return super.onTouchEvent(ev); } } playFullScreen(); return true; public void error(int id, String description) { cleanup(); } } @Override public boolean handleSslErrorRequest(SslError error) { public void onDetachedFromWindow() { // Don't care. If this happens, data() will never be called so super.onDetachedFromWindow(); // mPosterBytes will never be created, so no need to call cleanup. savePlaybackPosition(); return false; } // Tears down the poster bytes stream. Called on network thread. private void cleanup() { if (mPosterBytes != null) { try { mPosterBytes.close(); } catch (IOException ignored) { // Ignored. } finally { mPosterBytes = null; } } } // Queue management methods. Called on WebCore thread. private void retainQueue() { if (mRequestQueue == null) { mRequestQueue = new RequestQueue(mProxy.getContext()); } mQueueRefCount++; } private void releaseQueue() { if (mQueueRefCount == 0) { return; } if (--mQueueRefCount == 0) { mRequestQueue.shutdown(); mRequestQueue = null; } } } } } Loading @@ -159,53 +280,65 @@ class HTML5VideoViewProxy extends Handler { super(Looper.getMainLooper()); super(Looper.getMainLooper()); // Save the WebView object. // Save the WebView object. mWebView = webView; mWebView = webView; // create the message handler for this thread createWebCoreHandler(); } } private void createWebCoreHandler() { mWebCoreHandler = new Handler() { @Override @Override public void handleMessage(Message msg) { public void handleMessage(Message msg) { // This executes on the UI thread. switch (msg.what) { switch (msg.what) { case INIT: // TODO here we will process the messages from the VideoPlayer // Create the video view and set a default controller. // and will call native WebKit methods. mVideoView = new HTML5VideoView(mWebView.getContext()); } // This is needed because otherwise there will be a black square } // stuck on the screen. }; mVideoView.setWillNotDraw(false); } mVideoView.setMediaController(new MediaController(mWebView.getContext())); mChildView.mView = mVideoView; private void doSetPoster(Bitmap poster) { break; if (poster == null) { case PLAY: if (mVideoView == null) { return; return; } } HashMap<String, Object> map = // Send the bitmap over to the UI thread. (HashMap<String, Object>) msg.obj; Message message = obtainMessage(SET_POSTER); String url = (String) map.get("url"); message.obj = poster; mVideoView.setVideoURI(Uri.parse(url)); sendMessage(message); mVideoView.start(); break; } } public Context getContext() { return mWebView.getContext(); } } // The public methods below are all called from WebKit only. /** /** * Play a video stream. * Play a video stream. * @param url is the URL of the video stream. * @param url is the URL of the video stream. * @param webview is the WebViewCore that is requesting the playback. * @param webview is the WebViewCore that is requesting the playback. */ */ public void play(String url) { public void play(String url) { if (url == null) { return; } // We need to know the webview that is requesting the playback. // We need to know the webview that is requesting the playback. Message message = obtainMessage(PLAY); Message message = obtainMessage(PLAY); HashMap<String, Object> map = new HashMap(); message.obj = url; map.put("url", url); message.obj = map; sendMessage(message); sendMessage(message); } } /** * Create the child view that will cary the poster. */ public void createView() { public void createView() { mChildView = mWebView.mViewManager.createView(); mChildView = mWebView.mViewManager.createView(); sendMessage(obtainMessage(INIT)); sendMessage(obtainMessage(INIT)); } } /** * Attach the poster view. * @param x, y are the screen coordinates where the poster should be hung. * @param width, height denote the size of the poster. */ public void attachView(int x, int y, int width, int height) { public void attachView(int x, int y, int width, int height) { if (mChildView == null) { if (mChildView == null) { return; return; Loading @@ -213,11 +346,36 @@ class HTML5VideoViewProxy extends Handler { mChildView.attachView(x, y, width, height); mChildView.attachView(x, y, width, height); } } /** * Remove the child view and, thus, the poster. */ public void removeView() { public void removeView() { if (mChildView == null) { if (mChildView == null) { return; return; } } mChildView.removeView(); mChildView.removeView(); // This is called by the C++ MediaPlayerPrivate dtor. // Cancel any active poster download. if (mPosterDownloader != null) { mPosterDownloader.cancelAndReleaseQueue(); } } /** * Load the poster image. * @param url is the URL of the poster image. */ public void loadPoster(String url) { if (url == null) { return; } // Cancel any active poster download. if (mPosterDownloader != null) { mPosterDownloader.cancelAndReleaseQueue(); } // Load the poster asynchronously mPosterDownloader = new PosterDownloader(url, this); mPosterDownloader.start(); } } /** /** Loading