Merge change 6356
* changes: Add fullscreen support back in.
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user