Home | History | Annotate | Download | only in app
      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 
     17 package com.android.gallery3d.app;
     18 
     19 import android.app.ActionBar;
     20 import android.app.Activity;
     21 import android.app.ProgressDialog;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.media.MediaPlayer;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.provider.MediaStore;
     29 import android.view.View;
     30 import android.view.ViewGroup;
     31 import android.view.Window;
     32 import android.widget.TextView;
     33 import android.widget.Toast;
     34 import android.widget.VideoView;
     35 
     36 import com.android.gallery3d.R;
     37 import com.android.gallery3d.util.SaveVideoFileInfo;
     38 import com.android.gallery3d.util.SaveVideoFileUtils;
     39 
     40 import java.io.File;
     41 import java.io.IOException;
     42 
     43 public class TrimVideo extends Activity implements
     44         MediaPlayer.OnErrorListener,
     45         MediaPlayer.OnCompletionListener,
     46         ControllerOverlay.Listener {
     47 
     48     private VideoView mVideoView;
     49     private TextView mSaveVideoTextView;
     50     private TrimControllerOverlay mController;
     51     private Context mContext;
     52     private Uri mUri;
     53     private final Handler mHandler = new Handler();
     54     public static final String TRIM_ACTION = "com.android.camera.action.TRIM";
     55 
     56     public ProgressDialog mProgress;
     57 
     58     private int mTrimStartTime = 0;
     59     private int mTrimEndTime = 0;
     60     private int mVideoPosition = 0;
     61     public static final String KEY_TRIM_START = "trim_start";
     62     public static final String KEY_TRIM_END = "trim_end";
     63     public static final String KEY_VIDEO_POSITION = "video_pos";
     64     private boolean mHasPaused = false;
     65 
     66     private String mSrcVideoPath = null;
     67     private static final String TIME_STAMP_NAME = "'TRIM'_yyyyMMdd_HHmmss";
     68     private SaveVideoFileInfo mDstFileInfo = null;
     69 
     70     @Override
     71     public void onCreate(Bundle savedInstanceState) {
     72         mContext = getApplicationContext();
     73         super.onCreate(savedInstanceState);
     74 
     75         requestWindowFeature(Window.FEATURE_ACTION_BAR);
     76         requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
     77 
     78         ActionBar actionBar = getActionBar();
     79         int displayOptions = ActionBar.DISPLAY_SHOW_HOME;
     80         actionBar.setDisplayOptions(0, displayOptions);
     81         displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM;
     82         actionBar.setDisplayOptions(displayOptions, displayOptions);
     83         actionBar.setCustomView(R.layout.trim_menu);
     84 
     85         mSaveVideoTextView = (TextView) findViewById(R.id.start_trim);
     86         mSaveVideoTextView.setOnClickListener(new View.OnClickListener() {
     87             @Override
     88             public void onClick(View arg0) {
     89                 trimVideo();
     90             }
     91         });
     92         mSaveVideoTextView.setEnabled(false);
     93 
     94         Intent intent = getIntent();
     95         mUri = intent.getData();
     96         mSrcVideoPath = intent.getStringExtra(PhotoPage.KEY_MEDIA_ITEM_PATH);
     97         setContentView(R.layout.trim_view);
     98         View rootView = findViewById(R.id.trim_view_root);
     99 
    100         mVideoView = (VideoView) rootView.findViewById(R.id.surface_view);
    101 
    102         mController = new TrimControllerOverlay(mContext);
    103         ((ViewGroup) rootView).addView(mController.getView());
    104         mController.setListener(this);
    105         mController.setCanReplay(true);
    106 
    107         mVideoView.setOnErrorListener(this);
    108         mVideoView.setOnCompletionListener(this);
    109         mVideoView.setVideoURI(mUri);
    110 
    111         playVideo();
    112     }
    113 
    114     @Override
    115     public void onResume() {
    116         super.onResume();
    117         if (mHasPaused) {
    118             mVideoView.seekTo(mVideoPosition);
    119             mVideoView.resume();
    120             mHasPaused = false;
    121         }
    122         mHandler.post(mProgressChecker);
    123     }
    124 
    125     @Override
    126     public void onPause() {
    127         mHasPaused = true;
    128         mHandler.removeCallbacksAndMessages(null);
    129         mVideoPosition = mVideoView.getCurrentPosition();
    130         mVideoView.suspend();
    131         super.onPause();
    132     }
    133 
    134     @Override
    135     public void onStop() {
    136         if (mProgress != null) {
    137             mProgress.dismiss();
    138             mProgress = null;
    139         }
    140         super.onStop();
    141     }
    142 
    143     @Override
    144     public void onDestroy() {
    145         mVideoView.stopPlayback();
    146         super.onDestroy();
    147     }
    148 
    149     private final Runnable mProgressChecker = new Runnable() {
    150         @Override
    151         public void run() {
    152             int pos = setProgress();
    153             mHandler.postDelayed(mProgressChecker, 200 - (pos % 200));
    154         }
    155     };
    156 
    157     @Override
    158     public void onSaveInstanceState(Bundle savedInstanceState) {
    159         savedInstanceState.putInt(KEY_TRIM_START, mTrimStartTime);
    160         savedInstanceState.putInt(KEY_TRIM_END, mTrimEndTime);
    161         savedInstanceState.putInt(KEY_VIDEO_POSITION, mVideoPosition);
    162         super.onSaveInstanceState(savedInstanceState);
    163     }
    164 
    165     @Override
    166     public void onRestoreInstanceState(Bundle savedInstanceState) {
    167         super.onRestoreInstanceState(savedInstanceState);
    168         mTrimStartTime = savedInstanceState.getInt(KEY_TRIM_START, 0);
    169         mTrimEndTime = savedInstanceState.getInt(KEY_TRIM_END, 0);
    170         mVideoPosition = savedInstanceState.getInt(KEY_VIDEO_POSITION, 0);
    171     }
    172 
    173     // This updates the time bar display (if necessary). It is called by
    174     // mProgressChecker and also from places where the time bar needs
    175     // to be updated immediately.
    176     private int setProgress() {
    177         mVideoPosition = mVideoView.getCurrentPosition();
    178         // If the video position is smaller than the starting point of trimming,
    179         // correct it.
    180         if (mVideoPosition < mTrimStartTime) {
    181             mVideoView.seekTo(mTrimStartTime);
    182             mVideoPosition = mTrimStartTime;
    183         }
    184         // If the position is bigger than the end point of trimming, show the
    185         // replay button and pause.
    186         if (mVideoPosition >= mTrimEndTime && mTrimEndTime > 0) {
    187             if (mVideoPosition > mTrimEndTime) {
    188                 mVideoView.seekTo(mTrimEndTime);
    189                 mVideoPosition = mTrimEndTime;
    190             }
    191             mController.showEnded();
    192             mVideoView.pause();
    193         }
    194 
    195         int duration = mVideoView.getDuration();
    196         if (duration > 0 && mTrimEndTime == 0) {
    197             mTrimEndTime = duration;
    198         }
    199         mController.setTimes(mVideoPosition, duration, mTrimStartTime, mTrimEndTime);
    200         // Enable save if there's modifications
    201         mSaveVideoTextView.setEnabled(isModified());
    202         return mVideoPosition;
    203     }
    204 
    205     private void playVideo() {
    206         mVideoView.start();
    207         mController.showPlaying();
    208         setProgress();
    209     }
    210 
    211     private void pauseVideo() {
    212         mVideoView.pause();
    213         mController.showPaused();
    214     }
    215 
    216 
    217     private boolean isModified() {
    218         int delta = mTrimEndTime - mTrimStartTime;
    219 
    220         // Considering that we only trim at sync frame, we don't want to trim
    221         // when the time interval is too short or too close to the origin.
    222         if (delta < 100 || Math.abs(mVideoView.getDuration() - delta) < 100) {
    223             return false;
    224         } else {
    225             return true;
    226         }
    227     }
    228 
    229     private void trimVideo() {
    230 
    231         mDstFileInfo = SaveVideoFileUtils.getDstMp4FileInfo(TIME_STAMP_NAME,
    232                 getContentResolver(), mUri, getString(R.string.folder_download));
    233         final File mSrcFile = new File(mSrcVideoPath);
    234 
    235         showProgressDialog();
    236 
    237         new Thread(new Runnable() {
    238             @Override
    239             public void run() {
    240                 try {
    241                     VideoUtils.startTrim(mSrcFile, mDstFileInfo.mFile,
    242                             mTrimStartTime, mTrimEndTime);
    243                     // Update the database for adding a new video file.
    244                     SaveVideoFileUtils.insertContent(mDstFileInfo,
    245                             getContentResolver(), mUri);
    246                 } catch (IOException e) {
    247                     e.printStackTrace();
    248                 }
    249                 // After trimming is done, trigger the UI changed.
    250                 mHandler.post(new Runnable() {
    251                     @Override
    252                     public void run() {
    253                         Toast.makeText(getApplicationContext(),
    254                             getString(R.string.save_into, mDstFileInfo.mFolderName),
    255                             Toast.LENGTH_SHORT)
    256                             .show();
    257                         // TODO: change trimming into a service to avoid
    258                         // this progressDialog and add notification properly.
    259                         if (mProgress != null) {
    260                             mProgress.dismiss();
    261                             mProgress = null;
    262                             // Show the result only when the activity not stopped.
    263                             Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
    264                             intent.setDataAndType(Uri.fromFile(mDstFileInfo.mFile), "video/*");
    265                             intent.putExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, false);
    266                             startActivity(intent);
    267                             finish();
    268                         }
    269                     }
    270                 });
    271             }
    272         }).start();
    273     }
    274 
    275     private void showProgressDialog() {
    276         // create a background thread to trim the video.
    277         // and show the progress.
    278         mProgress = new ProgressDialog(this);
    279         mProgress.setTitle(getString(R.string.trimming));
    280         mProgress.setMessage(getString(R.string.please_wait));
    281         // TODO: make this cancelable.
    282         mProgress.setCancelable(false);
    283         mProgress.setCanceledOnTouchOutside(false);
    284         mProgress.show();
    285     }
    286 
    287     @Override
    288     public void onPlayPause() {
    289         if (mVideoView.isPlaying()) {
    290             pauseVideo();
    291         } else {
    292             playVideo();
    293         }
    294     }
    295 
    296     @Override
    297     public void onSeekStart() {
    298         pauseVideo();
    299     }
    300 
    301     @Override
    302     public void onSeekMove(int time) {
    303         mVideoView.seekTo(time);
    304     }
    305 
    306     @Override
    307     public void onSeekEnd(int time, int start, int end) {
    308         mVideoView.seekTo(time);
    309         mTrimStartTime = start;
    310         mTrimEndTime = end;
    311         setProgress();
    312     }
    313 
    314     @Override
    315     public void onShown() {
    316     }
    317 
    318     @Override
    319     public void onHidden() {
    320     }
    321 
    322     @Override
    323     public void onReplay() {
    324         mVideoView.seekTo(mTrimStartTime);
    325         playVideo();
    326     }
    327 
    328     @Override
    329     public void onCompletion(MediaPlayer mp) {
    330         mController.showEnded();
    331     }
    332 
    333     @Override
    334     public boolean onError(MediaPlayer mp, int what, int extra) {
    335         return false;
    336     }
    337 }
    338