Merge change 6356

* changes:
  Add fullscreen support back in.
This commit is contained in:
Android (Google) Code Review
2009-07-09 06:39:47 -07:00
3 changed files with 173 additions and 54 deletions

View File

@@ -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;
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());
}
}

View File

@@ -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) {

View File

@@ -45,13 +45,29 @@ public class WebChromeClient {
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