blob: eda2e72af3765baa502f26225aba91a373730252 [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;
Andrei Popescu64b86a12009-09-15 20:34:18 +010075
Andreas Huber25643002010-01-28 11:19:57 -080076 private static final String COOKIE = "Cookie";
77
Andrei Popescu048eb3b2010-01-11 21:12:54 +000078 // Timer thread -> UI thread
79 private static final int TIMEUPDATE = 300;
80
Andrei Popescu290c34a2009-09-17 15:55:24 +010081 // The C++ MediaPlayerPrivateAndroid object.
82 int mNativePointer;
Andrei Popescu64b86a12009-09-15 20:34:18 +010083 // The handler for WebCore thread messages;
84 private Handler mWebCoreHandler;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010085 // The WebView instance that created this view.
86 private WebView mWebView;
Andrei Popescu64b86a12009-09-15 20:34:18 +010087 // The poster image to be shown when the video is not playing.
Andrei Popescu50c86232009-09-30 16:51:25 +010088 // This ref prevents the bitmap from being GC'ed.
89 private Bitmap mPoster;
Andrei Popescu64b86a12009-09-15 20:34:18 +010090 // The poster downloader.
91 private PosterDownloader mPosterDownloader;
Andrei Popescu290c34a2009-09-17 15:55:24 +010092 // The seek position.
93 private int mSeekPosition;
Andrei Popescu64b86a12009-09-15 20:34:18 +010094 // A helper class to control the playback. This executes on the UI thread!
95 private static final class VideoPlayer {
96 // The proxy that is currently playing (if any).
97 private static HTML5VideoViewProxy mCurrentProxy;
98 // The VideoView instance. This is a singleton for now, at least until
99 // http://b/issue?id=1973663 is fixed.
100 private static VideoView mVideoView;
Andrei Popescubf385d72009-09-18 18:59:52 +0100101 // The progress view.
102 private static View mProgressView;
103 // The container for the progress view and video view
104 private static FrameLayout mLayout;
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000105 // The timer for timeupate events.
106 // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
107 private static Timer mTimer;
108 private static final class TimeupdateTask extends TimerTask {
109 private HTML5VideoViewProxy mProxy;
110
111 public TimeupdateTask(HTML5VideoViewProxy proxy) {
112 mProxy = proxy;
113 }
114
115 public void run() {
116 mProxy.onTimeupdate();
117 }
118 }
119 // The spec says the timer should fire every 250 ms or less.
120 private static final int TIMEUPDATE_PERIOD = 250; // ms
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100121
Andrei Popescu64b86a12009-09-15 20:34:18 +0100122 private static final WebChromeClient.CustomViewCallback mCallback =
123 new WebChromeClient.CustomViewCallback() {
124 public void onCustomViewHidden() {
125 // At this point the videoview is pretty much destroyed.
126 // It listens to SurfaceHolder.Callback.SurfaceDestroyed event
127 // which happens when the video view is detached from its parent
128 // view. This happens in the WebChromeClient before this method
129 // is invoked.
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000130 mTimer.cancel();
131 mTimer = null;
Andrei Popescu31d2aa12010-04-08 15:19:22 +0100132 if (mVideoView.isPlaying()) {
133 mVideoView.stopPlayback();
134 }
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100135 mCurrentProxy.dispatchOnEnded();
Andrei Popescu64b86a12009-09-15 20:34:18 +0100136 mCurrentProxy = null;
Andrei Popescubf385d72009-09-18 18:59:52 +0100137 mLayout.removeView(mVideoView);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100138 mVideoView = null;
Andrei Popescubf385d72009-09-18 18:59:52 +0100139 if (mProgressView != null) {
140 mLayout.removeView(mProgressView);
141 mProgressView = null;
142 }
143 mLayout = null;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100144 }
145 };
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100146
Andrei Popescu290c34a2009-09-17 15:55:24 +0100147 public static void play(String url, int time, HTML5VideoViewProxy proxy,
148 WebChromeClient client) {
Andrei Popescua41f97b2010-01-11 18:36:25 +0000149 if (mCurrentProxy == proxy) {
150 if (!mVideoView.isPlaying()) {
151 mVideoView.start();
152 }
153 return;
154 }
155
Andrei Popescu64b86a12009-09-15 20:34:18 +0100156 if (mCurrentProxy != null) {
157 // Some other video is already playing. Notify the caller that its playback ended.
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100158 proxy.dispatchOnEnded();
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100159 return;
160 }
Andrei Popescua41f97b2010-01-11 18:36:25 +0000161
Andrei Popescu64b86a12009-09-15 20:34:18 +0100162 mCurrentProxy = proxy;
Andrei Popescubf385d72009-09-18 18:59:52 +0100163 // Create a FrameLayout that will contain the VideoView and the
164 // progress view (if any).
165 mLayout = new FrameLayout(proxy.getContext());
166 FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
167 ViewGroup.LayoutParams.WRAP_CONTENT,
168 ViewGroup.LayoutParams.WRAP_CONTENT,
169 Gravity.CENTER);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100170 mVideoView = new VideoView(proxy.getContext());
171 mVideoView.setWillNotDraw(false);
172 mVideoView.setMediaController(new MediaController(proxy.getContext()));
Andreas Huber25643002010-01-28 11:19:57 -0800173
174 String cookieValue = CookieManager.getInstance().getCookie(url);
175 Map<String, String> headers = null;
176 if (cookieValue != null) {
177 headers = new HashMap<String, String>();
178 headers.put(COOKIE, cookieValue);
179 }
180
181 mVideoView.setVideoURI(Uri.parse(url), headers);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100182 mVideoView.setOnCompletionListener(proxy);
183 mVideoView.setOnPreparedListener(proxy);
Andrei Popescu50c86232009-09-30 16:51:25 +0100184 mVideoView.setOnErrorListener(proxy);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100185 mVideoView.seekTo(time);
Andrei Popescubf385d72009-09-18 18:59:52 +0100186 mLayout.addView(mVideoView, layoutParams);
187 mProgressView = client.getVideoLoadingProgressView();
188 if (mProgressView != null) {
189 mLayout.addView(mProgressView, layoutParams);
190 mProgressView.setVisibility(View.VISIBLE);
191 }
192 mLayout.setVisibility(View.VISIBLE);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000193 mTimer = new Timer();
Andrei Popescu64b86a12009-09-15 20:34:18 +0100194 mVideoView.start();
Andrei Popescubf385d72009-09-18 18:59:52 +0100195 client.onShowCustomView(mLayout, mCallback);
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100196 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100197
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000198 public static boolean isPlaying(HTML5VideoViewProxy proxy) {
199 return (mCurrentProxy == proxy && mVideoView != null && mVideoView.isPlaying());
200 }
201
202 public static int getCurrentPosition() {
203 int currentPosMs = 0;
204 if (mVideoView != null) {
205 currentPosMs = mVideoView.getCurrentPosition();
206 }
207 return currentPosMs;
208 }
209
Andrei Popescu290c34a2009-09-17 15:55:24 +0100210 public static void seek(int time, HTML5VideoViewProxy proxy) {
211 if (mCurrentProxy == proxy && time >= 0 && mVideoView != null) {
212 mVideoView.seekTo(time);
213 }
214 }
215
216 public static void pause(HTML5VideoViewProxy proxy) {
217 if (mCurrentProxy == proxy && mVideoView != null) {
218 mVideoView.pause();
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000219 mTimer.purge();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100220 }
221 }
Andrei Popescubf385d72009-09-18 18:59:52 +0100222
223 public static void onPrepared() {
Andrei Popescu50c86232009-09-30 16:51:25 +0100224 if (mProgressView == null || mLayout == null) {
225 return;
Andrei Popescubf385d72009-09-18 18:59:52 +0100226 }
Andrei Popescu8aab46a2010-02-27 15:47:46 +0000227 mTimer.schedule(new TimeupdateTask(mCurrentProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD);
Andrei Popescu50c86232009-09-30 16:51:25 +0100228 mProgressView.setVisibility(View.GONE);
229 mLayout.removeView(mProgressView);
230 mProgressView = null;
Andrei Popescubf385d72009-09-18 18:59:52 +0100231 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100232 }
233
234 // A bunch event listeners for our VideoView
235 // MediaPlayer.OnPreparedListener
236 public void onPrepared(MediaPlayer mp) {
Andrei Popescubf385d72009-09-18 18:59:52 +0100237 VideoPlayer.onPrepared();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100238 Message msg = Message.obtain(mWebCoreHandler, PREPARED);
239 Map<String, Object> map = new HashMap<String, Object>();
240 map.put("dur", new Integer(mp.getDuration()));
241 map.put("width", new Integer(mp.getVideoWidth()));
242 map.put("height", new Integer(mp.getVideoHeight()));
243 msg.obj = map;
244 mWebCoreHandler.sendMessage(msg);
245 }
246
247 // MediaPlayer.OnCompletionListener;
248 public void onCompletion(MediaPlayer mp) {
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100249 // The video ended by itself, so we need to
250 // send a message to the UI thread to dismiss
251 // the video view and to return to the WebView.
252 sendMessage(obtainMessage(ENDED));
Andrei Popescu290c34a2009-09-17 15:55:24 +0100253 }
254
Andrei Popescu50c86232009-09-30 16:51:25 +0100255 // MediaPlayer.OnErrorListener
256 public boolean onError(MediaPlayer mp, int what, int extra) {
257 sendMessage(obtainMessage(ERROR));
258 return false;
259 }
260
Andrei Popescuc4fbceb2010-04-14 13:30:23 +0100261 public void dispatchOnEnded() {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100262 Message msg = Message.obtain(mWebCoreHandler, ENDED);
263 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100264 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100265
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000266 public void onTimeupdate() {
267 sendMessage(obtainMessage(TIMEUPDATE));
268 }
269
270 // Handler for the messages from WebCore or Timer thread to the UI thread.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100271 @Override
272 public void handleMessage(Message msg) {
273 // This executes on the UI thread.
274 switch (msg.what) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100275 case PLAY: {
276 String url = (String) msg.obj;
277 WebChromeClient client = mWebView.getWebChromeClient();
278 if (client != null) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100279 VideoPlayer.play(url, mSeekPosition, this, client);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100280 }
281 break;
282 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100283 case SEEK: {
284 Integer time = (Integer) msg.obj;
285 mSeekPosition = time;
286 VideoPlayer.seek(mSeekPosition, this);
287 break;
288 }
289 case PAUSE: {
290 VideoPlayer.pause(this);
291 break;
292 }
Andrei Popescu46a83b42009-10-23 13:49:46 +0100293 case ENDED:
Andrei Popescu50c86232009-09-30 16:51:25 +0100294 case ERROR: {
295 WebChromeClient client = mWebView.getWebChromeClient();
296 if (client != null) {
297 client.onHideCustomView();
298 }
299 break;
300 }
301 case LOAD_DEFAULT_POSTER: {
302 WebChromeClient client = mWebView.getWebChromeClient();
303 if (client != null) {
304 doSetPoster(client.getDefaultVideoPoster());
305 }
306 break;
307 }
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000308 case TIMEUPDATE: {
309 if (VideoPlayer.isPlaying(this)) {
310 sendTimeupdate();
311 }
312 break;
313 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100314 }
315 }
316
Andrei Popescu64b86a12009-09-15 20:34:18 +0100317 // Everything below this comment executes on the WebCore thread, except for
318 // the EventHandler methods, which are called on the network thread.
319
320 // A helper class that knows how to download posters
321 private static final class PosterDownloader implements EventHandler {
322 // The request queue. This is static as we have one queue for all posters.
323 private static RequestQueue mRequestQueue;
324 private static int mQueueRefCount = 0;
325 // The poster URL
326 private String mUrl;
327 // The proxy we're doing this for.
328 private final HTML5VideoViewProxy mProxy;
329 // The poster bytes. We only touch this on the network thread.
330 private ByteArrayOutputStream mPosterBytes;
331 // The request handle. We only touch this on the WebCore thread.
332 private RequestHandle mRequestHandle;
333 // The response status code.
334 private int mStatusCode;
335 // The response headers.
336 private Headers mHeaders;
337 // The handler to handle messages on the WebCore thread.
338 private Handler mHandler;
339
340 public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
341 mUrl = url;
342 mProxy = proxy;
343 mHandler = new Handler();
344 }
345 // Start the download. Called on WebCore thread.
346 public void start() {
347 retainQueue();
348 mRequestHandle = mRequestQueue.queueRequest(mUrl, "GET", null, this, null, 0);
349 }
350 // Cancel the download if active and release the queue. Called on WebCore thread.
351 public void cancelAndReleaseQueue() {
352 if (mRequestHandle != null) {
353 mRequestHandle.cancel();
354 mRequestHandle = null;
355 }
356 releaseQueue();
357 }
358 // EventHandler methods. Executed on the network thread.
359 public void status(int major_version,
360 int minor_version,
361 int code,
362 String reason_phrase) {
363 mStatusCode = code;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100364 }
365
Andrei Popescu64b86a12009-09-15 20:34:18 +0100366 public void headers(Headers headers) {
367 mHeaders = headers;
368 }
369
370 public void data(byte[] data, int len) {
371 if (mPosterBytes == null) {
372 mPosterBytes = new ByteArrayOutputStream();
373 }
374 mPosterBytes.write(data, 0, len);
375 }
376
377 public void endData() {
378 if (mStatusCode == 200) {
379 if (mPosterBytes.size() > 0) {
380 Bitmap poster = BitmapFactory.decodeByteArray(
381 mPosterBytes.toByteArray(), 0, mPosterBytes.size());
Andrei Popescu50c86232009-09-30 16:51:25 +0100382 mProxy.doSetPoster(poster);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100383 }
384 cleanup();
385 } else if (mStatusCode >= 300 && mStatusCode < 400) {
386 // We have a redirect.
387 mUrl = mHeaders.getLocation();
388 if (mUrl != null) {
389 mHandler.post(new Runnable() {
390 public void run() {
391 if (mRequestHandle != null) {
392 mRequestHandle.setupRedirect(mUrl, mStatusCode,
393 new HashMap<String, String>());
394 }
395 }
396 });
397 }
398 }
399 }
400
401 public void certificate(SslCertificate certificate) {
402 // Don't care.
403 }
404
405 public void error(int id, String description) {
406 cleanup();
407 }
408
409 public boolean handleSslErrorRequest(SslError error) {
410 // Don't care. If this happens, data() will never be called so
411 // mPosterBytes will never be created, so no need to call cleanup.
412 return false;
413 }
414 // Tears down the poster bytes stream. Called on network thread.
415 private void cleanup() {
416 if (mPosterBytes != null) {
417 try {
418 mPosterBytes.close();
419 } catch (IOException ignored) {
420 // Ignored.
421 } finally {
422 mPosterBytes = null;
423 }
424 }
425 }
426
427 // Queue management methods. Called on WebCore thread.
428 private void retainQueue() {
429 if (mRequestQueue == null) {
430 mRequestQueue = new RequestQueue(mProxy.getContext());
431 }
432 mQueueRefCount++;
433 }
434
435 private void releaseQueue() {
436 if (mQueueRefCount == 0) {
437 return;
438 }
439 if (--mQueueRefCount == 0) {
440 mRequestQueue.shutdown();
441 mRequestQueue = null;
442 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100443 }
444 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100445
446 /**
447 * Private constructor.
Andrei Popescu290c34a2009-09-17 15:55:24 +0100448 * @param webView is the WebView that hosts the video.
449 * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100450 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100451 private HTML5VideoViewProxy(WebView webView, int nativePtr) {
Andrei Popescu6fa29582009-06-19 14:54:09 +0100452 // This handler is for the main (UI) thread.
453 super(Looper.getMainLooper());
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100454 // Save the WebView object.
455 mWebView = webView;
Andrei Popescu290c34a2009-09-17 15:55:24 +0100456 // Save the native ptr
457 mNativePointer = nativePtr;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100458 // create the message handler for this thread
459 createWebCoreHandler();
Andrei Popescu6fa29582009-06-19 14:54:09 +0100460 }
461
Andrei Popescu64b86a12009-09-15 20:34:18 +0100462 private void createWebCoreHandler() {
463 mWebCoreHandler = new Handler() {
464 @Override
465 public void handleMessage(Message msg) {
466 switch (msg.what) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100467 case PREPARED: {
468 Map<String, Object> map = (Map<String, Object>) msg.obj;
469 Integer duration = (Integer) map.get("dur");
470 Integer width = (Integer) map.get("width");
471 Integer height = (Integer) map.get("height");
472 nativeOnPrepared(duration.intValue(), width.intValue(),
473 height.intValue(), mNativePointer);
474 break;
475 }
476 case ENDED:
477 nativeOnEnded(mNativePointer);
478 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100479 case POSTER_FETCHED:
480 Bitmap poster = (Bitmap) msg.obj;
481 nativeOnPosterFetched(poster, mNativePointer);
482 break;
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000483 case TIMEUPDATE:
484 nativeOnTimeupdate(msg.arg1, mNativePointer);
485 break;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100486 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100487 }
488 };
Andrei Popescu6fa29582009-06-19 14:54:09 +0100489 }
490
Andrei Popescu64b86a12009-09-15 20:34:18 +0100491 private void doSetPoster(Bitmap poster) {
492 if (poster == null) {
493 return;
494 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100495 // Save a ref to the bitmap and send it over to the WebCore thread.
496 mPoster = poster;
497 Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED);
498 msg.obj = poster;
499 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100500 }
501
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000502 private void sendTimeupdate() {
503 Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
504 msg.arg1 = VideoPlayer.getCurrentPosition();
505 mWebCoreHandler.sendMessage(msg);
506 }
507
Andrei Popescu64b86a12009-09-15 20:34:18 +0100508 public Context getContext() {
509 return mWebView.getContext();
510 }
511
512 // The public methods below are all called from WebKit only.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100513 /**
514 * Play a video stream.
515 * @param url is the URL of the video stream.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100516 */
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100517 public void play(String url) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100518 if (url == null) {
519 return;
520 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100521 Message message = obtainMessage(PLAY);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100522 message.obj = url;
Andrei Popescu6fa29582009-06-19 14:54:09 +0100523 sendMessage(message);
524 }
525
Andrei Popescu64b86a12009-09-15 20:34:18 +0100526 /**
Andrei Popescu290c34a2009-09-17 15:55:24 +0100527 * Seek into the video stream.
528 * @param time is the position in the video stream.
529 */
530 public void seek(int time) {
531 Message message = obtainMessage(SEEK);
532 message.obj = new Integer(time);
533 sendMessage(message);
534 }
535
536 /**
537 * Pause the playback.
538 */
539 public void pause() {
540 Message message = obtainMessage(PAUSE);
541 sendMessage(message);
542 }
543
544 /**
Andrei Popescu50c86232009-09-30 16:51:25 +0100545 * Tear down this proxy object.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100546 */
Andrei Popescu50c86232009-09-30 16:51:25 +0100547 public void teardown() {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100548 // This is called by the C++ MediaPlayerPrivate dtor.
549 // Cancel any active poster download.
550 if (mPosterDownloader != null) {
551 mPosterDownloader.cancelAndReleaseQueue();
552 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100553 mNativePointer = 0;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100554 }
555
556 /**
557 * Load the poster image.
558 * @param url is the URL of the poster image.
559 */
560 public void loadPoster(String url) {
561 if (url == null) {
Andrei Popescu50c86232009-09-30 16:51:25 +0100562 Message message = obtainMessage(LOAD_DEFAULT_POSTER);
563 sendMessage(message);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100564 return;
565 }
566 // Cancel any active poster download.
567 if (mPosterDownloader != null) {
568 mPosterDownloader.cancelAndReleaseQueue();
569 }
570 // Load the poster asynchronously
571 mPosterDownloader = new PosterDownloader(url, this);
572 mPosterDownloader.start();
Patrick Scott0a5ce012009-07-02 08:56:10 -0400573 }
574
Andrei Popescu6fa29582009-06-19 14:54:09 +0100575 /**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100576 * The factory for HTML5VideoViewProxy instances.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100577 * @param webViewCore is the WebViewCore that is requesting the proxy.
578 *
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100579 * @return a new HTML5VideoViewProxy object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100580 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100581 public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
582 return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100583 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100584
585 private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
586 private native void nativeOnEnded(int nativePointer);
Andrei Popescu50c86232009-09-30 16:51:25 +0100587 private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000588 private native void nativeOnTimeupdate(int position, int nativePointer);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100589}