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