react-native-video-trim 3.0.10 → 4.0.0

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.
Files changed (87) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +46 -57
  3. package/VideoTrim.podspec +24 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +76 -51
  6. package/android/gradle.properties +5 -5
  7. package/android/src/main/AndroidManifest.xml +4 -2
  8. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  9. package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrim.kt +629 -0
  10. package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrimPackage.kt +22 -0
  11. package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/enums/ErrorCode.java +1 -1
  12. package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/interfaces/IVideoTrimmerView.java +1 -1
  13. package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/interfaces/VideoTrimListener.java +6 -5
  14. package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/utils/MediaMetadataUtil.java +1 -1
  15. package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/utils/StorageUtil.java +3 -1
  16. package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/utils/VideoTrimmerUtil.java +22 -20
  17. package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/widgets/VideoTrimmerView.java +38 -66
  18. package/ios/AssetLoader.swift +2 -2
  19. package/ios/ErrorCode.swift +2 -2
  20. package/ios/ProgressAlertController.swift +2 -2
  21. package/ios/VideoTrim.swift +48 -838
  22. package/ios/VideoTrimImpl.swift +860 -0
  23. package/ios/VideoTrimmer.swift +2 -3
  24. package/ios/VideoTrimmerThumb.swift +33 -26
  25. package/ios/VideoTrimmerViewController.swift +47 -28
  26. package/lib/module/VideoTrim.nitro.js +4 -0
  27. package/lib/module/VideoTrim.nitro.js.map +1 -0
  28. package/lib/module/index.js +71 -22
  29. package/lib/module/index.js.map +1 -1
  30. package/lib/module/package.json +1 -0
  31. package/lib/typescript/package.json +1 -0
  32. package/lib/typescript/{index.d.ts → src/VideoTrim.nitro.d.ts} +63 -89
  33. package/lib/typescript/src/VideoTrim.nitro.d.ts.map +1 -0
  34. package/lib/typescript/src/index.d.ts +41 -0
  35. package/lib/typescript/src/index.d.ts.map +1 -0
  36. package/nitrogen/generated/android/c++/JEditorConfig.hpp +229 -0
  37. package/nitrogen/generated/android/c++/JFileValidationResult.hpp +61 -0
  38. package/nitrogen/generated/android/c++/JFunc_void.hpp +74 -0
  39. package/nitrogen/generated/android/c++/JFunc_void_std__string_std__unordered_map_std__string__std__string_.hpp +89 -0
  40. package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.cpp +131 -0
  41. package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.hpp +67 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/EditorConfig.kt +70 -0
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/FileValidationResult.kt +28 -0
  44. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void.kt +80 -0
  45. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void_std__string_std__unordered_map_std__string__std__string_.kt +80 -0
  46. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/HybridVideoTrimSpec.kt +82 -0
  47. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/videotrimOnLoad.kt +35 -0
  48. package/nitrogen/generated/android/videotrim+autolinking.cmake +78 -0
  49. package/nitrogen/generated/android/videotrim+autolinking.gradle +27 -0
  50. package/nitrogen/generated/android/videotrimOnLoad.cpp +50 -0
  51. package/nitrogen/generated/android/videotrimOnLoad.hpp +25 -0
  52. package/nitrogen/generated/ios/VideoTrim+autolinking.rb +60 -0
  53. package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.cpp +88 -0
  54. package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.hpp +331 -0
  55. package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Umbrella.hpp +53 -0
  56. package/nitrogen/generated/ios/VideoTrimAutolinking.mm +33 -0
  57. package/nitrogen/generated/ios/VideoTrimAutolinking.swift +25 -0
  58. package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.cpp +11 -0
  59. package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.hpp +116 -0
  60. package/nitrogen/generated/ios/swift/EditorConfig.swift +519 -0
  61. package/nitrogen/generated/ios/swift/FileValidationResult.swift +57 -0
  62. package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
  63. package/nitrogen/generated/ios/swift/Func_void_FileValidationResult.swift +46 -0
  64. package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
  65. package/nitrogen/generated/ios/swift/Func_void_double.swift +46 -0
  66. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  67. package/nitrogen/generated/ios/swift/Func_void_std__string_std__unordered_map_std__string__std__string_.swift +54 -0
  68. package/nitrogen/generated/ios/swift/Func_void_std__vector_std__string_.swift +46 -0
  69. package/nitrogen/generated/ios/swift/HybridVideoTrimSpec.swift +53 -0
  70. package/nitrogen/generated/ios/swift/HybridVideoTrimSpec_cxx.swift +222 -0
  71. package/nitrogen/generated/shared/c++/EditorConfig.hpp +245 -0
  72. package/nitrogen/generated/shared/c++/FileValidationResult.hpp +77 -0
  73. package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.cpp +26 -0
  74. package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.hpp +76 -0
  75. package/package.json +75 -71
  76. package/src/VideoTrim.nitro.ts +244 -0
  77. package/src/index.tsx +87 -258
  78. package/android/src/main/AndroidManifestDeprecated.xml +0 -3
  79. package/android/src/main/java/com/videotrim/VideoTrimModule.java +0 -603
  80. package/android/src/main/java/com/videotrim/VideoTrimPackage.java +0 -28
  81. package/ios/VideoTrim-Bridging-Header.h +0 -2
  82. package/ios/VideoTrim.mm +0 -17
  83. package/ios/VideoTrim.xcodeproj/project.pbxproj +0 -283
  84. package/lib/commonjs/index.js +0 -87
  85. package/lib/commonjs/index.js.map +0 -1
  86. package/lib/typescript/index.d.ts.map +0 -1
  87. package/react-native-video-trim.podspec +0 -43
@@ -1,603 +0,0 @@
1
- package com.videotrim;
2
-
3
- import static android.app.Activity.RESULT_OK;
4
- import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
5
-
6
- import android.app.Activity;
7
- import android.content.Context;
8
- import android.content.pm.PackageManager;
9
- import android.content.pm.ResolveInfo;
10
- import android.content.res.ColorStateList;
11
- import android.graphics.Color;
12
- import android.net.Uri;
13
- import android.os.Build;
14
- import android.util.Log;
15
- import android.util.TypedValue;
16
- import android.view.Gravity;
17
- import android.view.ViewGroup;
18
- import android.widget.Button;
19
- import android.widget.LinearLayout;
20
- import android.widget.ProgressBar;
21
- import android.widget.TextView;
22
-
23
- import androidx.annotation.NonNull;
24
- import androidx.annotation.Nullable;
25
- import androidx.appcompat.app.AlertDialog;
26
- import androidx.core.content.ContextCompat;
27
- import androidx.core.content.FileProvider;
28
-
29
- import com.facebook.react.bridge.ActivityEventListener;
30
- import com.facebook.react.bridge.Arguments;
31
- import com.facebook.react.bridge.BaseActivityEventListener;
32
- import com.facebook.react.bridge.LifecycleEventListener;
33
- import com.facebook.react.bridge.Promise;
34
- import com.facebook.react.bridge.ReactApplicationContext;
35
- import com.facebook.react.bridge.ReactContext;
36
- import com.facebook.react.bridge.ReactContextBaseJavaModule;
37
- import com.facebook.react.bridge.ReactMethod;
38
- import com.facebook.react.bridge.ReadableMap;
39
- import com.facebook.react.bridge.WritableMap;
40
- import com.facebook.react.module.annotations.ReactModule;
41
- import com.facebook.react.modules.core.DeviceEventManagerModule;
42
- import com.videotrim.enums.ErrorCode;
43
- import com.videotrim.interfaces.VideoTrimListener;
44
- import com.videotrim.utils.MediaMetadataUtil;
45
- import com.videotrim.utils.StorageUtil;
46
- import com.videotrim.utils.VideoTrimmerUtil;
47
- import com.videotrim.widgets.VideoTrimmerView;
48
-
49
- import android.content.Intent;
50
-
51
- import java.io.File;
52
- import java.io.FileInputStream;
53
- import java.io.IOException;
54
- import java.io.OutputStream;
55
- import java.util.Objects;
56
-
57
- import iknow.android.utils.BaseUtils;
58
-
59
- @ReactModule(name = VideoTrimModule.NAME)
60
- public class VideoTrimModule extends ReactContextBaseJavaModule implements VideoTrimListener, LifecycleEventListener {
61
- private static final String TAG = VideoTrimmerUtil.class.getSimpleName();
62
-
63
- public static final String NAME = "VideoTrim";
64
- private static Boolean isInit = false;
65
- private VideoTrimmerView trimmerView;
66
- private AlertDialog alertDialog;
67
- private AlertDialog mProgressDialog;
68
- private AlertDialog cancelTrimmingConfirmDialog;
69
- private ProgressBar mProgressBar;
70
- private int listenerCount = 0;
71
- private boolean enableCancelTrimming = true;
72
-
73
- private String cancelTrimmingButtonText = "Cancel";
74
- private boolean enableCancelTrimmingDialog = true;
75
- private String cancelTrimmingDialogTitle = "Warning!";
76
- private String cancelTrimmingDialogMessage = "Are you sure want to cancel trimming?";
77
- private String cancelTrimmingDialogCancelText = "Close";
78
- private String cancelTrimmingDialogConfirmText = "Proceed";
79
- private boolean enableCancelDialog = true;
80
- private String cancelDialogTitle = "Warning!";
81
- private String cancelDialogMessage = "Are you sure want to cancel?";
82
- private String cancelDialogCancelText = "Close";
83
- private String cancelDialogConfirmText = "Proceed";
84
- private boolean enableSaveDialog = true;
85
- private String saveDialogTitle = "Confirmation!";
86
- private String saveDialogMessage = "Are you sure want to save?";
87
- private String saveDialogCancelText = "Close";
88
- private String saveDialogConfirmText = "Proceed";
89
- private String trimmingText = "Trimming video...";
90
- private String outputFile;
91
- private boolean saveToPhoto = false;
92
- private boolean removeAfterSavedToPhoto = false;
93
- private boolean removeAfterFailedToSavePhoto = false;
94
- private boolean removeAfterSavedToDocuments = false;
95
- private boolean removeAfterFailedToSaveDocuments = false;
96
- // private boolean removeAfterShared = false; // TODO: on Android there's no way to know if user shared the file or share sheet closed
97
- // private boolean removeAfterFailedToShare = false; // TODO: implement this
98
- private boolean openDocumentsOnFinish = false;
99
- private boolean openShareSheetOnFinish = false;
100
- private boolean isVideoType = true;
101
- private boolean closeWhenFinish = true;
102
-
103
- private static final int REQUEST_CODE_SAVE_FILE = 1;
104
-
105
- public VideoTrimModule(ReactApplicationContext reactContext) {
106
- super(reactContext);
107
- ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
108
-
109
- @Override
110
- public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
111
- if (requestCode == REQUEST_CODE_SAVE_FILE && resultCode == RESULT_OK) {
112
- Uri uri = intent.getData();
113
- if (uri == null) {
114
- return;
115
- }
116
- try {
117
- OutputStream outputStream = reactContext.getContentResolver().openOutputStream(uri);
118
- if (outputStream == null) {
119
- return;
120
- }
121
- FileInputStream fileInputStream = new FileInputStream(outputFile);
122
- byte[] buffer = new byte[1024];
123
- int length;
124
- while ((length = fileInputStream.read(buffer)) > 0) {
125
- outputStream.write(buffer, 0, length);
126
- }
127
- outputStream.close();
128
- fileInputStream.close();
129
- // File saved successfully
130
- Log.d(TAG, "File saved successfully to " + uri);
131
- if (removeAfterSavedToDocuments) {
132
- StorageUtil.deleteFile(outputFile);
133
- }
134
- } catch (Exception e) {
135
- e.printStackTrace();
136
- // Handle the error
137
- onError("Failed to save edited video to Documents: " + e.getLocalizedMessage(), ErrorCode.FAIL_TO_SAVE_TO_DOCUMENTS);
138
- if (removeAfterFailedToSaveDocuments) {
139
- StorageUtil.deleteFile(outputFile);
140
- }
141
- } finally {
142
- hideDialog(true);
143
- }
144
- }
145
- }
146
- };
147
- reactContext.addActivityEventListener(mActivityEventListener);
148
- }
149
-
150
- @Override
151
- @NonNull
152
- public String getName() {
153
- return NAME;
154
- }
155
-
156
-
157
- @ReactMethod
158
- public void showEditor(String videoPath, ReadableMap config) {
159
- if (trimmerView != null || alertDialog != null) {
160
- return;
161
- }
162
- enableCancelTrimming = !config.hasKey("enableCancelTrimming") || config.getBoolean("enableCancelTrimming");
163
-
164
- cancelTrimmingButtonText = config.hasKey("cancelTrimmingButtonText") ? config.getString("cancelTrimmingButtonText") : "Cancel";
165
- enableCancelTrimmingDialog = !config.hasKey("enableCancelTrimmingDialog") || config.getBoolean("enableCancelTrimmingDialog");
166
- cancelTrimmingDialogTitle = config.hasKey("cancelTrimmingDialogTitle") ? config.getString("cancelTrimmingDialogTitle") : "Warning!";
167
- cancelTrimmingDialogMessage = config.hasKey("cancelTrimmingDialogMessage") ? config.getString("cancelTrimmingDialogMessage") : "Are you sure want to cancel trimming?";
168
- cancelTrimmingDialogCancelText = config.hasKey("cancelTrimmingDialogCancelText") ? config.getString("cancelTrimmingDialogCancelText") : "Close";
169
- cancelTrimmingDialogConfirmText = config.hasKey("cancelTrimmingDialogConfirmText") ? config.getString("cancelTrimmingDialogConfirmText") : "Proceed";
170
-
171
- enableCancelDialog = !config.hasKey("enableCancelDialog") || config.getBoolean("enableCancelDialog");
172
- cancelDialogTitle = config.hasKey("cancelDialogTitle") ? config.getString("cancelDialogTitle") : "Warning!";
173
- cancelDialogMessage = config.hasKey("cancelDialogMessage") ? config.getString("cancelDialogMessage") : "Are you sure want to cancel?";
174
- cancelDialogCancelText = config.hasKey("cancelDialogCancelText") ? config.getString("cancelDialogCancelText") : "Close";
175
- cancelDialogConfirmText = config.hasKey("cancelDialogConfirmText") ? config.getString("cancelDialogConfirmText") : "Proceed";
176
-
177
- enableSaveDialog = !config.hasKey("enableSaveDialog") || config.getBoolean("enableSaveDialog");
178
- saveDialogTitle = config.hasKey("saveDialogTitle") ? config.getString("saveDialogTitle") : "Confirmation!";
179
- saveDialogMessage = config.hasKey("saveDialogMessage") ? config.getString("saveDialogMessage") : "Are you sure want to save?";
180
- saveDialogCancelText = config.hasKey("saveDialogCancelText") ? config.getString("saveDialogCancelText") : "Close";
181
- saveDialogConfirmText = config.hasKey("saveDialogConfirmText") ? config.getString("saveDialogConfirmText") : "Proceed";
182
- trimmingText = config.hasKey("trimmingText") ? config.getString("trimmingText") : "Trimming video...";
183
-
184
- saveToPhoto = config.hasKey("saveToPhoto") && config.getBoolean("saveToPhoto");
185
- removeAfterSavedToPhoto = config.hasKey("removeAfterSavedToPhoto") && config.getBoolean("removeAfterSavedToPhoto");
186
- removeAfterFailedToSavePhoto = config.hasKey("removeAfterFailedToSavePhoto") && config.getBoolean("removeAfterFailedToSavePhoto");
187
- removeAfterSavedToDocuments = config.hasKey("removeAfterSavedToDocuments") && config.getBoolean("removeAfterSavedToDocuments");
188
- removeAfterFailedToSaveDocuments = config.hasKey("removeAfterFailedToSaveDocuments") && config.getBoolean("removeAfterFailedToSaveDocuments");
189
- // removeAfterShared = config.hasKey("removeAfterShared") && config.getBoolean("removeAfterShared");
190
- // removeAfterFailedToShare = config.hasKey("removeAfterFailedToShare") && config.getBoolean("removeAfterFailedToShare");
191
- openDocumentsOnFinish = config.hasKey("openDocumentsOnFinish") && config.getBoolean("openDocumentsOnFinish");
192
- openShareSheetOnFinish = config.hasKey("openShareSheetOnFinish") && config.getBoolean("openShareSheetOnFinish");
193
-
194
- isVideoType = !config.hasKey("type") || !Objects.equals(config.getString("type"), "audio");
195
-
196
- closeWhenFinish = !config.hasKey("closeWhenFinish") || config.getBoolean("closeWhenFinish");
197
-
198
- Activity activity = getReactApplicationContext().getCurrentActivity();
199
-
200
- if (!isInit) {
201
- init(activity);
202
- isInit = true;
203
- }
204
-
205
- // here is NOT main thread, we need to create VideoTrimmerView on UI thread, so that later we can update it using same thread
206
-
207
- runOnUiThread(() -> {
208
- trimmerView = new VideoTrimmerView(getReactApplicationContext(), config, null);
209
- trimmerView.setOnTrimVideoListener(this);
210
- trimmerView.initByURI(Uri.parse(videoPath));
211
-
212
- AlertDialog.Builder builder = new AlertDialog.Builder(activity, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
213
- builder.setCancelable(false);
214
- alertDialog = builder.create();
215
- alertDialog.setView(trimmerView);
216
- alertDialog.show();
217
-
218
- // this is to ensure to release resource if dialog is dismissed in unexpected way (Eg. open control/notification center by dragging from top of screen)
219
- alertDialog.setOnDismissListener(dialog -> {
220
- // This is called in same thread as the trimmer view -> UI thread
221
- if (trimmerView != null) {
222
- trimmerView.onDestroy();
223
- trimmerView = null;
224
- }
225
- hideDialog(true);
226
- sendEvent(getReactApplicationContext(), "onHide", null);
227
- });
228
- sendEvent(getReactApplicationContext(), "onShow", null);
229
- });
230
- }
231
-
232
- private void init(Activity activity) {
233
- isInit = true;
234
- // we have to init this before create videoTrimmerView
235
- BaseUtils.init(getReactApplicationContext());
236
- }
237
-
238
- @Override
239
- public void onHostResume() {
240
- Log.d(TAG, "onHostResume: ");
241
- }
242
-
243
- @Override
244
- public void onHostPause() {
245
- Log.d(TAG, "onHostPause: ");
246
- if (trimmerView != null) {
247
- trimmerView.onMediaPause();
248
- }
249
- }
250
-
251
- @Override
252
- public void onHostDestroy() {
253
- hideDialog(true);
254
- }
255
-
256
- @Override
257
- public void onLoad(int duration) {
258
- WritableMap map = Arguments.createMap();
259
- map.putInt("duration", duration);
260
- sendEvent(getReactApplicationContext(), "onLoad", map);
261
- }
262
-
263
- @Override
264
- public void onTrimmingProgress(int percentage) {
265
- // prevent onTrimmingProgress is called after onFinishTrim (some rare cases)
266
- if (mProgressBar == null) {
267
- return;
268
- }
269
-
270
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
271
- mProgressBar.setProgress(percentage, true);
272
- } else {
273
- mProgressBar.setProgress(percentage);
274
- }
275
- }
276
-
277
-
278
- @Override
279
- public void onFinishTrim(String in, long startTime, long endTime, int duration) {
280
- outputFile = in;
281
- runOnUiThread(() -> {
282
- WritableMap map = Arguments.createMap();
283
- map.putString("outputPath", in);
284
- map.putInt("duration", duration);
285
- map.putDouble("startTime", (double) startTime);
286
- map.putDouble("endTime", (double) endTime);
287
- sendEvent(getReactApplicationContext(), "onFinishTrimming", map);
288
- });
289
-
290
- if (saveToPhoto && isVideoType) {
291
- try {
292
- StorageUtil.saveVideoToGallery(getReactApplicationContext(), in);
293
- Log.d(TAG, "Edited video saved to Photo Library successfully.");
294
- if (removeAfterSavedToPhoto) {
295
- StorageUtil.deleteFile(in);
296
- }
297
- } catch (IOException e) {
298
- e.printStackTrace();
299
- onError("Failed to save edited video to Photo Library: " + e.getLocalizedMessage(), ErrorCode.FAIL_TO_SAVE_TO_PHOTO);
300
- if (removeAfterFailedToSavePhoto) {
301
- StorageUtil.deleteFile(in);
302
- }
303
- } finally {
304
- hideDialog(closeWhenFinish);
305
- }
306
- } else if (openDocumentsOnFinish) {
307
- saveFileToExternalStorage(new File(in));
308
- } else if (openShareSheetOnFinish) {
309
- hideDialog(closeWhenFinish);
310
- shareFile(getReactApplicationContext(), new File(in));
311
- } else {
312
- hideDialog(closeWhenFinish);
313
- }
314
- }
315
-
316
- @Override
317
- public void onCancelTrim() {
318
- sendEvent(getReactApplicationContext(), "onCancelTrimming", null);
319
- }
320
-
321
- @Override
322
- public void onError(String errorMessage, ErrorCode errorCode) {
323
- WritableMap map = Arguments.createMap();
324
- map.putString("message", errorMessage);
325
- map.putString("errorCode", errorCode.name());
326
- sendEvent(getReactApplicationContext(), "onError", map);
327
- }
328
-
329
- @Override
330
- public void onCancel() {
331
- if (!enableCancelDialog) {
332
- sendEvent(getReactApplicationContext(), "onCancel", null);
333
- hideDialog(true);
334
- return;
335
- }
336
-
337
- AlertDialog.Builder builder = new AlertDialog.Builder(getReactApplicationContext().getCurrentActivity());
338
- builder.setMessage(cancelDialogMessage);
339
- builder.setTitle(cancelDialogTitle);
340
- builder.setCancelable(false);
341
- builder.setPositiveButton(cancelDialogConfirmText, (dialog, which) -> {
342
- dialog.cancel();
343
- sendEvent(getReactApplicationContext(), "onCancel", null);
344
- hideDialog(true);
345
- });
346
- builder.setNegativeButton(cancelDialogCancelText, (dialog, which) -> {
347
- dialog.cancel();
348
- });
349
- AlertDialog alertDialog = builder.create();
350
- alertDialog.show();
351
- }
352
-
353
- @Override
354
- public void onSave() {
355
- if (!enableSaveDialog) {
356
- startTrim();
357
- return;
358
- }
359
-
360
- AlertDialog.Builder builder = new AlertDialog.Builder(getReactApplicationContext().getCurrentActivity());
361
- builder.setMessage(saveDialogMessage);
362
- builder.setTitle(saveDialogTitle);
363
- builder.setCancelable(false);
364
- builder.setPositiveButton(saveDialogConfirmText, (dialog, which) -> {
365
- dialog.cancel();
366
- startTrim();
367
- });
368
- builder.setNegativeButton(saveDialogCancelText, (dialog, which) -> {
369
- dialog.cancel();
370
- });
371
- AlertDialog alertDialog = builder.create();
372
- alertDialog.show();
373
- }
374
-
375
- @Override
376
- public void onLog(WritableMap log) {
377
- sendEvent(getReactApplicationContext(), "onLog", log);
378
- }
379
-
380
- @Override
381
- public void onStatistics(WritableMap statistics) {
382
- sendEvent(getReactApplicationContext(), "onStatistics", statistics);
383
- }
384
-
385
- private void startTrim() {
386
- Activity activity = getReactApplicationContext().getCurrentActivity();
387
- // Create the parent layout for the dialog
388
- LinearLayout layout = new LinearLayout(activity);
389
- layout.setLayoutParams(new ViewGroup.LayoutParams(
390
- ViewGroup.LayoutParams.WRAP_CONTENT,
391
- ViewGroup.LayoutParams.WRAP_CONTENT
392
- ));
393
- layout.setOrientation(LinearLayout.VERTICAL);
394
- layout.setGravity(Gravity.CENTER_HORIZONTAL);
395
- layout.setPadding(16, 32, 16, 32);
396
-
397
- // Create and add the TextView
398
- TextView textView = new TextView(activity);
399
- textView.setLayoutParams(new ViewGroup.LayoutParams(
400
- ViewGroup.LayoutParams.WRAP_CONTENT,
401
- ViewGroup.LayoutParams.WRAP_CONTENT
402
- ));
403
- textView.setText(trimmingText);
404
- textView.setGravity(Gravity.CENTER);
405
- textView.setTextSize(18);
406
- layout.addView(textView);
407
-
408
- // Create and add the ProgressBar
409
- mProgressBar = new ProgressBar(activity, null, android.R.attr.progressBarStyleHorizontal);
410
- mProgressBar.setLayoutParams(new ViewGroup.LayoutParams(
411
- ViewGroup.LayoutParams.MATCH_PARENT,
412
- ViewGroup.LayoutParams.WRAP_CONTENT
413
- ));
414
- mProgressBar.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#2196F3")));
415
- layout.addView(mProgressBar);
416
-
417
- // Create button
418
- if (enableCancelTrimming) {
419
- Button button = new Button(activity);
420
- button.setLayoutParams(new ViewGroup.LayoutParams(
421
- ViewGroup.LayoutParams.WRAP_CONTENT,
422
- ViewGroup.LayoutParams.WRAP_CONTENT
423
- ));
424
- // Set the text and style it like a text button
425
- button.setText(cancelTrimmingButtonText);
426
- button.setTextColor(ContextCompat.getColor(activity, android.R.color.holo_red_light)); // or use your custom color
427
-
428
- // Apply ripple effect while keeping the button background transparent
429
- TypedValue outValue = new TypedValue();
430
- activity.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
431
- button.setBackgroundResource(outValue.resourceId);
432
- button.setOnClickListener(v -> {
433
- if (enableCancelTrimmingDialog) {
434
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
435
- builder.setMessage(cancelTrimmingDialogMessage);
436
- builder.setTitle(cancelTrimmingDialogTitle);
437
- builder.setCancelable(false);
438
- builder.setPositiveButton(cancelTrimmingDialogConfirmText, (dialog, which) -> {
439
- if (trimmerView != null) {
440
- trimmerView.onCancelTrimClicked();
441
- }
442
-
443
- if (mProgressDialog != null && mProgressDialog.isShowing()) {
444
- mProgressDialog.dismiss();
445
- }
446
- });
447
- builder.setNegativeButton(cancelTrimmingDialogCancelText, (dialog, which) -> {
448
- dialog.cancel();
449
- });
450
- cancelTrimmingConfirmDialog = builder.create();
451
- cancelTrimmingConfirmDialog.show();
452
- } else {
453
- if (trimmerView != null) {
454
- trimmerView.onCancelTrimClicked();
455
- }
456
-
457
- if (mProgressDialog != null && mProgressDialog.isShowing()) {
458
- mProgressDialog.dismiss();
459
- }
460
- }
461
- });
462
- layout.addView(button);
463
- }
464
-
465
- // Create the AlertDialog
466
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
467
- builder.setCancelable(false);
468
- builder.setView(layout);
469
-
470
- // Show the dialog
471
- mProgressDialog = builder.create();
472
-
473
- mProgressDialog.setOnShowListener(dialog -> {
474
- sendEvent(getReactApplicationContext(), "onStartTrimming", null);
475
-
476
- if (trimmerView != null) {
477
- trimmerView.onSaveClicked();
478
- }
479
- });
480
-
481
- mProgressDialog.show();
482
- }
483
-
484
- private void hideDialog(boolean shouldCloseEditor) {
485
- // handle the case when the cancel dialog is still showing but the trimming is finished
486
- if (cancelTrimmingConfirmDialog != null) {
487
- if (cancelTrimmingConfirmDialog.isShowing()) {
488
- cancelTrimmingConfirmDialog.dismiss();
489
- }
490
- cancelTrimmingConfirmDialog = null;
491
- }
492
-
493
- if (mProgressDialog != null) {
494
- if (mProgressDialog.isShowing()) mProgressDialog.dismiss();
495
- mProgressBar = null;
496
- mProgressDialog = null;
497
- }
498
-
499
- if (shouldCloseEditor) {
500
- if (alertDialog != null) {
501
- if (alertDialog.isShowing()) {
502
- alertDialog.dismiss();
503
- }
504
- alertDialog = null;
505
- }
506
- }
507
- }
508
-
509
- @ReactMethod
510
- public void addListener(String eventName) {
511
- // This method is required by React
512
- listenerCount += 1;
513
- }
514
-
515
- @ReactMethod
516
- public void removeListeners(Integer count) {
517
- // This method is required by React
518
- listenerCount -= count;
519
- }
520
-
521
- private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
522
- if (listenerCount > 0) {
523
- WritableMap map = params != null ? params : Arguments.createMap();
524
- map.putString("name", eventName);
525
- reactContext
526
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
527
- .emit("VideoTrim", map);
528
- }
529
- }
530
-
531
- @ReactMethod
532
- private void listFiles(Promise promise) {
533
- String[] files = StorageUtil.listFiles(getReactApplicationContext());
534
- promise.resolve(Arguments.fromArray(files));
535
- }
536
-
537
- @ReactMethod
538
- private void cleanFiles(Promise promise) {
539
- String[] files = StorageUtil.listFiles(getReactApplicationContext());
540
- int successCount = 0;
541
- for (String file : files) {
542
- boolean state = StorageUtil.deleteFile(file);
543
- if (state) {
544
- successCount++;
545
- }
546
- }
547
- promise.resolve(successCount);
548
- }
549
-
550
- @ReactMethod
551
- private void deleteFile(String filePath, Promise promise) {
552
- boolean state = StorageUtil.deleteFile(filePath);
553
- promise.resolve(state);
554
- }
555
-
556
- @ReactMethod
557
- private void closeEditor() {
558
- hideDialog(true);
559
- }
560
-
561
- @ReactMethod
562
- private void isValidFile(String filePath, Promise promise) {
563
- MediaMetadataUtil.checkFileValidity(filePath, (isValid, fileType, duration) -> {
564
- if (isValid) {
565
- Log.d(TAG, "Valid " + fileType + " file with duration: " + duration + " milliseconds");
566
- } else {
567
- Log.d(TAG, "Invalid file");
568
- }
569
-
570
- WritableMap map = Arguments.createMap();
571
- map.putBoolean("isValid", isValid);
572
- map.putString("fileType", fileType);
573
- map.putDouble("duration", duration);
574
- promise.resolve(map);
575
- });
576
- }
577
-
578
- private void saveFileToExternalStorage(File file) {
579
- Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
580
- intent.addCategory(Intent.CATEGORY_OPENABLE);
581
- intent.setType("*/*"); // Change MIME type as needed
582
- intent.putExtra(Intent.EXTRA_TITLE, file.getName());
583
- getReactApplicationContext().getCurrentActivity().startActivityForResult(intent, REQUEST_CODE_SAVE_FILE);
584
- }
585
-
586
- public void shareFile(Context context, File file) {
587
- Uri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
588
-
589
- Intent shareIntent = new Intent(Intent.ACTION_SEND);
590
- shareIntent.setType("*/*");
591
- shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
592
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
593
-
594
- // Grant permissions to all applications that can handle the intent
595
- for (ResolveInfo resolveInfo : context.getPackageManager().queryIntentActivities(shareIntent, PackageManager.MATCH_DEFAULT_ONLY)) {
596
- String packageName = resolveInfo.activityInfo.packageName;
597
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
598
- }
599
-
600
- // directly use context.startActivity(shareIntent) will cause crash
601
- getReactApplicationContext().getCurrentActivity().startActivity(Intent.createChooser(shareIntent, "Share file"));
602
- }
603
- }
@@ -1,28 +0,0 @@
1
- package com.videotrim;
2
-
3
- import androidx.annotation.NonNull;
4
-
5
- import com.facebook.react.ReactPackage;
6
- import com.facebook.react.bridge.NativeModule;
7
- import com.facebook.react.bridge.ReactApplicationContext;
8
- import com.facebook.react.uimanager.ViewManager;
9
-
10
- import java.util.ArrayList;
11
- import java.util.Collections;
12
- import java.util.List;
13
-
14
- public class VideoTrimPackage implements ReactPackage {
15
- @NonNull
16
- @Override
17
- public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
18
- List<NativeModule> modules = new ArrayList<>();
19
- modules.add(new VideoTrimModule(reactContext));
20
- return modules;
21
- }
22
-
23
- @NonNull
24
- @Override
25
- public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
26
- return Collections.emptyList();
27
- }
28
- }
@@ -1,2 +0,0 @@
1
- #import <React/RCTBridgeModule.h>
2
- #import <React/RCTViewManager.h>
package/ios/VideoTrim.mm DELETED
@@ -1,17 +0,0 @@
1
- #import <React/RCTBridgeModule.h>
2
- #import <React/RCTEventEmitter.h>
3
-
4
- @interface RCT_EXTERN_MODULE(VideoTrim, RCTEventEmitter)
5
-
6
- RCT_EXTERN_METHOD(showEditor:(NSString*)uri withConfig:(NSDictionary *)config)
7
- RCT_EXTERN_METHOD(listFiles:(RCTPromiseResolveBlock)resolve
8
- withRejecter:(RCTPromiseRejectBlock)reject)
9
- RCT_EXTERN_METHOD(cleanFiles:(RCTPromiseResolveBlock)resolve
10
- withRejecter:(RCTPromiseRejectBlock)reject)
11
- RCT_EXTERN_METHOD(deleteFile:(NSString*)uri withResolver:(RCTPromiseResolveBlock)resolve
12
- withRejecter:(RCTPromiseRejectBlock)reject)
13
- RCT_EXTERN_METHOD(closeEditor:(RCTPromiseResolveBlock)resolve
14
- withRejecter:(RCTPromiseRejectBlock)reject)
15
- RCT_EXTERN_METHOD(isValidFile:(NSString*)uri withResolver:(RCTPromiseResolveBlock)resolve
16
- withRejecter:(RCTPromiseRejectBlock)reject)
17
- @end