diff options
author | Teng-Hui Zhu <ztenghui@google.com> | 2011-03-16 23:05:41 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2011-03-16 23:05:41 -0700 |
commit | a13640b9efe1706ad0b6b608ccfca800166d7c01 (patch) | |
tree | 5fb9d3237cf8c5cc7aed6784df67bf03ec06adaf | |
parent | 2983bb6a363010403668d39fdc42f5712bff5eb3 (diff) | |
parent | 7a0949c60df2085fc38eea71d78f9c17eb26cd65 (diff) | |
download | frameworks_base-a13640b9efe1706ad0b6b608ccfca800166d7c01.zip frameworks_base-a13640b9efe1706ad0b6b608ccfca800166d7c01.tar.gz frameworks_base-a13640b9efe1706ad0b6b608ccfca800166d7c01.tar.bz2 |
am 7a0949c6: am 7af7e00b: Merge "Support html5 video switching b/t full screen and inline mode" into honeycomb-mr1
* commit '7a0949c60df2085fc38eea71d78f9c17eb26cd65':
Support html5 video switching b/t full screen and inline mode
-rw-r--r-- | core/java/android/webkit/HTML5VideoFullScreen.java | 286 | ||||
-rw-r--r-- | core/java/android/webkit/HTML5VideoInline.java | 99 | ||||
-rw-r--r-- | core/java/android/webkit/HTML5VideoView.java | 226 | ||||
-rw-r--r-- | core/java/android/webkit/HTML5VideoViewProxy.java | 128 | ||||
-rw-r--r-- | core/java/android/webkit/WebView.java | 6 | ||||
-rw-r--r-- | core/java/android/webkit/WebViewCore.java | 8 |
6 files changed, 589 insertions, 164 deletions
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java new file mode 100644 index 0000000..993d694 --- /dev/null +++ b/core/java/android/webkit/HTML5VideoFullScreen.java @@ -0,0 +1,286 @@ + +package android.webkit; + +import android.content.Context; +import android.media.MediaPlayer; +import android.media.Metadata; +import android.util.Log; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.HTML5VideoView; +import android.webkit.HTML5VideoViewProxy; +import android.widget.FrameLayout; +import android.widget.MediaController; +import android.widget.MediaController.MediaPlayerControl; + + +/** + * @hide This is only used by the browser + */ +public class HTML5VideoFullScreen extends HTML5VideoView + implements MediaPlayerControl, MediaPlayer.OnPreparedListener, + View.OnTouchListener { + + private SurfaceView mSurfaceView; + + // We need the full screen state to decide which surface to render to and + // when to create the MediaPlayer accordingly. + static final int FULLSCREEN_OFF = 0; + static final int FULLSCREEN_SURFACECREATING = 1; + static final int FULLSCREEN_SURFACECREATED = 2; + + private int mFullScreenMode; + // The Media Controller only used for full screen mode + private MediaController mMediaController; + + // SurfaceHolder for full screen + private SurfaceHolder mSurfaceHolder = null; + + // Data only for MediaController + private boolean mCanSeekBack; + private boolean mCanSeekForward; + private boolean mCanPause; + private int mCurrentBufferPercentage; + + // The progress view. + private static View mProgressView; + // The container for the progress view and video view + private static FrameLayout mLayout; + + SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() + { + public void surfaceChanged(SurfaceHolder holder, int format, + int w, int h) + { + if (mPlayer != null && mMediaController != null + && mCurrentState == STATE_PREPARED) { + if (mMediaController.isShowing()) { + // ensure the controller will get repositioned later + mMediaController.hide(); + } + mMediaController.show(); + } + } + + public void surfaceCreated(SurfaceHolder holder) + { + mSurfaceHolder = holder; + mFullScreenMode = FULLSCREEN_SURFACECREATED; + + prepareForFullScreen(); + } + + public void surfaceDestroyed(SurfaceHolder holder) + { + // after we return from this we can't use the surface any more + mSurfaceHolder = null; + // The current Video View will be destroy when we play a new video. + } + }; + + public SurfaceView getSurfaceView() { + return mSurfaceView; + } + + HTML5VideoFullScreen(Context context, int videoLayerId, int position, + boolean autoStart) { + mSurfaceView = new SurfaceView(context); + mFullScreenMode = FULLSCREEN_OFF; + init(videoLayerId, position, autoStart); + } + + private void setMediaController(MediaController m) { + mMediaController = m; + attachMediaController(); + } + + private void attachMediaController() { + if (mPlayer != null && mMediaController != null) { + mMediaController.setMediaPlayer(this); + mMediaController.setAnchorView(mSurfaceView); + //Will be enabled when prepared + mMediaController.setEnabled(false); + } + } + + @Override + public void decideDisplayMode() { + mPlayer.setDisplay(mSurfaceHolder); + } + + @Override + public void prepareForFullScreen() { + // So in full screen, we reset the MediaPlayer + mPlayer.reset(); + setMediaController(new MediaController(mProxy.getContext())); + + prepareDataAndDisplayMode(mProxy); + } + + + private void toggleMediaControlsVisiblity() { + if (mMediaController.isShowing()) { + mMediaController.hide(); + } else { + mMediaController.show(); + } + } + + @Override + public void onPrepared(MediaPlayer mp) { + super.onPrepared(mp); + + mSurfaceView.setOnTouchListener(this); + // Get the capabilities of the player for this stream + Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL, + MediaPlayer.BYPASS_METADATA_FILTER); + if (data != null) { + mCanPause = !data.has(Metadata.PAUSE_AVAILABLE) + || data.getBoolean(Metadata.PAUSE_AVAILABLE); + mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE) + || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE); + mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE) + || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE); + } else { + mCanPause = mCanSeekBack = mCanSeekForward = true; + } + + // mMediaController status depends on the Metadata result, so put it + // after reading the MetaData + if (mMediaController != null) { + mMediaController.setEnabled(true); + // If paused , should show the controller for ever! + if (getAutostart()) + mMediaController.show(); + else + mMediaController.show(0); + } + + if (mProgressView != null) { + mProgressView.setVisibility(View.GONE); + mLayout.removeView(mProgressView); + mProgressView = null; + } + } + + + private final WebChromeClient.CustomViewCallback mCallback = + new WebChromeClient.CustomViewCallback() { + public void onCustomViewHidden() { + // It listens to SurfaceHolder.Callback.SurfaceDestroyed event + // which happens when the video view is detached from its parent + // view. This happens in the WebChromeClient before this method + // is invoked. + mTimer.cancel(); + mTimer = null; + + pauseAndDispatch(mProxy); + + mLayout.removeView(getSurfaceView()); + + if (mProgressView != null) { + mLayout.removeView(mProgressView); + mProgressView = null; + } + mLayout = null; + // Re enable plugin views. + mProxy.getWebView().getViewManager().showAll(); + + mProxy = null; + } + }; + + @Override + public void enterFullScreenVideoState(int layerId, + HTML5VideoViewProxy proxy, WebView webView) { + mFullScreenMode = FULLSCREEN_SURFACECREATING; + mCurrentBufferPercentage = 0; + mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); + mProxy = proxy; + + mSurfaceView.getHolder().addCallback(mSHCallback); + mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + mSurfaceView.setFocusable(true); + mSurfaceView.setFocusableInTouchMode(true); + mSurfaceView.requestFocus(); + + // Create a FrameLayout that will contain the VideoView and the + // progress view (if any). + mLayout = new FrameLayout(mProxy.getContext()); + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + Gravity.CENTER); + + mLayout.addView(getSurfaceView(), layoutParams); + + mLayout.setVisibility(View.VISIBLE); + + WebChromeClient client = webView.getWebChromeClient(); + client.onShowCustomView(mLayout, mCallback); + // Plugins like Flash will draw over the video so hide + // them while we're playing. + mProxy.getWebView().getViewManager().hideAll(); + + mProgressView = client.getVideoLoadingProgressView(); + if (mProgressView != null) { + mLayout.addView(mProgressView, layoutParams); + mProgressView.setVisibility(View.VISIBLE); + } + + } + + /** + * @return true when we are in full screen mode, even the surface not fully + * created. + */ + public boolean isFullScreenMode() { + return true; + } + + // MediaController FUNCTIONS: + @Override + public boolean canPause() { + return mCanPause; + } + + @Override + public boolean canSeekBackward() { + return mCanSeekBack; + } + + @Override + public boolean canSeekForward() { + return mCanSeekForward; + } + + @Override + public int getBufferPercentage() { + if (mPlayer != null) { + return mCurrentBufferPercentage; + } + return 0; + } + + // Other listeners functions: + private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = + new MediaPlayer.OnBufferingUpdateListener() { + public void onBufferingUpdate(MediaPlayer mp, int percent) { + mCurrentBufferPercentage = percent; + } + }; + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mFullScreenMode >= FULLSCREEN_SURFACECREATED + && mMediaController != null) { + toggleMediaControlsVisiblity(); + } + return false; + } + +} diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java new file mode 100644 index 0000000..f1d9189 --- /dev/null +++ b/core/java/android/webkit/HTML5VideoInline.java @@ -0,0 +1,99 @@ + +package android.webkit; + +import android.graphics.SurfaceTexture; +import android.media.MediaPlayer; +import android.webkit.HTML5VideoView; +import android.webkit.HTML5VideoViewProxy; +import android.opengl.GLES20; + +/** + * @hide This is only used by the browser + */ +public class HTML5VideoInline extends HTML5VideoView{ + + // Due to the fact that SurfaceTexture consume a lot of memory, we make it + // as static. m_textureNames is the texture bound with this SurfaceTexture. + private static SurfaceTexture mSurfaceTexture = null; + private static int[] mTextureNames; + + // Only when the video is prepared, we render using SurfaceTexture. + // This in fact is used to avoid showing the obsolete content when + // switching videos. + private static boolean mReadyToUseSurfTex = false; + + // Video control FUNCTIONS: + @Override + public void start() { + super.start(); + if (mCurrentState == STATE_PREPARED) { + mReadyToUseSurfTex = true; + } + } + + HTML5VideoInline(int videoLayerId, int position, + boolean autoStart) { + init(videoLayerId, position, autoStart); + mReadyToUseSurfTex = false; + } + + @Override + public void decideDisplayMode() { + mPlayer.setTexture(getSurfaceTextureInstance()); + } + + // Normally called immediately after setVideoURI. But for full screen, + // this should be after surface holder created + @Override + public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { + super.prepareDataAndDisplayMode(proxy); + setFrameAvailableListener(proxy); + } + + // Pause the play and update the play/pause button + @Override + public void pauseAndDispatch(HTML5VideoViewProxy proxy) { + super.pauseAndDispatch(proxy); + mReadyToUseSurfTex = false; + } + + // Inline Video specific FUNCTIONS: + + @Override + public SurfaceTexture getSurfaceTexture() { + return mSurfaceTexture; + } + + @Override + public void deleteSurfaceTexture() { + mSurfaceTexture = null; + return; + } + + // SurfaceTexture is a singleton here , too + private SurfaceTexture getSurfaceTextureInstance() { + // Create the surface texture. + if (mSurfaceTexture == null) + { + mTextureNames = new int[1]; + GLES20.glGenTextures(1, mTextureNames, 0); + mSurfaceTexture = new SurfaceTexture(mTextureNames[0]); + } + return mSurfaceTexture; + } + + @Override + public int getTextureName() { + return mTextureNames[0]; + } + + @Override + public boolean getReadyToUseSurfTex() { + return mReadyToUseSurfTex; + } + + private void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) { + mSurfaceTexture.setOnFrameAvailableListener(l); + } + +} diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java index 2312160..ade7106 100644 --- a/core/java/android/webkit/HTML5VideoView.java +++ b/core/java/android/webkit/HTML5VideoView.java @@ -4,72 +4,93 @@ package android.webkit; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.util.Log; +import android.view.SurfaceView; import android.webkit.HTML5VideoViewProxy; -import android.widget.MediaController; -import android.opengl.GLES20; import java.io.IOException; +import java.util.HashMap; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; /** * @hide This is only used by the browser */ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ - // Due to the fact that SurfaceTexture consume a lot of memory, we make it - // as static. m_textureNames is the texture bound with this SurfaceTexture. - private static SurfaceTexture mSurfaceTexture = null; - private static int[] mTextureNames; - // Only when the video is prepared, we render using SurfaceTexture. - // This in fact is used to avoid showing the obsolete content when - // switching videos. - private static boolean mReadyToUseSurfTex = false; + protected static final String LOGTAG = "HTML5VideoView"; + + protected static final String COOKIE = "Cookie"; + protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log"; // For handling the seekTo before prepared, we need to know whether or not // the video is prepared. Therefore, we differentiate the state between // prepared and not prepared. // When the video is not prepared, we will have to save the seekTo time, // and use it when prepared to play. - private static final int STATE_NOTPREPARED = 0; - private static final int STATE_PREPARED = 1; + protected static final int STATE_NOTPREPARED = 0; + protected static final int STATE_PREPARED = 1; - // We only need state for handling seekTo - private int mCurrentState; + protected int mCurrentState; - // Basically for calling back the OnPrepared in the proxy - private HTML5VideoViewProxy mProxy; + protected HTML5VideoViewProxy mProxy; // Save the seek time when not prepared. This can happen when switching // video besides initial load. - private int mSaveSeekTime; + protected int mSaveSeekTime; // This is used to find the VideoLayer on the native side. - private int mVideoLayerId; + protected int mVideoLayerId; // Every video will have one MediaPlayer. Given the fact we only have one // SurfaceTexture, there is only one MediaPlayer in action. Every time we // switch videos, a new instance of MediaPlayer will be created in reset(). - private MediaPlayer mPlayer; + // Switching between inline and full screen will also create a new instance. + protected MediaPlayer mPlayer; + + // This will be set up every time we create the Video View object. + // Set to true only when switching into full screen while playing + protected boolean mAutostart; + + // We need to save such info. + protected String mUri; + protected Map<String, String> mHeaders; - private static HTML5VideoView mInstance = new HTML5VideoView(); + // The timer for timeupate events. + // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate + protected static Timer mTimer; - // Video control FUNCTIONS: + // The spec says the timer should fire every 250 ms or less. + private static final int TIMEUPDATE_PERIOD = 250; // ms + + // common Video control FUNCTIONS: public void start() { if (mCurrentState == STATE_PREPARED) { mPlayer.start(); - mReadyToUseSurfTex = true; } } public void pause() { - mPlayer.pause(); + if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) { + mPlayer.pause(); + } + if (mTimer != null) { + mTimer.purge(); + } } public int getDuration() { - return mPlayer.getDuration(); + if (mCurrentState == STATE_PREPARED) { + return mPlayer.getDuration(); + } else { + return -1; + } } public int getCurrentPosition() { - return mPlayer.getCurrentPosition(); + if (mCurrentState == STATE_PREPARED) { + return mPlayer.getCurrentPosition(); + } + return 0; } public void seekTo(int pos) { @@ -88,54 +109,51 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ } public void stopPlayback() { - mPlayer.stop(); + if (mCurrentState == STATE_PREPARED) { + mPlayer.stop(); + } } - private void reset(int videoLayerId) { + public boolean getAutostart() { + return mAutostart; + } + + // Every time we start a new Video, we create a VideoView and a MediaPlayer + public void init(int videoLayerId, int position, boolean autoStart) { mPlayer = new MediaPlayer(); mCurrentState = STATE_NOTPREPARED; mProxy = null; mVideoLayerId = videoLayerId; - mReadyToUseSurfTex = false; + mSaveSeekTime = position; + mAutostart = autoStart; } - public static HTML5VideoView getInstance(int videoLayerId) { - // Every time we switch between the videos, a new MediaPlayer will be - // created. Make sure we call the m_player.release() when it is done. - mInstance.reset(videoLayerId); - return mInstance; + protected HTML5VideoView() { } - private HTML5VideoView() { - // This is a singleton across WebViews (i.e. Tabs). - // HTML5VideoViewProxy will reset the internal state every time a new - // video start. - } + protected static Map<String, String> generateHeaders(String url, + HTML5VideoViewProxy proxy) { + boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled(); + String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate); + Map<String, String> headers = new HashMap<String, String>(); + if (cookieValue != null) { + headers.put(COOKIE, cookieValue); + } + if (isPrivate) { + headers.put(HIDE_URL_LOGS, "true"); + } - public void setMediaController(MediaController m) { - this.setMediaController(m); + return headers; } - public void setVideoURI(String uri, Map<String, String> headers) { + public void setVideoURI(String uri, HTML5VideoViewProxy proxy) { // When switching players, surface texture will be reused. - mPlayer.setTexture(getSurfaceTextureInstance()); + mUri = uri; + mHeaders = generateHeaders(uri, proxy); - // When there is exception, we could just bail out silently. - // No Video will be played though. Write the stack for debug - try { - mPlayer.setDataSource(uri, headers); - mPlayer.prepareAsync(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalStateException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } + mTimer = new Timer(); } - // TODO [FULL SCREEN SUPPORT] - // Listeners setup FUNCTIONS: public void setOnCompletionListener(HTML5VideoViewProxy proxy) { mPlayer.setOnCompletionListener(proxy); @@ -150,43 +168,47 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ mPlayer.setOnPreparedListener(this); } - // Inline Video specific FUNCTIONS: + // Normally called immediately after setVideoURI. But for full screen, + // this should be after surface holder created + public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { + // SurfaceTexture will be created lazily here for inline mode + decideDisplayMode(); - public SurfaceTexture getSurfaceTexture() { - return mSurfaceTexture; - } + setOnCompletionListener(proxy); + setOnPreparedListener(proxy); + setOnErrorListener(proxy); - public void deleteSurfaceTexture() { - mSurfaceTexture = null; - return; - } - - // SurfaceTexture is a singleton here , too - private SurfaceTexture getSurfaceTextureInstance() { - // Create the surface texture. - if (mSurfaceTexture == null) - { - mTextureNames = new int[1]; - GLES20.glGenTextures(1, mTextureNames, 0); - mSurfaceTexture = new SurfaceTexture(mTextureNames[0]); + // When there is exception, we could just bail out silently. + // No Video will be played though. Write the stack for debug + try { + mPlayer.setDataSource(mUri, mHeaders); + mPlayer.prepareAsync(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); } - return mSurfaceTexture; } - public int getTextureName() { - return mTextureNames[0]; - } + // Common code public int getVideoLayerId() { return mVideoLayerId; } - public boolean getReadyToUseSurfTex() { - return mReadyToUseSurfTex; - } + private static final class TimeupdateTask extends TimerTask { + private HTML5VideoViewProxy mProxy; - public void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) { - mSurfaceTexture.setOnFrameAvailableListener(l); + public TimeupdateTask(HTML5VideoViewProxy proxy) { + mProxy = proxy; + } + + @Override + public void run() { + mProxy.onTimeupdate(); + } } @Override @@ -195,6 +217,9 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ seekTo(mSaveSeekTime); if (mProxy != null) mProxy.onPrepared(mp); + + mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD); + } // Pause the play and update the play/pause button @@ -205,7 +230,42 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener{ proxy.dispatchOnPaused(); } } - mReadyToUseSurfTex = false; + } + + // Below are functions that are different implementation on inline and full- + // screen mode. Some are specific to one type, but currently are called + // directly from the proxy. + public void enterFullScreenVideoState(int layerId, + HTML5VideoViewProxy proxy, WebView webView) { + } + + public boolean isFullScreenMode() { + return false; + } + + public SurfaceView getSurfaceView() { + return null; + } + + public void decideDisplayMode() { + } + + public void prepareForFullScreen() { + } + + public boolean getReadyToUseSurfTex() { + return false; + } + + public SurfaceTexture getSurfaceTexture() { + return null; + } + + public void deleteSurfaceTexture() { + } + + public int getTextureName() { + return 0; } } diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java index b614d8f..d3fcfa5 100644 --- a/core/java/android/webkit/HTML5VideoViewProxy.java +++ b/core/java/android/webkit/HTML5VideoViewProxy.java @@ -21,29 +21,16 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; -import android.media.MediaPlayer.OnPreparedListener; -import android.media.MediaPlayer.OnCompletionListener; -import android.media.MediaPlayer.OnErrorListener; import android.net.http.EventHandler; import android.net.http.Headers; import android.net.http.RequestHandle; import android.net.http.RequestQueue; import android.net.http.SslCertificate; import android.net.http.SslError; -import android.net.Uri; -import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; -import android.view.MotionEvent; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsoluteLayout; -import android.widget.FrameLayout; -import android.widget.MediaController; -import android.widget.VideoView; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -51,8 +38,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; /** * <p>Proxy for HTML5 video views. @@ -78,9 +63,6 @@ class HTML5VideoViewProxy extends Handler private static final int POSTER_FETCHED = 202; private static final int PAUSED = 203; - private static final String COOKIE = "Cookie"; - private static final String HIDE_URL_LOGS = "x-hide-urls-from-log"; - // Timer thread -> UI thread private static final int TIMEUPDATE = 300; @@ -104,38 +86,19 @@ class HTML5VideoViewProxy extends Handler // The VideoView instance. This is a singleton for now, at least until // http://b/issue?id=1973663 is fixed. private static HTML5VideoView mHTML5VideoView; - // The progress view. - private static View mProgressView; - // The container for the progress view and video view - private static FrameLayout mLayout; - // The timer for timeupate events. - // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate - private static Timer mTimer; - private static final class TimeupdateTask extends TimerTask { - private HTML5VideoViewProxy mProxy; - - public TimeupdateTask(HTML5VideoViewProxy proxy) { - mProxy = proxy; - } - public void run() { - mProxy.onTimeupdate(); - } - } - // The spec says the timer should fire every 250 ms or less. - private static final int TIMEUPDATE_PERIOD = 250; // ms private static boolean isVideoSelfEnded = false; // By using the baseLayer and the current video Layer ID, we can // identify the exact layer on the UI thread to use the SurfaceTexture. private static int mBaseLayer = 0; - // TODO: [FULL SCREEN SUPPORT] - // Every time webView setBaseLayer, this will be called. // When we found the Video layer, then we set the Surface Texture to it. // Otherwise, we may want to delete the Surface Texture to save memory. public static void setBaseLayer(int layer) { - if (mHTML5VideoView != null) { + // Don't do this for full screen mode. + if (mHTML5VideoView != null + && !mHTML5VideoView.isFullScreenMode()) { mBaseLayer = layer; SurfaceTexture surfTexture = mHTML5VideoView.getSurfaceTexture(); int textureName = mHTML5VideoView.getTextureName(); @@ -165,16 +128,47 @@ class HTML5VideoViewProxy extends Handler } } + public static void enterFullScreenVideo(int layerId, String url, + HTML5VideoViewProxy proxy, WebView webView) { + // Save the inline video info and inherit it in the full screen + int savePosition = 0; + boolean savedIsPlaying = false; + if (mHTML5VideoView != null) { + // If we are playing the same video, then it is better to + // save the current position. + if (layerId == mHTML5VideoView.getVideoLayerId()) { + savePosition = mHTML5VideoView.getCurrentPosition(); + savedIsPlaying = mHTML5VideoView.isPlaying(); + } + mHTML5VideoView.pauseAndDispatch(mCurrentProxy); + mHTML5VideoView.release(); + } + mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(), + layerId, savePosition, savedIsPlaying); + mCurrentProxy = proxy; + + mHTML5VideoView.setVideoURI(url, mCurrentProxy); + + mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView); + } + // This is on the UI thread. // When native tell Java to play, we need to check whether or not it is // still the same video by using videoLayerId and treat it differently. public static void play(String url, int time, HTML5VideoViewProxy proxy, WebChromeClient client, int videoLayerId) { int currentVideoLayerId = -1; - if (mHTML5VideoView != null) + boolean backFromFullScreenMode = false; + + if (mHTML5VideoView != null) { currentVideoLayerId = mHTML5VideoView.getVideoLayerId(); + if (mHTML5VideoView instanceof HTML5VideoFullScreen) { + backFromFullScreenMode = true; + } + } - if (currentVideoLayerId != videoLayerId + if (backFromFullScreenMode + || currentVideoLayerId != videoLayerId || mHTML5VideoView.getSurfaceTexture() == null) { // Here, we handle the case when switching to a new video, // either inside a WebView or across WebViews @@ -186,35 +180,11 @@ class HTML5VideoViewProxy extends Handler // release the media player to avoid finalize error mHTML5VideoView.release(); } - // HTML5VideoView is singleton, however, the internal state will - // be reset since we are switching from one video to another. - // Then we need to set up all the source/listener etc... - mHTML5VideoView = HTML5VideoView.getInstance(videoLayerId); - mCurrentProxy = proxy; + mHTML5VideoView = new HTML5VideoInline(videoLayerId, time, false); - // TODO: [FULL SCREEN SUPPORT] - - boolean isPrivate = mCurrentProxy.getWebView().isPrivateBrowsingEnabled(); - String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate); - Map<String, String> headers = new HashMap<String, String>(); - if (cookieValue != null) { - headers.put(COOKIE, cookieValue); - } - if (isPrivate) { - headers.put(HIDE_URL_LOGS, "true"); - } - - mHTML5VideoView.setVideoURI(url, headers); - mHTML5VideoView.setOnCompletionListener(proxy); - mHTML5VideoView.setOnPreparedListener(proxy); - mHTML5VideoView.setOnErrorListener(proxy); - mHTML5VideoView.setFrameAvailableListener(proxy); - - mHTML5VideoView.seekTo(time); - - mTimer = new Timer(); - + mHTML5VideoView.setVideoURI(url, mCurrentProxy); + mHTML5VideoView.prepareDataAndDisplayMode(proxy); } else if (mCurrentProxy == proxy) { // Here, we handle the case when we keep playing with one video if (!mHTML5VideoView.isPlaying()) { @@ -222,7 +192,8 @@ class HTML5VideoViewProxy extends Handler mHTML5VideoView.start(); } } else if (mCurrentProxy != null) { - // Some other video is already playing. Notify the caller that its playback ended. + // Some other video is already playing. Notify the caller that + // its playback ended. proxy.dispatchOnEnded(); } } @@ -249,14 +220,14 @@ class HTML5VideoViewProxy extends Handler public static void pause(HTML5VideoViewProxy proxy) { if (mCurrentProxy == proxy && mHTML5VideoView != null) { mHTML5VideoView.pause(); - mTimer.purge(); } } public static void onPrepared() { - mHTML5VideoView.start(); - mTimer.schedule(new TimeupdateTask(mCurrentProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD); - // TODO: [FULL SCREEN SUPPORT] + if (!mHTML5VideoView.isFullScreenMode() || + mHTML5VideoView.isFullScreenMode() && + mHTML5VideoView.getAutostart() ) + mHTML5VideoView.start(); } public static void end() { @@ -349,8 +320,6 @@ class HTML5VideoViewProxy extends Handler VideoPlayer.isVideoSelfEnded = true; VideoPlayer.end(); break; - // TODO: [FULL SCREEN SUPPORT] - // For full screen case, end may need hide the view. case ERROR: { WebChromeClient client = mWebView.getWebChromeClient(); if (client != null) { @@ -665,7 +634,7 @@ class HTML5VideoViewProxy extends Handler mPosterDownloader.start(); } - // These two function are called from UI thread only by WebView. + // These three function are called from UI thread only by WebView. public void setBaseLayer(int layer) { VideoPlayer.setBaseLayer(layer); } @@ -673,6 +642,11 @@ class HTML5VideoViewProxy extends Handler public void pauseAndDispatch() { VideoPlayer.pauseAndDispatch(); } + + public void enterFullScreenVideo(int layerId, String url) { + VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView); + } + /** * The factory for HTML5VideoViewProxy instances. * @param webViewCore is the WebViewCore that is requesting the proxy. diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index ce166bc..e20dc81 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -7860,7 +7860,11 @@ public class WebView extends AbsoluteLayout case ENTER_FULLSCREEN_VIDEO: int layerId = msg.arg1; - Log.v(LOGTAG, "Display the video layer " + layerId + " fullscreen"); + + String url = (String) msg.obj; + if (mHTML5VideoViewProxy != null) { + mHTML5VideoViewProxy.enterFullScreenVideo(layerId, url); + } break; case SHOW_FULLSCREEN: { diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index bed77ef..3b989dc 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -483,10 +483,12 @@ final class WebViewCore { /** * Notify the webview that we want to display the video layer fullscreen. */ - protected void enterFullscreenForVideoLayer(int layerId) { + protected void enterFullscreenForVideoLayer(int layerId, String url) { if (mWebView == null) return; - Message.obtain(mWebView.mPrivateHandler, - WebView.ENTER_FULLSCREEN_VIDEO, layerId, 0).sendToTarget(); + Message message = Message.obtain(mWebView.mPrivateHandler, + WebView.ENTER_FULLSCREEN_VIDEO, layerId, 0); + message.obj = url; + message.sendToTarget(); } //------------------------------------------------------------------------- |