react-native-video-trim 3.0.10 → 4.1.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.
- package/LICENSE +1 -1
- package/README.md +80 -63
- package/VideoTrim.podspec +24 -0
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +82 -49
- package/android/gradle.properties +7 -5
- package/android/src/main/AndroidManifest.xml +4 -2
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrim.kt +646 -0
- package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrimPackage.kt +22 -0
- package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/enums/ErrorCode.java +1 -1
- package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/interfaces/IVideoTrimmerView.java +1 -1
- package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/interfaces/VideoTrimListener.java +6 -5
- package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/utils/MediaMetadataUtil.java +1 -1
- package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/utils/StorageUtil.java +3 -1
- package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/utils/VideoTrimmerUtil.java +51 -41
- package/android/src/main/java/com/{videotrim → margelo/nitro/videotrim}/widgets/VideoTrimmerView.java +45 -69
- package/ios/AssetLoader.swift +2 -2
- package/ios/ErrorCode.swift +2 -2
- package/ios/ProgressAlertController.swift +2 -2
- package/ios/VideoTrim.swift +52 -835
- package/ios/VideoTrimImpl.swift +957 -0
- package/ios/VideoTrimmer.swift +2 -3
- package/ios/VideoTrimmerThumb.swift +33 -26
- package/ios/VideoTrimmerViewController.swift +47 -28
- package/lib/module/VideoTrim.nitro.js +4 -0
- package/lib/module/VideoTrim.nitro.js.map +1 -0
- package/lib/module/index.js +98 -22
- package/lib/module/index.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/{index.d.ts → src/VideoTrim.nitro.d.ts} +125 -134
- package/lib/typescript/src/VideoTrim.nitro.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +49 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/nitrogen/generated/android/c++/JEditorConfig.hpp +237 -0
- package/nitrogen/generated/android/c++/JFileValidationResult.hpp +61 -0
- package/nitrogen/generated/android/c++/JFunc_void.hpp +74 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__string_std__unordered_map_std__string__std__string_.hpp +89 -0
- package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.cpp +151 -0
- package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.hpp +68 -0
- package/nitrogen/generated/android/c++/JTrimOptions.hpp +109 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/EditorConfig.kt +72 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/FileValidationResult.kt +28 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void_std__string_std__unordered_map_std__string__std__string_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/HybridVideoTrimSpec.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/TrimOptions.kt +40 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/videotrimOnLoad.kt +35 -0
- package/nitrogen/generated/android/videotrim+autolinking.cmake +78 -0
- package/nitrogen/generated/android/videotrim+autolinking.gradle +27 -0
- package/nitrogen/generated/android/videotrimOnLoad.cpp +50 -0
- package/nitrogen/generated/android/videotrimOnLoad.hpp +25 -0
- package/nitrogen/generated/ios/VideoTrim+autolinking.rb +60 -0
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.cpp +96 -0
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.hpp +374 -0
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Umbrella.hpp +56 -0
- package/nitrogen/generated/ios/VideoTrimAutolinking.mm +33 -0
- package/nitrogen/generated/ios/VideoTrimAutolinking.swift +25 -0
- package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.hpp +127 -0
- package/nitrogen/generated/ios/swift/EditorConfig.swift +541 -0
- package/nitrogen/generated/ios/swift/FileValidationResult.swift +57 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_FileValidationResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_double.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string_std__unordered_map_std__string__std__string_.swift +54 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_std__string_.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridVideoTrimSpec.swift +54 -0
- package/nitrogen/generated/ios/swift/HybridVideoTrimSpec_cxx.swift +241 -0
- package/nitrogen/generated/ios/swift/TrimOptions.swift +189 -0
- package/nitrogen/generated/shared/c++/EditorConfig.hpp +253 -0
- package/nitrogen/generated/shared/c++/FileValidationResult.hpp +77 -0
- package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.cpp +27 -0
- package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.hpp +80 -0
- package/nitrogen/generated/shared/c++/TrimOptions.hpp +125 -0
- package/package.json +75 -71
- package/src/VideoTrim.nitro.ts +263 -0
- package/src/index.tsx +120 -257
- package/android/src/main/AndroidManifestDeprecated.xml +0 -3
- package/android/src/main/java/com/videotrim/VideoTrimModule.java +0 -603
- package/android/src/main/java/com/videotrim/VideoTrimPackage.java +0 -28
- package/ios/VideoTrim-Bridging-Header.h +0 -2
- package/ios/VideoTrim.mm +0 -17
- package/ios/VideoTrim.xcodeproj/project.pbxproj +0 -283
- package/lib/commonjs/index.js +0 -87
- package/lib/commonjs/index.js.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- 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
|
-
}
|
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
|