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