Home | History | Annotate | Download | only in webkit
      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 
     43         public VideoSurfaceView(Context context) {
     44             super(context);
     45         }
     46 
     47         @Override
     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 
    146     private SurfaceView getSurfaceView() {
    147         return mVideoSurfaceView;
    148     }
    149 
    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 
    158     private void setMediaController(MediaController m) {
    159         mMediaController  = m;
    160         attachMediaController();
    161     }
    162 
    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
    173     public void decideDisplayMode() {
    174         mPlayer.setDisplay(mSurfaceHolder);
    175     }
    176 
    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 
    187     private void toggleMediaControlsVisiblity() {
    188         if (mMediaController.isShowing()) {
    189             mMediaController.hide();
    190         } else {
    191             mMediaController.show();
    192         }
    193     }
    194 
    195     @Override
    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
    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
    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
    315     public boolean isFullScreenMode() {
    316         return true;
    317     }
    318 
    319     // MediaController FUNCTIONS:
    320     @Override
    321     public boolean canPause() {
    322         return mCanPause;
    323     }
    324 
    325     @Override
    326     public boolean canSeekBackward() {
    327         return mCanSeekBack;
    328     }
    329 
    330     @Override
    331     public boolean canSeekForward() {
    332         return mCanSeekForward;
    333     }
    334 
    335     @Override
    336     public int getBufferPercentage() {
    337         if (mPlayer != null) {
    338             return mCurrentBufferPercentage;
    339         }
    340     return 0;
    341     }
    342 
    343     @Override
    344     public void showControllerInFullScreen() {
    345         if (mMediaController != null) {
    346             mMediaController.show(0);
    347         }
    348     }
    349 
    350     // Other listeners functions:
    351     private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
    352         new MediaPlayer.OnBufferingUpdateListener() {
    353         @Override
    354         public void onBufferingUpdate(MediaPlayer mp, int percent) {
    355             mCurrentBufferPercentage = percent;
    356         }
    357     };
    358 
    359     @Override
    360     public boolean onTouch(View v, MotionEvent event) {
    361         if (mFullScreenMode >= FULLSCREEN_SURFACECREATED
    362                 && mMediaController != null) {
    363             toggleMediaControlsVisiblity();
    364         }
    365         return false;
    366     }
    367 
    368     @Override
    369     protected void switchProgressView(boolean playerBuffering) {
    370         if (mProgressView != null) {
    371             if (playerBuffering) {
    372                 mProgressView.setVisibility(View.VISIBLE);
    373             } else {
    374                 mProgressView.setVisibility(View.GONE);
    375             }
    376         }
    377         return;
    378     }
    379 
    380     static class FullScreenMediaController extends MediaController {
    381 
    382         View mVideoView;
    383 
    384         public FullScreenMediaController(Context context, View video) {
    385             super(context);
    386             mVideoView = video;
    387         }
    388 
    389         @Override
    390         public void show() {
    391             super.show();
    392             if (mVideoView != null) {
    393                 mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
    394             }
    395         }
    396 
    397         @Override
    398         public void hide() {
    399             if (mVideoView != null) {
    400                 mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
    401                         | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    402             }
    403             super.hide();
    404         }
    405 
    406     }
    407 
    408 }
    409