blob: eee80251fd5dee95b7dfcb1c9753b3d1a55d4cae [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 Popescu64b86a12009-09-15 20:34:18 +0100135 mCurrentProxy = null;
Andrei Popescubf385d72009-09-18 18:59:52 +0100136 mLayout.removeView(mVideoView);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100137 mVideoView = null;
Andrei Popescubf385d72009-09-18 18:59:52 +0100138 if (mProgressView != null) {
139 mLayout.removeView(mProgressView);
140 mProgressView = null;
141 }
142 mLayout = null;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100143 }
144 };
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100145
Andrei Popescu290c34a2009-09-17 15:55:24 +0100146 public static void play(String url, int time, HTML5VideoViewProxy proxy,
147 WebChromeClient client) {
Andrei Popescua41f97b2010-01-11 18:36:25 +0000148 if (mCurrentProxy == proxy) {
149 if (!mVideoView.isPlaying()) {
150 mVideoView.start();
151 }
152 return;
153 }
154
Andrei Popescu64b86a12009-09-15 20:34:18 +0100155 if (mCurrentProxy != null) {
156 // Some other video is already playing. Notify the caller that its playback ended.
157 proxy.playbackEnded();
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100158 return;
159 }
Andrei Popescua41f97b2010-01-11 18:36:25 +0000160
Andrei Popescu64b86a12009-09-15 20:34:18 +0100161 mCurrentProxy = proxy;
Andrei Popescubf385d72009-09-18 18:59:52 +0100162 // Create a FrameLayout that will contain the VideoView and the
163 // progress view (if any).
164 mLayout = new FrameLayout(proxy.getContext());
165 FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
166 ViewGroup.LayoutParams.WRAP_CONTENT,
167 ViewGroup.LayoutParams.WRAP_CONTENT,
168 Gravity.CENTER);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100169 mVideoView = new VideoView(proxy.getContext());
170 mVideoView.setWillNotDraw(false);
171 mVideoView.setMediaController(new MediaController(proxy.getContext()));
Andreas Huber25643002010-01-28 11:19:57 -0800172
173 String cookieValue = CookieManager.getInstance().getCookie(url);
174 Map<String, String> headers = null;
175 if (cookieValue != null) {
176 headers = new HashMap<String, String>();
177 headers.put(COOKIE, cookieValue);
178 }
179
180 mVideoView.setVideoURI(Uri.parse(url), headers);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100181 mVideoView.setOnCompletionListener(proxy);
182 mVideoView.setOnPreparedListener(proxy);
Andrei Popescu50c86232009-09-30 16:51:25 +0100183 mVideoView.setOnErrorListener(proxy);
Andrei Popescu290c34a2009-09-17 15:55:24 +0100184 mVideoView.seekTo(time);
Andrei Popescubf385d72009-09-18 18:59:52 +0100185 mLayout.addView(mVideoView, layoutParams);
186 mProgressView = client.getVideoLoadingProgressView();
187 if (mProgressView != null) {
188 mLayout.addView(mProgressView, layoutParams);
189 mProgressView.setVisibility(View.VISIBLE);
190 }
191 mLayout.setVisibility(View.VISIBLE);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000192 mTimer = new Timer();
Andrei Popescu64b86a12009-09-15 20:34:18 +0100193 mVideoView.start();
Andrei Popescubf385d72009-09-18 18:59:52 +0100194 client.onShowCustomView(mLayout, mCallback);
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100195 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100196
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000197 public static boolean isPlaying(HTML5VideoViewProxy proxy) {
198 return (mCurrentProxy == proxy && mVideoView != null && mVideoView.isPlaying());
199 }
200
201 public static int getCurrentPosition() {
202 int currentPosMs = 0;
203 if (mVideoView != null) {
204 currentPosMs = mVideoView.getCurrentPosition();
205 }
206 return currentPosMs;
207 }
208
Andrei Popescu290c34a2009-09-17 15:55:24 +0100209 public static void seek(int time, HTML5VideoViewProxy proxy) {
210 if (mCurrentProxy == proxy && time >= 0 && mVideoView != null) {
211 mVideoView.seekTo(time);
212 }
213 }
214
215 public static void pause(HTML5VideoViewProxy proxy) {
216 if (mCurrentProxy == proxy && mVideoView != null) {
217 mVideoView.pause();
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000218 mTimer.purge();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100219 }
220 }
Andrei Popescubf385d72009-09-18 18:59:52 +0100221
222 public static void onPrepared() {
Andrei Popescu50c86232009-09-30 16:51:25 +0100223 if (mProgressView == null || mLayout == null) {
224 return;
Andrei Popescubf385d72009-09-18 18:59:52 +0100225 }
Andrei Popescu8aab46a2010-02-27 15:47:46 +0000226 mTimer.schedule(new TimeupdateTask(mCurrentProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD);
Andrei Popescu50c86232009-09-30 16:51:25 +0100227 mProgressView.setVisibility(View.GONE);
228 mLayout.removeView(mProgressView);
229 mProgressView = null;
Andrei Popescubf385d72009-09-18 18:59:52 +0100230 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100231 }
232
233 // A bunch event listeners for our VideoView
234 // MediaPlayer.OnPreparedListener
235 public void onPrepared(MediaPlayer mp) {
Andrei Popescubf385d72009-09-18 18:59:52 +0100236 VideoPlayer.onPrepared();
Andrei Popescu290c34a2009-09-17 15:55:24 +0100237 Message msg = Message.obtain(mWebCoreHandler, PREPARED);
238 Map<String, Object> map = new HashMap<String, Object>();
239 map.put("dur", new Integer(mp.getDuration()));
240 map.put("width", new Integer(mp.getVideoWidth()));
241 map.put("height", new Integer(mp.getVideoHeight()));
242 msg.obj = map;
243 mWebCoreHandler.sendMessage(msg);
244 }
245
246 // MediaPlayer.OnCompletionListener;
247 public void onCompletion(MediaPlayer mp) {
248 playbackEnded();
249 }
250
Andrei Popescu50c86232009-09-30 16:51:25 +0100251 // MediaPlayer.OnErrorListener
252 public boolean onError(MediaPlayer mp, int what, int extra) {
253 sendMessage(obtainMessage(ERROR));
254 return false;
255 }
256
Andrei Popescu290c34a2009-09-17 15:55:24 +0100257 public void playbackEnded() {
258 Message msg = Message.obtain(mWebCoreHandler, ENDED);
259 mWebCoreHandler.sendMessage(msg);
Andrei Popescu46a83b42009-10-23 13:49:46 +0100260 // also send a message to ourselves to return to the WebView
261 sendMessage(obtainMessage(ENDED));
Andrei Popescu64b86a12009-09-15 20:34:18 +0100262 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100263
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000264 public void onTimeupdate() {
265 sendMessage(obtainMessage(TIMEUPDATE));
266 }
267
268 // Handler for the messages from WebCore or Timer thread to the UI thread.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100269 @Override
270 public void handleMessage(Message msg) {
271 // This executes on the UI thread.
272 switch (msg.what) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100273 case PLAY: {
274 String url = (String) msg.obj;
275 WebChromeClient client = mWebView.getWebChromeClient();
276 if (client != null) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100277 VideoPlayer.play(url, mSeekPosition, this, client);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100278 }
279 break;
280 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100281 case SEEK: {
282 Integer time = (Integer) msg.obj;
283 mSeekPosition = time;
284 VideoPlayer.seek(mSeekPosition, this);
285 break;
286 }
287 case PAUSE: {
288 VideoPlayer.pause(this);
289 break;
290 }
Andrei Popescu46a83b42009-10-23 13:49:46 +0100291 case ENDED:
Andrei Popescu50c86232009-09-30 16:51:25 +0100292 case ERROR: {
293 WebChromeClient client = mWebView.getWebChromeClient();
294 if (client != null) {
295 client.onHideCustomView();
296 }
297 break;
298 }
299 case LOAD_DEFAULT_POSTER: {
300 WebChromeClient client = mWebView.getWebChromeClient();
301 if (client != null) {
302 doSetPoster(client.getDefaultVideoPoster());
303 }
304 break;
305 }
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000306 case TIMEUPDATE: {
307 if (VideoPlayer.isPlaying(this)) {
308 sendTimeupdate();
309 }
310 break;
311 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100312 }
313 }
314
Andrei Popescu64b86a12009-09-15 20:34:18 +0100315 // Everything below this comment executes on the WebCore thread, except for
316 // the EventHandler methods, which are called on the network thread.
317
318 // A helper class that knows how to download posters
319 private static final class PosterDownloader implements EventHandler {
320 // The request queue. This is static as we have one queue for all posters.
321 private static RequestQueue mRequestQueue;
322 private static int mQueueRefCount = 0;
323 // The poster URL
324 private String mUrl;
325 // The proxy we're doing this for.
326 private final HTML5VideoViewProxy mProxy;
327 // The poster bytes. We only touch this on the network thread.
328 private ByteArrayOutputStream mPosterBytes;
329 // The request handle. We only touch this on the WebCore thread.
330 private RequestHandle mRequestHandle;
331 // The response status code.
332 private int mStatusCode;
333 // The response headers.
334 private Headers mHeaders;
335 // The handler to handle messages on the WebCore thread.
336 private Handler mHandler;
337
338 public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
339 mUrl = url;
340 mProxy = proxy;
341 mHandler = new Handler();
342 }
343 // Start the download. Called on WebCore thread.
344 public void start() {
345 retainQueue();
346 mRequestHandle = mRequestQueue.queueRequest(mUrl, "GET", null, this, null, 0);
347 }
348 // Cancel the download if active and release the queue. Called on WebCore thread.
349 public void cancelAndReleaseQueue() {
350 if (mRequestHandle != null) {
351 mRequestHandle.cancel();
352 mRequestHandle = null;
353 }
354 releaseQueue();
355 }
356 // EventHandler methods. Executed on the network thread.
357 public void status(int major_version,
358 int minor_version,
359 int code,
360 String reason_phrase) {
361 mStatusCode = code;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100362 }
363
Andrei Popescu64b86a12009-09-15 20:34:18 +0100364 public void headers(Headers headers) {
365 mHeaders = headers;
366 }
367
368 public void data(byte[] data, int len) {
369 if (mPosterBytes == null) {
370 mPosterBytes = new ByteArrayOutputStream();
371 }
372 mPosterBytes.write(data, 0, len);
373 }
374
375 public void endData() {
376 if (mStatusCode == 200) {
377 if (mPosterBytes.size() > 0) {
378 Bitmap poster = BitmapFactory.decodeByteArray(
379 mPosterBytes.toByteArray(), 0, mPosterBytes.size());
Andrei Popescu50c86232009-09-30 16:51:25 +0100380 mProxy.doSetPoster(poster);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100381 }
382 cleanup();
383 } else if (mStatusCode >= 300 && mStatusCode < 400) {
384 // We have a redirect.
385 mUrl = mHeaders.getLocation();
386 if (mUrl != null) {
387 mHandler.post(new Runnable() {
388 public void run() {
389 if (mRequestHandle != null) {
390 mRequestHandle.setupRedirect(mUrl, mStatusCode,
391 new HashMap<String, String>());
392 }
393 }
394 });
395 }
396 }
397 }
398
399 public void certificate(SslCertificate certificate) {
400 // Don't care.
401 }
402
403 public void error(int id, String description) {
404 cleanup();
405 }
406
407 public boolean handleSslErrorRequest(SslError error) {
408 // Don't care. If this happens, data() will never be called so
409 // mPosterBytes will never be created, so no need to call cleanup.
410 return false;
411 }
412 // Tears down the poster bytes stream. Called on network thread.
413 private void cleanup() {
414 if (mPosterBytes != null) {
415 try {
416 mPosterBytes.close();
417 } catch (IOException ignored) {
418 // Ignored.
419 } finally {
420 mPosterBytes = null;
421 }
422 }
423 }
424
425 // Queue management methods. Called on WebCore thread.
426 private void retainQueue() {
427 if (mRequestQueue == null) {
428 mRequestQueue = new RequestQueue(mProxy.getContext());
429 }
430 mQueueRefCount++;
431 }
432
433 private void releaseQueue() {
434 if (mQueueRefCount == 0) {
435 return;
436 }
437 if (--mQueueRefCount == 0) {
438 mRequestQueue.shutdown();
439 mRequestQueue = null;
440 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100441 }
442 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100443
444 /**
445 * Private constructor.
Andrei Popescu290c34a2009-09-17 15:55:24 +0100446 * @param webView is the WebView that hosts the video.
447 * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100448 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100449 private HTML5VideoViewProxy(WebView webView, int nativePtr) {
Andrei Popescu6fa29582009-06-19 14:54:09 +0100450 // This handler is for the main (UI) thread.
451 super(Looper.getMainLooper());
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100452 // Save the WebView object.
453 mWebView = webView;
Andrei Popescu290c34a2009-09-17 15:55:24 +0100454 // Save the native ptr
455 mNativePointer = nativePtr;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100456 // create the message handler for this thread
457 createWebCoreHandler();
Andrei Popescu6fa29582009-06-19 14:54:09 +0100458 }
459
Andrei Popescu64b86a12009-09-15 20:34:18 +0100460 private void createWebCoreHandler() {
461 mWebCoreHandler = new Handler() {
462 @Override
463 public void handleMessage(Message msg) {
464 switch (msg.what) {
Andrei Popescu290c34a2009-09-17 15:55:24 +0100465 case PREPARED: {
466 Map<String, Object> map = (Map<String, Object>) msg.obj;
467 Integer duration = (Integer) map.get("dur");
468 Integer width = (Integer) map.get("width");
469 Integer height = (Integer) map.get("height");
470 nativeOnPrepared(duration.intValue(), width.intValue(),
471 height.intValue(), mNativePointer);
472 break;
473 }
474 case ENDED:
475 nativeOnEnded(mNativePointer);
476 break;
Andrei Popescu50c86232009-09-30 16:51:25 +0100477 case POSTER_FETCHED:
478 Bitmap poster = (Bitmap) msg.obj;
479 nativeOnPosterFetched(poster, mNativePointer);
480 break;
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000481 case TIMEUPDATE:
482 nativeOnTimeupdate(msg.arg1, mNativePointer);
483 break;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100484 }
Andrei Popescu64b86a12009-09-15 20:34:18 +0100485 }
486 };
Andrei Popescu6fa29582009-06-19 14:54:09 +0100487 }
488
Andrei Popescu64b86a12009-09-15 20:34:18 +0100489 private void doSetPoster(Bitmap poster) {
490 if (poster == null) {
491 return;
492 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100493 // Save a ref to the bitmap and send it over to the WebCore thread.
494 mPoster = poster;
495 Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED);
496 msg.obj = poster;
497 mWebCoreHandler.sendMessage(msg);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100498 }
499
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000500 private void sendTimeupdate() {
501 Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
502 msg.arg1 = VideoPlayer.getCurrentPosition();
503 mWebCoreHandler.sendMessage(msg);
504 }
505
Andrei Popescu64b86a12009-09-15 20:34:18 +0100506 public Context getContext() {
507 return mWebView.getContext();
508 }
509
510 // The public methods below are all called from WebKit only.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100511 /**
512 * Play a video stream.
513 * @param url is the URL of the video stream.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100514 */
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100515 public void play(String url) {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100516 if (url == null) {
517 return;
518 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100519 Message message = obtainMessage(PLAY);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100520 message.obj = url;
Andrei Popescu6fa29582009-06-19 14:54:09 +0100521 sendMessage(message);
522 }
523
Andrei Popescu64b86a12009-09-15 20:34:18 +0100524 /**
Andrei Popescu290c34a2009-09-17 15:55:24 +0100525 * Seek into the video stream.
526 * @param time is the position in the video stream.
527 */
528 public void seek(int time) {
529 Message message = obtainMessage(SEEK);
530 message.obj = new Integer(time);
531 sendMessage(message);
532 }
533
534 /**
535 * Pause the playback.
536 */
537 public void pause() {
538 Message message = obtainMessage(PAUSE);
539 sendMessage(message);
540 }
541
542 /**
Andrei Popescu50c86232009-09-30 16:51:25 +0100543 * Tear down this proxy object.
Andrei Popescu64b86a12009-09-15 20:34:18 +0100544 */
Andrei Popescu50c86232009-09-30 16:51:25 +0100545 public void teardown() {
Andrei Popescu64b86a12009-09-15 20:34:18 +0100546 // This is called by the C++ MediaPlayerPrivate dtor.
547 // Cancel any active poster download.
548 if (mPosterDownloader != null) {
549 mPosterDownloader.cancelAndReleaseQueue();
550 }
Andrei Popescu50c86232009-09-30 16:51:25 +0100551 mNativePointer = 0;
Andrei Popescu64b86a12009-09-15 20:34:18 +0100552 }
553
554 /**
555 * Load the poster image.
556 * @param url is the URL of the poster image.
557 */
558 public void loadPoster(String url) {
559 if (url == null) {
Andrei Popescu50c86232009-09-30 16:51:25 +0100560 Message message = obtainMessage(LOAD_DEFAULT_POSTER);
561 sendMessage(message);
Andrei Popescu64b86a12009-09-15 20:34:18 +0100562 return;
563 }
564 // Cancel any active poster download.
565 if (mPosterDownloader != null) {
566 mPosterDownloader.cancelAndReleaseQueue();
567 }
568 // Load the poster asynchronously
569 mPosterDownloader = new PosterDownloader(url, this);
570 mPosterDownloader.start();
Patrick Scott0a5ce012009-07-02 08:56:10 -0400571 }
572
Andrei Popescu6fa29582009-06-19 14:54:09 +0100573 /**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100574 * The factory for HTML5VideoViewProxy instances.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100575 * @param webViewCore is the WebViewCore that is requesting the proxy.
576 *
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100577 * @return a new HTML5VideoViewProxy object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100578 */
Andrei Popescu290c34a2009-09-17 15:55:24 +0100579 public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
580 return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100581 }
Andrei Popescu290c34a2009-09-17 15:55:24 +0100582
583 private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
584 private native void nativeOnEnded(int nativePointer);
Andrei Popescu50c86232009-09-30 16:51:25 +0100585 private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
Andrei Popescu048eb3b2010-01-11 21:12:54 +0000586 private native void nativeOnTimeupdate(int position, int nativePointer);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100587}