TrimVideo.java revision b5f374c30d06dc0409891f85a4bd5e1e3ddf8700
1/* 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 19import android.app.ProgressDialog; 20import android.content.ContentResolver; 21import android.content.ContentValues; 22import android.content.Context; 23import android.content.Intent; 24import android.database.Cursor; 25import android.media.MediaPlayer; 26import android.net.Uri; 27import android.os.Bundle; 28import android.os.Environment; 29import android.os.Handler; 30import android.provider.MediaStore; 31import android.provider.MediaStore.Video; 32import android.provider.MediaStore.Video.VideoColumns; 33import android.view.View; 34import android.view.ViewGroup; 35import android.view.Window; 36import android.widget.TextView; 37import android.widget.Toast; 38import android.widget.VideoView; 39 40import com.actionbarsherlock.app.ActionBar; 41import com.actionbarsherlock.app.SherlockActivity; 42import com.android.gallery3d.R; 43import com.android.gallery3d.util.BucketNames; 44import com.android.gallery3d.util.SaveVideoFileInfo; 45import com.android.gallery3d.util.SaveVideoFileUtils; 46 47import java.io.File; 48import java.io.IOException; 49import java.sql.Date; 50import java.text.SimpleDateFormat; 51 52public class TrimVideo extends SherlockActivity implements 53 MediaPlayer.OnErrorListener, 54 MediaPlayer.OnCompletionListener, 55 ControllerOverlay.Listener { 56 57 private VideoView mVideoView; 58 private TextView mSaveVideoTextView; 59 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 75 private String mSrcVideoPath = null; 76 private static final String TIME_STAMP_NAME = "'TRIM'_yyyyMMdd_HHmmss"; 77 private SaveVideoFileInfo mDstFileInfo = null; 78 79 @Override 80 public void onCreate(Bundle savedInstanceState) { 81 mContext = getApplicationContext(); 82 super.onCreate(savedInstanceState); 83 84 requestWindowFeature(Window.FEATURE_ACTION_BAR); 85 requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); 86 87 ActionBar actionBar = getSupportActionBar(); 88 int displayOptions = ActionBar.DISPLAY_SHOW_HOME; 89 actionBar.setDisplayOptions(0, displayOptions); 90 displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM; 91 actionBar.setDisplayOptions(displayOptions, displayOptions); 92 actionBar.setCustomView(R.layout.trim_menu); 93 94 mSaveVideoTextView = (TextView) findViewById(R.id.start_trim); 95 mSaveVideoTextView.setOnClickListener(new View.OnClickListener() { 96 @Override 97 public void onClick(View arg0) { 98 trimVideo(); 99 } 100 }); 101 mSaveVideoTextView.setEnabled(false); 102 103 Intent intent = getIntent(); 104 mUri = intent.getData(); 105 mSrcVideoPath = intent.getStringExtra(PhotoPage.KEY_MEDIA_ITEM_PATH); 106 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); 112 ((ViewGroup) rootView).addView(mController.getView()); 113 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 144 public void onStop() { 145 if (mProgress != null) { 146 mProgress.dismiss(); 147 mProgress = null; 148 } 149 super.onStop(); 150 } 151 152 @Override 153 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 223 224 private boolean isModified() { 225 int delta = mTrimEndTime - mTrimStartTime; 226 227 // 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. 229 if (delta < 100 || Math.abs(mVideoView.getDuration() - delta) < 100) { 230 return false; 231 } else { 232 return true; 233 } 234 } 235 236 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); 241 242 showProgressDialog(); 243 244 new Thread(new Runnable() { 245 @Override 246 public void run() { 247 try { 248 VideoUtils.startTrim(mSrcFile, mDstFileInfo.mFile, 249 mTrimStartTime, mTrimEndTime, mVideoView.getDuration()); 250 // Update the database for adding a new video file. 251 SaveVideoFileUtils.insertContent(mDstFileInfo, 252 getContentResolver(), mUri); 253 } 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() { 260 Toast.makeText(getApplicationContext(), 261 getString(R.string.save_into, mDstFileInfo.mFolderName), 262 Toast.LENGTH_SHORT) 263 .show(); 264 // TODO: change trimming into a service to avoid 265 // this progressDialog and add notification properly. 266 if (mProgress != null) { 267 mProgress.dismiss(); 268 mProgress = null; 269 // Show the result only when the activity not stopped. 270 Intent intent = new Intent(android.content.Intent.ACTION_VIEW); 271 intent.setDataAndType(Uri.fromFile(mDstFileInfo.mFile), "video/*"); 272 intent.putExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, false); 273 startActivity(intent); 274 finish(); 275 } 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); 286 mProgress.setTitle(getString(R.string.trimming)); 287 mProgress.setMessage(getString(R.string.please_wait)); 288 // TODO: make this cancelable. 289 mProgress.setCancelable(false); 290 mProgress.setCanceledOnTouchOutside(false); 291 mProgress.show(); 292 } 293 294 @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(); 319 // Enable save if there's modifications 320 mSaveVideoTextView.setEnabled(isModified()); 321 } 322 323 @Override 324 public void onShown() { 325 } 326 327 @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} 347