blob: 11b40bdf8387f98515946a90023db7204e2ac4e4 [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 Linf9a0a432011-08-17 22:07:43 +080019import android.app.AlertDialog;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.DialogInterface.OnCancelListener;
24import android.content.DialogInterface.OnClickListener;
25import android.content.Intent;
26import android.content.IntentFilter;
Owen Lin00bb8e42012-05-23 15:47:36 -070027import android.graphics.Color;
Owen Linf9a0a432011-08-17 22:07:43 +080028import android.media.AudioManager;
29import android.media.MediaPlayer;
30import android.net.Uri;
Owen Lin540e4692011-09-14 19:49:03 +080031import android.os.Bundle;
Owen Linf9a0a432011-08-17 22:07:43 +080032import android.os.Handler;
Chih-Chung Changfc8c5032011-11-29 14:21:11 +080033import android.view.KeyEvent;
Chih-Chung Chang209a9162011-10-14 16:09:09 +080034import android.view.MotionEvent;
Owen Linf9a0a432011-08-17 22:07:43 +080035import android.view.View;
Chih-Chung Chang209a9162011-10-14 16:09:09 +080036import android.view.ViewGroup;
Owen Linf9a0a432011-08-17 22:07:43 +080037import android.widget.VideoView;
38
Owen Linaea077a2011-11-11 12:01:09 +080039import com.android.gallery3d.R;
40import com.android.gallery3d.common.BlobCache;
41import com.android.gallery3d.util.CacheManager;
42import com.android.gallery3d.util.GalleryUtils;
43
Owen Linf9a0a432011-08-17 22:07:43 +080044import java.io.ByteArrayInputStream;
45import java.io.ByteArrayOutputStream;
46import java.io.DataInputStream;
47import java.io.DataOutputStream;
48
49public class MoviePlayer implements
Chih-Chung Chang209a9162011-10-14 16:09:09 +080050 MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener,
51 ControllerOverlay.Listener {
Owen Linf9a0a432011-08-17 22:07:43 +080052 @SuppressWarnings("unused")
53 private static final String TAG = "MoviePlayer";
54
Owen Lin540e4692011-09-14 19:49:03 +080055 private static final String KEY_VIDEO_POSITION = "video-position";
56 private static final String KEY_RESUMEABLE_TIME = "resumeable-timeout";
57
Owen Linf9a0a432011-08-17 22:07:43 +080058 // Copied from MediaPlaybackService in the Music Player app.
59 private static final String SERVICECMD = "com.android.music.musicservicecommand";
60 private static final String CMDNAME = "command";
61 private static final String CMDPAUSE = "pause";
62
Owen Lin00bb8e42012-05-23 15:47:36 -070063 private static final long BLACK_TIMEOUT = 500;
64
Owen Lin540e4692011-09-14 19:49:03 +080065 // If we resume the acitivty with in RESUMEABLE_TIMEOUT, we will keep playing.
66 // Otherwise, we pause the player.
67 private static final long RESUMEABLE_TIMEOUT = 3 * 60 * 1000; // 3 mins
68
Owen Linf9a0a432011-08-17 22:07:43 +080069 private Context mContext;
70 private final VideoView mVideoView;
Owen Lin00bb8e42012-05-23 15:47:36 -070071 private final View mRootView;
Owen Linf9a0a432011-08-17 22:07:43 +080072 private final Bookmarker mBookmarker;
73 private final Uri mUri;
74 private final Handler mHandler = new Handler();
75 private final AudioBecomingNoisyReceiver mAudioBecomingNoisyReceiver;
Owen Lin00bb8e42012-05-23 15:47:36 -070076 private final MovieControllerOverlay mController;
Owen Linf9a0a432011-08-17 22:07:43 +080077
Owen Lin540e4692011-09-14 19:49:03 +080078 private long mResumeableTime = Long.MAX_VALUE;
79 private int mVideoPosition = 0;
80 private boolean mHasPaused = false;
Ray Chen24525c22012-05-22 17:34:29 +080081 private int mLastSystemUiVis = 0;
Owen Linf9a0a432011-08-17 22:07:43 +080082
Chih-Chung Chang209a9162011-10-14 16:09:09 +080083 // If the time bar is being dragged.
84 private boolean mDragging;
85
86 // If the time bar is visible.
87 private boolean mShowing;
88
Owen Linf9a0a432011-08-17 22:07:43 +080089 private final Runnable mPlayingChecker = new Runnable() {
Owen Lin540e4692011-09-14 19:49:03 +080090 @Override
Owen Linf9a0a432011-08-17 22:07:43 +080091 public void run() {
92 if (mVideoView.isPlaying()) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +080093 mController.showPlaying();
Owen Linf9a0a432011-08-17 22:07:43 +080094 } else {
95 mHandler.postDelayed(mPlayingChecker, 250);
96 }
97 }
98 };
99
Owen Lin00bb8e42012-05-23 15:47:36 -0700100 private final Runnable mRemoveBackground = new Runnable() {
101 @Override
102 public void run() {
103 mRootView.setBackground(null);
104 }
105 };
106
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800107 private final Runnable mProgressChecker = new Runnable() {
108 @Override
109 public void run() {
110 int pos = setProgress();
111 mHandler.postDelayed(mProgressChecker, 1000 - (pos % 1000));
112 }
113 };
114
Owen Lin00bb8e42012-05-23 15:47:36 -0700115 public MoviePlayer(View rootView, final MovieActivity movieActivity,
116 Uri videoUri, Bundle savedInstance, boolean canReplay) {
Owen Linf9a0a432011-08-17 22:07:43 +0800117 mContext = movieActivity.getApplicationContext();
Owen Lin00bb8e42012-05-23 15:47:36 -0700118 mRootView = rootView;
Owen Linf9a0a432011-08-17 22:07:43 +0800119 mVideoView = (VideoView) rootView.findViewById(R.id.surface_view);
Owen Linf9a0a432011-08-17 22:07:43 +0800120 mBookmarker = new Bookmarker(movieActivity);
Owen Linf9a0a432011-08-17 22:07:43 +0800121 mUri = videoUri;
122
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800123 mController = new MovieControllerOverlay(mContext);
124 ((ViewGroup)rootView).addView(mController.getView());
125 mController.setListener(this);
126 mController.setCanReplay(canReplay);
Owen Linf9a0a432011-08-17 22:07:43 +0800127
128 mVideoView.setOnErrorListener(this);
129 mVideoView.setOnCompletionListener(this);
130 mVideoView.setVideoURI(mUri);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800131 mVideoView.setOnTouchListener(new View.OnTouchListener() {
Owen Lin00bb8e42012-05-23 15:47:36 -0700132 @Override
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800133 public boolean onTouch(View v, MotionEvent event) {
134 mController.show();
135 return true;
Owen Linf9a0a432011-08-17 22:07:43 +0800136 }
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800137 });
Chih-Chung Chang81760122011-09-27 16:50:16 +0800138
Owen Lin00bb8e42012-05-23 15:47:36 -0700139 // The SurfaceView is transparent before drawing the first frame.
140 // This makes the UI flashing when open a video. (black -> old screen
141 // -> video) However, we have no way to know the timing of the first
142 // frame. So, we hide the VideoView for a while to make sure the
143 // video has been drawn on it.
144 mVideoView.postDelayed(new Runnable() {
145 @Override
146 public void run() {
147 mVideoView.setVisibility(View.VISIBLE);
148 }
149 }, BLACK_TIMEOUT);
150
Chih-Chung Chang81760122011-09-27 16:50:16 +0800151 // When the user touches the screen or uses some hard key, the framework
152 // will change system ui visibility from invisible to visible. We show
Ray Chen392a2922012-05-14 17:19:56 +0800153 // the media control and enable system UI (e.g. ActionBar) to be visible at this point
Chih-Chung Chang81760122011-09-27 16:50:16 +0800154 mVideoView.setOnSystemUiVisibilityChangeListener(
155 new View.OnSystemUiVisibilityChangeListener() {
Owen Lin00bb8e42012-05-23 15:47:36 -0700156 @Override
Chih-Chung Chang81760122011-09-27 16:50:16 +0800157 public void onSystemUiVisibilityChange(int visibility) {
Ray Chen24525c22012-05-22 17:34:29 +0800158 int diff = mLastSystemUiVis ^ visibility;
159 mLastSystemUiVis = visibility;
160 if ((diff & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
161 && (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800162 mController.show();
Owen Lin00bb8e42012-05-23 15:47:36 -0700163
164 // We need to set the background to clear ghosting images
165 // when ActionBar slides in. However, if we keep the background,
166 // there will be one additional layer in HW composer, which is bad
167 // to battery. As a solution, we remove the background when we
168 // hide the action bar
169 mHandler.removeCallbacks(mRemoveBackground);
170 mRootView.setBackgroundColor(Color.BLACK);
171 } else {
172 mHandler.removeCallbacks(mRemoveBackground);
173
174 // Wait for the slide out animation, one second should be enough
175 mHandler.postDelayed(mRemoveBackground, 1000);
Chih-Chung Chang81760122011-09-27 16:50:16 +0800176 }
177 }
178 });
179
Ray Chen392a2922012-05-14 17:19:56 +0800180 // Hide system UI by default
181 showSystemUi(false);
182
Owen Linf9a0a432011-08-17 22:07:43 +0800183 mAudioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver();
184 mAudioBecomingNoisyReceiver.register();
185
Owen Linf9a0a432011-08-17 22:07:43 +0800186 Intent i = new Intent(SERVICECMD);
187 i.putExtra(CMDNAME, CMDPAUSE);
188 movieActivity.sendBroadcast(i);
189
Owen Lin540e4692011-09-14 19:49:03 +0800190 if (savedInstance != null) { // this is a resumed activity
191 mVideoPosition = savedInstance.getInt(KEY_VIDEO_POSITION, 0);
192 mResumeableTime = savedInstance.getLong(KEY_RESUMEABLE_TIME, Long.MAX_VALUE);
Owen Linf9a0a432011-08-17 22:07:43 +0800193 mVideoView.start();
Owen Lin540e4692011-09-14 19:49:03 +0800194 mVideoView.suspend();
195 mHasPaused = true;
196 } else {
197 final Integer bookmark = mBookmarker.getBookmark(mUri);
198 if (bookmark != null) {
199 showResumeDialog(movieActivity, bookmark);
200 } else {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800201 startVideo();
Owen Lin540e4692011-09-14 19:49:03 +0800202 }
Owen Linf9a0a432011-08-17 22:07:43 +0800203 }
204 }
205
Chih-Chung Chang81760122011-09-27 16:50:16 +0800206 private void showSystemUi(boolean visible) {
Ray Chen24525c22012-05-22 17:34:29 +0800207 int flag = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
208 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
209 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
210 if (!visible) {
211 flag |= View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN
212 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
Ray Chen392a2922012-05-14 17:19:56 +0800213 }
Ray Chen24525c22012-05-22 17:34:29 +0800214 mVideoView.setSystemUiVisibility(flag);
Chih-Chung Chang81760122011-09-27 16:50:16 +0800215 }
216
Owen Lin540e4692011-09-14 19:49:03 +0800217 public void onSaveInstanceState(Bundle outState) {
218 outState.putInt(KEY_VIDEO_POSITION, mVideoPosition);
219 outState.putLong(KEY_RESUMEABLE_TIME, mResumeableTime);
220 }
221
Owen Linf9a0a432011-08-17 22:07:43 +0800222 private void showResumeDialog(Context context, final int bookmark) {
223 AlertDialog.Builder builder = new AlertDialog.Builder(context);
224 builder.setTitle(R.string.resume_playing_title);
225 builder.setMessage(String.format(
226 context.getString(R.string.resume_playing_message),
227 GalleryUtils.formatDuration(context, bookmark / 1000)));
228 builder.setOnCancelListener(new OnCancelListener() {
Owen Lin540e4692011-09-14 19:49:03 +0800229 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800230 public void onCancel(DialogInterface dialog) {
231 onCompletion();
232 }
233 });
234 builder.setPositiveButton(
235 R.string.resume_playing_resume, new OnClickListener() {
Owen Lin540e4692011-09-14 19:49:03 +0800236 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800237 public void onClick(DialogInterface dialog, int which) {
238 mVideoView.seekTo(bookmark);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800239 startVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800240 }
241 });
242 builder.setNegativeButton(
243 R.string.resume_playing_restart, new OnClickListener() {
Owen Lin540e4692011-09-14 19:49:03 +0800244 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800245 public void onClick(DialogInterface dialog, int which) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800246 startVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800247 }
248 });
249 builder.show();
250 }
251
252 public void onPause() {
Owen Linf9a0a432011-08-17 22:07:43 +0800253 mHasPaused = true;
Owen Lin540e4692011-09-14 19:49:03 +0800254 mHandler.removeCallbacksAndMessages(null);
255 mVideoPosition = mVideoView.getCurrentPosition();
256 mBookmarker.setBookmark(mUri, mVideoPosition, mVideoView.getDuration());
257 mVideoView.suspend();
258 mResumeableTime = System.currentTimeMillis() + RESUMEABLE_TIMEOUT;
Owen Linf9a0a432011-08-17 22:07:43 +0800259 }
260
261 public void onResume() {
262 if (mHasPaused) {
Owen Lin540e4692011-09-14 19:49:03 +0800263 mVideoView.seekTo(mVideoPosition);
264 mVideoView.resume();
265
266 // If we have slept for too long, pause the play
267 if (System.currentTimeMillis() > mResumeableTime) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800268 pauseVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800269 }
270 }
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800271 mHandler.post(mProgressChecker);
Owen Linf9a0a432011-08-17 22:07:43 +0800272 }
273
274 public void onDestroy() {
275 mVideoView.stopPlayback();
276 mAudioBecomingNoisyReceiver.unregister();
277 }
278
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800279 // This updates the time bar display (if necessary). It is called every
280 // second by mProgressChecker and also from places where the time bar needs
281 // to be updated immediately.
282 private int setProgress() {
283 if (mDragging || !mShowing) {
284 return 0;
285 }
286 int position = mVideoView.getCurrentPosition();
287 int duration = mVideoView.getDuration();
288 mController.setTimes(position, duration);
289 return position;
290 }
291
292 private void startVideo() {
293 // For streams that we expect to be slow to start up, show a
294 // progress spinner until playback starts.
295 String scheme = mUri.getScheme();
296 if ("http".equalsIgnoreCase(scheme) || "rtsp".equalsIgnoreCase(scheme)) {
297 mController.showLoading();
298 mHandler.removeCallbacks(mPlayingChecker);
299 mHandler.postDelayed(mPlayingChecker, 250);
300 } else {
301 mController.showPlaying();
Owen Lin00bb8e42012-05-23 15:47:36 -0700302 mController.hide();
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800303 }
304
305 mVideoView.start();
306 setProgress();
307 }
308
309 private void playVideo() {
310 mVideoView.start();
311 mController.showPlaying();
312 setProgress();
313 }
314
315 private void pauseVideo() {
316 mVideoView.pause();
317 mController.showPaused();
318 }
319
320 // Below are notifications from VideoView
321 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800322 public boolean onError(MediaPlayer player, int arg1, int arg2) {
323 mHandler.removeCallbacksAndMessages(null);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800324 // VideoView will show an error dialog if we return false, so no need
325 // to show more message.
326 mController.showErrorMessage("");
Owen Linf9a0a432011-08-17 22:07:43 +0800327 return false;
328 }
329
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800330 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800331 public void onCompletion(MediaPlayer mp) {
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800332 mController.showEnded();
Owen Linf9a0a432011-08-17 22:07:43 +0800333 onCompletion();
334 }
335
336 public void onCompletion() {
337 }
338
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800339 // Below are notifications from ControllerOverlay
340 @Override
341 public void onPlayPause() {
342 if (mVideoView.isPlaying()) {
343 pauseVideo();
344 } else {
345 playVideo();
346 }
347 }
348
349 @Override
350 public void onSeekStart() {
351 mDragging = true;
352 }
353
354 @Override
355 public void onSeekMove(int time) {
356 mVideoView.seekTo(time);
357 }
358
359 @Override
360 public void onSeekEnd(int time) {
361 mDragging = false;
362 mVideoView.seekTo(time);
363 setProgress();
364 }
365
366 @Override
367 public void onShown() {
368 mShowing = true;
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800369 setProgress();
Owen Lin0b4e9092012-06-19 14:35:06 +0800370 showSystemUi(true);
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800371 }
372
373 @Override
374 public void onHidden() {
375 mShowing = false;
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800376 showSystemUi(false);
377 }
378
379 @Override
380 public void onReplay() {
381 startVideo();
382 }
383
Chih-Chung Changfc8c5032011-11-29 14:21:11 +0800384 // Below are key events passed from MovieActivity.
385 public boolean onKeyDown(int keyCode, KeyEvent event) {
386
387 // Some headsets will fire off 7-10 events on a single click
388 if (event.getRepeatCount() > 0) {
389 return isMediaKey(keyCode);
390 }
391
392 switch (keyCode) {
393 case KeyEvent.KEYCODE_HEADSETHOOK:
394 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
395 if (mVideoView.isPlaying()) {
396 pauseVideo();
397 } else {
398 playVideo();
399 }
400 return true;
401 case KeyEvent.KEYCODE_MEDIA_PAUSE:
402 if (mVideoView.isPlaying()) {
403 pauseVideo();
404 }
405 return true;
406 case KeyEvent.KEYCODE_MEDIA_PLAY:
407 if (!mVideoView.isPlaying()) {
408 playVideo();
409 }
410 return true;
411 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
412 case KeyEvent.KEYCODE_MEDIA_NEXT:
413 // TODO: Handle next / previous accordingly, for now we're
414 // just consuming the events.
415 return true;
416 }
417 return false;
418 }
419
420 public boolean onKeyUp(int keyCode, KeyEvent event) {
421 return isMediaKey(keyCode);
422 }
423
424 private static boolean isMediaKey(int keyCode) {
425 return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
426 || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS
427 || keyCode == KeyEvent.KEYCODE_MEDIA_NEXT
428 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
429 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY
430 || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE;
431 }
432
Chih-Chung Chang209a9162011-10-14 16:09:09 +0800433 // We want to pause when the headset is unplugged.
Owen Linf9a0a432011-08-17 22:07:43 +0800434 private class AudioBecomingNoisyReceiver extends BroadcastReceiver {
435
436 public void register() {
437 mContext.registerReceiver(this,
438 new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
439 }
440
441 public void unregister() {
442 mContext.unregisterReceiver(this);
443 }
444
445 @Override
446 public void onReceive(Context context, Intent intent) {
Owen Linaea077a2011-11-11 12:01:09 +0800447 if (mVideoView.isPlaying()) pauseVideo();
Owen Linf9a0a432011-08-17 22:07:43 +0800448 }
449 }
450}
451
452class Bookmarker {
453 private static final String TAG = "Bookmarker";
454
455 private static final String BOOKMARK_CACHE_FILE = "bookmark";
456 private static final int BOOKMARK_CACHE_MAX_ENTRIES = 100;
457 private static final int BOOKMARK_CACHE_MAX_BYTES = 10 * 1024;
458 private static final int BOOKMARK_CACHE_VERSION = 1;
459
460 private static final int HALF_MINUTE = 30 * 1000;
461 private static final int TWO_MINUTES = 4 * HALF_MINUTE;
462
463 private final Context mContext;
464
465 public Bookmarker(Context context) {
466 mContext = context;
467 }
468
469 public void setBookmark(Uri uri, int bookmark, int duration) {
470 try {
471 BlobCache cache = CacheManager.getCache(mContext,
472 BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
473 BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION);
474
475 ByteArrayOutputStream bos = new ByteArrayOutputStream();
476 DataOutputStream dos = new DataOutputStream(bos);
477 dos.writeUTF(uri.toString());
478 dos.writeInt(bookmark);
479 dos.writeInt(duration);
480 dos.flush();
481 cache.insert(uri.hashCode(), bos.toByteArray());
482 } catch (Throwable t) {
483 Log.w(TAG, "setBookmark failed", t);
484 }
485 }
486
487 public Integer getBookmark(Uri uri) {
488 try {
489 BlobCache cache = CacheManager.getCache(mContext,
490 BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
491 BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION);
492
493 byte[] data = cache.lookup(uri.hashCode());
494 if (data == null) return null;
495
496 DataInputStream dis = new DataInputStream(
497 new ByteArrayInputStream(data));
498
499 String uriString = dis.readUTF(dis);
500 int bookmark = dis.readInt();
501 int duration = dis.readInt();
502
503 if (!uriString.equals(uri.toString())) {
504 return null;
505 }
506
507 if ((bookmark < HALF_MINUTE) || (duration < TWO_MINUTES)
508 || (bookmark > (duration - HALF_MINUTE))) {
509 return null;
510 }
511 return Integer.valueOf(bookmark);
512 } catch (Throwable t) {
513 Log.w(TAG, "getBookmark failed", t);
514 }
515 return null;
516 }
517}