Merge "Support html5 video switching b/t full screen and inline mode" into honeycomb-mr1
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 @@
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;
- private static HTML5VideoView mInstance = new HTML5VideoView();
+ // 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;
- // Video control FUNCTIONS:
+ // We need to save such info.
+ protected String mUri;
+ protected Map<String, String> mHeaders;
+
+ // The timer for timeupate events.
+ // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
+ protected static Timer mTimer;
+
+ // 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,53 +109,50 @@
}
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.
- }
-
- public void setMediaController(MediaController m) {
- this.setMediaController(m);
- }
-
- public void setVideoURI(String uri, Map<String, String> headers) {
- // When switching players, surface texture will be reused.
- mPlayer.setTexture(getSurfaceTextureInstance());
-
- // 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();
+ 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");
+ }
+
+ return headers;
}
- // TODO [FULL SCREEN SUPPORT]
+ public void setVideoURI(String uri, HTML5VideoViewProxy proxy) {
+ // When switching players, surface texture will be reused.
+ mUri = uri;
+ mHeaders = generateHeaders(uri, proxy);
+
+ mTimer = new Timer();
+ }
// Listeners setup FUNCTIONS:
public void setOnCompletionListener(HTML5VideoViewProxy proxy) {
@@ -150,43 +168,47 @@
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 @@
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 @@
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.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.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 @@
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 @@
// 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 @@
}
}
+ 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)
- currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
+ boolean backFromFullScreenMode = false;
- if (currentVideoLayerId != videoLayerId
+ if (mHTML5VideoView != null) {
+ currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
+ if (mHTML5VideoView instanceof HTML5VideoFullScreen) {
+ backFromFullScreenMode = true;
+ }
+ }
+
+ 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/**
* 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();
}
//-------------------------------------------------------------------------