/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.webkit; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.MediaPlayer; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.net.http.EventHandler; import android.net.http.Headers; import android.net.http.RequestHandle; import android.net.http.RequestQueue; import android.net.http.SslCertificate; import android.net.http.SslError; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import android.view.MotionEvent; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.AbsoluteLayout; import android.widget.FrameLayout; import android.widget.MediaController; import android.widget.VideoView; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** *
Proxy for HTML5 video views.
*/
class HTML5VideoViewProxy extends Handler
implements MediaPlayer.OnPreparedListener,
MediaPlayer.OnCompletionListener,
MediaPlayer.OnErrorListener {
// Logging tag.
private static final String LOGTAG = "HTML5VideoViewProxy";
// Message Ids for WebCore thread -> UI thread communication.
private static final int PLAY = 100;
private static final int SEEK = 101;
private static final int PAUSE = 102;
private static final int ERROR = 103;
private static final int LOAD_DEFAULT_POSTER = 104;
// Message Ids to be handled on the WebCore thread
private static final int PREPARED = 200;
private static final int ENDED = 201;
private static final int POSTER_FETCHED = 202;
// The C++ MediaPlayerPrivateAndroid object.
int mNativePointer;
// The handler for WebCore thread messages;
private Handler mWebCoreHandler;
// The WebView instance that created this view.
private WebView mWebView;
// The poster image to be shown when the video is not playing.
// This ref prevents the bitmap from being GC'ed.
private Bitmap mPoster;
// The poster downloader.
private PosterDownloader mPosterDownloader;
// The seek position.
private int mSeekPosition;
// A helper class to control the playback. This executes on the UI thread!
private static final class VideoPlayer {
// The proxy that is currently playing (if any).
private static HTML5VideoViewProxy mCurrentProxy;
// The VideoView instance. This is a singleton for now, at least until
// http://b/issue?id=1973663 is fixed.
private static VideoView mVideoView;
// The progress view.
private static View mProgressView;
// The container for the progress view and video view
private static FrameLayout mLayout;
private static final WebChromeClient.CustomViewCallback mCallback =
new WebChromeClient.CustomViewCallback() {
public void onCustomViewHidden() {
// At this point the videoview is pretty much destroyed.
// It listens to SurfaceHolder.Callback.SurfaceDestroyed event
// which happens when the video view is detached from its parent
// view. This happens in the WebChromeClient before this method
// is invoked.
mCurrentProxy.playbackEnded();
mCurrentProxy = null;
mLayout.removeView(mVideoView);
mVideoView = null;
if (mProgressView != null) {
mLayout.removeView(mProgressView);
mProgressView = null;
}
mLayout = null;
}
};
public static void play(String url, int time, HTML5VideoViewProxy proxy,
WebChromeClient client) {
if (mCurrentProxy != null) {
// Some other video is already playing. Notify the caller that its playback ended.
proxy.playbackEnded();
return;
}
mCurrentProxy = proxy;
// Create a FrameLayout that will contain the VideoView and the
// progress view (if any).
mLayout = new FrameLayout(proxy.getContext());
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.CENTER);
mVideoView = new VideoView(proxy.getContext());
mVideoView.setWillNotDraw(false);
mVideoView.setMediaController(new MediaController(proxy.getContext()));
mVideoView.setVideoURI(Uri.parse(url));
mVideoView.setOnCompletionListener(proxy);
mVideoView.setOnPreparedListener(proxy);
mVideoView.setOnErrorListener(proxy);
mVideoView.seekTo(time);
mLayout.addView(mVideoView, layoutParams);
mProgressView = client.getVideoLoadingProgressView();
if (mProgressView != null) {
mLayout.addView(mProgressView, layoutParams);
mProgressView.setVisibility(View.VISIBLE);
}
mLayout.setVisibility(View.VISIBLE);
mVideoView.start();
client.onShowCustomView(mLayout, mCallback);
}
public static void seek(int time, HTML5VideoViewProxy proxy) {
if (mCurrentProxy == proxy && time >= 0 && mVideoView != null) {
mVideoView.seekTo(time);
}
}
public static void pause(HTML5VideoViewProxy proxy) {
if (mCurrentProxy == proxy && mVideoView != null) {
mVideoView.pause();
}
}
public static void onPrepared() {
if (mProgressView == null || mLayout == null) {
return;
}
mProgressView.setVisibility(View.GONE);
mLayout.removeView(mProgressView);
mProgressView = null;
}
}
// A bunch event listeners for our VideoView
// MediaPlayer.OnPreparedListener
public void onPrepared(MediaPlayer mp) {
VideoPlayer.onPrepared();
Message msg = Message.obtain(mWebCoreHandler, PREPARED);
Map