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