blob: 423994485fb7a9cf8457584813b47daf1ad36a83 [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
19import com.android.gallery3d.R;
20import com.android.gallery3d.common.BlobCache;
21import com.android.gallery3d.util.CacheManager;
22import com.android.gallery3d.util.GalleryUtils;
23
24import android.app.ActionBar;
25import android.app.AlertDialog;
26import android.content.BroadcastReceiver;
27import android.content.Context;
28import android.content.DialogInterface;
29import android.content.DialogInterface.OnCancelListener;
30import android.content.DialogInterface.OnClickListener;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.media.AudioManager;
34import android.media.MediaPlayer;
35import android.net.Uri;
36import android.os.Handler;
37import android.view.KeyEvent;
38import android.view.View;
39import android.widget.MediaController;
40import android.widget.VideoView;
41
42import java.io.ByteArrayInputStream;
43import java.io.ByteArrayOutputStream;
44import java.io.DataInputStream;
45import java.io.DataOutputStream;
46
47public class MoviePlayer implements
48 MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
49 @SuppressWarnings("unused")
50 private static final String TAG = "MoviePlayer";
51
52 // Copied from MediaPlaybackService in the Music Player app.
53 private static final String SERVICECMD = "com.android.music.musicservicecommand";
54 private static final String CMDNAME = "command";
55 private static final String CMDPAUSE = "pause";
56
57 private Context mContext;
58 private final VideoView mVideoView;
59 private final View mProgressView;
60 private final Bookmarker mBookmarker;
61 private final Uri mUri;
62 private final Handler mHandler = new Handler();
63 private final AudioBecomingNoisyReceiver mAudioBecomingNoisyReceiver;
64 private final ActionBar mActionBar;
65
66 private boolean mHasPaused;
67
68 private final Runnable mPlayingChecker = new Runnable() {
69 public void run() {
70 if (mVideoView.isPlaying()) {
71 mProgressView.setVisibility(View.GONE);
72 } else {
73 mHandler.postDelayed(mPlayingChecker, 250);
74 }
75 }
76 };
77
78 public MoviePlayer(View rootView, final MovieActivity movieActivity, Uri videoUri) {
79 mContext = movieActivity.getApplicationContext();
80 mVideoView = (VideoView) rootView.findViewById(R.id.surface_view);
81 mProgressView = rootView.findViewById(R.id.progress_indicator);
82 mBookmarker = new Bookmarker(movieActivity);
83 mActionBar = movieActivity.getActionBar();
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) || "rtsp".equalsIgnoreCase(scheme)) {
90 mHandler.postDelayed(mPlayingChecker, 250);
91 } else {
92 mProgressView.setVisibility(View.GONE);
93 }
94
95 mVideoView.setOnErrorListener(this);
96 mVideoView.setOnCompletionListener(this);
97 mVideoView.setVideoURI(mUri);
98
99 MediaController mediaController = new MediaController(movieActivity) {
100 @Override
101 public void show() {
102 super.show();
103 mActionBar.show();
104 }
105
106 @Override
107 public void hide() {
108 super.hide();
109 mActionBar.hide();
110 }
111 };
112 mVideoView.setMediaController(mediaController);
113 mediaController.setOnKeyListener(new View.OnKeyListener() {
114 public boolean onKey(View v, int keyCode, KeyEvent event) {
115 if (keyCode == KeyEvent.KEYCODE_BACK) {
116 if (event.getAction() == KeyEvent.ACTION_UP) {
117 movieActivity.onBackPressed();
118 }
119 return true;
120 }
121 return false;
122 }
123 });
124
125 mAudioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver();
126 mAudioBecomingNoisyReceiver.register();
127
128 // make the video view handle keys for seeking and pausing
129 mVideoView.requestFocus();
130
131 Intent i = new Intent(SERVICECMD);
132 i.putExtra(CMDNAME, CMDPAUSE);
133 movieActivity.sendBroadcast(i);
134
135 final Integer bookmark = mBookmarker.getBookmark(mUri);
136 if (bookmark != null) {
137 showResumeDialog(movieActivity, bookmark);
138 } else {
139 mVideoView.start();
140 }
141 }
142
143 private void showResumeDialog(Context context, final int bookmark) {
144 AlertDialog.Builder builder = new AlertDialog.Builder(context);
145 builder.setTitle(R.string.resume_playing_title);
146 builder.setMessage(String.format(
147 context.getString(R.string.resume_playing_message),
148 GalleryUtils.formatDuration(context, bookmark / 1000)));
149 builder.setOnCancelListener(new OnCancelListener() {
150 public void onCancel(DialogInterface dialog) {
151 onCompletion();
152 }
153 });
154 builder.setPositiveButton(
155 R.string.resume_playing_resume, new OnClickListener() {
156 public void onClick(DialogInterface dialog, int which) {
157 mVideoView.seekTo(bookmark);
158 mVideoView.start();
159 }
160 });
161 builder.setNegativeButton(
162 R.string.resume_playing_restart, new OnClickListener() {
163 public void onClick(DialogInterface dialog, int which) {
164 mVideoView.start();
165 }
166 });
167 builder.show();
168 }
169
170 public void onPause() {
171 mHandler.removeCallbacksAndMessages(null);
172 mBookmarker.setBookmark(mUri, mVideoView.getCurrentPosition(),
173 mVideoView.getDuration());
174 mVideoView.suspend();
175 mHasPaused = true;
176 }
177
178 public void onResume() {
179 if (mHasPaused) {
180 Integer bookmark = mBookmarker.getBookmark(mUri);
181 if (bookmark != null) {
182 mVideoView.seekTo(bookmark);
183 }
184 }
185 mVideoView.resume();
186 }
187
188 public void onDestroy() {
189 mVideoView.stopPlayback();
190 mAudioBecomingNoisyReceiver.unregister();
191 }
192
193 public boolean onError(MediaPlayer player, int arg1, int arg2) {
194 mHandler.removeCallbacksAndMessages(null);
195 mProgressView.setVisibility(View.GONE);
196 return false;
197 }
198
199 public void onCompletion(MediaPlayer mp) {
200 onCompletion();
201 }
202
203 public void onCompletion() {
204 }
205
206 private class AudioBecomingNoisyReceiver extends BroadcastReceiver {
207
208 public void register() {
209 mContext.registerReceiver(this,
210 new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
211 }
212
213 public void unregister() {
214 mContext.unregisterReceiver(this);
215 }
216
217 @Override
218 public void onReceive(Context context, Intent intent) {
219 if (mVideoView.isPlaying()) {
220 mVideoView.pause();
221 }
222 }
223 }
224}
225
226class Bookmarker {
227 private static final String TAG = "Bookmarker";
228
229 private static final String BOOKMARK_CACHE_FILE = "bookmark";
230 private static final int BOOKMARK_CACHE_MAX_ENTRIES = 100;
231 private static final int BOOKMARK_CACHE_MAX_BYTES = 10 * 1024;
232 private static final int BOOKMARK_CACHE_VERSION = 1;
233
234 private static final int HALF_MINUTE = 30 * 1000;
235 private static final int TWO_MINUTES = 4 * HALF_MINUTE;
236
237 private final Context mContext;
238
239 public Bookmarker(Context context) {
240 mContext = context;
241 }
242
243 public void setBookmark(Uri uri, int bookmark, int duration) {
244 try {
245 BlobCache cache = CacheManager.getCache(mContext,
246 BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
247 BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION);
248
249 ByteArrayOutputStream bos = new ByteArrayOutputStream();
250 DataOutputStream dos = new DataOutputStream(bos);
251 dos.writeUTF(uri.toString());
252 dos.writeInt(bookmark);
253 dos.writeInt(duration);
254 dos.flush();
255 cache.insert(uri.hashCode(), bos.toByteArray());
256 } catch (Throwable t) {
257 Log.w(TAG, "setBookmark failed", t);
258 }
259 }
260
261 public Integer getBookmark(Uri uri) {
262 try {
263 BlobCache cache = CacheManager.getCache(mContext,
264 BOOKMARK_CACHE_FILE, BOOKMARK_CACHE_MAX_ENTRIES,
265 BOOKMARK_CACHE_MAX_BYTES, BOOKMARK_CACHE_VERSION);
266
267 byte[] data = cache.lookup(uri.hashCode());
268 if (data == null) return null;
269
270 DataInputStream dis = new DataInputStream(
271 new ByteArrayInputStream(data));
272
273 String uriString = dis.readUTF(dis);
274 int bookmark = dis.readInt();
275 int duration = dis.readInt();
276
277 if (!uriString.equals(uri.toString())) {
278 return null;
279 }
280
281 if ((bookmark < HALF_MINUTE) || (duration < TWO_MINUTES)
282 || (bookmark > (duration - HALF_MINUTE))) {
283 return null;
284 }
285 return Integer.valueOf(bookmark);
286 } catch (Throwable t) {
287 Log.w(TAG, "getBookmark failed", t);
288 }
289 return null;
290 }
291}