blob: 7a76be5dcc43b62a7c6ac4cf9b4583db2fa8d57b [file] [log] [blame]
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -07001/*
2 * Copyright (C) 2012 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
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070019import android.app.ProgressDialog;
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -070020import android.content.ContentResolver;
21import android.content.ContentValues;
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070022import android.content.Context;
23import android.content.Intent;
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -070024import android.database.Cursor;
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070025import android.media.MediaPlayer;
26import android.net.Uri;
27import android.os.Bundle;
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -070028import android.os.Environment;
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070029import android.os.Handler;
Teng-Hui Zhu28333552012-10-02 11:21:37 -070030import android.provider.MediaStore;
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -070031import android.provider.MediaStore.Video;
32import android.provider.MediaStore.Video.VideoColumns;
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070033import android.view.View;
34import android.view.ViewGroup;
Teng-Hui Zhu028ecd42012-09-26 16:34:44 -070035import android.view.Window;
36import android.widget.TextView;
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070037import android.widget.Toast;
38import android.widget.VideoView;
39
Teng-Hui Zhub5f374c2012-11-13 10:39:38 -080040import com.actionbarsherlock.app.ActionBar;
41import com.actionbarsherlock.app.SherlockActivity;
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070042import com.android.gallery3d.R;
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -070043import com.android.gallery3d.util.BucketNames;
Teng-Hui Zhu648b3392012-11-13 10:39:26 -080044import com.android.gallery3d.util.SaveVideoFileInfo;
45import com.android.gallery3d.util.SaveVideoFileUtils;
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070046
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -070047import java.io.File;
48import java.io.IOException;
49import java.sql.Date;
50import java.text.SimpleDateFormat;
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070051
Teng-Hui Zhub5f374c2012-11-13 10:39:38 -080052public class TrimVideo extends SherlockActivity implements
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070053 MediaPlayer.OnErrorListener,
54 MediaPlayer.OnCompletionListener,
55 ControllerOverlay.Listener {
56
57 private VideoView mVideoView;
Doris Liubf1677a2012-10-29 13:11:10 -070058 private TextView mSaveVideoTextView;
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070059 private TrimControllerOverlay mController;
60 private Context mContext;
61 private Uri mUri;
62 private final Handler mHandler = new Handler();
63 public static final String TRIM_ACTION = "com.android.camera.action.TRIM";
64
65 public ProgressDialog mProgress;
66
67 private int mTrimStartTime = 0;
68 private int mTrimEndTime = 0;
69 private int mVideoPosition = 0;
70 public static final String KEY_TRIM_START = "trim_start";
71 public static final String KEY_TRIM_END = "trim_end";
72 public static final String KEY_VIDEO_POSITION = "video_pos";
73 private boolean mHasPaused = false;
74
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -070075 private String mSrcVideoPath = null;
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -070076 private static final String TIME_STAMP_NAME = "'TRIM'_yyyyMMdd_HHmmss";
Teng-Hui Zhu648b3392012-11-13 10:39:26 -080077 private SaveVideoFileInfo mDstFileInfo = null;
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -070078
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -070079 @Override
80 public void onCreate(Bundle savedInstanceState) {
81 mContext = getApplicationContext();
82 super.onCreate(savedInstanceState);
83
Teng-Hui Zhu5e926682012-09-26 14:20:46 -070084 requestWindowFeature(Window.FEATURE_ACTION_BAR);
85 requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
86
Teng-Hui Zhub5f374c2012-11-13 10:39:38 -080087 ActionBar actionBar = getSupportActionBar();
Teng-Hui Zhu028ecd42012-09-26 16:34:44 -070088 int displayOptions = ActionBar.DISPLAY_SHOW_HOME;
89 actionBar.setDisplayOptions(0, displayOptions);
90 displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM;
Teng-Hui Zhu5e926682012-09-26 14:20:46 -070091 actionBar.setDisplayOptions(displayOptions, displayOptions);
Teng-Hui Zhu028ecd42012-09-26 16:34:44 -070092 actionBar.setCustomView(R.layout.trim_menu);
93
Doris Liubf1677a2012-10-29 13:11:10 -070094 mSaveVideoTextView = (TextView) findViewById(R.id.start_trim);
Teng-Hui Zhu4450e432012-09-28 09:10:21 -070095 mSaveVideoTextView.setOnClickListener(new View.OnClickListener() {
Teng-Hui Zhu028ecd42012-09-26 16:34:44 -070096 @Override
Teng-Hui Zhu4450e432012-09-28 09:10:21 -070097 public void onClick(View arg0) {
98 trimVideo();
Teng-Hui Zhu028ecd42012-09-26 16:34:44 -070099 }
100 });
Doris Liubf1677a2012-10-29 13:11:10 -0700101 mSaveVideoTextView.setEnabled(false);
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700102
103 Intent intent = getIntent();
104 mUri = intent.getData();
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700105 mSrcVideoPath = intent.getStringExtra(PhotoPage.KEY_MEDIA_ITEM_PATH);
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700106 setContentView(R.layout.trim_view);
107 View rootView = findViewById(R.id.trim_view_root);
108
109 mVideoView = (VideoView) rootView.findViewById(R.id.surface_view);
110
111 mController = new TrimControllerOverlay(mContext);
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700112 ((ViewGroup) rootView).addView(mController.getView());
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700113 mController.setListener(this);
114 mController.setCanReplay(true);
115
116 mVideoView.setOnErrorListener(this);
117 mVideoView.setOnCompletionListener(this);
118 mVideoView.setVideoURI(mUri);
119
120 playVideo();
121 }
122
123 @Override
124 public void onResume() {
125 super.onResume();
126 if (mHasPaused) {
127 mVideoView.seekTo(mVideoPosition);
128 mVideoView.resume();
129 mHasPaused = false;
130 }
131 mHandler.post(mProgressChecker);
132 }
133
134 @Override
135 public void onPause() {
136 mHasPaused = true;
137 mHandler.removeCallbacksAndMessages(null);
138 mVideoPosition = mVideoView.getCurrentPosition();
139 mVideoView.suspend();
140 super.onPause();
141 }
142
143 @Override
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700144 public void onStop() {
145 if (mProgress != null) {
146 mProgress.dismiss();
147 mProgress = null;
148 }
149 super.onStop();
150 }
151
152 @Override
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700153 public void onDestroy() {
154 mVideoView.stopPlayback();
155 super.onDestroy();
156 }
157
158 private final Runnable mProgressChecker = new Runnable() {
159 @Override
160 public void run() {
161 int pos = setProgress();
162 mHandler.postDelayed(mProgressChecker, 200 - (pos % 200));
163 }
164 };
165
166 @Override
167 public void onSaveInstanceState(Bundle savedInstanceState) {
168 savedInstanceState.putInt(KEY_TRIM_START, mTrimStartTime);
169 savedInstanceState.putInt(KEY_TRIM_END, mTrimEndTime);
170 savedInstanceState.putInt(KEY_VIDEO_POSITION, mVideoPosition);
171 super.onSaveInstanceState(savedInstanceState);
172 }
173
174 @Override
175 public void onRestoreInstanceState(Bundle savedInstanceState) {
176 super.onRestoreInstanceState(savedInstanceState);
177 mTrimStartTime = savedInstanceState.getInt(KEY_TRIM_START, 0);
178 mTrimEndTime = savedInstanceState.getInt(KEY_TRIM_END, 0);
179 mVideoPosition = savedInstanceState.getInt(KEY_VIDEO_POSITION, 0);
180 }
181
182 // This updates the time bar display (if necessary). It is called by
183 // mProgressChecker and also from places where the time bar needs
184 // to be updated immediately.
185 private int setProgress() {
186 mVideoPosition = mVideoView.getCurrentPosition();
187 // If the video position is smaller than the starting point of trimming,
188 // correct it.
189 if (mVideoPosition < mTrimStartTime) {
190 mVideoView.seekTo(mTrimStartTime);
191 mVideoPosition = mTrimStartTime;
192 }
193 // If the position is bigger than the end point of trimming, show the
194 // replay button and pause.
195 if (mVideoPosition >= mTrimEndTime && mTrimEndTime > 0) {
196 if (mVideoPosition > mTrimEndTime) {
197 mVideoView.seekTo(mTrimEndTime);
198 mVideoPosition = mTrimEndTime;
199 }
200 mController.showEnded();
201 mVideoView.pause();
202 }
203
204 int duration = mVideoView.getDuration();
205 if (duration > 0 && mTrimEndTime == 0) {
206 mTrimEndTime = duration;
207 }
208 mController.setTimes(mVideoPosition, duration, mTrimStartTime, mTrimEndTime);
209 return mVideoPosition;
210 }
211
212 private void playVideo() {
213 mVideoView.start();
214 mController.showPlaying();
215 setProgress();
216 }
217
218 private void pauseVideo() {
219 mVideoView.pause();
220 mController.showPaused();
221 }
222
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700223
Doris Liubf1677a2012-10-29 13:11:10 -0700224 private boolean isModified() {
Teng-Hui Zhud44b0d82012-09-29 13:32:01 -0700225 int delta = mTrimEndTime - mTrimStartTime;
Doris Liubf1677a2012-10-29 13:11:10 -0700226
Teng-Hui Zhud44b0d82012-09-29 13:32:01 -0700227 // Considering that we only trim at sync frame, we don't want to trim
228 // when the time interval is too short or too close to the origin.
Doris Liubf1677a2012-10-29 13:11:10 -0700229 if (delta < 100 || Math.abs(mVideoView.getDuration() - delta) < 100) {
230 return false;
231 } else {
232 return true;
Teng-Hui Zhud44b0d82012-09-29 13:32:01 -0700233 }
Doris Liubf1677a2012-10-29 13:11:10 -0700234 }
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700235
Teng-Hui Zhu648b3392012-11-13 10:39:26 -0800236 private void trimVideo() {
237
238 mDstFileInfo = SaveVideoFileUtils.getDstMp4FileInfo(TIME_STAMP_NAME,
239 getContentResolver(), mUri, getString(R.string.folder_download));
240 final File mSrcFile = new File(mSrcVideoPath);
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700241
242 showProgressDialog();
243
244 new Thread(new Runnable() {
245 @Override
246 public void run() {
247 try {
Teng-Hui Zhu648b3392012-11-13 10:39:26 -0800248 VideoUtils.startTrim(mSrcFile, mDstFileInfo.mFile,
249 mTrimStartTime, mTrimEndTime, mVideoView.getDuration());
Teng-Hui Zhud44b0d82012-09-29 13:32:01 -0700250 // Update the database for adding a new video file.
Teng-Hui Zhu648b3392012-11-13 10:39:26 -0800251 SaveVideoFileUtils.insertContent(mDstFileInfo,
252 getContentResolver(), mUri);
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700253 } catch (IOException e) {
254 e.printStackTrace();
255 }
256 // After trimming is done, trigger the UI changed.
257 mHandler.post(new Runnable() {
258 @Override
259 public void run() {
Teng-Hui Zhud44b0d82012-09-29 13:32:01 -0700260 Toast.makeText(getApplicationContext(),
Teng-Hui Zhu648b3392012-11-13 10:39:26 -0800261 getString(R.string.save_into, mDstFileInfo.mFolderName),
Teng-Hui Zhud44b0d82012-09-29 13:32:01 -0700262 Toast.LENGTH_SHORT)
263 .show();
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700264 // TODO: change trimming into a service to avoid
265 // this progressDialog and add notification properly.
266 if (mProgress != null) {
267 mProgress.dismiss();
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700268 mProgress = null;
Teng-Hui Zhud44b0d82012-09-29 13:32:01 -0700269 // Show the result only when the activity not stopped.
270 Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
Teng-Hui Zhub5f374c2012-11-13 10:39:38 -0800271 intent.setDataAndType(Uri.fromFile(mDstFileInfo.mFile), "video/*");
Teng-Hui Zhu28333552012-10-02 11:21:37 -0700272 intent.putExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, false);
Teng-Hui Zhud44b0d82012-09-29 13:32:01 -0700273 startActivity(intent);
Teng-Hui Zhu28333552012-10-02 11:21:37 -0700274 finish();
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700275 }
276 }
277 });
278 }
279 }).start();
280 }
281
282 private void showProgressDialog() {
283 // create a background thread to trim the video.
284 // and show the progress.
285 mProgress = new ProgressDialog(this);
Teng-Hui Zhud44b0d82012-09-29 13:32:01 -0700286 mProgress.setTitle(getString(R.string.trimming));
287 mProgress.setMessage(getString(R.string.please_wait));
Teng-Hui Zhu15ff1b12012-09-23 15:02:56 -0700288 // TODO: make this cancelable.
289 mProgress.setCancelable(false);
290 mProgress.setCanceledOnTouchOutside(false);
291 mProgress.show();
292 }
293
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700294 @Override
295 public void onPlayPause() {
296 if (mVideoView.isPlaying()) {
297 pauseVideo();
298 } else {
299 playVideo();
300 }
301 }
302
303 @Override
304 public void onSeekStart() {
305 pauseVideo();
306 }
307
308 @Override
309 public void onSeekMove(int time) {
310 mVideoView.seekTo(time);
311 }
312
313 @Override
314 public void onSeekEnd(int time, int start, int end) {
315 mVideoView.seekTo(time);
316 mTrimStartTime = start;
317 mTrimEndTime = end;
318 setProgress();
Doris Liubf1677a2012-10-29 13:11:10 -0700319 // Enable save if there's modifications
320 mSaveVideoTextView.setEnabled(isModified());
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700321 }
322
323 @Override
324 public void onShown() {
325 }
326
Teng-Hui Zhu3f1f1ba2012-08-24 14:50:37 -0700327 @Override
328 public void onHidden() {
329 }
330
331 @Override
332 public void onReplay() {
333 mVideoView.seekTo(mTrimStartTime);
334 playVideo();
335 }
336
337 @Override
338 public void onCompletion(MediaPlayer mp) {
339 mController.showEnded();
340 }
341
342 @Override
343 public boolean onError(MediaPlayer mp, int what, int extra) {
344 return false;
345 }
346}