blob: ec228b3c2bd24ccd135e33b87df4d582b569d7b9 [file] [log] [blame]
Owen Linf9a0a432011-08-17 22:07:43 +08001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.gallery3d.app;
18
Owen Lin7a5e1e72012-06-20 16:54:24 +080019import android.annotation.TargetApi;
Owen Linf9a0a432011-08-17 22:07:43 +080020import android.app.AlertDialog;
21import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.DialogInterface;
24import android.content.DialogInterface.OnCancelListener;
25import android.content.DialogInterface.OnClickListener;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.media.AudioManager;
29import android.media.MediaPlayer;
Marco Nelissene0321522013-05-13 13:36:01 -070030import android.media.audiofx.AudioEffect;
31import android.media.audiofx.Virtualizer;
Owen Linf9a0a432011-08-17 22:07:43 +080032import android.net.Uri;
Owen Lindf4763c2012-06-28 17:12:20 +080033import android.os.Build;
Owen Lin540e4692011-09-14 19:49:03 +080034import android.os.Bundle;
Owen Linf9a0a432011-08-17 22:07:43 +080035import android.os.Handler;
Chih-Chung Changfc8c5032011-11-29 14:21:11 +080036import android.view.KeyEvent;
Chih-Chung Chang209a9162011-10-14 16:09:09 +080037import android.view.MotionEvent;
Owen Linf9a0a432011-08-17 22:07:43 +080038import android.view.View;
Chih-Chung Chang209a9162011-10-14 16:09:09 +080039import android.view.ViewGroup;
Owen Linf9a0a432011-08-17 22:07:43 +080040import android.widget.VideoView;
41
Owen Linaea077a2011-11-11 12:01:09 +080042import com.android.gallery3d.R;
Owen Lin7a5e1e72012-06-20 16:54:24 +080043import com.android.gallery3d.common.ApiHelper;
Owen Linaea077a2011-11-11 12:01:09 +080044import com.android.gallery3d.common.BlobCache;
45import com.android.gallery3d.util.CacheManager;
46import com.android.gallery3d.util.GalleryUtils;
47
Owen Linf9a0a432011-08-17 22:07:43 +080048import java.io.ByteArrayInputStream;
49import java.io.ByteArrayOutputStream;
50import java.io.DataInputStream;
51import java.io.DataOutputStream;
52
53public class MoviePlayer implements
Chih-Chung Chang209a9162011-10-14 16:09:09 +080054 MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener,
55 ControllerOverlay.Listener {
Owen Linf9a0a432011-08-17 22:07:43 +080056 @SuppressWarnings("unused")
57 private static final String TAG = "MoviePlayer";
58
Owen Lin540e4692011-09-14 19:49:03 +080059 private static final String KEY_VIDEO_POSITION = "video-position";
60 private static final String KEY_RESUMEABLE_TIME = "resumeable-timeout";
61
Chih-Chung Chang67721732012-07-03 12:49:45 +080062 // These are constants in KeyEvent, appearing on API level 11.
63 private static final int KEYCODE_MEDIA_PLAY = 126;
64 private static final int KEYCODE_MEDIA_PAUSE = 127;
65
Owen Linf9a0a432011-08-17 22:07:43 +080066 // Copied from MediaPlaybackService in the Music Player app.
67 private static final String SERVICECMD = "com.android.music.musicservicecommand";
68 private static final String CMDNAME = "command";
69 private static final String CMDPAUSE = "pause";
70
Marco Nelissene0321522013-05-13 13:36:01 -070071 private static final String VIRTUALIZE_EXTRA = "virtualize";
vivek mehtaa062a4a2014-11-04 19:13:47 -080072 private static final long BLACK_TIMEOUT = 50;
Owen Lin00bb8e42012-05-23 15:47:36 -070073
Owen Lin540e4692011-09-14 19:49:03 +080074 // If we resume the acitivty with in RESUMEABLE_TIMEOUT, we will keep playing.
75 // Otherwise, we pause the player.
76 private static final long RESUMEABLE_TIMEOUT = 3 * 60 * 1000; // 3 mins
77
Owen Linf9a0a432011-08-17 22:07:43 +080078 private Context mContext;
79 private final VideoView mVideoView;
Owen Lin00bb8e42012-05-23 15:47:36 -070080 private final View mRootView;
Owen Linf9a0a432011-08-17 22:07:43 +080081 private final Bookmarker mBookmarker;
82 private final Uri mUri;
83 private final Handler mHandler = new Handler();
84 private final AudioBecomingNoisyReceiver mAudioBecomingNoisyReceiver;
Owen Lin00bb8e42012-05-23 15:47:36 -070085 private final MovieControllerOverlay mController;
Owen Linf9a0a432011-08-17 22:07:43 +080086
Owen Lin540e4692011-09-14 19:49:03 +080087 private long mResumeableTime = Long.MAX_VALUE;
88 private int mVideoPosition = 0;
89 private boolean mHasPaused = false;
Ray Chen24525c22012-05-22 17:34:29 +080090 private int mLastSystemUiVis = 0;
Owen Linf9a0a432011-08-17 22:07:43 +080091
Chih-Chung Chang209a9162011-10-14 16:09:09 +080092 // If the time bar is being dragged.
93 private boolean mDragging;
94
95 // If the time bar is visible.
96 private boolean mShowing;
97
Marco Nelissene0321522013-05-13 13:36:01 -070098 private Virtualizer mVirtualizer;
99
Owen Linf9a0a432011-08-17 22:07:43 +0800100 private final Runnable mPlayingChecker = new Runnable() {
Owen Lin540e4692011-09-14 19:49:03 +0800101 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800102 public void run() {
103 if (mVideoView.isPlaying()) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800104 mController.showPlaying();
Owen Linf9a0a432011-08-17 22:07:43 +0800105 } else {
106 mHandler.postDelayed(mPlayingChecker, 250);
107 }
108 }
109 };
110
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800111 private final Runnable mProgressChecker = new Runnable() {
112 @Override
113 public void run() {
114 int pos = setProgress();
115 mHandler.postDelayed(mProgressChecker, 1000 - (pos % 1000));
116 }
117 };
118
xulei71e9a7b2015-09-16 10:28:17 +0800119 public boolean isPlaying() {
120 if (mVideoView != null) {
121 return mVideoView.isPlaying();
122 } else {
123 return false;
124 }
125 }
126
Owen Lin00bb8e42012-05-23 15:47:36 -0700127 public MoviePlayer(View rootView, final MovieActivity movieActivity,
128 Uri videoUri, Bundle savedInstance, boolean canReplay) {
Owen Linf9a0a432011-08-17 22:07:43 +0800129 mContext = movieActivity.getApplicationContext();
Owen Lin00bb8e42012-05-23 15:47:36 -0700130 mRootView = rootView;
Owen Linf9a0a432011-08-17 22:07:43 +0800131 mVideoView = (VideoView) rootView.findViewById(R.id.surface_view);
Owen Linf9a0a432011-08-17 22:07:43 +0800132 mBookmarker = new Bookmarker(movieActivity);
Owen Linf9a0a432011-08-17 22:07:43 +0800133 mUri = videoUri;
134
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800135 mController = new MovieControllerOverlay(mContext);
136 ((ViewGroup)rootView).addView(mController.getView());
137 mController.setListener(this);
138 mController.setCanReplay(canReplay);
Owen Linf9a0a432011-08-17 22:07:43 +0800139
140 mVideoView.setOnErrorListener(this);
141 mVideoView.setOnCompletionListener(this);
142 mVideoView.setVideoURI(mUri);
Marco Nelissene0321522013-05-13 13:36:01 -0700143
144 Intent ai = movieActivity.getIntent();
145 boolean virtualize = ai.getBooleanExtra(VIRTUALIZE_EXTRA, false);
146 if (virtualize) {
147 int session = mVideoView.getAudioSessionId();
148 if (session != 0) {
149 mVirtualizer = new Virtualizer(0, session);
150 mVirtualizer.setEnabled(true);
151 } else {
152 Log.w(TAG, "no audio session to virtualize");
153 }
154 }
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800155 mVideoView.setOnTouchListener(new View.OnTouchListener() {
Owen Lin00bb8e42012-05-23 15:47:36 -0700156 @Override
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800157 public boolean onTouch(View v, MotionEvent event) {
158 mController.show();
159 return true;
Owen Linf9a0a432011-08-17 22:07:43 +0800160 }
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800161 });
ztenghuie8b4b342013-04-17 14:41:05 -0700162 mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
163 @Override
164 public void onPrepared(MediaPlayer player) {
165 if (!mVideoView.canSeekForward() || !mVideoView.canSeekBackward()) {
166 mController.setSeekable(false);
167 } else {
168 mController.setSeekable(true);
169 }
170 setProgress();
171 }
172 });
Chih-Chung Chang81760122011-09-27 16:50:16 +0800173
Owen Lin00bb8e42012-05-23 15:47:36 -0700174 // The SurfaceView is transparent before drawing the first frame.
175 // This makes the UI flashing when open a video. (black -> old screen
176 // -> video) However, we have no way to know the timing of the first
177 // frame. So, we hide the VideoView for a while to make sure the
178 // video has been drawn on it.
179 mVideoView.postDelayed(new Runnable() {
180 @Override
181 public void run() {
182 mVideoView.setVisibility(View.VISIBLE);
183 }
184 }, BLACK_TIMEOUT);
185
Owen Lin7a5e1e72012-06-20 16:54:24 +0800186 setOnSystemUiVisibilityChangeListener();
187 // Hide system UI by default
188 showSystemUi(false);
189
190 mAudioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver();
191 mAudioBecomingNoisyReceiver.register();
192
193 Intent i = new Intent(SERVICECMD);
194 i.putExtra(CMDNAME, CMDPAUSE);
195 movieActivity.sendBroadcast(i);
196
197 if (savedInstance != null) { // this is a resumed activity
198 mVideoPosition = savedInstance.getInt(KEY_VIDEO_POSITION, 0);
199 mResumeableTime = savedInstance.getLong(KEY_RESUMEABLE_TIME, Long.MAX_VALUE);
200 mVideoView.start();
201 mVideoView.suspend();
202 mHasPaused = true;
203 } else {
204 final Integer bookmark = mBookmarker.getBookmark(mUri);
205 if (bookmark != null) {
206 showResumeDialog(movieActivity, bookmark);
207 } else {
208 startVideo();
209 }
210 }
211 }
212
Owen Lindf4763c2012-06-28 17:12:20 +0800213 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Owen Lin7a5e1e72012-06-20 16:54:24 +0800214 private void setOnSystemUiVisibilityChangeListener() {
215 if (!ApiHelper.HAS_VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION) return;
216
Chih-Chung Chang81760122011-09-27 16:50:16 +0800217 // When the user touches the screen or uses some hard key, the framework
218 // will change system ui visibility from invisible to visible. We show
Ray Chen392a2922012-05-14 17:19:56 +0800219 // the media control and enable system UI (e.g. ActionBar) to be visible at this point
Chih-Chung Chang81760122011-09-27 16:50:16 +0800220 mVideoView.setOnSystemUiVisibilityChangeListener(
221 new View.OnSystemUiVisibilityChangeListener() {
Owen Lin00bb8e42012-05-23 15:47:36 -0700222 @Override
Chih-Chung Chang81760122011-09-27 16:50:16 +0800223 public void onSystemUiVisibilityChange(int visibility) {
Ray Chen24525c22012-05-22 17:34:29 +0800224 int diff = mLastSystemUiVis ^ visibility;
225 mLastSystemUiVis = visibility;
226 if ((diff & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
227 && (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800228 mController.show();
Chih-Chung Chang81760122011-09-27 16:50:16 +0800229 }
230 }
231 });
Owen Linf9a0a432011-08-17 22:07:43 +0800232 }
233
Owen Lin28cb4162012-08-29 11:53:10 +0800234 @SuppressWarnings("deprecation")
Owen Lindf4763c2012-06-28 17:12:20 +0800235 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Chih-Chung Chang81760122011-09-27 16:50:16 +0800236 private void showSystemUi(boolean visible) {
Owen Lin7a5e1e72012-06-20 16:54:24 +0800237 if (!ApiHelper.HAS_VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE) return;
238
Ray Chen24525c22012-05-22 17:34:29 +0800239 int flag = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
240 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
Owen Lin7a5e1e72012-06-20 16:54:24 +0800241 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
Ray Chen24525c22012-05-22 17:34:29 +0800242 if (!visible) {
Owen Lin28cb4162012-08-29 11:53:10 +0800243 // We used the deprecated "STATUS_BAR_HIDDEN" for unbundling
The Android Open Source Projectca7d9bf2012-06-29 08:19:39 -0700244 flag |= View.STATUS_BAR_HIDDEN | View.SYSTEM_UI_FLAG_FULLSCREEN
Ray Chen24525c22012-05-22 17:34:29 +0800245 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
Ray Chen392a2922012-05-14 17:19:56 +0800246 }
Ray Chen24525c22012-05-22 17:34:29 +0800247 mVideoView.setSystemUiVisibility(flag);
Chih-Chung Chang81760122011-09-27 16:50:16 +0800248 }
249
Owen Lin540e4692011-09-14 19:49:03 +0800250 public void onSaveInstanceState(Bundle outState) {
251 outState.putInt(KEY_VIDEO_POSITION, mVideoPosition);
252 outState.putLong(KEY_RESUMEABLE_TIME, mResumeableTime);
253 }
254
Owen Linf9a0a432011-08-17 22:07:43 +0800255 private void showResumeDialog(Context context, final int bookmark) {
256 AlertDialog.Builder builder = new AlertDialog.Builder(context);
257 builder.setTitle(R.string.resume_playing_title);
258 builder.setMessage(String.format(
259 context.getString(R.string.resume_playing_message),
260 GalleryUtils.formatDuration(context, bookmark / 1000)));
261 builder.setOnCancelListener(new OnCancelListener() {
Owen Lin540e4692011-09-14 19:49:03 +0800262 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800263 public void onCancel(DialogInterface dialog) {
264 onCompletion();
265 }
266 });
267 builder.setPositiveButton(
268 R.string.resume_playing_resume, new OnClickListener() {
Owen Lin540e4692011-09-14 19:49:03 +0800269 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800270 public void onClick(DialogInterface dialog, int which) {
271 mVideoView.seekTo(bookmark);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800272 startVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800273 }
274 });
275 builder.setNegativeButton(
276 R.string.resume_playing_restart, new OnClickListener() {
Owen Lin540e4692011-09-14 19:49:03 +0800277 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800278 public void onClick(DialogInterface dialog, int which) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800279 startVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800280 }
281 });
282 builder.show();
283 }
284
285 public void onPause() {
Owen Linf9a0a432011-08-17 22:07:43 +0800286 mHasPaused = true;
Owen Lin540e4692011-09-14 19:49:03 +0800287 mHandler.removeCallbacksAndMessages(null);
288 mVideoPosition = mVideoView.getCurrentPosition();
289 mBookmarker.setBookmark(mUri, mVideoPosition, mVideoView.getDuration());
290 mVideoView.suspend();
291 mResumeableTime = System.currentTimeMillis() + RESUMEABLE_TIMEOUT;
Owen Linf9a0a432011-08-17 22:07:43 +0800292 }
293
294 public void onResume() {
295 if (mHasPaused) {
Owen Lin540e4692011-09-14 19:49:03 +0800296 mVideoView.seekTo(mVideoPosition);
297 mVideoView.resume();
298
299 // If we have slept for too long, pause the play
300 if (System.currentTimeMillis() > mResumeableTime) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800301 pauseVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800302 }
303 }
Hui (QRD) Yuf44565d2014-01-26 13:45:21 +0800304
305 if (System.currentTimeMillis() > mResumeableTime) {
306 mHandler.removeCallbacks(mPlayingChecker);
307 mHandler.postDelayed(mPlayingChecker, 250);
308 }
309
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800310 mHandler.post(mProgressChecker);
Owen Linf9a0a432011-08-17 22:07:43 +0800311 }
312
313 public void onDestroy() {
Marco Nelissene0321522013-05-13 13:36:01 -0700314 if (mVirtualizer != null) {
315 mVirtualizer.release();
316 mVirtualizer = null;
317 }
Owen Linf9a0a432011-08-17 22:07:43 +0800318 mVideoView.stopPlayback();
319 mAudioBecomingNoisyReceiver.unregister();
320 }
321
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800322 // This updates the time bar display (if necessary). It is called every
323 // second by mProgressChecker and also from places where the time bar needs
324 // to be updated immediately.
325 private int setProgress() {
326 if (mDragging || !mShowing) {
327 return 0;
328 }
329 int position = mVideoView.getCurrentPosition();
330 int duration = mVideoView.getDuration();
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700331 mController.setTimes(position, duration, 0, 0);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800332 return position;
333 }
334
335 private void startVideo() {
336 // For streams that we expect to be slow to start up, show a
337 // progress spinner until playback starts.
338 String scheme = mUri.getScheme();
339 if ("http".equalsIgnoreCase(scheme) || "rtsp".equalsIgnoreCase(scheme)) {
340 mController.showLoading();
341 mHandler.removeCallbacks(mPlayingChecker);
342 mHandler.postDelayed(mPlayingChecker, 250);
343 } else {
344 mController.showPlaying();
Owen Lin00bb8e42012-05-23 15:47:36 -0700345 mController.hide();
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800346 }
347
348 mVideoView.start();
349 setProgress();
350 }
351
352 private void playVideo() {
353 mVideoView.start();
354 mController.showPlaying();
355 setProgress();
356 }
357
358 private void pauseVideo() {
359 mVideoView.pause();
360 mController.showPaused();
361 }
362
363 // Below are notifications from VideoView
364 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800365 public boolean onError(MediaPlayer player, int arg1, int arg2) {
366 mHandler.removeCallbacksAndMessages(null);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800367 // VideoView will show an error dialog if we return false, so no need
368 // to show more message.
369 mController.showErrorMessage("");
Owen Linf9a0a432011-08-17 22:07:43 +0800370 return false;
371 }
372
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800373 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800374 public void onCompletion(MediaPlayer mp) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800375 mController.showEnded();
Owen Linf9a0a432011-08-17 22:07:43 +0800376 onCompletion();
377 }
378
379 public void onCompletion() {
380 }
381
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800382 // Below are notifications from ControllerOverlay
383 @Override
384 public void onPlayPause() {
385 if (mVideoView.isPlaying()) {
386 pauseVideo();
387 } else {
388 playVideo();
389 }
390 }
xulei71e9a7b2015-09-16 10:28:17 +0800391 public void onMoviePlayPause() {
392 if (mVideoView.isPlaying()) {
393 pauseVideo();
394 }
395 }
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800396
397 @Override
398 public void onSeekStart() {
399 mDragging = true;
400 }
401
402 @Override
403 public void onSeekMove(int time) {
404 mVideoView.seekTo(time);
405 }
406
407 @Override
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700408 public void onSeekEnd(int time, int start, int end) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800409 mDragging = false;
410 mVideoView.seekTo(time);
411 setProgress();
412 }
413
414 @Override
415 public void onShown() {
416 mShowing = true;
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800417 setProgress();
Owen Lin0b4e9092012-06-19 14:35:06 +0800418 showSystemUi(true);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800419 }
420
421 @Override
422 public void onHidden() {
423 mShowing = false;
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800424 showSystemUi(false);
425 }
426
427 @Override
428 public void onReplay() {
429 startVideo();
430 }
431
Chih-Chung Changfc8c5032011-11-29 14:21:11 +0800432 // Below are key events passed from MovieActivity.
433 public boolean onKeyDown(int keyCode, KeyEvent event) {
434
435 // Some headsets will fire off 7-10 events on a single click
436 if (event.getRepeatCount() > 0) {
437 return isMediaKey(keyCode);
438 }
439
440 switch (keyCode) {
441 case KeyEvent.KEYCODE_HEADSETHOOK:
442 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
443 if (mVideoView.isPlaying()) {
444 pauseVideo();
445 } else {
446 playVideo();
447 }
448 return true;
Chih-Chung Chang67721732012-07-03 12:49:45 +0800449 case KEYCODE_MEDIA_PAUSE:
Chih-Chung Changfc8c5032011-11-29 14:21:11 +0800450 if (mVideoView.isPlaying()) {
451 pauseVideo();
452 }
453 return true;
Chih-Chung Chang67721732012-07-03 12:49:45 +0800454 case KEYCODE_MEDIA_PLAY:
Chih-Chung Changfc8c5032011-11-29 14:21:11 +0800455 if (!mVideoView.isPlaying()) {
456 playVideo();
457 }
458 return true;
459 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
460 case KeyEvent.KEYCODE_MEDIA_NEXT:
461 // TODO: Handle next / previous accordingly, for now we're
462 // just consuming the events.
463 return true;
464 }
465 return false;
466 }
467
468 public boolean onKeyUp(int keyCode, KeyEvent event) {
469 return isMediaKey(keyCode);
470 }
471
472 private static boolean isMediaKey(int keyCode) {
473 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
474 || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS
475 || keyCode == KeyEvent.KEYCODE_MEDIA_NEXT
476 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
477 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY
478 || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE;
479 }
480
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800481 // We want to pause when the headset is unplugged.
Owen Linf9a0a432011-08-17 22:07:43 +0800482 private class AudioBecomingNoisyReceiver extends BroadcastReceiver {
483
484 public void register() {
485 mContext.registerReceiver(this,
486 new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
487 }
488
489 public void unregister() {
490 mContext.unregisterReceiver(this);
491 }
492
493 @Override
494 public void onReceive(Context context, Intent intent) {
Owen Linaea077a2011-11-11 12:01:09 +0800495 if (mVideoView.isPlaying()) pauseVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800496 }
497 }
498}
499
500class Bookmarker {
501 private static final String TAG = "Bookmarker";
502
503 private static final String BOOKMARK_CACHE_FILE = "bookmark";
504 private static final int BOOKMARK_CACHE_MAX_ENTRIES = 100;
505 private static final int BOOKMARK_CACHE_MAX_BYTES = 10 * 1024;
506 private static final int BOOKMARK_CACHE_VERSION = 1;
507
508 private static final int HALF_MINUTE = 30 * 1000;
509 private static final int TWO_MINUTES = 4 * HALF_MINUTE;
510
511 private final Context mContext;
512
513 public Bookmarker(Context context) {
514 mContext = context;
515 }
516
517 public void setBookmark(Uri uri, int bookmark, int duration) {
518 try {
519 BlobCache cache = CacheManager.getCache(mContext,
520 BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
521 BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION);
522
523 ByteArrayOutputStream bos = new ByteArrayOutputStream();
524 DataOutputStream dos = new DataOutputStream(bos);
525 dos.writeUTF(uri.toString());
526 dos.writeInt(bookmark);
527 dos.writeInt(duration);
528 dos.flush();
529 cache.insert(uri.hashCode(), bos.toByteArray());
530 } catch (Throwable t) {
531 Log.w(TAG, "setBookmark failed", t);
532 }
533 }
534
535 public Integer getBookmark(Uri uri) {
536 try {
537 BlobCache cache = CacheManager.getCache(mContext,
538 BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
539 BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION);
540
541 byte[] data = cache.lookup(uri.hashCode());
542 if (data == null) return null;
543
544 DataInputStream dis = new DataInputStream(
545 new ByteArrayInputStream(data));
546
Owen Lin7a5e1e72012-06-20 16:54:24 +0800547 String uriString = DataInputStream.readUTF(dis);
Owen Linf9a0a432011-08-17 22:07:43 +0800548 int bookmark = dis.readInt();
549 int duration = dis.readInt();
550
551 if (!uriString.equals(uri.toString())) {
552 return null;
553 }
554
555 if ((bookmark < HALF_MINUTE) || (duration < TWO_MINUTES)
556 || (bookmark > (duration - HALF_MINUTE))) {
557 return null;
558 }
559 return Integer.valueOf(bookmark);
560 } catch (Throwable t) {
561 Log.w(TAG, "getBookmark failed", t);
562 }
563 return null;
564 }
xulei71e9a7b2015-09-16 10:28:17 +0800565
Owen Linf9a0a432011-08-17 22:07:43 +0800566}