16fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu/*
26fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu * Copyright (C) 2009 The Android Open Source Project
36fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu *
46fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu * Licensed under the Apache License, Version 2.0 (the "License");
56fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu * you may not use this file except in compliance with the License.
66fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu * You may obtain a copy of the License at
76fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu *
86fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu *      http://www.apache.org/licenses/LICENSE-2.0
96fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu *
106fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu * Unless required by applicable law or agreed to in writing, software
116fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu * distributed under the License is distributed on an "AS IS" BASIS,
126fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu * See the License for the specific language governing permissions and
146fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu * limitations under the License.
156fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu */
166fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
176fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescupackage android.webkit;
186fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
196fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescuimport android.content.Context;
2064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport android.graphics.Bitmap;
2164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport android.graphics.BitmapFactory;
22661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhuimport android.graphics.SurfaceTexture;
233c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescuimport android.media.MediaPlayer;
2464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport android.net.http.EventHandler;
2564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport android.net.http.Headers;
2664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport android.net.http.RequestHandle;
2764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport android.net.http.RequestQueue;
2864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport android.net.http.SslCertificate;
2964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport android.net.http.SslError;
306fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescuimport android.os.Handler;
316fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescuimport android.os.Looper;
326fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescuimport android.os.Message;
336fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescuimport android.util.Log;
345558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhuimport android.view.KeyEvent;
355558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhuimport android.view.View;
366fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
3764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport java.io.ByteArrayOutputStream;
3864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescuimport java.io.IOException;
3942509799f261c844bf6be66ce630c47bdee620f3Ben Murdochimport java.net.MalformedURLException;
4042509799f261c844bf6be66ce630c47bdee620f3Ben Murdochimport java.net.URL;
416fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescuimport java.util.HashMap;
42290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescuimport java.util.Map;
436fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
446fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu/**
453c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu * <p>Proxy for HTML5 video views.
466fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu */
47290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescuclass HTML5VideoViewProxy extends Handler
48290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                          implements MediaPlayer.OnPreparedListener,
4950c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                          MediaPlayer.OnCompletionListener,
50661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                          MediaPlayer.OnErrorListener,
51c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu                          MediaPlayer.OnInfoListener,
525558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu                          SurfaceTexture.OnFrameAvailableListener,
535558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu                          View.OnKeyListener {
546fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu    // Logging tag.
556fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu    private static final String LOGTAG = "HTML5VideoViewProxy";
566fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
573c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu    // Message Ids for WebCore thread -> UI thread communication.
5850c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    private static final int PLAY                = 100;
5950c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    private static final int SEEK                = 101;
6050c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    private static final int PAUSE               = 102;
6150c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    private static final int ERROR               = 103;
6250c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    private static final int LOAD_DEFAULT_POSTER = 104;
63c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu    private static final int BUFFERING_START     = 105;
64c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu    private static final int BUFFERING_END       = 106;
65c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu    private static final int ENTER_FULLSCREEN    = 107;
666fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
6764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    // Message Ids to be handled on the WebCore thread
68290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    private static final int PREPARED          = 200;
69290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    private static final int ENDED             = 201;
7050c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    private static final int POSTER_FETCHED    = 202;
7143aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang    private static final int PAUSED            = 203;
72b109c8877139077fe25fa26ea2e0961b7cbb8341Teng-Hui Zhu    private static final int STOPFULLSCREEN    = 204;
73e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu    private static final int RESTORESTATE      = 205;
7464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
75048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu    // Timer thread -> UI thread
76048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu    private static final int TIMEUPDATE = 300;
77048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu
78290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    // The C++ MediaPlayerPrivateAndroid object.
79290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    int mNativePointer;
8064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    // The handler for WebCore thread messages;
8164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    private Handler mWebCoreHandler;
823c90952036a5ff7ddb2946c643f1a0bf1c31d53aJonathan Dixon    // The WebViewClassic instance that created this view.
833c90952036a5ff7ddb2946c643f1a0bf1c31d53aJonathan Dixon    private WebViewClassic mWebView;
8464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    // The poster image to be shown when the video is not playing.
8550c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    // This ref prevents the bitmap from being GC'ed.
8650c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    private Bitmap mPoster;
8764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    // The poster downloader.
8864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    private PosterDownloader mPosterDownloader;
89290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    // The seek position.
90290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    private int mSeekPosition;
9164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    // A helper class to control the playback. This executes on the UI thread!
9264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    private static final class VideoPlayer {
9364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The proxy that is currently playing (if any).
9464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private static HTML5VideoViewProxy mCurrentProxy;
9564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The VideoView instance. This is a singleton for now, at least until
9664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // http://b/issue?id=1973663 is fixed.
97661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        private static HTML5VideoView mHTML5VideoView;
98048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu
99661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        private static boolean isVideoSelfEnded = false;
100661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu
101f4d4e9e05f7605118910739c16e493e2ab869aaeTeng-Hui Zhu        private static void setPlayerBuffering(boolean playerBuffering) {
102f4d4e9e05f7605118910739c16e493e2ab869aaeTeng-Hui Zhu            mHTML5VideoView.setPlayerBuffering(playerBuffering);
103f4d4e9e05f7605118910739c16e493e2ab869aaeTeng-Hui Zhu        }
104f4d4e9e05f7605118910739c16e493e2ab869aaeTeng-Hui Zhu
105661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // Every time webView setBaseLayer, this will be called.
106661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // When we found the Video layer, then we set the Surface Texture to it.
107661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // Otherwise, we may want to delete the Surface Texture to save memory.
108661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        public static void setBaseLayer(int layer) {
10910ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu            // Don't do this for full screen mode.
11010ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu            if (mHTML5VideoView != null
111156f97b6c8580b790600b09b679b9dab8b271c5dTeng-Hui Zhu                && !mHTML5VideoView.isFullScreenMode()
112156f97b6c8580b790600b09b679b9dab8b271c5dTeng-Hui Zhu                && !mHTML5VideoView.isReleased()) {
113661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                int currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
114c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                SurfaceTexture surfTexture =
115c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                        HTML5VideoInline.getSurfaceTexture(currentVideoLayerId);
1163fafd39d0776a15c3613795183043a2c28277691Teng-Hui Zhu                int textureName = mHTML5VideoView.getTextureName();
1173fafd39d0776a15c3613795183043a2c28277691Teng-Hui Zhu
118661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) {
119265db32f31a9db60d4a93d59befa07c825cbe4f2Teng-Hui Zhu                    int playerState = mHTML5VideoView.getCurrentState();
120f4d4e9e05f7605118910739c16e493e2ab869aaeTeng-Hui Zhu                    if (mHTML5VideoView.getPlayerBuffering())
121c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                        playerState = HTML5VideoView.STATE_PREPARING;
122661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                    boolean foundInTree = nativeSendSurfaceTexture(surfTexture,
123661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                            layer, currentVideoLayerId, textureName,
124265db32f31a9db60d4a93d59befa07c825cbe4f2Teng-Hui Zhu                            playerState);
1251e26d82ffaf983dc288152cfe8c0a821de33f2a4Teng-Hui Zhu                    if (playerState >= HTML5VideoView.STATE_PREPARED
126265db32f31a9db60d4a93d59befa07c825cbe4f2Teng-Hui Zhu                            && !foundInTree) {
127661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                        mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
12831d2aa1e17f1ee580edb762a6ef634098bba02bbAndrei Popescu                    }
12964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                }
130661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            }
131661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        }
132661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu
133661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // When a WebView is paused, we also want to pause the video in it.
134661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        public static void pauseAndDispatch() {
135661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            if (mHTML5VideoView != null) {
136661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
137661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            }
138661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        }
13964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
14010ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu        public static void enterFullScreenVideo(int layerId, String url,
1413c90952036a5ff7ddb2946c643f1a0bf1c31d53aJonathan Dixon                HTML5VideoViewProxy proxy, WebViewClassic webView) {
14210ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                // Save the inline video info and inherit it in the full screen
14310ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                int savePosition = 0;
144c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                boolean canSkipPrepare = false;
1456a9586bc57b162a2d68dd3e417f0fd43d8932fd0Teng-Hui Zhu                boolean forceStart = false;
14610ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                if (mHTML5VideoView != null) {
1472b64c5a6f70bfae7db95f763441b52a506d6f630Teng-Hui Zhu                    // We don't allow enter full screen mode while the previous
1482b64c5a6f70bfae7db95f763441b52a506d6f630Teng-Hui Zhu                    // full screen video hasn't finished yet.
1492b64c5a6f70bfae7db95f763441b52a506d6f630Teng-Hui Zhu                    if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
1502b64c5a6f70bfae7db95f763441b52a506d6f630Teng-Hui Zhu                        Log.w(LOGTAG, "Try to reenter the full screen mode");
1512b64c5a6f70bfae7db95f763441b52a506d6f630Teng-Hui Zhu                        return;
1522b64c5a6f70bfae7db95f763441b52a506d6f630Teng-Hui Zhu                    }
1536a9586bc57b162a2d68dd3e417f0fd43d8932fd0Teng-Hui Zhu                    int playerState = mHTML5VideoView.getCurrentState();
15410ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                    // If we are playing the same video, then it is better to
15510ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                    // save the current position.
15610ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                    if (layerId == mHTML5VideoView.getVideoLayerId()) {
15710ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                        savePosition = mHTML5VideoView.getCurrentPosition();
158c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                        canSkipPrepare = (playerState == HTML5VideoView.STATE_PREPARING
159c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                                || playerState == HTML5VideoView.STATE_PREPARED
160c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                                || playerState == HTML5VideoView.STATE_PLAYING)
161c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                                && !mHTML5VideoView.isFullScreenMode();
162c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                    }
163c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                    if (!canSkipPrepare) {
164c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                        mHTML5VideoView.reset();
1656a9586bc57b162a2d68dd3e417f0fd43d8932fd0Teng-Hui Zhu                    } else {
1666a9586bc57b162a2d68dd3e417f0fd43d8932fd0Teng-Hui Zhu                        forceStart = playerState == HTML5VideoView.STATE_PREPARING
1676a9586bc57b162a2d68dd3e417f0fd43d8932fd0Teng-Hui Zhu                                || playerState == HTML5VideoView.STATE_PLAYING;
16810ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                    }
16910ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                }
17010ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(),
171c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                        layerId, savePosition, canSkipPrepare);
1726a9586bc57b162a2d68dd3e417f0fd43d8932fd0Teng-Hui Zhu                mHTML5VideoView.setStartWhenPrepared(forceStart);
17310ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                mCurrentProxy = proxy;
17410ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                mHTML5VideoView.setVideoURI(url, mCurrentProxy);
17510ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView);
17610ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu        }
17710ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu
178d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu        public static void exitFullScreenVideo(HTML5VideoViewProxy proxy,
1793c90952036a5ff7ddb2946c643f1a0bf1c31d53aJonathan Dixon                WebViewClassic webView) {
180d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu            if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
181d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu                WebChromeClient client = webView.getWebChromeClient();
182d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu                if (client != null) {
183d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu                    client.onHideCustomView();
184d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu                }
185d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu            }
186d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu        }
187d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu
188661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // This is on the UI thread.
189661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // When native tell Java to play, we need to check whether or not it is
190661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // still the same video by using videoLayerId and treat it differently.
191290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        public static void play(String url, int time, HTML5VideoViewProxy proxy,
192661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                WebChromeClient client, int videoLayerId) {
193661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            int currentVideoLayerId = -1;
19410ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu            boolean backFromFullScreenMode = false;
19510ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu            if (mHTML5VideoView != null) {
196661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
1971a88acbed9cd04d7c8ddd0c33a15926238157a07Teng-Hui Zhu                backFromFullScreenMode = mHTML5VideoView.fullScreenExited();
19896fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu
19996fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                // When playing video back to back in full screen mode,
20096fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                // javascript will switch the src and call play.
20196fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                // In this case, we can just reuse the same full screen view,
20296fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                // and play the video after prepared.
20396fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                if (mHTML5VideoView.isFullScreenMode()
20496fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                    && !backFromFullScreenMode
20596fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                    && currentVideoLayerId != videoLayerId
20696fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                    && mCurrentProxy != proxy) {
20796fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                    mCurrentProxy = proxy;
20896fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                    mHTML5VideoView.setStartWhenPrepared(true);
20996fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                    mHTML5VideoView.setVideoURI(url, proxy);
21096fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                    mHTML5VideoView.reprepareData(proxy);
21196fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                    return;
21296fae5e088916e1da8d4afaf40758ceec6e7187aTeng-Hui Zhu                }
21310ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu            }
214661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu
215c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            boolean skipPrepare = false;
216c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            boolean createInlineView = false;
217156f97b6c8580b790600b09b679b9dab8b271c5dTeng-Hui Zhu            if (backFromFullScreenMode
218156f97b6c8580b790600b09b679b9dab8b271c5dTeng-Hui Zhu                && currentVideoLayerId == videoLayerId
219156f97b6c8580b790600b09b679b9dab8b271c5dTeng-Hui Zhu                && !mHTML5VideoView.isReleased()) {
220c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                skipPrepare = true;
221c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                createInlineView = true;
222c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            } else if(backFromFullScreenMode
2233fafd39d0776a15c3613795183043a2c28277691Teng-Hui Zhu                || currentVideoLayerId != videoLayerId
224c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                || HTML5VideoInline.surfaceTextureDeleted()) {
225661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                // Here, we handle the case when switching to a new video,
226661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                // either inside a WebView or across WebViews
227661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                // For switching videos within a WebView or across the WebView,
228661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                // we need to pause the old one and re-create a new media player
229661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                // inside the HTML5VideoView.
230661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                if (mHTML5VideoView != null) {
23122954d435d645d48885f653bd4ca6bc79e1bcd83Teng-Hui Zhu                    if (!backFromFullScreenMode) {
23222954d435d645d48885f653bd4ca6bc79e1bcd83Teng-Hui Zhu                        mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
23322954d435d645d48885f653bd4ca6bc79e1bcd83Teng-Hui Zhu                    }
234c2b06d5a83f8f118eaf46f093126da08a28715ebTeng-Hui Zhu                    mHTML5VideoView.reset();
235a41f97bd77f2583e2e3698cfbba62230a8ac4789Andrei Popescu                }
236c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                createInlineView = true;
237c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            }
238c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            if (createInlineView) {
239661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                mCurrentProxy = proxy;
240c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                mHTML5VideoView = new HTML5VideoInline(videoLayerId, time, skipPrepare);
241a41f97bd77f2583e2e3698cfbba62230a8ac4789Andrei Popescu
24210ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                mHTML5VideoView.setVideoURI(url, mCurrentProxy);
24310ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                mHTML5VideoView.prepareDataAndDisplayMode(proxy);
244c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                return;
245c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            }
246c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu
247c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            if (mCurrentProxy == proxy) {
248661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                // Here, we handle the case when we keep playing with one video
249661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                if (!mHTML5VideoView.isPlaying()) {
250661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                    mHTML5VideoView.seekTo(time);
251661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                    mHTML5VideoView.start();
252661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                }
253661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            } else if (mCurrentProxy != null) {
25410ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                // Some other video is already playing. Notify the caller that
25510ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu                // its playback ended.
256661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                proxy.dispatchOnEnded();
257bf385d7c7b093564738f8cfd4386daf5e94b8a6dAndrei Popescu            }
25864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
259290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu
260048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu        public static boolean isPlaying(HTML5VideoViewProxy proxy) {
261661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            return (mCurrentProxy == proxy && mHTML5VideoView != null
262661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                    && mHTML5VideoView.isPlaying());
263048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu        }
264048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu
265048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu        public static int getCurrentPosition() {
266048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu            int currentPosMs = 0;
267661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            if (mHTML5VideoView != null) {
268661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                currentPosMs = mHTML5VideoView.getCurrentPosition();
269048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu            }
270048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu            return currentPosMs;
271048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu        }
272048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu
273290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        public static void seek(int time, HTML5VideoViewProxy proxy) {
274661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            if (mCurrentProxy == proxy && time >= 0 && mHTML5VideoView != null) {
275661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                mHTML5VideoView.seekTo(time);
276290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu            }
277290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        }
278290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu
279290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        public static void pause(HTML5VideoViewProxy proxy) {
280661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            if (mCurrentProxy == proxy && mHTML5VideoView != null) {
281661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                mHTML5VideoView.pause();
282290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu            }
283290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        }
284bf385d7c7b093564738f8cfd4386daf5e94b8a6dAndrei Popescu
285bf385d7c7b093564738f8cfd4386daf5e94b8a6dAndrei Popescu        public static void onPrepared() {
286e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu            if (!mHTML5VideoView.isFullScreenMode()) {
2870504967cb05f7bbd0c17962360a5d88ace8d173fTeng-Hui Zhu                mHTML5VideoView.start();
2880504967cb05f7bbd0c17962360a5d88ace8d173fTeng-Hui Zhu            }
289661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        }
290661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu
291661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        public static void end() {
2924dd9dc8204105a25e37dacbc392ea3c7212a8063Teng-Hui Zhu            mHTML5VideoView.showControllerInFullScreen();
293661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            if (mCurrentProxy != null) {
294661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                if (isVideoSelfEnded)
295661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                    mCurrentProxy.dispatchOnEnded();
296661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                else
297661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                    mCurrentProxy.dispatchOnPaused();
298661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            }
299661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            isVideoSelfEnded = false;
300bf385d7c7b093564738f8cfd4386daf5e94b8a6dAndrei Popescu        }
301290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    }
302290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu
303290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    // A bunch event listeners for our VideoView
304290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    // MediaPlayer.OnPreparedListener
3055558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu    @Override
306290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    public void onPrepared(MediaPlayer mp) {
307bf385d7c7b093564738f8cfd4386daf5e94b8a6dAndrei Popescu        VideoPlayer.onPrepared();
308290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        Message msg = Message.obtain(mWebCoreHandler, PREPARED);
309290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        Map<String, Object> map = new HashMap<String, Object>();
310290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        map.put("dur", new Integer(mp.getDuration()));
311290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        map.put("width", new Integer(mp.getVideoWidth()));
312290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        map.put("height", new Integer(mp.getVideoHeight()));
313290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        msg.obj = map;
314290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        mWebCoreHandler.sendMessage(msg);
315290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    }
316290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu
317290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    // MediaPlayer.OnCompletionListener;
3185558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu    @Override
319290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    public void onCompletion(MediaPlayer mp) {
320c4fbceb57bcfbabb6e370e2dd6448184dc116015Andrei Popescu        // The video ended by itself, so we need to
321c4fbceb57bcfbabb6e370e2dd6448184dc116015Andrei Popescu        // send a message to the UI thread to dismiss
322c4fbceb57bcfbabb6e370e2dd6448184dc116015Andrei Popescu        // the video view and to return to the WebView.
32343aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang        // arg1 == 1 means the video ends by itself.
32443aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang        sendMessage(obtainMessage(ENDED, 1, 0));
325290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    }
326290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu
32750c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    // MediaPlayer.OnErrorListener
3285558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu    @Override
32950c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    public boolean onError(MediaPlayer mp, int what, int extra) {
33050c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu        sendMessage(obtainMessage(ERROR));
33150c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu        return false;
33250c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    }
33350c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu
334c4fbceb57bcfbabb6e370e2dd6448184dc116015Andrei Popescu    public void dispatchOnEnded() {
335290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        Message msg = Message.obtain(mWebCoreHandler, ENDED);
336290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        mWebCoreHandler.sendMessage(msg);
33764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    }
3383c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu
33943aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang    public void dispatchOnPaused() {
340b109c8877139077fe25fa26ea2e0961b7cbb8341Teng-Hui Zhu        Message msg = Message.obtain(mWebCoreHandler, PAUSED);
341b109c8877139077fe25fa26ea2e0961b7cbb8341Teng-Hui Zhu        mWebCoreHandler.sendMessage(msg);
342b109c8877139077fe25fa26ea2e0961b7cbb8341Teng-Hui Zhu    }
343b109c8877139077fe25fa26ea2e0961b7cbb8341Teng-Hui Zhu
344c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu    public void dispatchOnStopFullScreen(boolean stillPlaying) {
345b109c8877139077fe25fa26ea2e0961b7cbb8341Teng-Hui Zhu        Message msg = Message.obtain(mWebCoreHandler, STOPFULLSCREEN);
346c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu        msg.arg1 = stillPlaying ? 1 : 0;
347b109c8877139077fe25fa26ea2e0961b7cbb8341Teng-Hui Zhu        mWebCoreHandler.sendMessage(msg);
34843aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang    }
34943aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang
350e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu    public void dispatchOnRestoreState() {
351e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu        Message msg = Message.obtain(mWebCoreHandler, RESTORESTATE);
352e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu        mWebCoreHandler.sendMessage(msg);
353e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu    }
354e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu
355048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu    public void onTimeupdate() {
356048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu        sendMessage(obtainMessage(TIMEUPDATE));
357048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu    }
358048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu
359661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    // When there is a frame ready from surface texture, we should tell WebView
360661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    // to refresh.
361661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    @Override
362661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
363661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // TODO: This should support partial invalidation too.
364661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        mWebView.invalidate();
365661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    }
366661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu
367048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu    // Handler for the messages from WebCore or Timer thread to the UI thread.
36864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    @Override
36964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    public void handleMessage(Message msg) {
37064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // This executes on the UI thread.
37164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        switch (msg.what) {
37264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            case PLAY: {
37364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                String url = (String) msg.obj;
37464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                WebChromeClient client = mWebView.getWebChromeClient();
375661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                int videoLayerID = msg.arg1;
37664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                if (client != null) {
377661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                    VideoPlayer.play(url, mSeekPosition, this, client, videoLayerID);
37864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                }
37964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                break;
38064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            }
381c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            case ENTER_FULLSCREEN:{
382c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                String url = (String) msg.obj;
383c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                WebChromeClient client = mWebView.getWebChromeClient();
384c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                int videoLayerID = msg.arg1;
385c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                if (client != null) {
386c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                    VideoPlayer.enterFullScreenVideo(videoLayerID, url, this, mWebView);
387c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                }
388c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                break;
389c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            }
390290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu            case SEEK: {
391290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                Integer time = (Integer) msg.obj;
392290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                mSeekPosition = time;
393290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                VideoPlayer.seek(mSeekPosition, this);
394290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                break;
395290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu            }
396290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu            case PAUSE: {
397290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                VideoPlayer.pause(this);
398290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                break;
399290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu            }
40046a83b4ead517b9fc13b4bee4c59c3eaf4d3fdd9Andrei Popescu            case ENDED:
40143aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang                if (msg.arg1 == 1)
40243aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang                    VideoPlayer.isVideoSelfEnded = true;
403661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                VideoPlayer.end();
404661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu                break;
40550c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu            case ERROR: {
40650c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                WebChromeClient client = mWebView.getWebChromeClient();
40750c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                if (client != null) {
40850c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                    client.onHideCustomView();
40950c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                }
41050c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                break;
41150c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu            }
41250c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu            case LOAD_DEFAULT_POSTER: {
41350c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                WebChromeClient client = mWebView.getWebChromeClient();
41450c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                if (client != null) {
41550c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                    doSetPoster(client.getDefaultVideoPoster());
41650c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                }
41750c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                break;
41850c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu            }
419048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu            case TIMEUPDATE: {
420048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu                if (VideoPlayer.isPlaying(this)) {
421048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu                    sendTimeupdate();
422048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu                }
423048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu                break;
424048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu            }
425c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu            case BUFFERING_START: {
426f4d4e9e05f7605118910739c16e493e2ab869aaeTeng-Hui Zhu                VideoPlayer.setPlayerBuffering(true);
427c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu                break;
428c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu            }
429c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu            case BUFFERING_END: {
430f4d4e9e05f7605118910739c16e493e2ab869aaeTeng-Hui Zhu                VideoPlayer.setPlayerBuffering(false);
431c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu                break;
432c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu            }
43364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
43464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    }
43564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
43664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    // Everything below this comment executes on the WebCore thread, except for
43764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    // the EventHandler methods, which are called on the network thread.
43864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
43964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    // A helper class that knows how to download posters
44064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    private static final class PosterDownloader implements EventHandler {
44164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The request queue. This is static as we have one queue for all posters.
44264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private static RequestQueue mRequestQueue;
44364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private static int mQueueRefCount = 0;
44464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The poster URL
44542509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch        private URL mUrl;
44664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The proxy we're doing this for.
44764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private final HTML5VideoViewProxy mProxy;
44864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The poster bytes. We only touch this on the network thread.
44964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private ByteArrayOutputStream mPosterBytes;
45064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The request handle. We only touch this on the WebCore thread.
45164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private RequestHandle mRequestHandle;
45264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The response status code.
45364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private int mStatusCode;
45464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The response headers.
45564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private Headers mHeaders;
45664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // The handler to handle messages on the WebCore thread.
45764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private Handler mHandler;
45864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
45964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
46042509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            try {
46142509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                mUrl = new URL(url);
46242509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            } catch (MalformedURLException e) {
46342509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                mUrl = null;
46442509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            }
46564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            mProxy = proxy;
46664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            mHandler = new Handler();
46764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
46864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // Start the download. Called on WebCore thread.
46964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public void start() {
47064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            retainQueue();
47142509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch
47242509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            if (mUrl == null) {
47342509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                return;
47442509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            }
47542509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch
47642509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            // Only support downloading posters over http/https.
47742509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            // FIXME: Add support for other schemes. WebKit seems able to load
47842509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            // posters over other schemes e.g. file://, but gets the dimensions wrong.
47942509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            String protocol = mUrl.getProtocol();
48042509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            if ("http".equals(protocol) || "https".equals(protocol)) {
48142509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                mRequestHandle = mRequestQueue.queueRequest(mUrl.toString(), "GET", null,
48242509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                        this, null, 0);
48342509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch            }
48464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
48564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // Cancel the download if active and release the queue. Called on WebCore thread.
48664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public void cancelAndReleaseQueue() {
48764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            if (mRequestHandle != null) {
48864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                mRequestHandle.cancel();
48964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                mRequestHandle = null;
49064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            }
49164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            releaseQueue();
49264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
49364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // EventHandler methods. Executed on the network thread.
4945558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        @Override
49564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public void status(int major_version,
49664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                int minor_version,
49764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                int code,
49864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                String reason_phrase) {
49964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            mStatusCode = code;
50064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
5013c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu
5025558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        @Override
50364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public void headers(Headers headers) {
50464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            mHeaders = headers;
5053c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu        }
5063c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu
5075558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        @Override
50864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public void data(byte[] data, int len) {
50964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            if (mPosterBytes == null) {
51064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                mPosterBytes = new ByteArrayOutputStream();
5113c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu            }
51264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            mPosterBytes.write(data, 0, len);
5133c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu        }
5143c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu
5155558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        @Override
51664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public void endData() {
51764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            if (mStatusCode == 200) {
51864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                if (mPosterBytes.size() > 0) {
51964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                    Bitmap poster = BitmapFactory.decodeByteArray(
52064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                            mPosterBytes.toByteArray(), 0, mPosterBytes.size());
52150c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                    mProxy.doSetPoster(poster);
52264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                }
52364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                cleanup();
52464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            } else if (mStatusCode >= 300 && mStatusCode < 400) {
52564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                // We have a redirect.
52642509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                try {
52742509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                    mUrl = new URL(mHeaders.getLocation());
52842509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                } catch (MalformedURLException e) {
52942509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                    mUrl = null;
53042509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                }
53164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                if (mUrl != null) {
53264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                    mHandler.post(new Runnable() {
5335558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu                       @Override
53464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                       public void run() {
53564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                           if (mRequestHandle != null) {
53642509799f261c844bf6be66ce630c47bdee620f3Ben Murdoch                               mRequestHandle.setupRedirect(mUrl.toString(), mStatusCode,
53764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                                       new HashMap<String, String>());
53864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                           }
53964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                       }
54064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                    });
54164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                }
54264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            }
5433c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu        }
5443c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu
5455558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        @Override
54664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public void certificate(SslCertificate certificate) {
54764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            // Don't care.
5483c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu        }
5493c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu
5505558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        @Override
55164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public void error(int id, String description) {
55264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            cleanup();
55364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
55464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
5555558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        @Override
55664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        public boolean handleSslErrorRequest(SslError error) {
55764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            // Don't care. If this happens, data() will never be called so
55864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            // mPosterBytes will never be created, so no need to call cleanup.
55964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            return false;
56064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
56164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // Tears down the poster bytes stream. Called on network thread.
56264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private void cleanup() {
56364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            if (mPosterBytes != null) {
56464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                try {
56564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                    mPosterBytes.close();
56664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                } catch (IOException ignored) {
56764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                    // Ignored.
56864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                } finally {
56964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                    mPosterBytes = null;
57064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                }
5713c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu            }
57264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
57364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
57464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // Queue management methods. Called on WebCore thread.
57564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private void retainQueue() {
57664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            if (mRequestQueue == null) {
57764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                mRequestQueue = new RequestQueue(mProxy.getContext());
5783c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu            }
57964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            mQueueRefCount++;
5803c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu        }
5813c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu
58264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        private void releaseQueue() {
58364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            if (mQueueRefCount == 0) {
58464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                return;
58564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            }
58664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            if (--mQueueRefCount == 0) {
58764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                mRequestQueue.shutdown();
58864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                mRequestQueue = null;
58964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            }
5903c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu        }
5913c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu    }
5926fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
5936fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu    /**
5946fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu     * Private constructor.
595290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu     * @param webView is the WebView that hosts the video.
596290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu     * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
5976fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu     */
5983c90952036a5ff7ddb2946c643f1a0bf1c31d53aJonathan Dixon    private HTML5VideoViewProxy(WebViewClassic webView, int nativePtr) {
5996fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu        // This handler is for the main (UI) thread.
6006fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu        super(Looper.getMainLooper());
6013c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu        // Save the WebView object.
6023c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu        mWebView = webView;
603661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // Pass Proxy into webview, such that every time we have a setBaseLayer
604661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // call, we tell this Proxy to call the native to update the layer tree
605661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        // for the Video Layer's surface texture info
606661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        mWebView.setHTML5VideoViewProxy(this);
607290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        // Save the native ptr
608290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        mNativePointer = nativePtr;
60964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // create the message handler for this thread
61064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        createWebCoreHandler();
6116fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu    }
6126fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
61364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    private void createWebCoreHandler() {
61464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        mWebCoreHandler = new Handler() {
61564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            @Override
61664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            public void handleMessage(Message msg) {
61764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu                switch (msg.what) {
618290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                    case PREPARED: {
619290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                        Map<String, Object> map = (Map<String, Object>) msg.obj;
620290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                        Integer duration = (Integer) map.get("dur");
621290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                        Integer width = (Integer) map.get("width");
622290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                        Integer height = (Integer) map.get("height");
623290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                        nativeOnPrepared(duration.intValue(), width.intValue(),
624290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                                height.intValue(), mNativePointer);
625290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                        break;
626290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                    }
627290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                    case ENDED:
628ff19d19e016f67edf0a4e8eea374d144dc5129efBen Murdoch                        mSeekPosition = 0;
629290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                        nativeOnEnded(mNativePointer);
630290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu                        break;
63143aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang                    case PAUSED:
63243aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang                        nativeOnPaused(mNativePointer);
63343aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang                        break;
63450c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                    case POSTER_FETCHED:
63550c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                        Bitmap poster = (Bitmap) msg.obj;
63650c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                        nativeOnPosterFetched(poster, mNativePointer);
63750c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu                        break;
638048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu                    case TIMEUPDATE:
639048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu                        nativeOnTimeupdate(msg.arg1, mNativePointer);
640048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu                        break;
641b109c8877139077fe25fa26ea2e0961b7cbb8341Teng-Hui Zhu                    case STOPFULLSCREEN:
642c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu                        nativeOnStopFullscreen(msg.arg1, mNativePointer);
643b109c8877139077fe25fa26ea2e0961b7cbb8341Teng-Hui Zhu                        break;
644e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu                    case RESTORESTATE:
645e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu                        nativeOnRestoreState(mNativePointer);
646e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu                        break;
6473c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu                }
64864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            }
64964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        };
65064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    }
65164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
65264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    private void doSetPoster(Bitmap poster) {
65364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        if (poster == null) {
65464b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            return;
6556fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu        }
65650c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu        // Save a ref to the bitmap and send it over to the WebCore thread.
65750c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu        mPoster = poster;
65850c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu        Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED);
65950c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu        msg.obj = poster;
66050c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu        mWebCoreHandler.sendMessage(msg);
6616fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu    }
6626fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
663048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu    private void sendTimeupdate() {
664048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu        Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
665048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu        msg.arg1 = VideoPlayer.getCurrentPosition();
666048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu        mWebCoreHandler.sendMessage(msg);
667048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu    }
668048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu
66964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    public Context getContext() {
67064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        return mWebView.getContext();
67164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    }
67264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
67364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    // The public methods below are all called from WebKit only.
6746fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu    /**
6756fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu     * Play a video stream.
6766fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu     * @param url is the URL of the video stream.
6776fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu     */
678661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    public void play(String url, int position, int videoLayerID) {
67964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        if (url == null) {
68064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            return;
68164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
682ff19d19e016f67edf0a4e8eea374d144dc5129efBen Murdoch
683ff19d19e016f67edf0a4e8eea374d144dc5129efBen Murdoch        if (position > 0) {
684ff19d19e016f67edf0a4e8eea374d144dc5129efBen Murdoch            seek(position);
685ff19d19e016f67edf0a4e8eea374d144dc5129efBen Murdoch        }
6866fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu        Message message = obtainMessage(PLAY);
687661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        message.arg1 = videoLayerID;
68864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        message.obj = url;
6896fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu        sendMessage(message);
6906fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu    }
6916fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu
69264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    /**
693c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu     * Play a video stream in full screen mode.
694c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu     * @param url is the URL of the video stream.
695c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu     */
696c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu    public void enterFullscreenForVideoLayer(String url, int videoLayerID) {
697c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu        if (url == null) {
698c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu            return;
699c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu        }
700c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu
701c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu        Message message = obtainMessage(ENTER_FULLSCREEN);
702c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu        message.arg1 = videoLayerID;
703c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu        message.obj = url;
704c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu        sendMessage(message);
705c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu    }
706c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu
707c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu    /**
708290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu     * Seek into the video stream.
709290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu     * @param  time is the position in the video stream.
710290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu     */
711290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    public void seek(int time) {
712290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        Message message = obtainMessage(SEEK);
713290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        message.obj = new Integer(time);
714290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        sendMessage(message);
715290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    }
716290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu
717290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    /**
718290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu     * Pause the playback.
719290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu     */
720290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    public void pause() {
721290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        Message message = obtainMessage(PAUSE);
722290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu        sendMessage(message);
723290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    }
724290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu
725290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    /**
72650c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu     * Tear down this proxy object.
72764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu     */
72850c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    public void teardown() {
72964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // This is called by the C++ MediaPlayerPrivate dtor.
73064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // Cancel any active poster download.
73164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        if (mPosterDownloader != null) {
73264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            mPosterDownloader.cancelAndReleaseQueue();
73364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
73450c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu        mNativePointer = 0;
73564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    }
73664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu
73764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    /**
73864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu     * Load the poster image.
73964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu     * @param url is the URL of the poster image.
74064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu     */
74164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu    public void loadPoster(String url) {
74264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        if (url == null) {
74350c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu            Message message = obtainMessage(LOAD_DEFAULT_POSTER);
74450c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu            sendMessage(message);
74564b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            return;
74664b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
74764b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // Cancel any active poster download.
74864b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        if (mPosterDownloader != null) {
74964b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu            mPosterDownloader.cancelAndReleaseQueue();
75064b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        }
75164b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        // Load the poster asynchronously
75264b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        mPosterDownloader = new PosterDownloader(url, this);
75364b86a1e8b6679ed10ecf344abcaebc8412b2efbAndrei Popescu        mPosterDownloader.start();
7540a5ce01eda210f83619ada0603a80fd883512fe9Patrick Scott    }
7550a5ce01eda210f83619ada0603a80fd883512fe9Patrick Scott
75610ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu    // These three function are called from UI thread only by WebView.
757661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    public void setBaseLayer(int layer) {
758661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        VideoPlayer.setBaseLayer(layer);
759661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    }
760661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu
761661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    public void pauseAndDispatch() {
762661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu        VideoPlayer.pauseAndDispatch();
763661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    }
76410ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu
76510ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu    public void enterFullScreenVideo(int layerId, String url) {
76610ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu        VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView);
76710ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu    }
76810ab654943b386c0816a33dbd639c3b8d6260b01Teng-Hui Zhu
769d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu    public void exitFullScreenVideo() {
770d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu        VideoPlayer.exitFullScreenVideo(this, mWebView);
771d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu    }
772d7678a1684966cac10717b34b161b5241b9865c5Teng-Hui Zhu
7736fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu    /**
7743c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu     * The factory for HTML5VideoViewProxy instances.
7756fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu     * @param webViewCore is the WebViewCore that is requesting the proxy.
7766fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu     *
7773c946a1a9637e85e4256f40b68f3b9d4b9f40c27Andrei Popescu     * @return a new HTML5VideoViewProxy object.
7786fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu     */
779290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
780a6c4d8e018e39ff94263ce8f2a31b768104d1bb6Jonathan Dixon        return new HTML5VideoViewProxy(webViewCore.getWebViewClassic(), nativePtr);
7816fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu    }
782290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu
7833c90952036a5ff7ddb2946c643f1a0bf1c31d53aJonathan Dixon    /* package */ WebViewClassic getWebView() {
7841708ad53c3669b72d20724e52301eecd35e86377Ben Murdoch        return mWebView;
7851708ad53c3669b72d20724e52301eecd35e86377Ben Murdoch    }
7861708ad53c3669b72d20724e52301eecd35e86377Ben Murdoch
787290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
788290c34ac3a36b407b74b42c37501c2d0cdac70adAndrei Popescu    private native void nativeOnEnded(int nativePointer);
78943aaa2dd3f1f5169145338ba93f3c9f59fa4b1eaShimeng (Simon) Wang    private native void nativeOnPaused(int nativePointer);
79050c862324b8c4b9a130beeba6378ca3398960fb7Andrei Popescu    private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
791048eb3bfa124f0869f644fc82a50696f593fe592Andrei Popescu    private native void nativeOnTimeupdate(int position, int nativePointer);
792c3a2858de909145a382e7932d5fb044e1388c0b3Teng-Hui Zhu    private native void nativeOnStopFullscreen(int stillPlaying, int nativePointer);
793e4c89e321c7a33a7baf1a90237d21220d637e966Teng-Hui Zhu    private native void nativeOnRestoreState(int nativePointer);
794661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu    private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
795661e8b1f21f03ea429ff34f8fdb58b68a5049092Teng-Hui Zhu            int baseLayer, int videoLayerId, int textureName,
796265db32f31a9db60d4a93d59befa07c825cbe4f2Teng-Hui Zhu            int playerState);
797c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu
798c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu    @Override
799c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu    public boolean onInfo(MediaPlayer mp, int what, int extra) {
800c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu        if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
801c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu            sendMessage(obtainMessage(BUFFERING_START, what, extra));
802c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu        } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
803c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu            sendMessage(obtainMessage(BUFFERING_END, what, extra));
804c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu        }
805c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu        return false;
806c0fccd1292de4d2743fb30fef8310bcda4ce9824Teng-Hui Zhu    }
8075558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu
8085558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu    @Override
8095558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu    public boolean onKey(View v, int keyCode, KeyEvent event) {
8105558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        if (keyCode == KeyEvent.KEYCODE_BACK) {
8115558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu            if (event.getAction() == KeyEvent.ACTION_DOWN) {
8125558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu                return true;
8135558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu            } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled()) {
8145558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu                VideoPlayer.exitFullScreenVideo(this, mWebView);
8155558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu                return true;
8165558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu            }
8175558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        }
8185558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu        return false;
8195558eacfbfb1294912c5dde2179793ccfe2b7324Teng-Hui Zhu    }
8206fa2958059a08390ed2b87b8572f8847ad9999e4Andrei Popescu}
821