blob: d8f34e05d2fefb4cb7193d8141eb1f1ef5a1658c [file] [log] [blame]
Andrei Popescu6fa29582009-06-19 14:54:09 +01001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.content.Context;
Andrei Popescu64b86a12009-09-15 20:34:18 +010020import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010022import android.media.MediaPlayer;
23import android.media.MediaPlayer.OnPreparedListener;
Andrei Popescu64b86a12009-09-15 20:34:18 +010024import android.media.MediaPlayer.OnCompletionListener;
25import android.media.MediaPlayer.OnErrorListener;
26import android.net.http.EventHandler;
27import android.net.http.Headers;
28import android.net.http.RequestHandle;
29import android.net.http.RequestQueue;
30import android.net.http.SslCertificate;
31import android.net.http.SslError;
Andrei Popescu6fa29582009-06-19 14:54:09 +010032import android.net.Uri;
33import android.os.Bundle;
34import android.os.Handler;
35import android.os.Looper;
36import android.os.Message;
37import android.util.Log;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010038import android.view.MotionEvent;
Andrei Popescubf385d72009-09-18 18:59:52 +010039import android.view.Gravity;
Andrei Popescu6fa29582009-06-19 14:54:09 +010040import android.view.View;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010041import android.view.ViewGroup;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010042import android.widget.AbsoluteLayout;
Andrei Popescubf385d72009-09-18 18:59:52 +010043import android.widget.FrameLayout;
Andrei Popescu6fa29582009-06-19 14:54:09 +010044import android.widget.MediaController;
45import android.widget.VideoView;
46
Andrei Popescu64b86a12009-09-15 20:34:18 +010047import java.io.ByteArrayOutputStream;
48import java.io.IOException;
Andrei Popescu6fa29582009-06-19 14:54:09 +010049import java.util.HashMap;
Andrei Popescu290c34a2009-09-17 15:55:24 +010050import java.util.Map;
Andrei Popescu048eb3b2010-01-11 21:12:54 +000051import java.util.Timer;
52import java.util.TimerTask;
Andrei Popescu6fa29582009-06-19 14:54:09 +010053
54/**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010055 * <p>Proxy for HTML5 video views.
Andrei Popescu6fa29582009-06-19 14:54:09 +010056 */
Andrei Popescu290c34a2009-09-17 15:55:24 +010057class HTML5VideoViewProxy extends Handler
58 implements MediaPlayer.OnPreparedListener,
Andrei Popescu50c86232009-09-30 16:51:25 +010059 MediaPlayer.OnCompletionListener,
60 MediaPlayer.OnErrorListener {
Andrei Popescu6fa29582009-06-19 14:54:09 +010061 // Logging tag.
62 private static final String LOGTAG = "HTML5VideoViewProxy";
63
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010064 // Message Ids for WebCore thread -> UI thread communication.
Andrei Popescu50c86232009-09-30 16:51:25 +010065 private static final int PLAY = 100;
66 private static final int SEEK = 101;
67 private static final int PAUSE = 102;
68 private static final int ERROR = 103;
69 private static final int LOAD_DEFAULT_POSTER = 104;
Andrei Popescu6fa29582009-06-19 14:54:09 +010070
Andrei Popescu64b86a12009-09-15 20:34:18 +010071 // Message Ids to be handled on the WebCore thread
Andrei Popescu290c34a2009-09-17 15:55:24 +010072 private static final int PREPARED = 200;
73 private static final int ENDED = 201;
Andrei Popescu50c86232009-09-30 16:51:25 +010074 private static final int POSTER_FETCHED = 202;
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -070075 private static final int PAUSED = 203;
Andrei Popescu64b86a12009-09-15 20:34:18 +010076
Andreas Huber25643002010-01-28 11:19:57 -080077 private static final String COOKIE = "Cookie";
78
Andrei Popescu048eb3b2010-01-11 21:12:54 +000079 // Timer thread -> UI thread
80 private static final int TIMEUPDATE = 300;
81
Andrei Popescu290c34a2009-09-17 15:55:24 +010082 // The C++ MediaPlayerPrivateAndroid object.
83 int mNativePointer;
Andrei Popescu64b86a12009-09-15 20:34:18 +010084 // The handler for WebCore thread messages;
85 private Handler mWebCoreHandler;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010086 // The WebView instance that created this view.
87 private WebView mWebView;
Andrei Popescu64b86a12009-09-15 20:34:18 +010088 // The poster image to be shown when the video is not playing.
Andrei Popescu50c86232009-09-30 16:51:25 +010089 // This ref prevents the bitmap from being GC'ed.
90 private Bitmap mPoster;
Andrei Popescu64b86a12009-09-15 20:34:18 +010091 // The poster downloader.
92 private PosterDownloader mPosterDownloader;
Andrei Popescu290c34a2009-09-17 15:55:24 +010093 // The seek position.
94 private int mSeekPosition;
Andrei Popescu64b86a12009-09-15 20:34:18 +010095 // A helper class to control the playback. This executes on the UI thread!
96 private static final class VideoPlayer {
97 // The proxy that is currently playing (if any).
98 private static HTML5VideoViewProxy mCurrentProxy;
99 // The VideoView instance. This is a singleton for now, at least until
100 // http://b/issue?id=1973663 is fixed.
101 private static VideoView mVideoView;
Andrei Popescubf385d72009-09-18 18:59:52 +0100102 // The progress view.
103 private static View mProgressView;
104 // The container for the progress view and video view
105 private static FrameLayout mLayout;
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000106 // The timer for timeupate events.
107 // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
108 private static Timer mTimer;
109 private static final class TimeupdateTask extends TimerTask {
110 private HTML5VideoViewProxy mProxy;
111
112 public TimeupdateTask(HTML5VideoViewProxy proxy) {
113 mProxy = proxy;
114 }
115
116 public void run() {
117 mProxy.onTimeupdate();
118 }
119 }
120 // The spec says the timer should fire every 250 ms or less.
121 private static final int TIMEUPDATE_PERIOD = 250; // ms
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700122 static boolean isVideoSelfEnded = false;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100123
Andrei Popescu64b86a12009-09-15 20:34:18 +0100124 private static final WebChromeClient.CustomViewCallback mCallback =
125 new WebChromeClient.CustomViewCallback() {
126 public void onCustomViewHidden() {
127 // At this point the videoview is pretty much destroyed.
128 // It listens to SurfaceHolder.Callback.SurfaceDestroyed event
129 // which happens when the video view is detached from its parent
130 // view. This happens in the WebChromeClient before this method
131 // is invoked.
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000132 mTimer.cancel();
133 mTimer = null;
Andrei Popescu31d2aa12010-04-08 15:19:22 +0100134 if (mVideoView.isPlaying()) {
135 mVideoView.stopPlayback();
136 }
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700137 if (isVideoSelfEnded)
138 mCurrentProxy.dispatchOnEnded();
139 else
140 mCurrentProxy.dispatchOnPaused();
Ben Murdoch1708ad52010-11-11 15:56:16 +0000141
142 // Re enable plugin views.
143 mCurrentProxy.getWebView().getViewManager().showAll();
144
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700145 isVideoSelfEnded = false;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100146 mCurrentProxy = null;
Andrei Popescubf385d72009-09-18 18:59:52 +0100147 mLayout.removeView(mVideoView);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100148 mVideoView = null;
Andrei Popescubf385d72009-09-18 18:59:52 +0100149 if (mProgressView != null) {
150 mLayout.removeView(mProgressView);
151 mProgressView = null;
152 }
153 mLayout = null;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100154 }
155 };
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100156
Andrei Popescu290c34a2009-09-17 15:55:24 +0100157 public static void play(String url, int time, HTML5VideoViewProxy proxy,
158 WebChromeClient client) {
Andrei Popescua41f97b2010-01-11 18:36:25 +0000159 if (mCurrentProxy == proxy) {
160 if (!mVideoView.isPlaying()) {
161 mVideoView.start();
162 }
163 return;
164 }
165
Andrei Popescu64b86a12009-09-15 20:34:18 +0100166 if (mCurrentProxy != null) {
167 // Some other video is already playing. Notify the caller that its playback ended.
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100168 proxy.dispatchOnEnded();
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100169 return;
170 }
Andrei Popescua41f97b2010-01-11 18:36:25 +0000171
Andrei Popescu64b86a12009-09-15 20:34:18 +0100172 mCurrentProxy = proxy;
Andrei Popescubf385d72009-09-18 18:59:52 +0100173 // Create a FrameLayout that will contain the VideoView and the
174 // progress view (if any).
175 mLayout = new FrameLayout(proxy.getContext());
176 FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
177 ViewGroup.LayoutParams.WRAP_CONTENT,
178 ViewGroup.LayoutParams.WRAP_CONTENT,
179 Gravity.CENTER);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100180 mVideoView = new VideoView(proxy.getContext());
181 mVideoView.setWillNotDraw(false);
182 mVideoView.setMediaController(new MediaController(proxy.getContext()));
Andreas Huber25643002010-01-28 11:19:57 -0800183
184 String cookieValue = CookieManager.getInstance().getCookie(url);
185 Map<String, String> headers = null;
186 if (cookieValue != null) {
187 headers = new HashMap<String, String>();
188 headers.put(COOKIE, cookieValue);
189 }
190
191 mVideoView.setVideoURI(Uri.parse(url), headers);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100192 mVideoView.setOnCompletionListener(proxy);
193 mVideoView.setOnPreparedListener(proxy);
Andrei Popescu50c86232009-09-30 16:51:25 +0100194 mVideoView.setOnErrorListener(proxy);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100195 mVideoView.seekTo(time);
Andrei Popescubf385d72009-09-18 18:59:52 +0100196 mLayout.addView(mVideoView, layoutParams);
197 mProgressView = client.getVideoLoadingProgressView();
198 if (mProgressView != null) {
199 mLayout.addView(mProgressView, layoutParams);
200 mProgressView.setVisibility(View.VISIBLE);
201 }
202 mLayout.setVisibility(View.VISIBLE);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000203 mTimer = new Timer();
Andrei Popescu64b86a12009-09-15 20:34:18 +0100204 mVideoView.start();
Andrei Popescubf385d72009-09-18 18:59:52 +0100205 client.onShowCustomView(mLayout, mCallback);
Ben Murdoch1708ad52010-11-11 15:56:16 +0000206 // Plugins like Flash will draw over the video so hide
207 // them while we're playing.
208 mCurrentProxy.getWebView().getViewManager().hideAll();
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100209 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100210
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000211 public static boolean isPlaying(HTML5VideoViewProxy proxy) {
212 return (mCurrentProxy == proxy && mVideoView != null && mVideoView.isPlaying());
213 }
214
215 public static int getCurrentPosition() {
216 int currentPosMs = 0;
217 if (mVideoView != null) {
218 currentPosMs = mVideoView.getCurrentPosition();
219 }
220 return currentPosMs;
221 }
222
Andrei Popescu290c34a2009-09-17 15:55:24 +0100223 public static void seek(int time, HTML5VideoViewProxy proxy) {
224 if (mCurrentProxy == proxy && time >= 0 && mVideoView != null) {
225 mVideoView.seekTo(time);
226 }
227 }
228
229 public static void pause(HTML5VideoViewProxy proxy) {
230 if (mCurrentProxy == proxy && mVideoView != null) {
231 mVideoView.pause();
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000232 mTimer.purge();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100233 }
234 }
Andrei Popescubf385d72009-09-18 18:59:52 +0100235
236 public static void onPrepared() {
Andrei Popescu50c86232009-09-30 16:51:25 +0100237 if (mProgressView == null || mLayout == null) {
238 return;
Andrei Popescubf385d72009-09-18 18:59:52 +0100239 }
Andrei Popescu8aab46a2010-02-27 15:47:46 +0000240 mTimer.schedule(new TimeupdateTask(mCurrentProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD);
Andrei Popescu50c86232009-09-30 16:51:25 +0100241 mProgressView.setVisibility(View.GONE);
242 mLayout.removeView(mProgressView);
243 mProgressView = null;
Andrei Popescubf385d72009-09-18 18:59:52 +0100244 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100245 }
246
247 // A bunch event listeners for our VideoView
248 // MediaPlayer.OnPreparedListener
249 public void onPrepared(MediaPlayer mp) {
Andrei Popescubf385d72009-09-18 18:59:52 +0100250 VideoPlayer.onPrepared();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100251 Message msg = Message.obtain(mWebCoreHandler, PREPARED);
252 Map<String, Object> map = new HashMap<String, Object>();
253 map.put("dur", new Integer(mp.getDuration()));
254 map.put("width", new Integer(mp.getVideoWidth()));
255 map.put("height", new Integer(mp.getVideoHeight()));
256 msg.obj = map;
257 mWebCoreHandler.sendMessage(msg);
258 }
259
260 // MediaPlayer.OnCompletionListener;
261 public void onCompletion(MediaPlayer mp) {
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100262 // The video ended by itself, so we need to
263 // send a message to the UI thread to dismiss
264 // the video view and to return to the WebView.
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700265 // arg1 == 1 means the video ends by itself.
266 sendMessage(obtainMessage(ENDED, 1, 0));
Andrei Popescu290c34a2009-09-17 15:55:24 +0100267 }
268
Andrei Popescu50c86232009-09-30 16:51:25 +0100269 // MediaPlayer.OnErrorListener
270 public boolean onError(MediaPlayer mp, int what, int extra) {
271 sendMessage(obtainMessage(ERROR));
272 return false;
273 }
274
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100275 public void dispatchOnEnded() {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100276 Message msg = Message.obtain(mWebCoreHandler, ENDED);
277 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100278 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100279
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700280 public void dispatchOnPaused() {
281 Message msg = Message.obtain(mWebCoreHandler, PAUSED);
282 mWebCoreHandler.sendMessage(msg);
283 }
284
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000285 public void onTimeupdate() {
286 sendMessage(obtainMessage(TIMEUPDATE));
287 }
288
289 // Handler for the messages from WebCore or Timer thread to the UI thread.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100290 @Override
291 public void handleMessage(Message msg) {
292 // This executes on the UI thread.
293 switch (msg.what) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100294 case PLAY: {
295 String url = (String) msg.obj;
296 WebChromeClient client = mWebView.getWebChromeClient();
297 if (client != null) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100298 VideoPlayer.play(url, mSeekPosition, this, client);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100299 }
300 break;
301 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100302 case SEEK: {
303 Integer time = (Integer) msg.obj;
304 mSeekPosition = time;
305 VideoPlayer.seek(mSeekPosition, this);
306 break;
307 }
308 case PAUSE: {
309 VideoPlayer.pause(this);
310 break;
311 }
Andrei Popescu46a83b42009-10-23 13:49:46 +0100312 case ENDED:
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700313 if (msg.arg1 == 1)
314 VideoPlayer.isVideoSelfEnded = true;
Andrei Popescu50c86232009-09-30 16:51:25 +0100315 case ERROR: {
316 WebChromeClient client = mWebView.getWebChromeClient();
317 if (client != null) {
318 client.onHideCustomView();
319 }
320 break;
321 }
322 case LOAD_DEFAULT_POSTER: {
323 WebChromeClient client = mWebView.getWebChromeClient();
324 if (client != null) {
325 doSetPoster(client.getDefaultVideoPoster());
326 }
327 break;
328 }
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000329 case TIMEUPDATE: {
330 if (VideoPlayer.isPlaying(this)) {
331 sendTimeupdate();
332 }
333 break;
334 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100335 }
336 }
337
Andrei Popescu64b86a12009-09-15 20:34:18 +0100338 // Everything below this comment executes on the WebCore thread, except for
339 // the EventHandler methods, which are called on the network thread.
340
341 // A helper class that knows how to download posters
342 private static final class PosterDownloader implements EventHandler {
343 // The request queue. This is static as we have one queue for all posters.
344 private static RequestQueue mRequestQueue;
345 private static int mQueueRefCount = 0;
346 // The poster URL
347 private String mUrl;
348 // The proxy we're doing this for.
349 private final HTML5VideoViewProxy mProxy;
350 // The poster bytes. We only touch this on the network thread.
351 private ByteArrayOutputStream mPosterBytes;
352 // The request handle. We only touch this on the WebCore thread.
353 private RequestHandle mRequestHandle;
354 // The response status code.
355 private int mStatusCode;
356 // The response headers.
357 private Headers mHeaders;
358 // The handler to handle messages on the WebCore thread.
359 private Handler mHandler;
360
361 public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
362 mUrl = url;
363 mProxy = proxy;
364 mHandler = new Handler();
365 }
366 // Start the download. Called on WebCore thread.
367 public void start() {
368 retainQueue();
369 mRequestHandle = mRequestQueue.queueRequest(mUrl, "GET", null, this, null, 0);
370 }
371 // Cancel the download if active and release the queue. Called on WebCore thread.
372 public void cancelAndReleaseQueue() {
373 if (mRequestHandle != null) {
374 mRequestHandle.cancel();
375 mRequestHandle = null;
376 }
377 releaseQueue();
378 }
379 // EventHandler methods. Executed on the network thread.
380 public void status(int major_version,
381 int minor_version,
382 int code,
383 String reason_phrase) {
384 mStatusCode = code;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100385 }
386
Andrei Popescu64b86a12009-09-15 20:34:18 +0100387 public void headers(Headers headers) {
388 mHeaders = headers;
389 }
390
391 public void data(byte[] data, int len) {
392 if (mPosterBytes == null) {
393 mPosterBytes = new ByteArrayOutputStream();
394 }
395 mPosterBytes.write(data, 0, len);
396 }
397
398 public void endData() {
399 if (mStatusCode == 200) {
400 if (mPosterBytes.size() > 0) {
401 Bitmap poster = BitmapFactory.decodeByteArray(
402 mPosterBytes.toByteArray(), 0, mPosterBytes.size());
Andrei Popescu50c86232009-09-30 16:51:25 +0100403 mProxy.doSetPoster(poster);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100404 }
405 cleanup();
406 } else if (mStatusCode >= 300 && mStatusCode < 400) {
407 // We have a redirect.
408 mUrl = mHeaders.getLocation();
409 if (mUrl != null) {
410 mHandler.post(new Runnable() {
411 public void run() {
412 if (mRequestHandle != null) {
413 mRequestHandle.setupRedirect(mUrl, mStatusCode,
414 new HashMap<String, String>());
415 }
416 }
417 });
418 }
419 }
420 }
421
422 public void certificate(SslCertificate certificate) {
423 // Don't care.
424 }
425
426 public void error(int id, String description) {
427 cleanup();
428 }
429
430 public boolean handleSslErrorRequest(SslError error) {
431 // Don't care. If this happens, data() will never be called so
432 // mPosterBytes will never be created, so no need to call cleanup.
433 return false;
434 }
435 // Tears down the poster bytes stream. Called on network thread.
436 private void cleanup() {
437 if (mPosterBytes != null) {
438 try {
439 mPosterBytes.close();
440 } catch (IOException ignored) {
441 // Ignored.
442 } finally {
443 mPosterBytes = null;
444 }
445 }
446 }
447
448 // Queue management methods. Called on WebCore thread.
449 private void retainQueue() {
450 if (mRequestQueue == null) {
451 mRequestQueue = new RequestQueue(mProxy.getContext());
452 }
453 mQueueRefCount++;
454 }
455
456 private void releaseQueue() {
457 if (mQueueRefCount == 0) {
458 return;
459 }
460 if (--mQueueRefCount == 0) {
461 mRequestQueue.shutdown();
462 mRequestQueue = null;
463 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100464 }
465 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100466
467 /**
468 * Private constructor.
Andrei Popescu290c34a2009-09-17 15:55:24 +0100469 * @param webView is the WebView that hosts the video.
470 * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100471 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100472 private HTML5VideoViewProxy(WebView webView, int nativePtr) {
Andrei Popescu6fa29582009-06-19 14:54:09 +0100473 // This handler is for the main (UI) thread.
474 super(Looper.getMainLooper());
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100475 // Save the WebView object.
476 mWebView = webView;
Andrei Popescu290c34a2009-09-17 15:55:24 +0100477 // Save the native ptr
478 mNativePointer = nativePtr;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100479 // create the message handler for this thread
480 createWebCoreHandler();
Andrei Popescu6fa29582009-06-19 14:54:09 +0100481 }
482
Andrei Popescu64b86a12009-09-15 20:34:18 +0100483 private void createWebCoreHandler() {
484 mWebCoreHandler = new Handler() {
485 @Override
486 public void handleMessage(Message msg) {
487 switch (msg.what) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100488 case PREPARED: {
489 Map<String, Object> map = (Map<String, Object>) msg.obj;
490 Integer duration = (Integer) map.get("dur");
491 Integer width = (Integer) map.get("width");
492 Integer height = (Integer) map.get("height");
493 nativeOnPrepared(duration.intValue(), width.intValue(),
494 height.intValue(), mNativePointer);
495 break;
496 }
497 case ENDED:
Ben Murdochff19d192011-01-17 18:08:56 +0000498 mSeekPosition = 0;
Andrei Popescu290c34a2009-09-17 15:55:24 +0100499 nativeOnEnded(mNativePointer);
500 break;
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700501 case PAUSED:
502 nativeOnPaused(mNativePointer);
503 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100504 case POSTER_FETCHED:
505 Bitmap poster = (Bitmap) msg.obj;
506 nativeOnPosterFetched(poster, mNativePointer);
507 break;
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000508 case TIMEUPDATE:
509 nativeOnTimeupdate(msg.arg1, mNativePointer);
510 break;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100511 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100512 }
513 };
Andrei Popescu6fa29582009-06-19 14:54:09 +0100514 }
515
Andrei Popescu64b86a12009-09-15 20:34:18 +0100516 private void doSetPoster(Bitmap poster) {
517 if (poster == null) {
518 return;
519 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100520 // Save a ref to the bitmap and send it over to the WebCore thread.
521 mPoster = poster;
522 Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED);
523 msg.obj = poster;
524 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100525 }
526
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000527 private void sendTimeupdate() {
528 Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
529 msg.arg1 = VideoPlayer.getCurrentPosition();
530 mWebCoreHandler.sendMessage(msg);
531 }
532
Andrei Popescu64b86a12009-09-15 20:34:18 +0100533 public Context getContext() {
534 return mWebView.getContext();
535 }
536
537 // The public methods below are all called from WebKit only.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100538 /**
539 * Play a video stream.
540 * @param url is the URL of the video stream.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100541 */
Ben Murdochff19d192011-01-17 18:08:56 +0000542 public void play(String url, int position) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100543 if (url == null) {
544 return;
545 }
Ben Murdochff19d192011-01-17 18:08:56 +0000546
547 if (position > 0) {
548 seek(position);
549 }
550
Andrei Popescu6fa29582009-06-19 14:54:09 +0100551 Message message = obtainMessage(PLAY);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100552 message.obj = url;
Andrei Popescu6fa29582009-06-19 14:54:09 +0100553 sendMessage(message);
554 }
555
Andrei Popescu64b86a12009-09-15 20:34:18 +0100556 /**
Andrei Popescu290c34a2009-09-17 15:55:24 +0100557 * Seek into the video stream.
558 * @param time is the position in the video stream.
559 */
560 public void seek(int time) {
561 Message message = obtainMessage(SEEK);
562 message.obj = new Integer(time);
563 sendMessage(message);
564 }
565
566 /**
567 * Pause the playback.
568 */
569 public void pause() {
570 Message message = obtainMessage(PAUSE);
571 sendMessage(message);
572 }
573
574 /**
Andrei Popescu50c86232009-09-30 16:51:25 +0100575 * Tear down this proxy object.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100576 */
Andrei Popescu50c86232009-09-30 16:51:25 +0100577 public void teardown() {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100578 // This is called by the C++ MediaPlayerPrivate dtor.
579 // Cancel any active poster download.
580 if (mPosterDownloader != null) {
581 mPosterDownloader.cancelAndReleaseQueue();
582 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100583 mNativePointer = 0;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100584 }
585
586 /**
587 * Load the poster image.
588 * @param url is the URL of the poster image.
589 */
590 public void loadPoster(String url) {
591 if (url == null) {
Andrei Popescu50c86232009-09-30 16:51:25 +0100592 Message message = obtainMessage(LOAD_DEFAULT_POSTER);
593 sendMessage(message);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100594 return;
595 }
596 // Cancel any active poster download.
597 if (mPosterDownloader != null) {
598 mPosterDownloader.cancelAndReleaseQueue();
599 }
600 // Load the poster asynchronously
601 mPosterDownloader = new PosterDownloader(url, this);
602 mPosterDownloader.start();
Patrick Scott0a5ce012009-07-02 08:56:10 -0400603 }
604
Andrei Popescu6fa29582009-06-19 14:54:09 +0100605 /**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100606 * The factory for HTML5VideoViewProxy instances.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100607 * @param webViewCore is the WebViewCore that is requesting the proxy.
608 *
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100609 * @return a new HTML5VideoViewProxy object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100610 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100611 public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
612 return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100613 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100614
Ben Murdoch1708ad52010-11-11 15:56:16 +0000615 /* package */ WebView getWebView() {
616 return mWebView;
617 }
618
Andrei Popescu290c34a2009-09-17 15:55:24 +0100619 private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
620 private native void nativeOnEnded(int nativePointer);
Shimeng (Simon) Wang43aaa2d2010-10-18 16:25:59 -0700621 private native void nativeOnPaused(int nativePointer);
Andrei Popescu50c86232009-09-30 16:51:25 +0100622 private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000623 private native void nativeOnTimeupdate(int position, int nativePointer);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100624}