blob: f6bd3672522fa3c82e8728435cb75110cbc6301c [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";
Owen Lin00bb8e42012-05-23 15:47:36 -070072 private static final long BLACK_TIMEOUT = 500;
73
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
Owen Lin00bb8e42012-05-23 15:47:36 -0700119 public MoviePlayer(View rootView, final MovieActivity movieActivity,
120 Uri videoUri, Bundle savedInstance, boolean canReplay) {
Owen Linf9a0a432011-08-17 22:07:43 +0800121 mContext = movieActivity.getApplicationContext();
Owen Lin00bb8e42012-05-23 15:47:36 -0700122 mRootView = rootView;
Owen Linf9a0a432011-08-17 22:07:43 +0800123 mVideoView = (VideoView) rootView.findViewById(R.id.surface_view);
Owen Linf9a0a432011-08-17 22:07:43 +0800124 mBookmarker = new Bookmarker(movieActivity);
Owen Linf9a0a432011-08-17 22:07:43 +0800125 mUri = videoUri;
126
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800127 mController = new MovieControllerOverlay(mContext);
128 ((ViewGroup)rootView).addView(mController.getView());
129 mController.setListener(this);
130 mController.setCanReplay(canReplay);
Owen Linf9a0a432011-08-17 22:07:43 +0800131
132 mVideoView.setOnErrorListener(this);
133 mVideoView.setOnCompletionListener(this);
134 mVideoView.setVideoURI(mUri);
Marco Nelissene0321522013-05-13 13:36:01 -0700135
136 Intent ai = movieActivity.getIntent();
137 boolean virtualize = ai.getBooleanExtra(VIRTUALIZE_EXTRA, false);
138 if (virtualize) {
139 int session = mVideoView.getAudioSessionId();
140 if (session != 0) {
141 mVirtualizer = new Virtualizer(0, session);
142 mVirtualizer.setEnabled(true);
143 } else {
144 Log.w(TAG, "no audio session to virtualize");
145 }
146 }
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800147 mVideoView.setOnTouchListener(new View.OnTouchListener() {
Owen Lin00bb8e42012-05-23 15:47:36 -0700148 @Override
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800149 public boolean onTouch(View v, MotionEvent event) {
150 mController.show();
151 return true;
Owen Linf9a0a432011-08-17 22:07:43 +0800152 }
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800153 });
ztenghuie8b4b342013-04-17 14:41:05 -0700154 mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
155 @Override
156 public void onPrepared(MediaPlayer player) {
157 if (!mVideoView.canSeekForward() || !mVideoView.canSeekBackward()) {
158 mController.setSeekable(false);
159 } else {
160 mController.setSeekable(true);
161 }
162 setProgress();
163 }
164 });
Chih-Chung Chang81760122011-09-27 16:50:16 +0800165
Owen Lin00bb8e42012-05-23 15:47:36 -0700166 // The SurfaceView is transparent before drawing the first frame.
167 // This makes the UI flashing when open a video. (black -> old screen
168 // -> video) However, we have no way to know the timing of the first
169 // frame. So, we hide the VideoView for a while to make sure the
170 // video has been drawn on it.
171 mVideoView.postDelayed(new Runnable() {
172 @Override
173 public void run() {
174 mVideoView.setVisibility(View.VISIBLE);
175 }
176 }, BLACK_TIMEOUT);
177
Owen Lin7a5e1e72012-06-20 16:54:24 +0800178 setOnSystemUiVisibilityChangeListener();
179 // Hide system UI by default
180 showSystemUi(false);
181
182 mAudioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver();
183 mAudioBecomingNoisyReceiver.register();
184
185 Intent i = new Intent(SERVICECMD);
186 i.putExtra(CMDNAME, CMDPAUSE);
187 movieActivity.sendBroadcast(i);
188
189 if (savedInstance != null) { // this is a resumed activity
190 mVideoPosition = savedInstance.getInt(KEY_VIDEO_POSITION, 0);
191 mResumeableTime = savedInstance.getLong(KEY_RESUMEABLE_TIME, Long.MAX_VALUE);
192 mVideoView.start();
193 mVideoView.suspend();
194 mHasPaused = true;
195 } else {
196 final Integer bookmark = mBookmarker.getBookmark(mUri);
197 if (bookmark != null) {
198 showResumeDialog(movieActivity, bookmark);
199 } else {
200 startVideo();
201 }
202 }
203 }
204
Owen Lindf4763c2012-06-28 17:12:20 +0800205 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Owen Lin7a5e1e72012-06-20 16:54:24 +0800206 private void setOnSystemUiVisibilityChangeListener() {
207 if (!ApiHelper.HAS_VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION) return;
208
Chih-Chung Chang81760122011-09-27 16:50:16 +0800209 // When the user touches the screen or uses some hard key, the framework
210 // will change system ui visibility from invisible to visible. We show
Ray Chen392a2922012-05-14 17:19:56 +0800211 // the media control and enable system UI (e.g. ActionBar) to be visible at this point
Chih-Chung Chang81760122011-09-27 16:50:16 +0800212 mVideoView.setOnSystemUiVisibilityChangeListener(
213 new View.OnSystemUiVisibilityChangeListener() {
Owen Lin00bb8e42012-05-23 15:47:36 -0700214 @Override
Chih-Chung Chang81760122011-09-27 16:50:16 +0800215 public void onSystemUiVisibilityChange(int visibility) {
Ray Chen24525c22012-05-22 17:34:29 +0800216 int diff = mLastSystemUiVis ^ visibility;
217 mLastSystemUiVis = visibility;
218 if ((diff & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
219 && (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800220 mController.show();
Chih-Chung Chang81760122011-09-27 16:50:16 +0800221 }
222 }
223 });
Owen Linf9a0a432011-08-17 22:07:43 +0800224 }
225
Owen Lin28cb4162012-08-29 11:53:10 +0800226 @SuppressWarnings("deprecation")
Owen Lindf4763c2012-06-28 17:12:20 +0800227 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Chih-Chung Chang81760122011-09-27 16:50:16 +0800228 private void showSystemUi(boolean visible) {
Owen Lin7a5e1e72012-06-20 16:54:24 +0800229 if (!ApiHelper.HAS_VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE) return;
230
Ray Chen24525c22012-05-22 17:34:29 +0800231 int flag = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
232 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
Owen Lin7a5e1e72012-06-20 16:54:24 +0800233 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
Ray Chen24525c22012-05-22 17:34:29 +0800234 if (!visible) {
Owen Lin28cb4162012-08-29 11:53:10 +0800235 // We used the deprecated "STATUS_BAR_HIDDEN" for unbundling
The Android Open Source Projectca7d9bf2012-06-29 08:19:39 -0700236 flag |= View.STATUS_BAR_HIDDEN | View.SYSTEM_UI_FLAG_FULLSCREEN
Ray Chen24525c22012-05-22 17:34:29 +0800237 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
Ray Chen392a2922012-05-14 17:19:56 +0800238 }
Ray Chen24525c22012-05-22 17:34:29 +0800239 mVideoView.setSystemUiVisibility(flag);
Chih-Chung Chang81760122011-09-27 16:50:16 +0800240 }
241
Owen Lin540e4692011-09-14 19:49:03 +0800242 public void onSaveInstanceState(Bundle outState) {
243 outState.putInt(KEY_VIDEO_POSITION, mVideoPosition);
244 outState.putLong(KEY_RESUMEABLE_TIME, mResumeableTime);
245 }
246
Owen Linf9a0a432011-08-17 22:07:43 +0800247 private void showResumeDialog(Context context, final int bookmark) {
248 AlertDialog.Builder builder = new AlertDialog.Builder(context);
249 builder.setTitle(R.string.resume_playing_title);
250 builder.setMessage(String.format(
251 context.getString(R.string.resume_playing_message),
252 GalleryUtils.formatDuration(context, bookmark / 1000)));
253 builder.setOnCancelListener(new OnCancelListener() {
Owen Lin540e4692011-09-14 19:49:03 +0800254 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800255 public void onCancel(DialogInterface dialog) {
256 onCompletion();
257 }
258 });
259 builder.setPositiveButton(
260 R.string.resume_playing_resume, new OnClickListener() {
Owen Lin540e4692011-09-14 19:49:03 +0800261 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800262 public void onClick(DialogInterface dialog, int which) {
263 mVideoView.seekTo(bookmark);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800264 startVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800265 }
266 });
267 builder.setNegativeButton(
268 R.string.resume_playing_restart, 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) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800271 startVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800272 }
273 });
274 builder.show();
275 }
276
277 public void onPause() {
Owen Linf9a0a432011-08-17 22:07:43 +0800278 mHasPaused = true;
Owen Lin540e4692011-09-14 19:49:03 +0800279 mHandler.removeCallbacksAndMessages(null);
280 mVideoPosition = mVideoView.getCurrentPosition();
281 mBookmarker.setBookmark(mUri, mVideoPosition, mVideoView.getDuration());
282 mVideoView.suspend();
283 mResumeableTime = System.currentTimeMillis() + RESUMEABLE_TIMEOUT;
Owen Linf9a0a432011-08-17 22:07:43 +0800284 }
285
286 public void onResume() {
287 if (mHasPaused) {
Owen Lin540e4692011-09-14 19:49:03 +0800288 mVideoView.seekTo(mVideoPosition);
289 mVideoView.resume();
290
291 // If we have slept for too long, pause the play
292 if (System.currentTimeMillis() > mResumeableTime) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800293 pauseVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800294 }
295 }
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800296 mHandler.post(mProgressChecker);
Owen Linf9a0a432011-08-17 22:07:43 +0800297 }
298
299 public void onDestroy() {
Marco Nelissene0321522013-05-13 13:36:01 -0700300 if (mVirtualizer != null) {
301 mVirtualizer.release();
302 mVirtualizer = null;
303 }
Owen Linf9a0a432011-08-17 22:07:43 +0800304 mVideoView.stopPlayback();
305 mAudioBecomingNoisyReceiver.unregister();
306 }
307
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800308 // This updates the time bar display (if necessary). It is called every
309 // second by mProgressChecker and also from places where the time bar needs
310 // to be updated immediately.
311 private int setProgress() {
312 if (mDragging || !mShowing) {
313 return 0;
314 }
315 int position = mVideoView.getCurrentPosition();
316 int duration = mVideoView.getDuration();
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700317 mController.setTimes(position, duration, 0, 0);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800318 return position;
319 }
320
321 private void startVideo() {
322 // For streams that we expect to be slow to start up, show a
323 // progress spinner until playback starts.
324 String scheme = mUri.getScheme();
325 if ("http".equalsIgnoreCase(scheme) || "rtsp".equalsIgnoreCase(scheme)) {
326 mController.showLoading();
327 mHandler.removeCallbacks(mPlayingChecker);
328 mHandler.postDelayed(mPlayingChecker, 250);
329 } else {
330 mController.showPlaying();
Owen Lin00bb8e42012-05-23 15:47:36 -0700331 mController.hide();
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800332 }
333
334 mVideoView.start();
335 setProgress();
336 }
337
338 private void playVideo() {
339 mVideoView.start();
340 mController.showPlaying();
341 setProgress();
342 }
343
344 private void pauseVideo() {
345 mVideoView.pause();
346 mController.showPaused();
347 }
348
349 // Below are notifications from VideoView
350 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800351 public boolean onError(MediaPlayer player, int arg1, int arg2) {
352 mHandler.removeCallbacksAndMessages(null);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800353 // VideoView will show an error dialog if we return false, so no need
354 // to show more message.
355 mController.showErrorMessage("");
Owen Linf9a0a432011-08-17 22:07:43 +0800356 return false;
357 }
358
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800359 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800360 public void onCompletion(MediaPlayer mp) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800361 mController.showEnded();
Owen Linf9a0a432011-08-17 22:07:43 +0800362 onCompletion();
363 }
364
365 public void onCompletion() {
366 }
367
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800368 // Below are notifications from ControllerOverlay
369 @Override
370 public void onPlayPause() {
371 if (mVideoView.isPlaying()) {
372 pauseVideo();
373 } else {
374 playVideo();
375 }
376 }
377
378 @Override
379 public void onSeekStart() {
380 mDragging = true;
381 }
382
383 @Override
384 public void onSeekMove(int time) {
385 mVideoView.seekTo(time);
386 }
387
388 @Override
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700389 public void onSeekEnd(int time, int start, int end) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800390 mDragging = false;
391 mVideoView.seekTo(time);
392 setProgress();
393 }
394
395 @Override
396 public void onShown() {
397 mShowing = true;
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800398 setProgress();
Owen Lin0b4e9092012-06-19 14:35:06 +0800399 showSystemUi(true);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800400 }
401
402 @Override
403 public void onHidden() {
404 mShowing = false;
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800405 showSystemUi(false);
406 }
407
408 @Override
409 public void onReplay() {
410 startVideo();
411 }
412
Chih-Chung Changfc8c5032011-11-29 14:21:11 +0800413 // Below are key events passed from MovieActivity.
414 public boolean onKeyDown(int keyCode, KeyEvent event) {
415
416 // Some headsets will fire off 7-10 events on a single click
417 if (event.getRepeatCount() > 0) {
418 return isMediaKey(keyCode);
419 }
420
421 switch (keyCode) {
422 case KeyEvent.KEYCODE_HEADSETHOOK:
423 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
424 if (mVideoView.isPlaying()) {
425 pauseVideo();
426 } else {
427 playVideo();
428 }
429 return true;
Chih-Chung Chang67721732012-07-03 12:49:45 +0800430 case KEYCODE_MEDIA_PAUSE:
Chih-Chung Changfc8c5032011-11-29 14:21:11 +0800431 if (mVideoView.isPlaying()) {
432 pauseVideo();
433 }
434 return true;
Chih-Chung Chang67721732012-07-03 12:49:45 +0800435 case KEYCODE_MEDIA_PLAY:
Chih-Chung Changfc8c5032011-11-29 14:21:11 +0800436 if (!mVideoView.isPlaying()) {
437 playVideo();
438 }
439 return true;
440 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
441 case KeyEvent.KEYCODE_MEDIA_NEXT:
442 // TODO: Handle next / previous accordingly, for now we're
443 // just consuming the events.
444 return true;
445 }
446 return false;
447 }
448
449 public boolean onKeyUp(int keyCode, KeyEvent event) {
450 return isMediaKey(keyCode);
451 }
452
453 private static boolean isMediaKey(int keyCode) {
454 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
455 || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS
456 || keyCode == KeyEvent.KEYCODE_MEDIA_NEXT
457 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
458 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY
459 || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE;
460 }
461
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800462 // We want to pause when the headset is unplugged.
Owen Linf9a0a432011-08-17 22:07:43 +0800463 private class AudioBecomingNoisyReceiver extends BroadcastReceiver {
464
465 public void register() {
466 mContext.registerReceiver(this,
467 new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
468 }
469
470 public void unregister() {
471 mContext.unregisterReceiver(this);
472 }
473
474 @Override
475 public void onReceive(Context context, Intent intent) {
Owen Linaea077a2011-11-11 12:01:09 +0800476 if (mVideoView.isPlaying()) pauseVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800477 }
478 }
479}
480
481class Bookmarker {
482 private static final String TAG = "Bookmarker";
483
484 private static final String BOOKMARK_CACHE_FILE = "bookmark";
485 private static final int BOOKMARK_CACHE_MAX_ENTRIES = 100;
486 private static final int BOOKMARK_CACHE_MAX_BYTES = 10 * 1024;
487 private static final int BOOKMARK_CACHE_VERSION = 1;
488
489 private static final int HALF_MINUTE = 30 * 1000;
490 private static final int TWO_MINUTES = 4 * HALF_MINUTE;
491
492 private final Context mContext;
493
494 public Bookmarker(Context context) {
495 mContext = context;
496 }
497
498 public void setBookmark(Uri uri, int bookmark, int duration) {
499 try {
500 BlobCache cache = CacheManager.getCache(mContext,
501 BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
502 BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION);
503
504 ByteArrayOutputStream bos = new ByteArrayOutputStream();
505 DataOutputStream dos = new DataOutputStream(bos);
506 dos.writeUTF(uri.toString());
507 dos.writeInt(bookmark);
508 dos.writeInt(duration);
509 dos.flush();
510 cache.insert(uri.hashCode(), bos.toByteArray());
511 } catch (Throwable t) {
512 Log.w(TAG, "setBookmark failed", t);
513 }
514 }
515
516 public Integer getBookmark(Uri uri) {
517 try {
518 BlobCache cache = CacheManager.getCache(mContext,
519 BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
520 BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION);
521
522 byte[] data = cache.lookup(uri.hashCode());
523 if (data == null) return null;
524
525 DataInputStream dis = new DataInputStream(
526 new ByteArrayInputStream(data));
527
Owen Lin7a5e1e72012-06-20 16:54:24 +0800528 String uriString = DataInputStream.readUTF(dis);
Owen Linf9a0a432011-08-17 22:07:43 +0800529 int bookmark = dis.readInt();
530 int duration = dis.readInt();
531
532 if (!uriString.equals(uri.toString())) {
533 return null;
534 }
535
536 if ((bookmark < HALF_MINUTE) || (duration < TWO_MINUTES)
537 || (bookmark > (duration - HALF_MINUTE))) {
538 return null;
539 }
540 return Integer.valueOf(bookmark);
541 } catch (Throwable t) {
542 Log.w(TAG, "getBookmark failed", t);
543 }
544 return null;
545 }
546}