Merge "Support html5 video switching b/t full screen and inline mode" into honeycomb-mr1

This commit is contained in:
Teng-Hui Zhu
2011-03-16 23:01:02 -07:00
committed by Android (Google) Code Review
6 changed files with 593 additions and 168 deletions

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
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 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.
}
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 @@ 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;
}
}

View File

@@ -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)
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 @@ 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.

View File

@@ -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: {

View File

@@ -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();
}
//-------------------------------------------------------------------------