blob: 1004b5f09170d5d8604c2c70ddc22a880d3816ac [file] [log] [blame]
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -07001
2package android.webkit;
3
4import android.content.Context;
5import android.media.MediaPlayer;
6import android.media.Metadata;
7import android.util.Log;
8import android.view.Gravity;
9import android.view.MotionEvent;
10import android.view.SurfaceHolder;
11import android.view.SurfaceView;
12import android.view.View;
13import android.view.ViewGroup;
14import android.webkit.HTML5VideoView;
15import android.webkit.HTML5VideoViewProxy;
16import android.widget.FrameLayout;
17import android.widget.MediaController;
18import android.widget.MediaController.MediaPlayerControl;
19
20
21/**
22 * @hide This is only used by the browser
23 */
24public class HTML5VideoFullScreen extends HTML5VideoView
25 implements MediaPlayerControl, MediaPlayer.OnPreparedListener,
26 View.OnTouchListener {
27
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -070028 // Add this sub-class to handle the resizing when rotating screen.
29 private class VideoSurfaceView extends SurfaceView {
30
31 public VideoSurfaceView(Context context) {
32 super(context);
33 }
34
35 @Override
36 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
37 int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
38 int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
39 if (mVideoWidth > 0 && mVideoHeight > 0) {
40 if ( mVideoWidth * height > width * mVideoHeight ) {
41 height = width * mVideoHeight / mVideoWidth;
42 } else if ( mVideoWidth * height < width * mVideoHeight ) {
43 width = height * mVideoWidth / mVideoHeight;
44 }
45 }
46 setMeasuredDimension(width, height);
47 }
48 }
49
50 // This view will contain the video.
51 private VideoSurfaceView mVideoSurfaceView;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -070052
53 // We need the full screen state to decide which surface to render to and
54 // when to create the MediaPlayer accordingly.
55 static final int FULLSCREEN_OFF = 0;
56 static final int FULLSCREEN_SURFACECREATING = 1;
57 static final int FULLSCREEN_SURFACECREATED = 2;
58
59 private int mFullScreenMode;
60 // The Media Controller only used for full screen mode
61 private MediaController mMediaController;
62
63 // SurfaceHolder for full screen
64 private SurfaceHolder mSurfaceHolder = null;
65
66 // Data only for MediaController
67 private boolean mCanSeekBack;
68 private boolean mCanSeekForward;
69 private boolean mCanPause;
70 private int mCurrentBufferPercentage;
71
72 // The progress view.
73 private static View mProgressView;
74 // The container for the progress view and video view
75 private static FrameLayout mLayout;
76
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -070077 // The video size will be ready when prepared. Used to make sure the aspect
78 // ratio is correct.
79 private int mVideoWidth;
80 private int mVideoHeight;
81
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -070082 SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
83 {
84 public void surfaceChanged(SurfaceHolder holder, int format,
85 int w, int h)
86 {
87 if (mPlayer != null && mMediaController != null
88 && mCurrentState == STATE_PREPARED) {
89 if (mMediaController.isShowing()) {
90 // ensure the controller will get repositioned later
91 mMediaController.hide();
92 }
93 mMediaController.show();
94 }
95 }
96
97 public void surfaceCreated(SurfaceHolder holder)
98 {
99 mSurfaceHolder = holder;
100 mFullScreenMode = FULLSCREEN_SURFACECREATED;
101
102 prepareForFullScreen();
103 }
104
105 public void surfaceDestroyed(SurfaceHolder holder)
106 {
107 // after we return from this we can't use the surface any more
108 mSurfaceHolder = null;
109 // The current Video View will be destroy when we play a new video.
110 }
111 };
112
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -0700113 private SurfaceView getSurfaceView() {
114 return mVideoSurfaceView;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700115 }
116
117 HTML5VideoFullScreen(Context context, int videoLayerId, int position,
118 boolean autoStart) {
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -0700119 mVideoSurfaceView = new VideoSurfaceView(context);
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700120 mFullScreenMode = FULLSCREEN_OFF;
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -0700121 mVideoWidth = 0;
122 mVideoHeight = 0;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700123 init(videoLayerId, position, autoStart);
124 }
125
126 private void setMediaController(MediaController m) {
127 mMediaController = m;
128 attachMediaController();
129 }
130
131 private void attachMediaController() {
132 if (mPlayer != null && mMediaController != null) {
133 mMediaController.setMediaPlayer(this);
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -0700134 mMediaController.setAnchorView(mVideoSurfaceView);
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700135 //Will be enabled when prepared
136 mMediaController.setEnabled(false);
137 }
138 }
139
140 @Override
141 public void decideDisplayMode() {
142 mPlayer.setDisplay(mSurfaceHolder);
143 }
144
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -0700145 private void prepareForFullScreen() {
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700146 // So in full screen, we reset the MediaPlayer
147 mPlayer.reset();
148 setMediaController(new MediaController(mProxy.getContext()));
Teng-Hui Zhu2deec6652011-03-23 15:29:22 -0700149 mPlayer.setScreenOnWhilePlaying(true);
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700150 prepareDataAndDisplayMode(mProxy);
151 }
152
153
154 private void toggleMediaControlsVisiblity() {
155 if (mMediaController.isShowing()) {
156 mMediaController.hide();
157 } else {
158 mMediaController.show();
159 }
160 }
161
162 @Override
163 public void onPrepared(MediaPlayer mp) {
164 super.onPrepared(mp);
165
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -0700166 mVideoSurfaceView.setOnTouchListener(this);
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700167 // Get the capabilities of the player for this stream
168 Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
169 MediaPlayer.BYPASS_METADATA_FILTER);
170 if (data != null) {
171 mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
172 || data.getBoolean(Metadata.PAUSE_AVAILABLE);
173 mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
174 || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
175 mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
176 || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
177 } else {
178 mCanPause = mCanSeekBack = mCanSeekForward = true;
179 }
180
181 // mMediaController status depends on the Metadata result, so put it
182 // after reading the MetaData
183 if (mMediaController != null) {
184 mMediaController.setEnabled(true);
185 // If paused , should show the controller for ever!
186 if (getAutostart())
187 mMediaController.show();
188 else
189 mMediaController.show(0);
190 }
191
192 if (mProgressView != null) {
193 mProgressView.setVisibility(View.GONE);
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700194 }
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -0700195
196 mVideoWidth = mp.getVideoWidth();
197 mVideoHeight = mp.getVideoHeight();
198 // This will trigger the onMeasure to get the display size right.
199 mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700200 }
201
202
203 private final WebChromeClient.CustomViewCallback mCallback =
204 new WebChromeClient.CustomViewCallback() {
205 public void onCustomViewHidden() {
206 // It listens to SurfaceHolder.Callback.SurfaceDestroyed event
207 // which happens when the video view is detached from its parent
208 // view. This happens in the WebChromeClient before this method
209 // is invoked.
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700210 pauseAndDispatch(mProxy);
Teng-Hui Zhub109c882011-05-04 16:19:49 -0700211 mProxy.dispatchOnStopFullScreen();
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700212 mLayout.removeView(getSurfaceView());
213
214 if (mProgressView != null) {
215 mLayout.removeView(mProgressView);
216 mProgressView = null;
217 }
218 mLayout = null;
219 // Re enable plugin views.
220 mProxy.getWebView().getViewManager().showAll();
221
222 mProxy = null;
223 }
224 };
225
226 @Override
227 public void enterFullScreenVideoState(int layerId,
228 HTML5VideoViewProxy proxy, WebView webView) {
229 mFullScreenMode = FULLSCREEN_SURFACECREATING;
230 mCurrentBufferPercentage = 0;
231 mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
232 mProxy = proxy;
233
Teng-Hui Zhu1ab0df72011-03-17 10:27:04 -0700234 mVideoSurfaceView.getHolder().addCallback(mSHCallback);
235 mVideoSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
236 mVideoSurfaceView.setFocusable(true);
237 mVideoSurfaceView.setFocusableInTouchMode(true);
238 mVideoSurfaceView.requestFocus();
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700239
240 // Create a FrameLayout that will contain the VideoView and the
241 // progress view (if any).
242 mLayout = new FrameLayout(mProxy.getContext());
243 FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
244 ViewGroup.LayoutParams.WRAP_CONTENT,
245 ViewGroup.LayoutParams.WRAP_CONTENT,
246 Gravity.CENTER);
247
248 mLayout.addView(getSurfaceView(), layoutParams);
249
250 mLayout.setVisibility(View.VISIBLE);
251
252 WebChromeClient client = webView.getWebChromeClient();
253 client.onShowCustomView(mLayout, mCallback);
254 // Plugins like Flash will draw over the video so hide
255 // them while we're playing.
Teng-Hui Zhub109c882011-05-04 16:19:49 -0700256 if (webView.getViewManager() != null)
257 webView.getViewManager().hideAll();
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700258
259 mProgressView = client.getVideoLoadingProgressView();
260 if (mProgressView != null) {
261 mLayout.addView(mProgressView, layoutParams);
262 mProgressView.setVisibility(View.VISIBLE);
263 }
264
265 }
266
267 /**
268 * @return true when we are in full screen mode, even the surface not fully
269 * created.
270 */
271 public boolean isFullScreenMode() {
272 return true;
273 }
274
275 // MediaController FUNCTIONS:
276 @Override
277 public boolean canPause() {
278 return mCanPause;
279 }
280
281 @Override
282 public boolean canSeekBackward() {
283 return mCanSeekBack;
284 }
285
286 @Override
287 public boolean canSeekForward() {
288 return mCanSeekForward;
289 }
290
291 @Override
292 public int getBufferPercentage() {
293 if (mPlayer != null) {
294 return mCurrentBufferPercentage;
295 }
296 return 0;
297 }
298
299 // Other listeners functions:
300 private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
301 new MediaPlayer.OnBufferingUpdateListener() {
302 public void onBufferingUpdate(MediaPlayer mp, int percent) {
303 mCurrentBufferPercentage = percent;
304 }
305 };
306
307 @Override
308 public boolean onTouch(View v, MotionEvent event) {
309 if (mFullScreenMode >= FULLSCREEN_SURFACECREATED
310 && mMediaController != null) {
311 toggleMediaControlsVisiblity();
312 }
313 return false;
314 }
315
Teng-Hui Zhuf4d4e9e2011-03-30 14:39:56 -0700316 @Override
317 protected void switchProgressView(boolean playerBuffering) {
318 if (playerBuffering) {
319 mProgressView.setVisibility(View.VISIBLE);
320 } else {
321 mProgressView.setVisibility(View.GONE);
322 }
323 return;
324 }
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -0700325}