blob: 5a164f8af57bedc1b18c4aa7a08f2338494c8e8f [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 Popescu3c946a1a2009-07-03 08:20:53 +010020import android.media.MediaPlayer;
21import android.media.MediaPlayer.OnPreparedListener;
Andrei Popescu6fa29582009-06-19 14:54:09 +010022import android.net.Uri;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.util.Log;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010028import android.view.MotionEvent;
Andrei Popescu6fa29582009-06-19 14:54:09 +010029import android.view.View;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010030import android.view.ViewGroup;
Patrick Scott0a5ce012009-07-02 08:56:10 -040031import android.webkit.ViewManager.ChildView;
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010032import android.widget.AbsoluteLayout;
Andrei Popescu6fa29582009-06-19 14:54:09 +010033import android.widget.MediaController;
34import android.widget.VideoView;
35
36import java.util.HashMap;
37
38/**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010039 * <p>Proxy for HTML5 video views.
Andrei Popescu6fa29582009-06-19 14:54:09 +010040 */
41class HTML5VideoViewProxy extends Handler {
42 // Logging tag.
43 private static final String LOGTAG = "HTML5VideoViewProxy";
44
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010045 // Message Ids for WebCore thread -> UI thread communication.
Andrei Popescu6fa29582009-06-19 14:54:09 +010046 private static final int INIT = 100;
47 private static final int PLAY = 101;
48
Andrei Popescu3c946a1a2009-07-03 08:20:53 +010049 // The WebView instance that created this view.
50 private WebView mWebView;
51 // The ChildView instance used by the ViewManager.
52 private ChildView mChildView;
53 // The VideoView instance. Note that we could
54 // also access this via mChildView.mView but it would
55 // always require cast, so it is more convenient to store
56 // it here as well.
57 private HTML5VideoView mVideoView;
58
59 // A VideoView subclass that responds to double-tap
60 // events by going fullscreen.
61 class HTML5VideoView extends VideoView {
62 // Used to save the layout parameters if the view
63 // is changed to fullscreen.
64 private AbsoluteLayout.LayoutParams mEmbeddedLayoutParams;
65 // Flag that denotes whether the view is fullscreen or not.
66 private boolean mIsFullscreen;
67 // Used to save the current playback position when
68 // transitioning to/from fullscreen.
69 private int mPlaybackPosition;
70 // The callback object passed to the host application. This callback
71 // is invoked when the host application dismisses our VideoView
72 // (e.g. the user presses the back key).
73 private WebChromeClient.CustomViewCallback mCallback =
74 new WebChromeClient.CustomViewCallback() {
75 public void onCustomViewHidden() {
76 playEmbedded();
77 }
78 };
79
80 // The OnPreparedListener, used to automatically resume
81 // playback when transitioning to/from fullscreen.
82 private MediaPlayer.OnPreparedListener mPreparedListener =
83 new MediaPlayer.OnPreparedListener() {
84 public void onPrepared(MediaPlayer mp) {
85 resumePlayback();
86 }
87 };
88
89 HTML5VideoView(Context context) {
90 super(context);
91 }
92
93 void savePlaybackPosition() {
94 if (isPlaying()) {
95 mPlaybackPosition = getCurrentPosition();
96 }
97 }
98
99 void resumePlayback() {
100 seekTo(mPlaybackPosition);
101 start();
102 setOnPreparedListener(null);
103 }
104
105 void playEmbedded() {
106 // Attach to the WebView.
107 mChildView.attachViewOnUIThread(mEmbeddedLayoutParams);
108 // Make sure we're visible
109 setVisibility(View.VISIBLE);
110 // Set the onPrepared listener so we start
111 // playing when the video view is reattached
112 // and its surface is recreated.
113 setOnPreparedListener(mPreparedListener);
114 mIsFullscreen = false;
115 }
116
117 void playFullScreen() {
118 WebChromeClient client = mWebView.getWebChromeClient();
119 if (client == null) {
120 return;
121 }
122 // Save the current layout params.
123 mEmbeddedLayoutParams =
124 (AbsoluteLayout.LayoutParams) getLayoutParams();
125 // Detach from the WebView.
126 mChildView.removeViewOnUIThread();
127 // Attach to the browser UI.
128 client.onShowCustomView(this, mCallback);
129 // Set the onPrepared listener so we start
130 // playing when after the video view is reattached
131 // and its surface is recreated.
132 setOnPreparedListener(mPreparedListener);
133 mIsFullscreen = true;
134 }
135
136 @Override
137 public boolean onTouchEvent(MotionEvent ev) {
138 // TODO: implement properly (i.e. detect double tap)
139 if (mIsFullscreen || !isPlaying()) {
140 return super.onTouchEvent(ev);
141 }
142 playFullScreen();
143 return true;
144 }
145
146 @Override
147 public void onDetachedFromWindow() {
148 super.onDetachedFromWindow();
149 savePlaybackPosition();
150 }
151 }
Andrei Popescu6fa29582009-06-19 14:54:09 +0100152
153 /**
154 * Private constructor.
155 * @param context is the application context.
156 */
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100157 private HTML5VideoViewProxy(WebView webView) {
Andrei Popescu6fa29582009-06-19 14:54:09 +0100158 // This handler is for the main (UI) thread.
159 super(Looper.getMainLooper());
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100160 // Save the WebView object.
161 mWebView = webView;
Andrei Popescu6fa29582009-06-19 14:54:09 +0100162 }
163
164 @Override
165 public void handleMessage(Message msg) {
166 // This executes on the UI thread.
167 switch (msg.what) {
168 case INIT:
169 // Create the video view and set a default controller.
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100170 mVideoView = new HTML5VideoView(mWebView.getContext());
Patrick Scott0a5ce012009-07-02 08:56:10 -0400171 // This is needed because otherwise there will be a black square
172 // stuck on the screen.
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100173 mVideoView.setWillNotDraw(false);
174 mVideoView.setMediaController(new MediaController(mWebView.getContext()));
175 mChildView.mView = mVideoView;
Andrei Popescu6fa29582009-06-19 14:54:09 +0100176 break;
177 case PLAY:
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100178 if (mVideoView == null) {
179 return;
180 }
Patrick Scott0a5ce012009-07-02 08:56:10 -0400181 HashMap<String, Object> map =
Andrei Popescu6fa29582009-06-19 14:54:09 +0100182 (HashMap<String, Object>) msg.obj;
Patrick Scott0a5ce012009-07-02 08:56:10 -0400183 String url = (String) map.get("url");
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100184 mVideoView.setVideoURI(Uri.parse(url));
185 mVideoView.start();
Andrei Popescu6fa29582009-06-19 14:54:09 +0100186 break;
187 }
188 }
189
190 /**
191 * Play a video stream.
192 * @param url is the URL of the video stream.
193 * @param webview is the WebViewCore that is requesting the playback.
194 */
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100195 public void play(String url) {
196 // We need to know the webview that is requesting the playback.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100197 Message message = obtainMessage(PLAY);
198 HashMap<String, Object> map = new HashMap();
199 map.put("url", url);
Andrei Popescu6fa29582009-06-19 14:54:09 +0100200 message.obj = map;
201 sendMessage(message);
202 }
203
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100204 public void createView() {
205 mChildView = mWebView.mViewManager.createView();
206 sendMessage(obtainMessage(INIT));
207 }
208
209 public void attachView(int x, int y, int width, int height) {
210 if (mChildView == null) {
211 return;
Patrick Scott0a5ce012009-07-02 08:56:10 -0400212 }
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100213 mChildView.attachView(x, y, width, height);
Patrick Scott0a5ce012009-07-02 08:56:10 -0400214 }
215
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100216 public void removeView() {
217 if (mChildView == null) {
218 return;
219 }
220 mChildView.removeView();
Patrick Scott0a5ce012009-07-02 08:56:10 -0400221 }
222
Andrei Popescu6fa29582009-06-19 14:54:09 +0100223 /**
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100224 * The factory for HTML5VideoViewProxy instances.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100225 * @param webViewCore is the WebViewCore that is requesting the proxy.
226 *
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100227 * @return a new HTML5VideoViewProxy object.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100228 */
229 public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore) {
Andrei Popescu3c946a1a2009-07-03 08:20:53 +0100230 return new HTML5VideoViewProxy(webViewCore.getWebView());
Andrei Popescu6fa29582009-06-19 14:54:09 +0100231 }
232}