Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e541ba9e authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 6356

* changes:
  Add fullscreen support back in.
parents b5d9132d 3c946a1a
Loading
Loading
Loading
Loading
+140 −45
Original line number Diff line number Diff line
@@ -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) {
    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());
    }
}
+12 −4
Original line number Diff line number Diff line
@@ -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) {
+17 −1
Original line number Diff line number Diff line
@@ -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