1 2 package android.webkit; 3 4 import android.content.Context; 5 import android.media.MediaPlayer; 6 import android.media.Metadata; 7 import android.util.Log; 8 import android.view.Gravity; 9 import android.view.MotionEvent; 10 import android.view.SurfaceHolder; 11 import android.view.SurfaceView; 12 import android.view.View; 13 import android.view.ViewGroup; 14 import android.webkit.HTML5VideoView; 15 import android.webkit.HTML5VideoViewProxy; 16 import android.widget.FrameLayout; 17 import android.widget.MediaController; 18 import android.widget.MediaController.MediaPlayerControl; 19 20 21 /** 22 * @hide This is only used by the browser 23 */ 24 public class HTML5VideoFullScreen extends HTML5VideoView 25 implements MediaPlayerControl, MediaPlayer.OnPreparedListener, 26 View.OnTouchListener { 27 28 // Add this sub-class to handle the resizing when rotating screen. 29 private class VideoSurfaceView extends SurfaceView { 30 VideoSurfaceView(Context context)31 public VideoSurfaceView(Context context) { 32 super(context); 33 } 34 35 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)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; 52 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 77 // 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 82 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 // The current Video View will be destroy when we play a new video. 109 pauseAndDispatch(mProxy); 110 mPlayer.release(); 111 mSurfaceHolder = null; 112 if (mMediaController != null) { 113 mMediaController.hide(); 114 } 115 } 116 }; 117 getSurfaceView()118 private SurfaceView getSurfaceView() { 119 return mVideoSurfaceView; 120 } 121 HTML5VideoFullScreen(Context context, int videoLayerId, int position, boolean autoStart)122 HTML5VideoFullScreen(Context context, int videoLayerId, int position, 123 boolean autoStart) { 124 mVideoSurfaceView = new VideoSurfaceView(context); 125 mFullScreenMode = FULLSCREEN_OFF; 126 mVideoWidth = 0; 127 mVideoHeight = 0; 128 init(videoLayerId, position, autoStart); 129 } 130 setMediaController(MediaController m)131 private void setMediaController(MediaController m) { 132 mMediaController = m; 133 attachMediaController(); 134 } 135 attachMediaController()136 private void attachMediaController() { 137 if (mPlayer != null && mMediaController != null) { 138 mMediaController.setMediaPlayer(this); 139 mMediaController.setAnchorView(mVideoSurfaceView); 140 //Will be enabled when prepared 141 mMediaController.setEnabled(false); 142 } 143 } 144 145 @Override decideDisplayMode()146 public void decideDisplayMode() { 147 mPlayer.setDisplay(mSurfaceHolder); 148 } 149 prepareForFullScreen()150 private void prepareForFullScreen() { 151 // So in full screen, we reset the MediaPlayer 152 mPlayer.reset(); 153 setMediaController(new MediaController(mProxy.getContext())); 154 mPlayer.setScreenOnWhilePlaying(true); 155 prepareDataAndDisplayMode(mProxy); 156 } 157 158 toggleMediaControlsVisiblity()159 private void toggleMediaControlsVisiblity() { 160 if (mMediaController.isShowing()) { 161 mMediaController.hide(); 162 } else { 163 mMediaController.show(); 164 } 165 } 166 167 @Override onPrepared(MediaPlayer mp)168 public void onPrepared(MediaPlayer mp) { 169 super.onPrepared(mp); 170 171 mVideoSurfaceView.setOnTouchListener(this); 172 // Get the capabilities of the player for this stream 173 Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL, 174 MediaPlayer.BYPASS_METADATA_FILTER); 175 if (data != null) { 176 mCanPause = !data.has(Metadata.PAUSE_AVAILABLE) 177 || data.getBoolean(Metadata.PAUSE_AVAILABLE); 178 mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE) 179 || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE); 180 mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE) 181 || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE); 182 } else { 183 mCanPause = mCanSeekBack = mCanSeekForward = true; 184 } 185 186 // mMediaController status depends on the Metadata result, so put it 187 // after reading the MetaData 188 if (mMediaController != null) { 189 mMediaController.setEnabled(true); 190 // If paused , should show the controller for ever! 191 if (getAutostart()) 192 mMediaController.show(); 193 else 194 mMediaController.show(0); 195 } 196 197 if (mProgressView != null) { 198 mProgressView.setVisibility(View.GONE); 199 } 200 201 mVideoWidth = mp.getVideoWidth(); 202 mVideoHeight = mp.getVideoHeight(); 203 // This will trigger the onMeasure to get the display size right. 204 mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight); 205 } 206 fullScreenExited()207 public boolean fullScreenExited() { 208 return (mLayout == null); 209 } 210 211 private final WebChromeClient.CustomViewCallback mCallback = 212 new WebChromeClient.CustomViewCallback() { 213 public void onCustomViewHidden() { 214 // It listens to SurfaceHolder.Callback.SurfaceDestroyed event 215 // which happens when the video view is detached from its parent 216 // view. This happens in the WebChromeClient before this method 217 // is invoked. 218 mProxy.dispatchOnStopFullScreen(); 219 mLayout.removeView(getSurfaceView()); 220 221 if (mProgressView != null) { 222 mLayout.removeView(mProgressView); 223 mProgressView = null; 224 } 225 mLayout = null; 226 // Re enable plugin views. 227 mProxy.getWebView().getViewManager().showAll(); 228 229 mProxy = null; 230 231 // Don't show the controller after exiting the full screen. 232 mMediaController = null; 233 mCurrentState = STATE_RELEASED; 234 } 235 }; 236 237 @Override enterFullScreenVideoState(int layerId, HTML5VideoViewProxy proxy, WebView webView)238 public void enterFullScreenVideoState(int layerId, 239 HTML5VideoViewProxy proxy, WebView webView) { 240 mFullScreenMode = FULLSCREEN_SURFACECREATING; 241 mCurrentBufferPercentage = 0; 242 mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); 243 mProxy = proxy; 244 245 mVideoSurfaceView.getHolder().addCallback(mSHCallback); 246 mVideoSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 247 mVideoSurfaceView.setFocusable(true); 248 mVideoSurfaceView.setFocusableInTouchMode(true); 249 mVideoSurfaceView.requestFocus(); 250 251 // Create a FrameLayout that will contain the VideoView and the 252 // progress view (if any). 253 mLayout = new FrameLayout(mProxy.getContext()); 254 FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( 255 ViewGroup.LayoutParams.WRAP_CONTENT, 256 ViewGroup.LayoutParams.WRAP_CONTENT, 257 Gravity.CENTER); 258 259 mLayout.addView(getSurfaceView(), layoutParams); 260 261 mLayout.setVisibility(View.VISIBLE); 262 263 WebChromeClient client = webView.getWebChromeClient(); 264 if (client != null) { 265 client.onShowCustomView(mLayout, mCallback); 266 // Plugins like Flash will draw over the video so hide 267 // them while we're playing. 268 if (webView.getViewManager() != null) 269 webView.getViewManager().hideAll(); 270 271 mProgressView = client.getVideoLoadingProgressView(); 272 if (mProgressView != null) { 273 mLayout.addView(mProgressView, layoutParams); 274 mProgressView.setVisibility(View.VISIBLE); 275 } 276 } 277 } 278 279 /** 280 * @return true when we are in full screen mode, even the surface not fully 281 * created. 282 */ isFullScreenMode()283 public boolean isFullScreenMode() { 284 return true; 285 } 286 287 // MediaController FUNCTIONS: 288 @Override canPause()289 public boolean canPause() { 290 return mCanPause; 291 } 292 293 @Override canSeekBackward()294 public boolean canSeekBackward() { 295 return mCanSeekBack; 296 } 297 298 @Override canSeekForward()299 public boolean canSeekForward() { 300 return mCanSeekForward; 301 } 302 303 @Override getBufferPercentage()304 public int getBufferPercentage() { 305 if (mPlayer != null) { 306 return mCurrentBufferPercentage; 307 } 308 return 0; 309 } 310 311 // Other listeners functions: 312 private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = 313 new MediaPlayer.OnBufferingUpdateListener() { 314 public void onBufferingUpdate(MediaPlayer mp, int percent) { 315 mCurrentBufferPercentage = percent; 316 } 317 }; 318 319 @Override onTouch(View v, MotionEvent event)320 public boolean onTouch(View v, MotionEvent event) { 321 if (mFullScreenMode >= FULLSCREEN_SURFACECREATED 322 && mMediaController != null) { 323 toggleMediaControlsVisiblity(); 324 } 325 return false; 326 } 327 328 @Override switchProgressView(boolean playerBuffering)329 protected void switchProgressView(boolean playerBuffering) { 330 if (mProgressView != null) { 331 if (playerBuffering) { 332 mProgressView.setVisibility(View.VISIBLE); 333 } else { 334 mProgressView.setVisibility(View.GONE); 335 } 336 } 337 return; 338 } 339 } 340