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
17package com.android.camera;
18
19import com.android.gallery.R;
20
21import android.app.AlertDialog;
22import android.content.ContentResolver;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.DialogInterface.OnCancelListener;
28import android.content.DialogInterface.OnClickListener;
29import android.database.Cursor;
30import android.database.sqlite.SQLiteException;
31import android.media.MediaPlayer;
32import android.net.Uri;
33import android.os.Handler;
34import android.provider.MediaStore;
35import android.provider.MediaStore.Video;
36import android.view.View;
37import android.widget.MediaController;
38import android.widget.VideoView;
39
40public 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