summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/app/MoviePlayer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/app/MoviePlayer.java')
-rwxr-xr-x[-rw-r--r--]src/com/android/gallery3d/app/MoviePlayer.java1251
1 files changed, 1206 insertions, 45 deletions
diff --git a/src/com/android/gallery3d/app/MoviePlayer.java b/src/com/android/gallery3d/app/MoviePlayer.java
index f6bd36725..2ef7c2e85 100644..100755
--- a/src/com/android/gallery3d/app/MoviePlayer.java
+++ b/src/com/android/gallery3d/app/MoviePlayer.java
@@ -23,10 +23,15 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.DialogInterface.OnShowListener;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.graphics.Color;
import android.media.AudioManager;
import android.media.MediaPlayer;
+import android.media.Metadata;
import android.media.audiofx.AudioEffect;
import android.media.audiofx.Virtualizer;
import android.net.Uri;
@@ -35,26 +40,47 @@ import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.VideoView;
+import android.widget.Toast;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.BlobCache;
import com.android.gallery3d.util.CacheManager;
import com.android.gallery3d.util.GalleryUtils;
+import org.codeaurora.gallery3d.ext.IContrllerOverlayExt;
+import org.codeaurora.gallery3d.ext.IMoviePlayer;
+import org.codeaurora.gallery3d.ext.IMovieItem;
+import org.codeaurora.gallery3d.ext.MovieUtils;
+import org.codeaurora.gallery3d.video.BookmarkEnhance;
+import org.codeaurora.gallery3d.video.ExtensionHelper;
+import org.codeaurora.gallery3d.video.IControllerRewindAndForward;
+import org.codeaurora.gallery3d.video.IControllerRewindAndForward.IRewindAndForwardListener;
+import org.codeaurora.gallery3d.video.ScreenModeManager;
+import org.codeaurora.gallery3d.video.ScreenModeManager.ScreenModeListener;
+import org.codeaurora.gallery3d.video.CodeauroraVideoView;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
+import java.util.HashMap;
+import java.util.Map;
public class MoviePlayer implements
MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener,
- ControllerOverlay.Listener {
+ ControllerOverlay.Listener,
+ MediaPlayer.OnInfoListener,
+ MediaPlayer.OnPreparedListener,
+ MediaPlayer.OnSeekCompleteListener,
+ MediaPlayer.OnVideoSizeChangedListener,
+ MediaPlayer.OnBufferingUpdateListener {
@SuppressWarnings("unused")
private static final String TAG = "MoviePlayer";
+ private static final boolean LOG = false;
private static final String KEY_VIDEO_POSITION = "video-position";
private static final String KEY_RESUMEABLE_TIME = "resumeable-timeout";
@@ -68,18 +94,32 @@ public class MoviePlayer implements
private static final String CMDNAME = "command";
private static final String CMDPAUSE = "pause";
+ private static final String KEY_VIDEO_CAN_SEEK = "video_can_seek";
+ private static final String KEY_VIDEO_CAN_PAUSE = "video_can_pause";
+ private static final String KEY_VIDEO_LAST_DURATION = "video_last_duration";
+ private static final String KEY_VIDEO_LAST_DISCONNECT_TIME = "last_disconnect_time";
+ private static final String KEY_VIDEO_STREAMING_TYPE = "video_streaming_type";
+ private static final String KEY_VIDEO_STATE = "video_state";
+
private static final String VIRTUALIZE_EXTRA = "virtualize";
private static final long BLACK_TIMEOUT = 500;
+ private static final int DELAY_REMOVE_MS = 10000;
+ public static final int SERVER_TIMEOUT = 8801;
// If we resume the acitivty with in RESUMEABLE_TIMEOUT, we will keep playing.
// Otherwise, we pause the player.
private static final long RESUMEABLE_TIMEOUT = 3 * 60 * 1000; // 3 mins
+ public static final int STREAMING_LOCAL = 0;
+ public static final int STREAMING_HTTP = 1;
+ public static final int STREAMING_RTSP = 2;
+ public static final int STREAMING_SDP = 3;
+ private int mStreamingType = STREAMING_LOCAL;
+
private Context mContext;
- private final VideoView mVideoView;
+ private final CodeauroraVideoView mVideoView;
private final View mRootView;
private final Bookmarker mBookmarker;
- private final Uri mUri;
private final Handler mHandler = new Handler();
private final AudioBecomingNoisyReceiver mAudioBecomingNoisyReceiver;
private final MovieControllerOverlay mController;
@@ -87,6 +127,11 @@ public class MoviePlayer implements
private long mResumeableTime = Long.MAX_VALUE;
private int mVideoPosition = 0;
private boolean mHasPaused = false;
+ private boolean mVideoHasPaused = false;
+ private boolean mCanResumed = false;
+ private boolean mFirstBePlayed = false;
+ private boolean mKeyguardLocked = false;
+ private boolean mIsOnlyAudio = false;
private int mLastSystemUiVis = 0;
// If the time bar is being dragged.
@@ -97,6 +142,36 @@ public class MoviePlayer implements
private Virtualizer mVirtualizer;
+ private MovieActivity mActivityContext;//for dialog and toast context
+ private MoviePlayerExtension mPlayerExt = new MoviePlayerExtension();
+ private RetryExtension mRetryExt = new RetryExtension();
+ private ServerTimeoutExtension mServerTimeoutExt = new ServerTimeoutExtension();
+ private ScreenModeExt mScreenModeExt = new ScreenModeExt();
+ private IContrllerOverlayExt mOverlayExt;
+ private IControllerRewindAndForward mControllerRewindAndForwardExt;
+ private IRewindAndForwardListener mRewindAndForwardListener = new ControllerRewindAndForwardExt();
+ private boolean mCanReplay;
+ private boolean mVideoCanSeek = false;
+ private boolean mVideoCanPause = false;
+ private boolean mWaitMetaData;
+ private boolean mIsShowResumingDialog;
+ private TState mTState = TState.PLAYING;
+ private IMovieItem mMovieItem;
+ private int mVideoLastDuration;//for duration displayed in init state
+
+ private enum TState {
+ PLAYING,
+ PAUSED,
+ STOPED,
+ COMPELTED,
+ RETRY_ERROR
+ }
+
+ interface Restorable {
+ void onRestoreInstanceState(Bundle icicle);
+ void onSaveInstanceState(Bundle outState);
+ }
+
private final Runnable mPlayingChecker = new Runnable() {
@Override
public void run() {
@@ -116,22 +191,57 @@ public class MoviePlayer implements
}
};
+ private Runnable mDelayVideoRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (LOG) {
+ Log.v(TAG, "mDelayVideoRunnable.run()");
+ }
+ mVideoView.setVisibility(View.VISIBLE);
+ }
+ };
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ mKeyguardLocked = true;
+ } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
+ if ((mCanResumed) && (!mVideoHasPaused)) {
+ playVideo();
+ }
+ mKeyguardLocked = false;
+ mCanResumed = false;
+ } else if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
+ if (LOG) {
+ Log.v(TAG, "Intent.ACTION_SHUTDOWN received");
+ }
+ mActivityContext.finish();
+ }
+ }
+ };
+
public MoviePlayer(View rootView, final MovieActivity movieActivity,
- Uri videoUri, Bundle savedInstance, boolean canReplay) {
+ IMovieItem info, Bundle savedInstance, boolean canReplay) {
mContext = movieActivity.getApplicationContext();
mRootView = rootView;
- mVideoView = (VideoView) rootView.findViewById(R.id.surface_view);
+ mVideoView = (CodeauroraVideoView) rootView.findViewById(R.id.surface_view);
mBookmarker = new Bookmarker(movieActivity);
- mUri = videoUri;
- mController = new MovieControllerOverlay(mContext);
+ mController = new MovieControllerOverlay(movieActivity);
((ViewGroup)rootView).addView(mController.getView());
mController.setListener(this);
mController.setCanReplay(canReplay);
+ init(movieActivity, info, canReplay);
+
mVideoView.setOnErrorListener(this);
mVideoView.setOnCompletionListener(this);
- mVideoView.setVideoURI(mUri);
+
+ if (mVirtualizer != null) {
+ mVirtualizer.release();
+ mVirtualizer = null;
+ }
Intent ai = movieActivity.getIntent();
boolean virtualize = ai.getBooleanExtra(VIRTUALIZE_EXTRA, false);
@@ -186,20 +296,39 @@ public class MoviePlayer implements
i.putExtra(CMDNAME, CMDPAUSE);
movieActivity.sendBroadcast(i);
+ // Listen for broadcasts related to user-presence
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_USER_PRESENT);
+ filter.addAction(Intent.ACTION_SHUTDOWN);
+ mContext.registerReceiver(mReceiver, filter);
+
if (savedInstance != null) { // this is a resumed activity
mVideoPosition = savedInstance.getInt(KEY_VIDEO_POSITION, 0);
mResumeableTime = savedInstance.getLong(KEY_RESUMEABLE_TIME, Long.MAX_VALUE);
- mVideoView.start();
- mVideoView.suspend();
+ onRestoreInstanceState(savedInstance);
mHasPaused = true;
+ doStartVideo(true, mVideoPosition, mVideoLastDuration,false);
+ mVideoView.start();
+ mActivityContext.initEffects(mVideoView.getAudioSessionId());
} else {
- final Integer bookmark = mBookmarker.getBookmark(mUri);
- if (bookmark != null) {
- showResumeDialog(movieActivity, bookmark);
+ mTState = TState.PLAYING;
+ mFirstBePlayed = true;
+ String mUri = mMovieItem.getUri().toString();
+ boolean isLive = mUri.startsWith("rtsp://") && (mUri.contains(".sdp")
+ || mUri.contains(".smil"));
+ if (!isLive) {
+ final BookmarkerInfo bookmark = mBookmarker.getBookmark(mMovieItem.getUri());
+ if (bookmark != null) {
+ showResumeDialog(movieActivity, bookmark);
+ } else {
+ doStartVideo(false, 0, 0);
+ }
} else {
- startVideo();
+ doStartVideo(false, 0, 0);
}
}
+ mScreenModeExt.setScreenMode();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@@ -213,11 +342,17 @@ public class MoviePlayer implements
new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
+ boolean finish = (mActivityContext == null ? true : mActivityContext.isFinishing());
int diff = mLastSystemUiVis ^ visibility;
mLastSystemUiVis = visibility;
if ((diff & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
&& (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
mController.show();
+ mRootView.setBackgroundColor(Color.BLACK);
+ }
+
+ if (LOG) {
+ Log.v(TAG, "onSystemUiVisibilityChange(" + visibility + ") finishing()=" + finish);
}
}
});
@@ -242,14 +377,15 @@ public class MoviePlayer implements
public void onSaveInstanceState(Bundle outState) {
outState.putInt(KEY_VIDEO_POSITION, mVideoPosition);
outState.putLong(KEY_RESUMEABLE_TIME, mResumeableTime);
+ onSaveInstanceStateMore(outState);
}
- private void showResumeDialog(Context context, final int bookmark) {
+ private void showResumeDialog(Context context, final BookmarkerInfo bookmark) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.resume_playing_title);
builder.setMessage(String.format(
context.getString(R.string.resume_playing_message),
- GalleryUtils.formatDuration(context, bookmark / 1000)));
+ GalleryUtils.formatDuration(context, bookmark.mBookmark / 1000)));
builder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
@@ -260,42 +396,157 @@ public class MoviePlayer implements
R.string.resume_playing_resume, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- mVideoView.seekTo(bookmark);
- startVideo();
+ // here try to seek for bookmark
+ mVideoCanSeek = true;
+ doStartVideo(true, bookmark.mBookmark, bookmark.mDuration);
}
});
builder.setNegativeButton(
R.string.resume_playing_restart, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- startVideo();
+ doStartVideo(true, 0, bookmark.mDuration);
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.setOnShowListener(new OnShowListener() {
+ @Override
+ public void onShow(DialogInterface arg0) {
+ mIsShowResumingDialog = true;
}
});
- builder.show();
+ dialog.setOnDismissListener(new OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface arg0) {
+ mIsShowResumingDialog = false;
+ }
+ });
+ dialog.show();
+ }
+
+ public void setDefaultScreenMode() {
+ addBackground();
+ mController.setDefaultScreenMode();
+ removeBackground();
+ }
+
+ public boolean onPause() {
+ if (LOG) {
+ Log.v(TAG, "onPause() isLiveStreaming()=" + isLiveStreaming());
+ }
+ boolean pause = false;
+ if (isLiveStreaming()) {
+ pause = false;
+ } else {
+ doOnPause();
+ pause = true;
+ }
+ if (LOG) {
+ Log.v(TAG, "onPause() , return " + pause);
+ }
+ return pause;
}
- public void onPause() {
+ // we should stop video anyway after this function called.
+ public void onStop() {
+ if (LOG) {
+ Log.v(TAG, "onStop() mHasPaused=" + mHasPaused);
+ }
+ if (!mHasPaused) {
+ doOnPause();
+ }
+ }
+
+ private void doOnPause() {
+ long start = System.currentTimeMillis();
+ addBackground();
mHasPaused = true;
mHandler.removeCallbacksAndMessages(null);
- mVideoPosition = mVideoView.getCurrentPosition();
- mBookmarker.setBookmark(mUri, mVideoPosition, mVideoView.getDuration());
+ int position = mVideoView.getCurrentPosition();
+ mVideoPosition = position >= 0 ? position : mVideoPosition;
+ Log.v(TAG, "mVideoPosition is " + mVideoPosition);
+ int duration = mVideoView.getDuration();
+ mVideoLastDuration = duration > 0 ? duration : mVideoLastDuration;
+ mBookmarker.setBookmark(mMovieItem.getUri(), mVideoPosition, mVideoLastDuration);
+ long end1 = System.currentTimeMillis();
mVideoView.suspend();
mResumeableTime = System.currentTimeMillis() + RESUMEABLE_TIMEOUT;
+ mVideoView.setResumed(false);// avoid start after surface created
+ long end2 = System.currentTimeMillis();
+ // TODO comments by sunlei
+ mOverlayExt.clearBuffering();
+ mServerTimeoutExt.recordDisconnectTime();
+ if (LOG) {
+ Log.v(TAG, "doOnPause() save video info consume:" + (end1 - start));
+ Log.v(TAG, "doOnPause() suspend video consume:" + (end2 - end1));
+ Log.v(TAG, "doOnPause() mVideoPosition=" + mVideoPosition + ", mResumeableTime="
+ + mResumeableTime
+ + ", mVideoLastDuration=" + mVideoLastDuration + ", mIsShowResumingDialog="
+ + mIsShowResumingDialog);
+ }
}
public void onResume() {
+ mDragging = false;// clear drag info
if (mHasPaused) {
- mVideoView.seekTo(mVideoPosition);
- mVideoView.resume();
+ //M: same as launch case to delay transparent.
+ mVideoView.removeCallbacks(mDelayVideoRunnable);
+ mVideoView.postDelayed(mDelayVideoRunnable, BLACK_TIMEOUT);
- // If we have slept for too long, pause the play
- if (System.currentTimeMillis() > mResumeableTime) {
- pauseVideo();
+ if (mServerTimeoutExt.handleOnResume() || mIsShowResumingDialog) {
+ mHasPaused = false;
+ return;
+ }
+ switch (mTState) {
+ case RETRY_ERROR:
+ mRetryExt.showRetry();
+ break;
+ case STOPED:
+ mPlayerExt.stopVideo();
+ break;
+ case COMPELTED:
+ mController.showEnded();
+ if (mVideoCanSeek || mVideoView.canSeekForward()) {
+ mVideoView.seekTo(mVideoPosition);
+ }
+ mVideoView.setDuration(mVideoLastDuration);
+ break;
+ case PAUSED:
+ // if video was paused, so it should be started.
+ doStartVideo(true, mVideoPosition, mVideoLastDuration, false);
+ pauseVideo();
+ break;
+ default:
+ mVideoView.seekTo(mVideoPosition);
+ mVideoView.resume();
+ pauseVideoMoreThanThreeMinutes();
+ break;
}
+ mHasPaused = false;
+ }
+
+ if (System.currentTimeMillis() > mResumeableTime) {
+ mHandler.removeCallbacks(mPlayingChecker);
+ mHandler.postDelayed(mPlayingChecker, 250);
}
+
mHandler.post(mProgressChecker);
}
+ private void pauseVideoMoreThanThreeMinutes() {
+ // If we have slept for too long, pause the play
+ // If is live streaming, do not pause it too
+ long now = System.currentTimeMillis();
+ if (now > mResumeableTime && !isLiveStreaming()) {
+ if (mVideoCanPause || mVideoView.canPause()) {
+ pauseVideo();
+ }
+ }
+ if (LOG) {
+ Log.v(TAG, "pauseVideoMoreThanThreeMinutes() now=" + now);
+ }
+ }
+
public void onDestroy() {
if (mVirtualizer != null) {
mVirtualizer.release();
@@ -303,27 +554,36 @@ public class MoviePlayer implements
}
mVideoView.stopPlayback();
mAudioBecomingNoisyReceiver.unregister();
+ mContext.unregisterReceiver(mReceiver);
+ mServerTimeoutExt.clearTimeoutDialog();
}
// This updates the time bar display (if necessary). It is called every
// second by mProgressChecker and also from places where the time bar needs
// to be updated immediately.
private int setProgress() {
- if (mDragging || !mShowing) {
+ if (mDragging || (!mShowing && !mIsOnlyAudio)) {
return 0;
}
int position = mVideoView.getCurrentPosition();
int duration = mVideoView.getDuration();
mController.setTimes(position, duration, 0, 0);
+ if (mControllerRewindAndForwardExt != null
+ && mControllerRewindAndForwardExt.getPlayPauseEanbled()) {
+ updateRewindAndForwardUI();
+ }
return position;
}
- private void startVideo() {
+ private void doStartVideo(final boolean enableFasten, final int position, final int duration,
+ boolean start) {
// For streams that we expect to be slow to start up, show a
// progress spinner until playback starts.
- String scheme = mUri.getScheme();
- if ("http".equalsIgnoreCase(scheme) || "rtsp".equalsIgnoreCase(scheme)) {
+ String scheme = mMovieItem.getUri().getScheme();
+ if ("http".equalsIgnoreCase(scheme) || "rtsp".equalsIgnoreCase(scheme)
+ || "https".equalsIgnoreCase(scheme)) {
mController.showLoading();
+ mOverlayExt.setPlayingInfo(isLiveStreaming());
mHandler.removeCallbacks(mPlayingChecker);
mHandler.postDelayed(mPlayingChecker, 250);
} else {
@@ -331,35 +591,94 @@ public class MoviePlayer implements
mController.hide();
}
- mVideoView.start();
+ if (onIsRTSP()) {
+ Map<String, String> header = new HashMap<String, String>(1);
+ header.put("CODEAURORA-ASYNC-RTSP-PAUSE-PLAY", "true");
+ mVideoView.setVideoURI(mMovieItem.getUri(), header, !mWaitMetaData);
+ } else {
+ mVideoView.setVideoURI(mMovieItem.getUri(), null, !mWaitMetaData);
+ }
+ if (start) {
+ mVideoView.start();
+ mVideoView.setVisibility(View.VISIBLE);
+ mActivityContext.initEffects(mVideoView.getAudioSessionId());
+ }
+ //we may start video from stopVideo,
+ //this case, we should reset canReplay flag according canReplay and loop
+ boolean loop = mPlayerExt.getLoop();
+ boolean canReplay = loop ? loop : mCanReplay;
+ mController.setCanReplay(canReplay);
+ if (position > 0 && (mVideoCanSeek || mVideoView.canSeek())) {
+ mVideoView.seekTo(position);
+ }
+ if (enableFasten) {
+ mVideoView.setDuration(duration);
+ }
setProgress();
}
+ private void doStartVideo(boolean enableFasten, int position, int duration) {
+ doStartVideo(enableFasten, position, duration, true);
+ }
+
private void playVideo() {
+ if (LOG) {
+ Log.v(TAG, "playVideo()");
+ }
+ mTState = TState.PLAYING;
mVideoView.start();
mController.showPlaying();
setProgress();
}
private void pauseVideo() {
+ if (LOG) {
+ Log.v(TAG, "pauseVideo()");
+ }
+ mTState = TState.PAUSED;
mVideoView.pause();
+ setProgress();
mController.showPaused();
}
// Below are notifications from VideoView
@Override
public boolean onError(MediaPlayer player, int arg1, int arg2) {
+ mMovieItem.setError();
+ if (mServerTimeoutExt.onError(player, arg1, arg2)) {
+ return true;
+ }
+ if (mRetryExt.onError(player, arg1, arg2)) {
+ return true;
+ }
mHandler.removeCallbacksAndMessages(null);
// VideoView will show an error dialog if we return false, so no need
// to show more message.
+ //M:resume controller
+ mController.setViewEnabled(true);
mController.showErrorMessage("");
return false;
}
@Override
public void onCompletion(MediaPlayer mp) {
- mController.showEnded();
- onCompletion();
+ if (LOG) {
+ Log.v(TAG, "onCompletion() mCanReplay=" + mCanReplay);
+ }
+ if (mMovieItem.getError()) {
+ Log.w(TAG, "error occured, exit the video player!");
+ mActivityContext.finish();
+ return;
+ }
+ if (mPlayerExt.getLoop()) {
+ onReplay();
+ } else { //original logic
+ mTState = TState.COMPELTED;
+ if (mCanReplay) {
+ mController.showEnded();
+ }
+ onCompletion();
+ }
}
public void onCompletion() {
@@ -369,9 +688,23 @@ public class MoviePlayer implements
@Override
public void onPlayPause() {
if (mVideoView.isPlaying()) {
- pauseVideo();
+ if (mVideoView.canPause()) {
+ pauseVideo();
+ //set view disabled(play/pause asynchronous processing)
+ mController.setViewEnabled(true);
+ if (mControllerRewindAndForwardExt != null) {
+ mControllerRewindAndForwardExt.showControllerButtonsView(mPlayerExt
+ .canStop(), false, false);
+ }
+ }
} else {
playVideo();
+ //set view disabled(play/pause asynchronous processing)
+ mController.setViewEnabled(true);
+ if (mControllerRewindAndForwardExt != null) {
+ mControllerRewindAndForwardExt.showControllerButtonsView(mPlayerExt
+ .canStop(), false, false);
+ }
}
}
@@ -382,18 +715,27 @@ public class MoviePlayer implements
@Override
public void onSeekMove(int time) {
- mVideoView.seekTo(time);
+ if (mVideoView.canSeek()) {
+ mVideoView.seekTo(time);
+ }
}
@Override
public void onSeekEnd(int time, int start, int end) {
mDragging = false;
- mVideoView.seekTo(time);
+ if (mVideoView.canSeek()) {
+ mVideoView.seekTo(time);
+ }
+ }
+
+ @Override
+ public void onSeekComplete(MediaPlayer mp) {
setProgress();
}
@Override
public void onShown() {
+ addBackground();
mShowing = true;
setProgress();
showSystemUi(true);
@@ -403,11 +745,82 @@ public class MoviePlayer implements
public void onHidden() {
mShowing = false;
showSystemUi(false);
+ removeBackground();
+ }
+
+ @Override
+ public boolean onInfo(MediaPlayer mp, int what, int extra) {
+ if (LOG) {
+ Log.v(TAG, "onInfo() what:" + what + " extra:" + extra);
+ }
+ if (what == MediaPlayer.MEDIA_INFO_NOT_SEEKABLE && mOverlayExt != null) {
+ boolean flag = (extra == 1);
+ mOverlayExt.setCanPause(flag);
+ mOverlayExt.setCanScrubbing(flag);
+ } else if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE && mServerTimeoutExt != null) {
+ Log.e(TAG, "setServerTimeout " + extra);
+ mServerTimeoutExt.setTimeout(extra * 1000);
+ }
+ if (mRetryExt.onInfo(mp, what, extra)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onBufferingUpdate(MediaPlayer mp, int percent) {
+ boolean fullBuffer = isFullBuffer();
+ mOverlayExt.showBuffering(fullBuffer, percent);
+ }
+
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ if (LOG) {
+ Log.v(TAG, "onPrepared(" + mp + ")");
+ }
+ if (!isLocalFile()) {
+ mOverlayExt.setPlayingInfo(isLiveStreaming());
+ }
+ getVideoInfo(mp);
+ boolean canPause = mVideoView.canPause();
+ boolean canSeek = mVideoView.canSeek();
+ mOverlayExt.setCanPause(canPause);
+ mOverlayExt.setCanScrubbing(canSeek);
+ mController.setPlayPauseReplayResume();
+ if (!canPause && !mVideoView.isTargetPlaying()) {
+ mVideoView.start();
+ }
+ updateRewindAndForwardUI();
+ if (LOG) {
+ Log.v(TAG, "onPrepared() canPause=" + canPause + ", canSeek=" + canSeek);
+ }
+ }
+
+ public boolean onIsRTSP() {
+ if (MovieUtils.isRtspStreaming(mMovieItem.getUri(), mMovieItem
+ .getMimeType())) {
+ if (LOG) {
+ Log.v(TAG, "onIsRTSP() is RTSP");
+ }
+ return true;
+ }
+ if (LOG) {
+ Log.v(TAG, "onIsRTSP() is not RTSP");
+ }
+ return false;
}
@Override
public void onReplay() {
- startVideo();
+ if (LOG) {
+ Log.v(TAG, "onReplay()");
+ }
+ mTState = TState.PLAYING;
+ mFirstBePlayed = true;
+ if (mRetryExt.handleOnReplay()) {
+ return;
+ }
+ doStartVideo(false, 0, 0);
}
// Below are key events passed from MovieActivity.
@@ -421,14 +834,14 @@ public class MoviePlayer implements
switch (keyCode) {
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- if (mVideoView.isPlaying()) {
+ if (mVideoView.isPlaying() && mVideoView.canPause()) {
pauseVideo();
} else {
playVideo();
}
return true;
case KEYCODE_MEDIA_PAUSE:
- if (mVideoView.isPlaying()) {
+ if (mVideoView.isPlaying() && mVideoView.canPause()) {
pauseVideo();
}
return true;
@@ -450,6 +863,21 @@ public class MoviePlayer implements
return isMediaKey(keyCode);
}
+ public void updateRewindAndForwardUI() {
+ if (LOG) {
+ Log.v(TAG, "updateRewindAndForwardUI");
+ Log.v(TAG, "updateRewindAndForwardUI== getCurrentPosition = " + mVideoView.getCurrentPosition());
+ Log.v(TAG, "updateRewindAndForwardUI==getDuration =" + mVideoView.getDuration());
+ }
+ if (mControllerRewindAndForwardExt != null) {
+ mControllerRewindAndForwardExt.showControllerButtonsView(mPlayerExt
+ .canStop(), mVideoView.canSeekBackward()
+ && mControllerRewindAndForwardExt.getTimeBarEanbled(), mVideoView
+ .canSeekForward()
+ && mControllerRewindAndForwardExt.getTimeBarEanbled());
+ }
+ }
+
private static boolean isMediaKey(int keyCode) {
return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
|| keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS
@@ -459,6 +887,30 @@ public class MoviePlayer implements
|| keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE;
}
+ private void init(MovieActivity movieActivity, IMovieItem info, boolean canReplay) {
+ mActivityContext = movieActivity;
+ mCanReplay = canReplay;
+ mMovieItem = info;
+ judgeStreamingType(info.getUri(), info.getMimeType());
+
+ mVideoView.setOnInfoListener(this);
+ mVideoView.setOnPreparedListener(this);
+ mVideoView.setOnBufferingUpdateListener(this);
+ mVideoView.setOnVideoSizeChangedListener(this);
+ mRootView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ mController.show();
+ return true;
+ }
+ });
+ mOverlayExt = mController.getOverlayExt();
+ mControllerRewindAndForwardExt = mController.getControllerRewindAndForwardExt();
+ if (mControllerRewindAndForwardExt != null) {
+ mControllerRewindAndForwardExt.setIListener(mRewindAndForwardListener);
+ }
+ }
+
// We want to pause when the headset is unplugged.
private class AudioBecomingNoisyReceiver extends BroadcastReceiver {
@@ -473,7 +925,691 @@ public class MoviePlayer implements
@Override
public void onReceive(Context context, Intent intent) {
- if (mVideoView.isPlaying()) pauseVideo();
+ if (mVideoView.isPlaying() && mVideoView.canPause()) pauseVideo();
+ }
+ }
+
+ public int getAudioSessionId() {
+ return mVideoView.getAudioSessionId();
+ }
+
+ public void setOnPreparedListener(MediaPlayer.OnPreparedListener listener) {
+ mVideoView.setOnPreparedListener(listener);
+ }
+
+ public boolean isFullBuffer() {
+ if (mStreamingType == STREAMING_RTSP || mStreamingType == STREAMING_SDP) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isLocalFile() {
+ if (mStreamingType == STREAMING_LOCAL) {
+ return true;
+ }
+ return false;
+ }
+
+ private void getVideoInfo(MediaPlayer mp) {
+ if (!MovieUtils.isLocalFile(mMovieItem.getUri(), mMovieItem.getMimeType())) {
+ Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
+ MediaPlayer.BYPASS_METADATA_FILTER);
+ if (data != null) {
+ // TODO comments by sunlei
+ mServerTimeoutExt.setVideoInfo(data);
+ } else {
+ Log.w(TAG, "Metadata is null!");
+ }
+ }
+ }
+
+ private void judgeStreamingType(Uri uri, String mimeType) {
+ if (LOG) {
+ Log.v(TAG, "judgeStreamingType(" + uri + ")");
+ }
+ if (uri == null) {
+ return;
+ }
+ String scheme = uri.getScheme();
+ mWaitMetaData = true;
+ if (MovieUtils.isSdpStreaming(uri, mimeType)) {
+ mStreamingType = STREAMING_SDP;
+ mWaitMetaData = false;
+ } else if (MovieUtils.isRtspStreaming(uri, mimeType)) {
+ mStreamingType = STREAMING_RTSP;
+ mWaitMetaData = false;
+ } else if (MovieUtils.isHttpStreaming(uri, mimeType)) {
+ mStreamingType = STREAMING_HTTP;
+ mWaitMetaData = false;
+ } else {
+ mStreamingType = STREAMING_LOCAL;
+ mWaitMetaData = false;
+ }
+ if (LOG) {
+ Log.v(TAG, "mStreamingType=" + mStreamingType
+ + " mCanGetMetaData=" + mWaitMetaData);
+ }
+ }
+
+ public boolean isLiveStreaming() {
+ boolean isLive = false;
+ if (mStreamingType == STREAMING_SDP) {
+ isLive = true;
+ }
+ if (LOG) {
+ Log.v(TAG, "isLiveStreaming() return " + isLive);
+ }
+ return isLive;
+ }
+
+ public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+ // reget the audio type
+ if (width != 0 && height != 0) {
+ mIsOnlyAudio = false;
+ } else {
+ mIsOnlyAudio = true;
+ }
+ mOverlayExt.setBottomPanel(mIsOnlyAudio, true);
+ if (LOG) {
+ Log.v(TAG, "onVideoSizeChanged(" + width + ", " + height + ") mIsOnlyAudio="
+ + mIsOnlyAudio);
+ }
+ }
+
+ public IMoviePlayer getMoviePlayerExt() {
+ return mPlayerExt;
+ }
+
+ public SurfaceView getVideoSurface() {
+ return mVideoView;
+ }
+
+ // Wait for any animation, ten seconds should be enough
+ private final Runnable mRemoveBackground = new Runnable() {
+ @Override
+ public void run() {
+ if (LOG) {
+ Log.v(TAG, "mRemoveBackground.run()");
+ }
+ mRootView.setBackground(null);
+ }
+ };
+
+ private void removeBackground() {
+ if (LOG) {
+ Log.v(TAG, "removeBackground()");
+ }
+ mHandler.removeCallbacks(mRemoveBackground);
+ mHandler.postDelayed(mRemoveBackground, DELAY_REMOVE_MS);
+ }
+
+ // add background for removing ghost image.
+ private void addBackground() {
+ if (LOG) {
+ Log.v(TAG, "addBackground()");
+ }
+ mHandler.removeCallbacks(mRemoveBackground);
+ mRootView.setBackgroundColor(Color.BLACK);
+ }
+
+ private void clearVideoInfo() {
+ mVideoPosition = 0;
+ mVideoLastDuration = 0;
+ mIsOnlyAudio = false;
+
+ if (mServerTimeoutExt != null) {
+ mServerTimeoutExt.clearServerInfo();
+ }
+ }
+
+ private void onSaveInstanceStateMore(Bundle outState) {
+ outState.putInt(KEY_VIDEO_LAST_DURATION, mVideoLastDuration);
+ outState.putBoolean(KEY_VIDEO_CAN_SEEK, mVideoView.canSeekForward());
+ outState.putBoolean(KEY_VIDEO_CAN_PAUSE, mVideoView.canPause());
+ outState.putInt(KEY_VIDEO_STREAMING_TYPE, mStreamingType);
+ outState.putString(KEY_VIDEO_STATE, String.valueOf(mTState));
+ mServerTimeoutExt.onSaveInstanceState(outState);
+ mScreenModeExt.onSaveInstanceState(outState);
+ mRetryExt.onSaveInstanceState(outState);
+ mPlayerExt.onSaveInstanceState(outState);
+ }
+
+ private void onRestoreInstanceState(Bundle icicle) {
+ mVideoLastDuration = icicle.getInt(KEY_VIDEO_LAST_DURATION);
+ mVideoCanSeek = icicle.getBoolean(KEY_VIDEO_CAN_SEEK);
+ mVideoCanPause = icicle.getBoolean(KEY_VIDEO_CAN_PAUSE);
+ mStreamingType = icicle.getInt(KEY_VIDEO_STREAMING_TYPE);
+ mTState = TState.valueOf(icicle.getString(KEY_VIDEO_STATE));
+ mServerTimeoutExt.onRestoreInstanceState(icicle);
+ mScreenModeExt.onRestoreInstanceState(icicle);
+ mRetryExt.onRestoreInstanceState(icicle);
+ mPlayerExt.onRestoreInstanceState(icicle);
+ }
+
+ private class MoviePlayerExtension implements IMoviePlayer, Restorable {
+
+ private static final String KEY_VIDEO_IS_LOOP = "video_is_loop";
+
+ private BookmarkEnhance mBookmark;//for bookmark
+ private boolean mIsLoop;
+ private boolean mLastPlaying;
+ private boolean mLastCanPaused;
+
+ @Override
+ public boolean getLoop() {
+ return mIsLoop;
+ }
+
+ @Override
+ public void setLoop(boolean loop) {
+ if (isLocalFile()) {
+ mIsLoop = loop;
+ mController.setCanReplay(loop);
+ }
+ }
+
+ @Override
+ public void startNextVideo(IMovieItem item) {
+ IMovieItem next = item;
+ if (next != null && next != mMovieItem) {
+ int position = mVideoView.getCurrentPosition();
+ int duration = mVideoView.getDuration();
+ mBookmarker.setBookmark(mMovieItem.getUri(), position, duration);
+ mVideoView.stopPlayback();
+ mVideoView.setVisibility(View.INVISIBLE);
+ clearVideoInfo();
+ mActivityContext.releaseEffects();
+ mMovieItem = next;
+ mActivityContext.refreshMovieInfo(mMovieItem);
+ doStartVideo(false, 0, 0);
+ mVideoView.setVisibility(View.VISIBLE);
+ } else {
+ Log.e(TAG, "Cannot play the next video! " + item);
+ }
+ mActivityContext.closeOptionsMenu();
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle icicle) {
+ mIsLoop = icicle.getBoolean(KEY_VIDEO_IS_LOOP, false);
+ if (mIsLoop) {
+ mController.setCanReplay(true);
+ } // else will get can replay from intent.
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putBoolean(KEY_VIDEO_IS_LOOP, mIsLoop);
+ }
+
+ @Override
+ public void stopVideo() {
+ if (LOG) {
+ Log.v(TAG, "stopVideo()");
+ }
+ mTState = TState.STOPED;
+ mVideoView.clearSeek();
+ mVideoView.clearDuration();
+ mVideoView.stopPlayback();
+ mVideoView.setResumed(false);
+ mVideoView.setVisibility(View.INVISIBLE);
+ clearVideoInfo();
+ mActivityContext.releaseEffects();
+ mFirstBePlayed = false;
+ mController.setCanReplay(true);
+ mController.showEnded();
+ mController.setViewEnabled(true);
+ setProgress();
+ }
+
+ @Override
+ public boolean canStop() {
+ boolean stopped = false;
+ if (mController != null) {
+ stopped = mOverlayExt.isPlayingEnd();
+ }
+ if (LOG) {
+ Log.v(TAG, "canStop() stopped=" + stopped);
+ }
+ return !stopped;
+ }
+
+ @Override
+ public void addBookmark() {
+ if (mBookmark == null) {
+ mBookmark = new BookmarkEnhance(mActivityContext);
+ }
+ String uri = String.valueOf(mMovieItem.getUri());
+ if (mBookmark.exists(uri)) {
+ Toast.makeText(mActivityContext, R.string.bookmark_exist, Toast.LENGTH_SHORT)
+ .show();
+ } else {
+ mBookmark.insert(mMovieItem.getTitle(), uri,
+ mMovieItem.getMimeType(), 0);
+ Toast.makeText(mActivityContext, R.string.bookmark_add_success, Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+ };
+
+ private class RetryExtension implements Restorable, MediaPlayer.OnErrorListener,
+ MediaPlayer.OnInfoListener {
+ private static final String KEY_VIDEO_RETRY_COUNT = "video_retry_count";
+ private int mRetryDuration;
+ private int mRetryPosition;
+ private int mRetryCount;
+
+ public void retry() {
+ doStartVideo(true, mRetryPosition, mRetryDuration);
+ if (LOG) {
+ Log.v(TAG, "retry() mRetryCount=" + mRetryCount + ", mRetryPosition="
+ + mRetryPosition);
+ }
+ }
+
+ public void clearRetry() {
+ if (LOG) {
+ Log.v(TAG, "clearRetry() mRetryCount=" + mRetryCount);
+ }
+ mRetryCount = 0;
+ }
+
+ public boolean reachRetryCount() {
+ if (LOG) {
+ Log.v(TAG, "reachRetryCount() mRetryCount=" + mRetryCount);
+ }
+ if (mRetryCount > 3) {
+ return true;
+ }
+ return false;
+ }
+
+ public int getRetryCount() {
+ if (LOG) {
+ Log.v(TAG, "getRetryCount() return " + mRetryCount);
+ }
+ return mRetryCount;
+ }
+
+ public boolean isRetrying() {
+ boolean retry = false;
+ if (mRetryCount > 0) {
+ retry = true;
+ }
+ if (LOG) {
+ Log.v(TAG, "isRetrying() mRetryCount=" + mRetryCount);
+ }
+ return retry;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle icicle) {
+ mRetryCount = icicle.getInt(KEY_VIDEO_RETRY_COUNT);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putInt(KEY_VIDEO_RETRY_COUNT, mRetryCount);
+ }
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ return false;
+ }
+
+ @Override
+ public boolean onInfo(MediaPlayer mp, int what, int extra) {
+ return false;
+ }
+
+ public boolean handleOnReplay() {
+ if (isRetrying()) { // from connecting error
+ clearRetry();
+ int errorPosition = mVideoView.getCurrentPosition();
+ int errorDuration = mVideoView.getDuration();
+ doStartVideo(errorPosition > 0, errorPosition, errorDuration);
+ if (LOG) {
+ Log.v(TAG, "onReplay() errorPosition=" + errorPosition + ", errorDuration="
+ + errorDuration);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public void showRetry() {
+ if (mVideoCanSeek || mVideoView.canSeekForward()) {
+ mVideoView.seekTo(mVideoPosition);
+ }
+ mVideoView.setDuration(mVideoLastDuration);
+ mRetryPosition = mVideoPosition;
+ mRetryDuration = mVideoLastDuration;
+ }
+ }
+ private class ServerTimeoutExtension implements Restorable, MediaPlayer.OnErrorListener {
+ // for cmcc server timeout case
+ // please remember to clear this value when changed video.
+ private int mServerTimeout = -1;
+ private long mLastDisconnectTime;
+ private boolean mIsShowDialog = false;
+ private AlertDialog mServerTimeoutDialog;
+ private int RESUME_DIALOG_TIMEOUT = 3 * 60 * 1000; // 3 mins
+
+ // check whether disconnect from server timeout or not.
+ // if timeout, return false. otherwise, return true.
+ private boolean passDisconnectCheck() {
+ if (!isFullBuffer()) {
+ // record the time disconnect from server
+ long now = System.currentTimeMillis();
+ if (LOG) {
+ Log.v(TAG, "passDisconnectCheck() now=" + now + ", mLastDisconnectTime="
+ + mLastDisconnectTime
+ + ", mServerTimeout=" + mServerTimeout);
+ }
+ if (mServerTimeout > 0 && (now - mLastDisconnectTime) > mServerTimeout) {
+ // disconnect time more than server timeout, notify user
+ notifyServerTimeout();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void recordDisconnectTime() {
+ if (!isFullBuffer()) {
+ // record the time disconnect from server
+ mLastDisconnectTime = System.currentTimeMillis();
+ }
+ if (LOG) {
+ Log.v(TAG, "recordDisconnectTime() mLastDisconnectTime=" + mLastDisconnectTime);
+ }
+ }
+
+ private void clearServerInfo() {
+ mServerTimeout = -1;
+ }
+
+ private void notifyServerTimeout() {
+ if (mServerTimeoutDialog == null) {
+ // for updating last position and duration.
+ if (mVideoCanSeek || mVideoView.canSeekForward()) {
+ mVideoView.seekTo(mVideoPosition);
+ }
+ mVideoView.setDuration(mVideoLastDuration);
+ AlertDialog.Builder builder = new AlertDialog.Builder(mActivityContext);
+ mServerTimeoutDialog = builder.setTitle(R.string.server_timeout_title)
+ .setMessage(R.string.server_timeout_message)
+ .setNegativeButton(android.R.string.cancel, new OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (LOG) {
+ Log.v(TAG, "NegativeButton.onClick() mIsShowDialog="
+ + mIsShowDialog);
+ }
+ mController.showEnded();
+ onCompletion();
+ }
+
+ })
+ .setPositiveButton(R.string.resume_playing_resume, new OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (LOG) {
+ Log.v(TAG, "PositiveButton.onClick() mIsShowDialog="
+ + mIsShowDialog);
+ }
+ doStartVideo(true, mVideoPosition, mVideoLastDuration);
+ }
+
+ })
+ .setOnCancelListener(new OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ mController.showEnded();
+ onCompletion();
+ }
+ })
+ .create();
+ mServerTimeoutDialog.setOnDismissListener(new OnDismissListener() {
+
+ public void onDismiss(DialogInterface dialog) {
+ if (LOG) {
+ Log.v(TAG, "mServerTimeoutDialog.onDismiss()");
+ }
+ mVideoView.setDialogShowState(false);
+ mIsShowDialog = false;
+ }
+
+ });
+ mServerTimeoutDialog.setOnShowListener(new OnShowListener() {
+
+ public void onShow(DialogInterface dialog) {
+ if (LOG) {
+ Log.v(TAG, "mServerTimeoutDialog.onShow()");
+ }
+ mVideoView.setDialogShowState(true);
+ mIsShowDialog = true;
+ }
+
+ });
+ }
+ mServerTimeoutDialog.show();
+ }
+
+ private void clearTimeoutDialog() {
+ if (mServerTimeoutDialog != null && mServerTimeoutDialog.isShowing()) {
+ mServerTimeoutDialog.dismiss();
+ }
+ mServerTimeoutDialog = null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle icicle) {
+ mLastDisconnectTime = icicle.getLong(KEY_VIDEO_LAST_DISCONNECT_TIME);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putLong(KEY_VIDEO_LAST_DISCONNECT_TIME, mLastDisconnectTime);
+ }
+
+ public boolean handleOnResume() {
+ if (mIsShowDialog && !isLiveStreaming()) {
+ // wait for user's operation
+ return true;
+ }
+ if (!passDisconnectCheck()) {
+ return true;
+ }
+ return false;
+ }
+
+ public void setVideoInfo(Metadata data) {
+ mServerTimeout = RESUME_DIALOG_TIMEOUT;
+ if (data.has(SERVER_TIMEOUT)) {
+ mServerTimeout = data.getInt(SERVER_TIMEOUT);
+ if (mServerTimeout == 0) {
+ mServerTimeout = RESUME_DIALOG_TIMEOUT;
+ }
+ if (LOG) {
+ Log.v(TAG, "get server timeout from metadata. mServerTimeout="
+ + mServerTimeout);
+ }
+ }
+ }
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ // if we are showing a dialog, cancel the error dialog
+ if (mIsShowDialog) {
+ return true;
+ }
+ return false;
+ }
+
+ public void setTimeout(int timeout) {
+ mServerTimeout = timeout;
+ }
+ }
+
+ private class ScreenModeExt implements Restorable, ScreenModeListener {
+ private static final String KEY_VIDEO_SCREEN_MODE = "video_screen_mode";
+ private int mScreenMode = ScreenModeManager.SCREENMODE_BIGSCREEN;
+ private ScreenModeManager mScreenModeManager = new ScreenModeManager();
+
+ public void setScreenMode() {
+ mVideoView.setScreenModeManager(mScreenModeManager);
+ mController.setScreenModeManager(mScreenModeManager);
+ mScreenModeManager.addListener(this);
+ //notify all listener to change screen mode
+ mScreenModeManager.setScreenMode(mScreenMode);
+ if (LOG) {
+ Log.v(TAG, "setScreenMode() mScreenMode=" + mScreenMode);
+ }
+ }
+
+ @Override
+ public void onScreenModeChanged(int newMode) {
+ mScreenMode = newMode;// changed from controller
+ if (LOG) {
+ Log.v(TAG, "OnScreenModeClicked(" + newMode + ")");
+ }
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle icicle) {
+ mScreenMode = icicle.getInt(KEY_VIDEO_SCREEN_MODE,
+ ScreenModeManager.SCREENMODE_BIGSCREEN);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putInt(KEY_VIDEO_SCREEN_MODE, mScreenMode);
+ }
+ }
+
+ private class ControllerRewindAndForwardExt implements IRewindAndForwardListener {
+ @Override
+ public void onPlayPause() {
+ onPlayPause();
+ }
+
+ @Override
+ public void onSeekStart() {
+ onSeekStart();
+ }
+
+ @Override
+ public void onSeekMove(int time) {
+ onSeekMove(time);
+ }
+
+ @Override
+ public void onSeekEnd(int time, int trimStartTime, int trimEndTime) {
+ onSeekEnd(time, trimStartTime, trimEndTime);
+ }
+
+ @Override
+ public void onShown() {
+ onShown();
+ }
+
+ @Override
+ public void onHidden() {
+ onHidden();
+ }
+
+ @Override
+ public void onReplay() {
+ onReplay();
+ }
+
+ @Override
+ public boolean onIsRTSP() {
+ return false;
+ }
+
+ @Override
+ public void onStopVideo() {
+ if (LOG) {
+ Log.v(TAG, "ControllerRewindAndForwardExt onStopVideo()");
+ }
+ if (mPlayerExt.canStop()) {
+ mPlayerExt.stopVideo();
+ mControllerRewindAndForwardExt.showControllerButtonsView(false,
+ false, false);
+ }
+ }
+
+ @Override
+ public void onRewind() {
+ if (LOG) {
+ Log.v(TAG, "ControllerRewindAndForwardExt onRewind()");
+ }
+ if (mVideoView != null && mVideoView.canSeekBackward()) {
+ mControllerRewindAndForwardExt.showControllerButtonsView(mPlayerExt
+ .canStop(),
+ false, false);
+ int stepValue = getStepOptionValue();
+ int targetDuration = mVideoView.getCurrentPosition()
+ - stepValue < 0 ? 0 : mVideoView.getCurrentPosition()
+ - stepValue;
+ if (LOG) {
+ Log.v(TAG, "onRewind targetDuration " + targetDuration);
+ }
+ mVideoView.seekTo(targetDuration);
+ } else {
+ mControllerRewindAndForwardExt.showControllerButtonsView(mPlayerExt
+ .canStop(),
+ false, false);
+ }
+ }
+
+ @Override
+ public void onForward() {
+ if (LOG) {
+ Log.v(TAG, "ControllerRewindAndForwardExt onForward()");
+ }
+ if (mVideoView != null && mVideoView.canSeekForward()) {
+ mControllerRewindAndForwardExt.showControllerButtonsView(mPlayerExt
+ .canStop(),
+ false, false);
+ int stepValue = getStepOptionValue();
+ int targetDuration = mVideoView.getCurrentPosition()
+ + stepValue > mVideoView.getDuration() ? mVideoView
+ .getDuration() : mVideoView.getCurrentPosition()
+ + stepValue;
+ if (LOG) {
+ Log.v(TAG, "onForward targetDuration " + targetDuration);
+ }
+ mVideoView.seekTo(targetDuration);
+ } else {
+ mControllerRewindAndForwardExt.showControllerButtonsView(mPlayerExt
+ .canStop(),
+ false, false);
+ }
+ }
+ }
+
+ public int getStepOptionValue() {
+ final String slectedStepOption = "selected_step_option";
+ final String videoPlayerData = "video_player_data";
+ final int stepBase = 3000;
+ final int stepOptionThreeSeconds = 0;
+ SharedPreferences mPrefs = mContext.getSharedPreferences(
+ videoPlayerData, 0);
+ return (mPrefs.getInt(slectedStepOption, stepOptionThreeSeconds) + 1) * stepBase;
+ }
+
+ public void restartHidingController() {
+ if (mController != null) {
+ mController.maybeStartHiding();
+ }
+ }
+
+ public void cancelHidingController() {
+ if (mController != null) {
+ mController.cancelHiding();
}
}
}
@@ -497,6 +1633,10 @@ class Bookmarker {
public void setBookmark(Uri uri, int bookmark, int duration) {
try {
+ // do not record or override bookmark if duration is not valid.
+ if (duration <= 0) {
+ return;
+ }
BlobCache cache = CacheManager.getCache(mContext,
BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION);
@@ -505,7 +1645,7 @@ class Bookmarker {
DataOutputStream dos = new DataOutputStream(bos);
dos.writeUTF(uri.toString());
dos.writeInt(bookmark);
- dos.writeInt(duration);
+ dos.writeInt(Math.abs(duration));
dos.flush();
cache.insert(uri.hashCode(), bos.toByteArray());
} catch (Throwable t) {
@@ -513,7 +1653,7 @@ class Bookmarker {
}
}
- public Integer getBookmark(Uri uri) {
+ public BookmarkerInfo getBookmark(Uri uri) {
try {
BlobCache cache = CacheManager.getCache(mContext,
BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
@@ -537,10 +1677,31 @@ class Bookmarker {
|| (bookmark > (duration - HALF_MINUTE))) {
return null;
}
- return Integer.valueOf(bookmark);
+ return new BookmarkerInfo(bookmark, duration);
} catch (Throwable t) {
Log.w(TAG, "getBookmark failed", t);
}
return null;
}
}
+
+class BookmarkerInfo {
+ public final int mBookmark;
+ public final int mDuration;
+
+ public BookmarkerInfo(int bookmark, int duration) {
+ this.mBookmark = bookmark;
+ this.mDuration = duration;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("BookmarkInfo(bookmark=")
+ .append(mBookmark)
+ .append(", duration=")
+ .append(mDuration)
+ .append(")")
+ .toString();
+ }
+}