react-native-video-trim 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -146,18 +146,38 @@ Main method to show Video Editor UI.
146
146
  - `config` (optional):
147
147
 
148
148
  - `saveToPhoto` (optional, `default = true`): whether to save video to photo/gallery after editing
149
+ - `removeAfterSavedToPhoto` (optional, `default = false`): whether to remove output file from storage after saved to Photo
149
150
  - `maxDuration` (optional): maximum duration for the trimmed video
150
151
  - `cancelButtonText` (optional): text of left button in Editor dialog
151
152
  - `saveButtonText` (optional): text of right button in Editor dialog
152
- - `title` (optional, iOS only): title of Editor dialog
153
+ - `enableCancelDialog` (optional, `default = true`): whether to show alert dialog on press Cancel
154
+ - `cancelDialogTitle` (optional, `default = "Warning!"`)
155
+ - `cancelDialogMessage` (optional, `default = "Are you sure want to cancel?"`)
156
+ - `cancelDialogCancelText` (optional, `default = "Close"`)
157
+ - `cancelDialogConfirmText` (optional, `default = "Proceed"`)
158
+ - `enableSaveDialog` (optional, `default = true`): whether to show alert dialog on press Save
159
+ - `saveDialogTitle` (optional, `default = "Confirmation!"`)
160
+ - `saveDialogMessage` (optional, `default = "Are you sure want to save?"`)
161
+ - `saveDialogCancelText` (optional, `default = "Close"`)
162
+ - `saveDialogConfirmText` (optional, `default = "Proceed"`)
163
+ - `trimmingText` (optional, `default = "Trimming video..."`): trimming text on the progress dialog
153
164
 
154
165
  If `saveToPhoto = true`, you must ensure that you have request permission to write to photo/gallery
155
166
  - For Android: you need to have `<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />` in AndroidManifest.xml
156
167
  - For iOS: you need `NSPhotoLibraryUsageDescription` in Info.plist
157
168
 
158
- ## isValidVideo (videoPath: string)
169
+ ## isValidVideo(videoPath: string)
159
170
 
160
- This method is to check if a path is a actual video. It returns `Promise<boolean>`
171
+ This method is to check if a path is a actual video and editable. It returns `Promise<boolean>`
172
+
173
+ ## listFiles()
174
+ Return array of generated output files in app storage. (`Promise<string[]>`)
175
+
176
+ ## cleanFiles()
177
+ Clean all generated output files in app storage. Return number of successfully deleted files (`Promise<number>`)
178
+
179
+ ## deleteFile()
180
+ Delete a file in app storage. Return `true` if success
161
181
 
162
182
  # Events
163
183
  To listen for events you interest, do the following:
@@ -177,7 +197,7 @@ useEffect(() => {
177
197
  break;
178
198
  }
179
199
  case 'onStartTrimming': {
180
- // Android only: on start trimming
200
+ // on start trimming
181
201
  console.log('onStartTrimming', event);
182
202
  break;
183
203
  }
@@ -204,6 +224,19 @@ useEffect(() => {
204
224
  };
205
225
  }, []);
206
226
  ```
227
+ # FFMPEG Version
228
+ This library uses FFMPEG-Kit Android under the hood, by default FFMPEG-min is used, which gives smallest bundle size: https://github.com/arthenica/ffmpeg-kit#9-packages
229
+
230
+ If you ever need to use other version of FFMPEG-Kit for Android, you can do the following, in your `android/build.gradle` > `buildscript` > `ext`:
207
231
 
232
+ ```gradle
233
+ buildscript {
234
+ ext {
235
+ ffmpegKitPackage = "full" // default "min", if followed by lts then LTS version is use. Eg "full-lts"
236
+
237
+ ffmpegKitPackageVersion = "5.1.LTS" // use exact version, highest precedence, default 6.0-2 if ignored
238
+ }
239
+ ```
208
240
  # Thanks
209
- Android part is created by modified + fix bugs from [original project](Android-Video-Trimmer)
241
+ - Android part is created by modified + fix bugs from: https://github.com/iknow4/Android-Video-Trimmer
242
+ - iOS UI is created from: https://github.com/AndreasVerhoeven/VideoTrimmerControl
@@ -44,11 +44,22 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
44
44
  private AlertDialog alertDialog;
45
45
  private AlertDialog mProgressDialog;
46
46
  private ProgressBar mProgressBar;
47
- private Boolean mSaveToPhoto = true;
48
47
  private int listenerCount = 0;
49
48
 
50
49
  private Promise showEditorPromise;
51
50
 
51
+ private boolean enableCancelDialog = true;
52
+ private String cancelDialogTitle = "Warning!";
53
+ private String cancelDialogMessage = "Are you sure want to cancel?";
54
+ private String cancelDialogCancelText = "Close";
55
+ private String cancelDialogConfirmText = "Proceed";
56
+ private boolean enableSaveDialog = true;
57
+ private String saveDialogTitle = "Confirmation!";
58
+ private String saveDialogMessage = "Are you sure want to save?";
59
+ private String saveDialogCancelText = "Close";
60
+ private String saveDialogConfirmText = "Proceed";
61
+ private String trimmingText = "Trimming video...";
62
+
52
63
  public VideoTrimModule(ReactApplicationContext reactContext) {
53
64
  super(reactContext);
54
65
  }
@@ -67,17 +78,26 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
67
78
  return;
68
79
  }
69
80
 
70
- if (config.hasKey("saveToPhoto")) {
71
- this.mSaveToPhoto = config.getBoolean("saveToPhoto");
72
- }
73
-
74
- if (!_isValidVideo(videoPath)) {
81
+ if (!isValidVideo(videoPath)) {
75
82
  WritableMap map = Arguments.createMap();
76
83
  map.putString("message", "File is not a valid video");
77
84
  sendEvent(getReactApplicationContext(), "onError", map);
78
85
  return;
79
86
  }
80
87
 
88
+ enableCancelDialog = config.hasKey("enableCancelDialog") ? config.getBoolean("enableCancelDialog") : true;
89
+ cancelDialogTitle = config.hasKey("cancelDialogTitle") ? config.getString("cancelDialogTitle") : "Warning!";
90
+ cancelDialogMessage = config.hasKey("cancelDialogMessage") ? config.getString("cancelDialogMessage") : "Are you sure want to cancel?";
91
+ cancelDialogCancelText = config.hasKey("cancelDialogCancelText") ? config.getString("cancelDialogCancelText") : "Close";
92
+ cancelDialogConfirmText = config.hasKey("cancelDialogConfirmText") ? config.getString("cancelDialogConfirmText") : "Proceed";
93
+
94
+ enableSaveDialog = config.hasKey("enableSaveDialog") ? config.getBoolean("enableSaveDialog") : true;
95
+ saveDialogTitle = config.hasKey("saveDialogTitle") ? config.getString("saveDialogTitle") : "Confirmation!";
96
+ saveDialogMessage = config.hasKey("saveDialogMessage") ? config.getString("saveDialogMessage") : "Are you sure want to save?";
97
+ saveDialogCancelText = config.hasKey("saveDialogCancelText") ? config.getString("saveDialogCancelText") : "Close";
98
+ saveDialogConfirmText = config.hasKey("saveDialogConfirmText") ? config.getString("saveDialogConfirmText") : "Proceed";
99
+ trimmingText = config.hasKey("trimmingText") ? config.getString("trimmingText") : "Trimming video...";
100
+
81
101
  Activity activity = getReactApplicationContext().getCurrentActivity();
82
102
 
83
103
  if (!isInit) {
@@ -169,8 +189,47 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
169
189
  }
170
190
 
171
191
  @Override public void onCancel() {
172
- sendEvent(getReactApplicationContext(), "onCancelTrimming", null);
173
- this.hideDialog();
192
+ if (!enableCancelDialog) {
193
+ sendEvent(getReactApplicationContext(), "onCancelTrimming", null);
194
+ hideDialog();
195
+ return;
196
+ }
197
+
198
+ AlertDialog.Builder builder = new AlertDialog.Builder(getReactApplicationContext().getCurrentActivity());
199
+ builder.setMessage(cancelDialogMessage);
200
+ builder.setTitle(cancelDialogTitle);
201
+ builder.setCancelable(false);
202
+ builder.setPositiveButton(cancelDialogConfirmText, (dialog, which) -> {
203
+ dialog.cancel();
204
+ sendEvent(getReactApplicationContext(), "onCancelTrimming", null);
205
+ hideDialog();
206
+ });
207
+ builder.setNegativeButton(cancelDialogCancelText, (dialog, which) -> {
208
+ dialog.cancel();
209
+ });
210
+ AlertDialog alertDialog = builder.create();
211
+ alertDialog.show();
212
+ }
213
+
214
+ @Override public void onSave() {
215
+ if (!enableSaveDialog) {
216
+ trimmerView.onSaveClicked();
217
+ return;
218
+ }
219
+
220
+ AlertDialog.Builder builder = new AlertDialog.Builder(getReactApplicationContext().getCurrentActivity());
221
+ builder.setMessage(saveDialogMessage);
222
+ builder.setTitle(saveDialogTitle);
223
+ builder.setCancelable(false);
224
+ builder.setPositiveButton(saveDialogConfirmText, (dialog, which) -> {
225
+ dialog.cancel();
226
+ trimmerView.onSaveClicked();
227
+ });
228
+ builder.setNegativeButton(saveDialogCancelText, (dialog, which) -> {
229
+ dialog.cancel();
230
+ });
231
+ AlertDialog alertDialog = builder.create();
232
+ alertDialog.show();
174
233
  }
175
234
 
176
235
  @ReactMethod
@@ -190,7 +249,7 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
190
249
  }
191
250
 
192
251
  private void buildDialog() {
193
- Activity activity = getReactApplicationContext().getCurrentActivity();
252
+ Activity activity = getReactApplicationContext().getCurrentActivity();
194
253
  // Create the parent layout for the dialog
195
254
  LinearLayout layout = new LinearLayout(activity);
196
255
  layout.setLayoutParams(new ViewGroup.LayoutParams(
@@ -207,7 +266,7 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
207
266
  ViewGroup.LayoutParams.WRAP_CONTENT,
208
267
  ViewGroup.LayoutParams.WRAP_CONTENT
209
268
  ));
210
- textView.setText(getReactApplicationContext().getResources().getString(R.string.trimming));
269
+ textView.setText(trimmingText);
211
270
  textView.setTextSize(18);
212
271
  layout.addView(textView);
213
272
 
@@ -253,7 +312,7 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
253
312
  }
254
313
 
255
314
 
256
- public boolean _isValidVideo(String filePath) {
315
+ public boolean isValidVideo(String filePath) {
257
316
  MediaMetadataRetriever retriever = new MediaMetadataRetriever();
258
317
 
259
318
  try {
@@ -269,7 +328,7 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
269
328
 
270
329
  @ReactMethod
271
330
  private void isValidVideo(String filePath, Promise promise) {
272
- promise.resolve(_isValidVideo(filePath));
331
+ promise.resolve(isValidVideo(filePath));
273
332
  }
274
333
 
275
334
  @ReactMethod
@@ -286,4 +345,29 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
286
345
  this.hideDialog();
287
346
  promise.resolve(null);
288
347
  }
348
+
349
+ @ReactMethod
350
+ private void listFiles(Promise promise) {
351
+ String[] files = StorageUtil.listFiles(getReactApplicationContext());
352
+ promise.resolve(Arguments.fromArray(files));
353
+ }
354
+
355
+ @ReactMethod
356
+ private void cleanFiles(Promise promise) {
357
+ String[] files = StorageUtil.listFiles(getReactApplicationContext());
358
+ int successCount = 0;
359
+ for (String file : files) {
360
+ boolean state = StorageUtil.deleteFile(file);
361
+ if (state) {
362
+ successCount++;
363
+ }
364
+ }
365
+ promise.resolve(successCount);
366
+ }
367
+
368
+ @ReactMethod
369
+ private void deleteFile(String filePath, Promise promise) {
370
+ boolean state = StorageUtil.deleteFile(filePath);
371
+ promise.resolve(state);
372
+ }
289
373
  }
@@ -6,4 +6,5 @@ public interface VideoTrimListener {
6
6
  void onFinishTrim(String url);
7
7
  void onError();
8
8
  void onCancel();
9
+ void onSave();
9
10
  }
@@ -1,6 +1,5 @@
1
1
  package com.videotrim.utils;
2
2
 
3
- import android.annotation.SuppressLint;
4
3
  import android.content.ContentValues;
5
4
  import android.content.Context;
6
5
  import android.media.MediaScannerConnection;
@@ -9,7 +8,6 @@ import android.os.Build;
9
8
  import android.os.Environment;
10
9
  import android.provider.MediaStore;
11
10
  import android.text.TextUtils;
12
- import android.util.Log;
13
11
  import com.facebook.react.bridge.ReactApplicationContext;
14
12
  import java.io.File;
15
13
  import java.io.FileInputStream;
@@ -17,163 +15,36 @@ import java.io.FileOutputStream;
17
15
  import java.io.IOException;
18
16
  import java.io.InputStream;
19
17
  import java.io.OutputStream;
20
- import java.util.Locale;
21
-
22
- import iknow.android.utils.BaseUtils;
23
- import iknow.android.utils.BuildConfig;
24
-
18
+ import java.util.ArrayList;
19
+ import java.util.List;
25
20
 
26
21
  @SuppressWarnings({ "ResultOfMethodCallIgnored", "FieldCanBeLocal" })
27
22
  public class StorageUtil {
28
-
29
- private static final String TAG = "StorageUtil";
30
- private static String APP_DATA_PATH = "/Android/data/" + BuildConfig.APPLICATION_ID;
31
-
32
- private static String sDataDir;
33
- private static String sCacheDir;
34
-
35
- public static String getAppDataDir() {
36
- if (TextUtils.isEmpty(sDataDir)) {
37
- try {
38
- if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
39
- sDataDir = Environment.getExternalStorageDirectory().getPath() + APP_DATA_PATH;
40
- if (TextUtils.isEmpty(sDataDir)) {
41
- sDataDir = BaseUtils.getContext().getFilesDir().getAbsolutePath();
42
- }
43
- } else {
44
- sDataDir = BaseUtils.getContext().getFilesDir().getAbsolutePath();
45
- }
46
- } catch (Throwable e) {
47
- e.printStackTrace();
48
- sDataDir = BaseUtils.getContext().getFilesDir().getAbsolutePath();
49
- }
50
- File file = new File(sDataDir);
51
- if (!file.exists()) {//判断文件目录是否存在
52
- file.mkdirs();
53
- }
54
- }
55
- return sDataDir;
56
- }
57
-
58
- public static String getCacheDir() {
59
- if (TextUtils.isEmpty(sCacheDir)) {
60
- File file = null;
61
- Context context = BaseUtils.getContext();
62
- try {
63
- if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
64
- file = context.getExternalCacheDir();
65
- if (file == null || !file.exists()) {
66
- file = getExternalCacheDirManual(context);
67
- }
68
- }
69
- if (file == null) {
70
- file = context.getCacheDir();
71
- if (file == null || !file.exists()) {
72
- file = getCacheDirManual(context);
73
- }
74
- }
75
- Log.w(TAG, "cache dir = " + file.getAbsolutePath());
76
- sCacheDir = file.getAbsolutePath();
77
- } catch (Throwable ignored) {
78
- }
79
- }
80
- return sCacheDir;
81
- }
82
-
83
- private static File getExternalCacheDirManual(Context context) {
84
- File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data");
85
- File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache");
86
- if (!appCacheDir.exists()) {
87
- if (!appCacheDir.mkdirs()) {//
88
- Log.w(TAG, "Unable to create external cache directory");
89
- return null;
90
- }
91
- try {
92
- new File(appCacheDir, ".nomedia").createNewFile();
93
- } catch (IOException e) {
94
- Log.i(TAG, "Can't create \".nomedia\" file in application external cache directory");
95
- }
96
- }
97
- return appCacheDir;
23
+ public static String getOutputPath(Context context) { // use same extension as inputFile
24
+ long timestamp = System.currentTimeMillis() / 1000;
25
+ File file = new File(context.getFilesDir(), VideoTrimmerUtil.FILE_PREFIX + "_" + timestamp + ".mp4"); // always use mp4 to prevent any issue with ffmpeg
26
+ return file.getAbsolutePath();
98
27
  }
99
28
 
100
- @SuppressLint("SdCardPath")
101
- private static File getCacheDirManual(Context context) {
102
- String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache";
103
- return new File(cacheDirPath);
104
- }
29
+ public static String[] listFiles(Context context) {
30
+ File filesDir = context.getFilesDir();
31
+ File[] files = filesDir.listFiles((dir, name) -> name.startsWith(VideoTrimmerUtil.FILE_PREFIX));
105
32
 
106
- public static boolean delFiles(String path) {
107
- File cacheFile = new File(path);
108
- if (!cacheFile.exists()) {
109
- return false;
110
- }
111
- File[] files = cacheFile.listFiles();
112
- for (int i = 0; i < files.length; i++) {
113
- // 是文件则直接删除
114
- if (files[i].exists() && files[i].isFile()) {
115
- files[i].delete();
116
- } else if (files[i].exists() && files[i].isDirectory()) {
117
- // 递归删除文件
118
- delFiles(files[i].getAbsolutePath());
119
- // 删除完目录下面的所有文件后再删除该文件夹
120
- files[i].delete();
33
+ List<String> fileUrls = new ArrayList<>();
34
+ if (files != null) {
35
+ for (File file : files) {
36
+ fileUrls.add(file.getAbsolutePath());
121
37
  }
122
38
  }
123
39
 
124
- return true;
125
- }
126
-
127
- public static long sizeOfDirectory(File dir) {
128
- if (dir.exists()) {
129
- long result = 0;
130
- File[] fileList = dir.listFiles();
131
- for (int i = 0; i < fileList.length; i++) {
132
- // Recursive call if it's a directory
133
- if (fileList[i].isDirectory()) {
134
- result += sizeOfDirectory(fileList[i]);
135
- } else {
136
- // Sum the file size in bytes
137
- result += fileList[i].length();
138
- }
139
- }
140
- return result; // return the file size
141
- }
142
- return 0;
143
- }
144
-
145
- /**
146
- * @param length 长度 byte为单位
147
- * 将文件大小转换为KB,MB格式
148
- */
149
- public static String getFileSize(long length) {
150
- int MB = 1024 * 1024;
151
- if (length < MB) {
152
- double resultKB = length * 1.0 / 1024;
153
- return String.format(Locale.getDefault(), "%.1f", resultKB) + "Kb";
154
- }
155
- double resultMB = length * 1.0 / MB;
156
- return String.format(Locale.getDefault(), "%.1f", resultMB) + "Mb";
157
- }
158
-
159
- public static boolean isFileExist(String path) {
160
- if (TextUtils.isEmpty(path)) return false;
161
- File file = new File(path);
162
- return file.exists();
40
+ return fileUrls.toArray(new String[0]);
163
41
  }
164
42
 
165
- /**
166
- * @param path 路径
167
- * @return 是否删除成功
168
- */
169
43
  public static boolean deleteFile(String path) {
170
44
  if (TextUtils.isEmpty(path)) return true;
171
45
  return deleteFile(new File(path));
172
46
  }
173
47
 
174
- /**
175
- * @return 是否删除成功
176
- */
177
48
  public static boolean deleteFile(File file) {
178
49
  if (file == null || !file.exists()) return true;
179
50
 
@@ -23,6 +23,7 @@ import iknow.android.utils.thread.BackgroundExecutor;
23
23
  public class VideoTrimmerUtil {
24
24
 
25
25
  private static final String TAG = VideoTrimmerUtil.class.getSimpleName();
26
+ public static final String FILE_PREFIX = "trimmedVideo";
26
27
  public static final long MIN_SHOOT_DURATION = 3000L;// min 3 seconds for trimming
27
28
  // public static final int VIDEO_MAX_TIME = 10;// max 10 seconds for trimming
28
29
  // public static final long MAX_SHOOT_DURATION = VIDEO_MAX_TIME * 1000L;
@@ -37,22 +38,13 @@ public class VideoTrimmerUtil {
37
38
  private static final int THUMB_RESOLUTION_RES = 2; // double thumb resolution for better quality
38
39
 
39
40
  public static void trim(Context context, String inputFile, String outputFile, int videoDuration, long startMs, long endMs, final VideoTrimListener callback) {
40
- final String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
41
- final String outputName = "trimmedVideo_" + timeStamp + ".mp4";
42
- outputFile = outputFile + "/" + outputName;
43
-
44
41
  String cmd = "-i " + inputFile + " -ss " + startMs + "ms" + " -to " + endMs + "ms -c copy " + outputFile;
45
- final String tempOutFile = outputFile;
46
-
47
42
  callback.onStartTrim();
48
43
  FFmpegKit.executeAsync(cmd, session -> {
49
44
  SessionState state = session.getState();
50
- ReturnCode returnCode = session.getReturnCode();
51
-
52
- Log.d(TAG, String.format("FFmpeg process exited with state %s and rc %s.%s", state, returnCode, session.getFailStackTrace()));
53
45
 
54
46
  if (state.equals(SessionState.COMPLETED)) {
55
- callback.onFinishTrim(tempOutFile);
47
+ callback.onFinishTrim(outputFile);
56
48
  } else {
57
49
  callback.onError();
58
50
  }
@@ -165,10 +165,6 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
165
165
  });
166
166
  }
167
167
 
168
- private void onCancelClicked() {
169
- mOnTrimVideoListener.onCancel();
170
- }
171
-
172
168
  private void videoPrepared(MediaPlayer mp) {
173
169
  ViewGroup.LayoutParams lp = mVideoView.getLayoutParams();
174
170
  int videoWidth = mp.getVideoWidth();
@@ -245,36 +241,13 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
245
241
 
246
242
  private void setUpListeners() {
247
243
  findViewById(R.id.cancelBtn).setOnClickListener(view -> {
248
- AlertDialog.Builder builder = new AlertDialog.Builder(mContext.getCurrentActivity());
249
- builder.setMessage("Are you sure want to cancel?");
250
- builder.setTitle("Warning!");
251
- builder.setCancelable(false);
252
- builder.setPositiveButton("Proceed", (dialog, which) -> {
253
- dialog.cancel();
254
- onCancelClicked();
255
- });
256
- builder.setNegativeButton("Close", (dialog, which) -> {
257
- dialog.cancel();
258
- });
259
- AlertDialog alertDialog = builder.create();
260
- alertDialog.show();
244
+ mOnTrimVideoListener.onCancel();
261
245
  });
262
246
 
263
247
  findViewById(R.id.saveBtn).setOnClickListener(view -> {
264
- AlertDialog.Builder builder = new AlertDialog.Builder(mContext.getCurrentActivity());
265
- builder.setMessage("Are you sure want to save?");
266
- builder.setTitle("Confirmation!");
267
- builder.setCancelable(false);
268
- builder.setPositiveButton("Proceed", (dialog, which) -> {
269
- dialog.cancel();
270
- onSaveClicked();
271
- });
272
- builder.setNegativeButton("Close", (dialog, which) -> {
273
- dialog.cancel();
274
- });
275
- AlertDialog alertDialog = builder.create();
276
- alertDialog.show();
248
+ mOnTrimVideoListener.onSave();
277
249
  });
250
+
278
251
  mVideoView.setOnPreparedListener(mp -> {
279
252
  // this is called everytime activity goes active, and can fire multiple times
280
253
  // so that we create a flag to not run below code more than once
@@ -293,14 +266,14 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
293
266
  });
294
267
  }
295
268
 
296
- private void onSaveClicked() {
269
+ public void onSaveClicked() {
297
270
  if (mRightProgressPos - mLeftProgressPos < VideoTrimmerUtil.MIN_SHOOT_DURATION) {
298
271
  Toast.makeText(mContext, "Video shorter than 3s, can't proceed", Toast.LENGTH_SHORT).show();
299
272
  } else {
300
273
  mVideoView.pause();
301
274
  VideoTrimmerUtil.trim(mContext,
302
275
  mSourceUri.getPath(),
303
- StorageUtil.getCacheDir(),
276
+ StorageUtil.getOutputPath(mContext),
304
277
  mDuration,
305
278
  mLeftProgressPos,
306
279
  mRightProgressPos,
@@ -3,7 +3,7 @@
3
3
  tools:ignore="MissingTranslation">
4
4
  <string name="app_name">Video Trim</string>
5
5
  <string name="trimmed_done">video trim completed!</string>
6
- <string name="trimming">Trimming video...</string>
6
+ <!-- <string name="trimming">Trimming video...</string>-->
7
7
  <string name="compressing">Compressing video...</string>
8
8
  <string name="compressed_done">video compression completed!</string>
9
9
  <string name="open_permission">Open permission</string>
package/ios/VideoTrim.mm CHANGED
@@ -6,5 +6,10 @@
6
6
  RCT_EXTERN_METHOD(showEditor:(NSString*)uri withConfig:(NSDictionary *)config)
7
7
  RCT_EXTERN_METHOD(isValidVideo:(NSString*)uri withResolver:(RCTPromiseResolveBlock)resolve
8
8
  withRejecter:(RCTPromiseRejectBlock)reject)
9
-
9
+ RCT_EXTERN_METHOD(listFiles:(RCTPromiseResolveBlock)resolve
10
+ withRejecter:(RCTPromiseRejectBlock)reject)
11
+ RCT_EXTERN_METHOD(cleanFiles:(RCTPromiseResolveBlock)resolve
12
+ withRejecter:(RCTPromiseRejectBlock)reject)
13
+ RCT_EXTERN_METHOD(deleteFile:(NSString*)uri withResolver:(RCTPromiseResolveBlock)resolve
14
+ withRejecter:(RCTPromiseRejectBlock)reject)
10
15
  @end