• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 
17 package android.webkit;
18 
19 import android.content.Context;
20 import android.media.MediaPlayer;
21 import android.media.Metadata;
22 import android.view.Gravity;
23 import android.view.MotionEvent;
24 import android.view.SurfaceHolder;
25 import android.view.SurfaceView;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.widget.FrameLayout;
29 import android.widget.MediaController;
30 import android.widget.MediaController.MediaPlayerControl;
31 
32 
33 /**
34  * @hide This is only used by the browser
35  */
36 public class HTML5VideoFullScreen extends HTML5VideoView
37     implements MediaPlayerControl, MediaPlayer.OnPreparedListener,
38     View.OnTouchListener {
39 
40     // Add this sub-class to handle the resizing when rotating screen.
41     private class VideoSurfaceView extends SurfaceView {
42 
VideoSurfaceView(Context context)43         public VideoSurfaceView(Context context) {
44             super(context);
45         }
46 
47         @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)48         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
49             int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
50             int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
51             if (mVideoWidth > 0 && mVideoHeight > 0) {
52                 if ( mVideoWidth * height  > width * mVideoHeight ) {
53                     height = width * mVideoHeight / mVideoWidth;
54                 } else if ( mVideoWidth * height  < width * mVideoHeight ) {
55                     width = height * mVideoWidth / mVideoHeight;
56                 }
57             }
58             setMeasuredDimension(width, height);
59         }
60     }
61 
62     // This view will contain the video.
63     private VideoSurfaceView mVideoSurfaceView;
64 
65     // We need the full screen state to decide which surface to render to and
66     // when to create the MediaPlayer accordingly.
67     static final int FULLSCREEN_OFF               = 0;
68     static final int FULLSCREEN_SURFACECREATING   = 1;
69     static final int FULLSCREEN_SURFACECREATED    = 2;
70 
71     private int mFullScreenMode;
72     // The Media Controller only used for full screen mode
73     private MediaController mMediaController;
74 
75     // SurfaceHolder for full screen
76     private SurfaceHolder mSurfaceHolder = null;
77 
78     // Data only for MediaController
79     private boolean mCanSeekBack;
80     private boolean mCanSeekForward;
81     private boolean mCanPause;
82     private int mCurrentBufferPercentage;
83 
84     // The progress view.
85     private static View mProgressView;
86     // The container for the progress view and video view
87     private static FrameLayout mLayout;
88 
89     // The video size will be ready when prepared. Used to make sure the aspect
90     // ratio is correct.
91     private int mVideoWidth;
92     private int mVideoHeight;
93     private boolean mPlayingWhenDestroyed = false;
94     SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
95     {
96         @Override
97         public void surfaceChanged(SurfaceHolder holder, int format,
98                                     int w, int h)
99         {
100             if (mPlayer != null && mMediaController != null
101                     && mCurrentState == STATE_PREPARED) {
102                 if (mMediaController.isShowing()) {
103                     // ensure the controller will get repositioned later
104                     mMediaController.hide();
105                 }
106                 mMediaController.show();
107             }
108         }
109 
110         @Override
111         public void surfaceCreated(SurfaceHolder holder)
112         {
113             mSurfaceHolder = holder;
114             mFullScreenMode = FULLSCREEN_SURFACECREATED;
115 
116             prepareForFullScreen();
117         }
118 
119         @Override
120         public void surfaceDestroyed(SurfaceHolder holder)
121         {
122             mPlayingWhenDestroyed = mPlayer.isPlaying();
123             pauseAndDispatch(mProxy);
124             // We need to set the display to null before switching into inline
125             // mode to avoid error.
126             mPlayer.setDisplay(null);
127             mSurfaceHolder = null;
128             if (mMediaController != null) {
129                 mMediaController.hide();
130             }
131         }
132     };
133 
134     MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
135         new MediaPlayer.OnVideoSizeChangedListener() {
136             @Override
137             public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
138                 mVideoWidth = mp.getVideoWidth();
139                 mVideoHeight = mp.getVideoHeight();
140                 if (mVideoWidth != 0 && mVideoHeight != 0) {
141                     mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
142                 }
143             }
144     };
145 
getSurfaceView()146     private SurfaceView getSurfaceView() {
147         return mVideoSurfaceView;
148     }
149 
HTML5VideoFullScreen(Context context, int videoLayerId, int position, boolean skipPrepare)150     HTML5VideoFullScreen(Context context, int videoLayerId, int position, boolean skipPrepare) {
151         mVideoSurfaceView = new VideoSurfaceView(context);
152         mFullScreenMode = FULLSCREEN_OFF;
153         mVideoWidth = 0;
154         mVideoHeight = 0;
155         init(videoLayerId, position, skipPrepare);
156     }
157 
setMediaController(MediaController m)158     private void setMediaController(MediaController m) {
159         mMediaController  = m;
160         attachMediaController();
161     }
162 
attachMediaController()163     private void attachMediaController() {
164         if (mPlayer != null && mMediaController != null) {
165             mMediaController.setMediaPlayer(this);
166             mMediaController.setAnchorView(mVideoSurfaceView);
167             //Will be enabled when prepared
168             mMediaController.setEnabled(false);
169         }
170     }
171 
172     @Override
decideDisplayMode()173     public void decideDisplayMode() {
174         mPlayer.setDisplay(mSurfaceHolder);
175     }
176 
prepareForFullScreen()177     private void prepareForFullScreen() {
178         MediaController mc = new FullScreenMediaController(mProxy.getContext(), mLayout);
179         mc.setSystemUiVisibility(mLayout.getSystemUiVisibility());
180         setMediaController(mc);
181         mPlayer.setScreenOnWhilePlaying(true);
182         mPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
183         prepareDataAndDisplayMode(mProxy);
184     }
185 
186 
toggleMediaControlsVisiblity()187     private void toggleMediaControlsVisiblity() {
188         if (mMediaController.isShowing()) {
189             mMediaController.hide();
190         } else {
191             mMediaController.show();
192         }
193     }
194 
195     @Override
onPrepared(MediaPlayer mp)196     public void onPrepared(MediaPlayer mp) {
197         super.onPrepared(mp);
198 
199         mVideoSurfaceView.setOnTouchListener(this);
200         // Get the capabilities of the player for this stream
201         Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
202                 MediaPlayer.BYPASS_METADATA_FILTER);
203         if (data != null) {
204             mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
205                     || data.getBoolean(Metadata.PAUSE_AVAILABLE);
206             mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
207                     || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
208             mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
209                     || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
210         } else {
211             mCanPause = mCanSeekBack = mCanSeekForward = true;
212         }
213 
214         if (getStartWhenPrepared()) {
215             mPlayer.start();
216             // Clear the flag.
217             setStartWhenPrepared(false);
218         }
219 
220         // mMediaController status depends on the Metadata result, so put it
221         // after reading the MetaData.
222         // And make sure mPlayer state is updated before showing the controller.
223         if (mMediaController != null) {
224             mMediaController.setEnabled(true);
225             mMediaController.show();
226         }
227 
228         if (mProgressView != null) {
229             mProgressView.setVisibility(View.GONE);
230         }
231 
232         mVideoWidth = mp.getVideoWidth();
233         mVideoHeight = mp.getVideoHeight();
234         // This will trigger the onMeasure to get the display size right.
235         mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
236 
237     }
238 
239     @Override
fullScreenExited()240     public boolean fullScreenExited() {
241         return (mLayout == null);
242     }
243 
244     private final WebChromeClient.CustomViewCallback mCallback =
245         new WebChromeClient.CustomViewCallback() {
246             @Override
247             public void onCustomViewHidden() {
248                 // It listens to SurfaceHolder.Callback.SurfaceDestroyed event
249                 // which happens when the video view is detached from its parent
250                 // view. This happens in the WebChromeClient before this method
251                 // is invoked.
252                 mLayout.removeView(getSurfaceView());
253 
254                 if (mProgressView != null) {
255                     mLayout.removeView(mProgressView);
256                     mProgressView = null;
257                 }
258                 mLayout = null;
259                 // Re enable plugin views.
260                 mProxy.getWebView().getViewManager().showAll();
261                 // Don't show the controller after exiting the full screen.
262                 mMediaController = null;
263                 // Continue the inline mode playing if necessary.
264                 mProxy.dispatchOnStopFullScreen(mPlayingWhenDestroyed);
265                 mProxy = null;
266             }
267         };
268 
269     @Override
enterFullScreenVideoState(int layerId, HTML5VideoViewProxy proxy, WebViewClassic webView)270     public void enterFullScreenVideoState(int layerId,
271             HTML5VideoViewProxy proxy, WebViewClassic webView) {
272         mFullScreenMode = FULLSCREEN_SURFACECREATING;
273         mCurrentBufferPercentage = 0;
274         mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
275         mProxy = proxy;
276 
277         mVideoSurfaceView.getHolder().addCallback(mSHCallback);
278         mVideoSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
279         mVideoSurfaceView.setFocusable(true);
280         mVideoSurfaceView.setFocusableInTouchMode(true);
281         mVideoSurfaceView.requestFocus();
282         mVideoSurfaceView.setOnKeyListener(mProxy);
283         // Create a FrameLayout that will contain the VideoView and the
284         // progress view (if any).
285         mLayout = new FrameLayout(mProxy.getContext());
286         FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
287                             ViewGroup.LayoutParams.WRAP_CONTENT,
288                             ViewGroup.LayoutParams.WRAP_CONTENT,
289                             Gravity.CENTER);
290 
291         mLayout.addView(getSurfaceView(), layoutParams);
292 
293         mLayout.setVisibility(View.VISIBLE);
294         WebChromeClient client = webView.getWebChromeClient();
295         if (client != null) {
296             client.onShowCustomView(mLayout, mCallback);
297             // Plugins like Flash will draw over the video so hide
298             // them while we're playing.
299             if (webView.getViewManager() != null)
300                 webView.getViewManager().hideAll();
301 
302             mProgressView = client.getVideoLoadingProgressView();
303             if (mProgressView != null) {
304                 mLayout.addView(mProgressView, layoutParams);
305                 mProgressView.setVisibility(View.VISIBLE);
306             }
307         }
308     }
309 
310     /**
311      * @return true when we are in full screen mode, even the surface not fully
312      * created.
313      */
314     @Override
isFullScreenMode()315     public boolean isFullScreenMode() {
316         return true;
317     }
318 
319     // MediaController FUNCTIONS:
320     @Override
canPause()321     public boolean canPause() {
322         return mCanPause;
323     }
324 
325     @Override
canSeekBackward()326     public boolean canSeekBackward() {
327         return mCanSeekBack;
328     }
329 
330     @Override
canSeekForward()331     public boolean canSeekForward() {
332         return mCanSeekForward;
333     }
334 
335     @Override
getBufferPercentage()336     public int getBufferPercentage() {
337         if (mPlayer != null) {
338             return mCurrentBufferPercentage;
339         }
340     return 0;
341     }
342 
343     @Override
getAudioSessionId()344     public int getAudioSessionId() {
345         if (mPlayer == null) {
346             return 0;
347         }
348         return mPlayer.getAudioSessionId();
349     }
350 
351     @Override
showControllerInFullScreen()352     public void showControllerInFullScreen() {
353         if (mMediaController != null) {
354             mMediaController.show(0);
355         }
356     }
357 
358     // Other listeners functions:
359     private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
360         new MediaPlayer.OnBufferingUpdateListener() {
361         @Override
362         public void onBufferingUpdate(MediaPlayer mp, int percent) {
363             mCurrentBufferPercentage = percent;
364         }
365     };
366 
367     @Override
onTouch(View v, MotionEvent event)368     public boolean onTouch(View v, MotionEvent event) {
369         if (mFullScreenMode >= FULLSCREEN_SURFACECREATED
370                 && mMediaController != null) {
371             toggleMediaControlsVisiblity();
372         }
373         return false;
374     }
375 
376     @Override
switchProgressView(boolean playerBuffering)377     protected void switchProgressView(boolean playerBuffering) {
378         if (mProgressView != null) {
379             if (playerBuffering) {
380                 mProgressView.setVisibility(View.VISIBLE);
381             } else {
382                 mProgressView.setVisibility(View.GONE);
383             }
384         }
385         return;
386     }
387 
388     static class FullScreenMediaController extends MediaController {
389 
390         View mVideoView;
391 
FullScreenMediaController(Context context, View video)392         public FullScreenMediaController(Context context, View video) {
393             super(context);
394             mVideoView = video;
395         }
396 
397         @Override
show()398         public void show() {
399             super.show();
400             if (mVideoView != null) {
401                 mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
402             }
403         }
404 
405         @Override
hide()406         public void hide() {
407             if (mVideoView != null) {
408                 mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
409                         | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
410             }
411             super.hide();
412         }
413 
414     }
415 
416 }
417