1 /* 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 17 package com.android.camera; 18 19 import com.android.gallery.R; 20 21 import android.app.AlertDialog; 22 import android.content.ContentResolver; 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.content.DialogInterface.OnCancelListener; 28 import android.content.DialogInterface.OnClickListener; 29 import android.database.Cursor; 30 import android.database.sqlite.SQLiteException; 31 import android.media.MediaPlayer; 32 import android.net.Uri; 33 import android.os.Handler; 34 import android.provider.MediaStore; 35 import android.provider.MediaStore.Video; 36 import android.view.View; 37 import android.widget.MediaController; 38 import android.widget.VideoView; 39 40 public class MovieViewControl implements MediaPlayer.OnErrorListener, 41 MediaPlayer.OnCompletionListener { 42 43 @SuppressWarnings("unused") 44 private static final String TAG = "MovieViewControl"; 45 46 private static final int ONE_MINUTE = 60 * 1000; 47 private static final int TWO_MINUTES = 2 * ONE_MINUTE; 48 private static final int FIVE_MINUTES = 5 * ONE_MINUTE; 49 50 // Copied from MediaPlaybackService in the Music Player app. Should be 51 // public, but isn't. 52 private static final String SERVICECMD = 53 "com.android.music.musicservicecommand"; 54 private static final String CMDNAME = "command"; 55 private static final String CMDPAUSE = "pause"; 56 57 private final VideoView mVideoView; 58 private final View mProgressView; 59 private final Uri mUri; 60 private final ContentResolver mContentResolver; 61 62 // State maintained for proper onPause/OnResume behaviour. 63 private int mPositionWhenPaused = -1; 64 private boolean mWasPlayingWhenPaused = false; 65 private MediaController mMediaController; 66 67 Handler mHandler = new Handler(); 68 69 Runnable mPlayingChecker = new Runnable() { 70 public void run() { 71 if (mVideoView.isPlaying()) { 72 mProgressView.setVisibility(View.GONE); 73 } else { 74 mHandler.postDelayed(mPlayingChecker, 250); 75 } 76 } 77 }; 78 79 public MovieViewControl(View rootView, Context context, Uri videoUri) { 80 mContentResolver = context.getContentResolver(); 81 mVideoView = (VideoView) rootView.findViewById(R.id.surface_view); 82 mProgressView = rootView.findViewById(R.id.progress_indicator); 83 84 mUri = videoUri; 85 86 // For streams that we expect to be slow to start up, show a 87 // progress spinner until playback starts. 88 String scheme = mUri.getScheme(); 89 if ("http".equalsIgnoreCase(scheme) 90 || "rtsp".equalsIgnoreCase(scheme)) { 91 mHandler.postDelayed(mPlayingChecker, 250); 92 } else { 93 mProgressView.setVisibility(View.GONE); 94 } 95 96 mVideoView.setOnErrorListener(this); 97 mVideoView.setOnCompletionListener(this); 98 mVideoView.setVideoURI(mUri); 99 mMediaController = new MediaController(context); 100 mVideoView.setMediaController(mMediaController); 101 102 // make the video view handle keys for seeking and pausing 103 mVideoView.requestFocus(); 104 105 Intent i = new Intent(SERVICECMD); 106 i.putExtra(CMDNAME, CMDPAUSE); 107 context.sendBroadcast(i); 108 109 final Integer bookmark = getBookmark(); 110 if (bookmark != null) { 111 AlertDialog.Builder builder = new AlertDialog.Builder(context); 112 builder.setTitle(R.string.resume_playing_title); 113 builder.setMessage(String.format( 114 context.getString(R.string.resume_playing_message), 115 MenuHelper.formatDuration(context, bookmark))); 116 builder.setOnCancelListener(new OnCancelListener() { 117 public void onCancel(DialogInterface dialog) { 118 onCompletion(); 119 }}); 120 builder.setPositiveButton(R.string.resume_playing_resume, 121 new OnClickListener() { 122 public void onClick(DialogInterface dialog, int which) { 123 mVideoView.seekTo(bookmark); 124 mVideoView.start(); 125 }}); 126 builder.setNegativeButton(R.string.resume_playing_restart, 127 new OnClickListener() { 128 public void onClick(DialogInterface dialog, int which) { 129 mVideoView.start(); 130 }}); 131 builder.show(); 132 } else { 133 mVideoView.start(); 134 } 135 } 136 137 private static boolean uriSupportsBookmarks(Uri uri) { 138 String scheme = uri.getScheme(); 139 String authority = uri.getAuthority(); 140 return ("content".equalsIgnoreCase(scheme) 141 && MediaStore.AUTHORITY.equalsIgnoreCase(authority)); 142 } 143 144 private Integer getBookmark() { 145 if (!uriSupportsBookmarks(mUri)) { 146 return null; 147 } 148 149 String[] projection = new String[] { 150 Video.VideoColumns.DURATION, 151 Video.VideoColumns.BOOKMARK}; 152 153 try { 154 Cursor cursor = mContentResolver.query( 155 mUri, projection, null, null, null); 156 if (cursor != null) { 157 try { 158 if (cursor.moveToFirst()) { 159 int duration = getCursorInteger(cursor, 0); 160 int bookmark = getCursorInteger(cursor, 1); 161 if ((bookmark < TWO_MINUTES) 162 || (duration < FIVE_MINUTES) 163 || (bookmark > (duration - ONE_MINUTE))) { 164 return null; 165 } 166 return Integer.valueOf(bookmark); 167 } 168 } finally { 169 cursor.close(); 170 } 171 } 172 } catch (SQLiteException e) { 173 // ignore 174 } 175 176 return null; 177 } 178 179 private static int getCursorInteger(Cursor cursor, int index) { 180 try { 181 return cursor.getInt(index); 182 } catch (SQLiteException e) { 183 return 0; 184 } catch (NumberFormatException e) { 185 return 0; 186 } 187 188 } 189 190 private void setBookmark(int bookmark) { 191 if (!uriSupportsBookmarks(mUri)) { 192 return; 193 } 194 195 ContentValues values = new ContentValues(); 196 values.put(Video.VideoColumns.BOOKMARK, Integer.toString(bookmark)); 197 try { 198 mContentResolver.update(mUri, values, null, null); 199 } catch (SecurityException ex) { 200 // Ignore, can happen if we try to set the bookmark on a read-only 201 // resource such as a video attached to GMail. 202 } catch (SQLiteException e) { 203 // ignore. can happen if the content doesn't support a bookmark 204 // column. 205 } catch (UnsupportedOperationException e) { 206 // ignore. can happen if the external volume is already detached. 207 } 208 } 209 210 public void onPause() { 211 mHandler.removeCallbacksAndMessages(null); 212 setBookmark(mVideoView.getCurrentPosition()); 213 214 mPositionWhenPaused = mVideoView.getCurrentPosition(); 215 mWasPlayingWhenPaused = mVideoView.isPlaying(); 216 mVideoView.stopPlayback(); 217 } 218 219 public void onResume() { 220 if (mPositionWhenPaused >= 0) { 221 mVideoView.setVideoURI(mUri); 222 mVideoView.seekTo(mPositionWhenPaused); 223 mPositionWhenPaused = -1; 224 if (mWasPlayingWhenPaused) { 225 mMediaController.show(0); 226 } 227 } 228 } 229 230 public boolean onError(MediaPlayer player, int arg1, int arg2) { 231 mHandler.removeCallbacksAndMessages(null); 232 mProgressView.setVisibility(View.GONE); 233 return false; 234 } 235 236 public void onCompletion(MediaPlayer mp) { 237 onCompletion(); 238 } 239 240 public void onCompletion() { 241 } 242 } 243