diff options
Diffstat (limited to 'src/com/android/gallery3d/app/MoviePlayer.java')
-rw-r--r-- | src/com/android/gallery3d/app/MoviePlayer.java | 525 |
1 files changed, 0 insertions, 525 deletions
diff --git a/src/com/android/gallery3d/app/MoviePlayer.java b/src/com/android/gallery3d/app/MoviePlayer.java deleted file mode 100644 index ce9183483..000000000 --- a/src/com/android/gallery3d/app/MoviePlayer.java +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.gallery3d.app; - -import android.annotation.TargetApi; -import android.app.AlertDialog; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -import android.content.DialogInterface.OnClickListener; -import android.content.Intent; -import android.content.IntentFilter; -import android.media.AudioManager; -import android.media.MediaPlayer; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.VideoView; - -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 java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; - -public class MoviePlayer implements - MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, - ControllerOverlay.Listener { - @SuppressWarnings("unused") - private static final String TAG = "MoviePlayer"; - - private static final String KEY_VIDEO_POSITION = "video-position"; - private static final String KEY_RESUMEABLE_TIME = "resumeable-timeout"; - - // These are constants in KeyEvent, appearing on API level 11. - private static final int KEYCODE_MEDIA_PLAY = 126; - private static final int KEYCODE_MEDIA_PAUSE = 127; - - // Copied from MediaPlaybackService in the Music Player app. - private static final String SERVICECMD = "com.android.music.musicservicecommand"; - private static final String CMDNAME = "command"; - private static final String CMDPAUSE = "pause"; - - private static final long BLACK_TIMEOUT = 500; - - // 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 - - private Context mContext; - private final VideoView 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; - - private long mResumeableTime = Long.MAX_VALUE; - private int mVideoPosition = 0; - private boolean mHasPaused = false; - private int mLastSystemUiVis = 0; - - // If the time bar is being dragged. - private boolean mDragging; - - // If the time bar is visible. - private boolean mShowing; - - private final Runnable mPlayingChecker = new Runnable() { - @Override - public void run() { - if (mVideoView.isPlaying()) { - mController.showPlaying(); - } else { - mHandler.postDelayed(mPlayingChecker, 250); - } - } - }; - - private final Runnable mProgressChecker = new Runnable() { - @Override - public void run() { - int pos = setProgress(); - mHandler.postDelayed(mProgressChecker, 1000 - (pos % 1000)); - } - }; - - public MoviePlayer(View rootView, final MovieActivity movieActivity, - Uri videoUri, Bundle savedInstance, boolean canReplay) { - mContext = movieActivity.getApplicationContext(); - mRootView = rootView; - mVideoView = (VideoView) rootView.findViewById(R.id.surface_view); - mBookmarker = new Bookmarker(movieActivity); - mUri = videoUri; - - mController = new MovieControllerOverlay(mContext); - ((ViewGroup)rootView).addView(mController.getView()); - mController.setListener(this); - mController.setCanReplay(canReplay); - - mVideoView.setOnErrorListener(this); - mVideoView.setOnCompletionListener(this); - mVideoView.setVideoURI(mUri); - mVideoView.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - mController.show(); - return true; - } - }); - mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { - @Override - public void onPrepared(MediaPlayer player) { - if (!mVideoView.canSeekForward() || !mVideoView.canSeekBackward()) { - mController.setSeekable(false); - } else { - mController.setSeekable(true); - } - setProgress(); - } - }); - - // The SurfaceView is transparent before drawing the first frame. - // This makes the UI flashing when open a video. (black -> old screen - // -> video) However, we have no way to know the timing of the first - // frame. So, we hide the VideoView for a while to make sure the - // video has been drawn on it. - mVideoView.postDelayed(new Runnable() { - @Override - public void run() { - mVideoView.setVisibility(View.VISIBLE); - } - }, BLACK_TIMEOUT); - - setOnSystemUiVisibilityChangeListener(); - // Hide system UI by default - showSystemUi(false); - - mAudioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(); - mAudioBecomingNoisyReceiver.register(); - - Intent i = new Intent(SERVICECMD); - i.putExtra(CMDNAME, CMDPAUSE); - movieActivity.sendBroadcast(i); - - 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(); - mHasPaused = true; - } else { - final Integer bookmark = mBookmarker.getBookmark(mUri); - if (bookmark != null) { - showResumeDialog(movieActivity, bookmark); - } else { - startVideo(); - } - } - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - private void setOnSystemUiVisibilityChangeListener() { - if (!ApiHelper.HAS_VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION) return; - - // When the user touches the screen or uses some hard key, the framework - // will change system ui visibility from invisible to visible. We show - // the media control and enable system UI (e.g. ActionBar) to be visible at this point - mVideoView.setOnSystemUiVisibilityChangeListener( - new View.OnSystemUiVisibilityChangeListener() { - @Override - public void onSystemUiVisibilityChange(int visibility) { - 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(); - } - } - }); - } - - @SuppressWarnings("deprecation") - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - private void showSystemUi(boolean visible) { - if (!ApiHelper.HAS_VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE) return; - - int flag = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; - if (!visible) { - // We used the deprecated "STATUS_BAR_HIDDEN" for unbundling - flag |= View.STATUS_BAR_HIDDEN | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - } - mVideoView.setSystemUiVisibility(flag); - } - - public void onSaveInstanceState(Bundle outState) { - outState.putInt(KEY_VIDEO_POSITION, mVideoPosition); - outState.putLong(KEY_RESUMEABLE_TIME, mResumeableTime); - } - - private void showResumeDialog(Context context, final int 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))); - builder.setOnCancelListener(new OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - onCompletion(); - } - }); - builder.setPositiveButton( - R.string.resume_playing_resume, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mVideoView.seekTo(bookmark); - startVideo(); - } - }); - builder.setNegativeButton( - R.string.resume_playing_restart, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - startVideo(); - } - }); - builder.show(); - } - - public void onPause() { - mHasPaused = true; - mHandler.removeCallbacksAndMessages(null); - mVideoPosition = mVideoView.getCurrentPosition(); - mBookmarker.setBookmark(mUri, mVideoPosition, mVideoView.getDuration()); - mVideoView.suspend(); - mResumeableTime = System.currentTimeMillis() + RESUMEABLE_TIMEOUT; - } - - public void onResume() { - if (mHasPaused) { - mVideoView.seekTo(mVideoPosition); - mVideoView.resume(); - - // If we have slept for too long, pause the play - if (System.currentTimeMillis() > mResumeableTime) { - pauseVideo(); - } - } - mHandler.post(mProgressChecker); - } - - public void onDestroy() { - mVideoView.stopPlayback(); - mAudioBecomingNoisyReceiver.unregister(); - } - - // 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) { - return 0; - } - int position = mVideoView.getCurrentPosition(); - int duration = mVideoView.getDuration(); - mController.setTimes(position, duration, 0, 0); - return position; - } - - private void startVideo() { - // 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)) { - mController.showLoading(); - mHandler.removeCallbacks(mPlayingChecker); - mHandler.postDelayed(mPlayingChecker, 250); - } else { - mController.showPlaying(); - mController.hide(); - } - - mVideoView.start(); - setProgress(); - } - - private void playVideo() { - mVideoView.start(); - mController.showPlaying(); - setProgress(); - } - - private void pauseVideo() { - mVideoView.pause(); - mController.showPaused(); - } - - // Below are notifications from VideoView - @Override - public boolean onError(MediaPlayer player, int arg1, int arg2) { - mHandler.removeCallbacksAndMessages(null); - // VideoView will show an error dialog if we return false, so no need - // to show more message. - mController.showErrorMessage(""); - return false; - } - - @Override - public void onCompletion(MediaPlayer mp) { - mController.showEnded(); - onCompletion(); - } - - public void onCompletion() { - } - - // Below are notifications from ControllerOverlay - @Override - public void onPlayPause() { - if (mVideoView.isPlaying()) { - pauseVideo(); - } else { - playVideo(); - } - } - - @Override - public void onSeekStart() { - mDragging = true; - } - - @Override - public void onSeekMove(int time) { - mVideoView.seekTo(time); - } - - @Override - public void onSeekEnd(int time, int start, int end) { - mDragging = false; - mVideoView.seekTo(time); - setProgress(); - } - - @Override - public void onShown() { - mShowing = true; - setProgress(); - showSystemUi(true); - } - - @Override - public void onHidden() { - mShowing = false; - showSystemUi(false); - } - - @Override - public void onReplay() { - startVideo(); - } - - // Below are key events passed from MovieActivity. - public boolean onKeyDown(int keyCode, KeyEvent event) { - - // Some headsets will fire off 7-10 events on a single click - if (event.getRepeatCount() > 0) { - return isMediaKey(keyCode); - } - - switch (keyCode) { - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - if (mVideoView.isPlaying()) { - pauseVideo(); - } else { - playVideo(); - } - return true; - case KEYCODE_MEDIA_PAUSE: - if (mVideoView.isPlaying()) { - pauseVideo(); - } - return true; - case KEYCODE_MEDIA_PLAY: - if (!mVideoView.isPlaying()) { - playVideo(); - } - return true; - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_NEXT: - // TODO: Handle next / previous accordingly, for now we're - // just consuming the events. - return true; - } - return false; - } - - public boolean onKeyUp(int keyCode, KeyEvent event) { - return isMediaKey(keyCode); - } - - private static boolean isMediaKey(int keyCode) { - return keyCode == KeyEvent.KEYCODE_HEADSETHOOK - || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS - || keyCode == KeyEvent.KEYCODE_MEDIA_NEXT - || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE - || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY - || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE; - } - - // We want to pause when the headset is unplugged. - private class AudioBecomingNoisyReceiver extends BroadcastReceiver { - - public void register() { - mContext.registerReceiver(this, - new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); - } - - public void unregister() { - mContext.unregisterReceiver(this); - } - - @Override - public void onReceive(Context context, Intent intent) { - if (mVideoView.isPlaying()) pauseVideo(); - } - } -} - -class Bookmarker { - private static final String TAG = "Bookmarker"; - - private static final String BOOKMARK_CACHE_FILE = "bookmark"; - private static final int BOOKMARK_CACHE_MAX_ENTRIES = 100; - private static final int BOOKMARK_CACHE_MAX_BYTES = 10 * 1024; - private static final int BOOKMARK_CACHE_VERSION = 1; - - private static final int HALF_MINUTE = 30 * 1000; - private static final int TWO_MINUTES = 4 * HALF_MINUTE; - - private final Context mContext; - - public Bookmarker(Context context) { - mContext = context; - } - - public void setBookmark(Uri uri, int bookmark, int duration) { - try { - BlobCache cache = CacheManager.getCache(mContext, - BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES, - BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - dos.writeUTF(uri.toString()); - dos.writeInt(bookmark); - dos.writeInt(duration); - dos.flush(); - cache.insert(uri.hashCode(), bos.toByteArray()); - } catch (Throwable t) { - Log.w(TAG, "setBookmark failed", t); - } - } - - public Integer getBookmark(Uri uri) { - try { - BlobCache cache = CacheManager.getCache(mContext, - BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES, - BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION); - - byte[] data = cache.lookup(uri.hashCode()); - if (data == null) return null; - - DataInputStream dis = new DataInputStream( - new ByteArrayInputStream(data)); - - String uriString = DataInputStream.readUTF(dis); - int bookmark = dis.readInt(); - int duration = dis.readInt(); - - if (!uriString.equals(uri.toString())) { - return null; - } - - if ((bookmark < HALF_MINUTE) || (duration < TWO_MINUTES) - || (bookmark > (duration - HALF_MINUTE))) { - return null; - } - return Integer.valueOf(bookmark); - } catch (Throwable t) { - Log.w(TAG, "getBookmark failed", t); - } - return null; - } -} |