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 
MovieViewControl(View rootView, Context context, Uri videoUri)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 
uriSupportsBookmarks(Uri uri)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 
getBookmark()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 
getCursorInteger(Cursor cursor, int index)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 
setBookmark(int bookmark)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 
onPause()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 
onResume()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 
onError(MediaPlayer player, int arg1, int arg2)230     public boolean onError(MediaPlayer player, int arg1, int arg2) {
231         mHandler.removeCallbacksAndMessages(null);
232         mProgressView.setVisibility(View.GONE);
233         return false;
234     }
235 
onCompletion(MediaPlayer mp)236     public void onCompletion(MediaPlayer mp) {
237         onCompletion();
238     }
239 
onCompletion()240     public void onCompletion() {
241     }
242 }
243