react-native-video-trim 4.1.0 → 5.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.
- package/LICENSE +1 -1
- package/README.md +89 -76
- package/VideoTrim.podspec +3 -3
- package/android/build.gradle +6 -53
- package/android/gradle.properties +1 -1
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/com/{margelo/nitro/videotrim/VideoTrim.kt → videotrim/VideoTrimModule.kt} +246 -232
- package/android/src/main/java/com/videotrim/VideoTrimPackage.kt +33 -0
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/enums/ErrorCode.java +1 -1
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/interfaces/IVideoTrimmerView.java +1 -1
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/interfaces/VideoTrimListener.java +5 -4
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/MediaMetadataUtil.java +1 -1
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/StorageUtil.java +1 -1
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/VideoTrimmerUtil.java +20 -18
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/widgets/VideoTrimmerView.java +44 -45
- package/ios/AssetLoader.h +19 -0
- package/ios/AssetLoader.mm +87 -0
- package/ios/ErrorCode.h +9 -0
- package/ios/ProgressAlertController.h +15 -0
- package/ios/ProgressAlertController.mm +78 -0
- package/ios/VideoTrim.h +31 -0
- package/ios/VideoTrim.mm +663 -0
- package/ios/VideoTrimmer.h +67 -0
- package/ios/VideoTrimmer.mm +863 -0
- package/ios/VideoTrimmerThumb.h +23 -0
- package/ios/VideoTrimmerThumb.mm +175 -0
- package/ios/VideoTrimmerViewController.h +52 -0
- package/ios/VideoTrimmerViewController.mm +533 -0
- package/lib/module/NativeVideoTrim.js +5 -0
- package/lib/module/NativeVideoTrim.js.map +1 -0
- package/lib/module/index.js +22 -24
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeVideoTrim.d.ts +107 -0
- package/lib/typescript/src/NativeVideoTrim.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +13 -10
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +15 -18
- package/src/NativeVideoTrim.ts +113 -0
- package/src/index.tsx +26 -31
- package/android/CMakeLists.txt +0 -24
- package/android/src/main/cpp/cpp-adapter.cpp +0 -6
- package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrimPackage.kt +0 -22
- package/ios/AssetLoader.swift +0 -99
- package/ios/ErrorCode.swift +0 -17
- package/ios/ProgressAlertController.swift +0 -100
- package/ios/VideoTrim.swift +0 -67
- package/ios/VideoTrimImpl.swift +0 -957
- package/ios/VideoTrimmer.swift +0 -872
- package/ios/VideoTrimmerThumb.swift +0 -175
- package/ios/VideoTrimmerViewController.swift +0 -557
- package/lib/module/VideoTrim.nitro.js +0 -4
- package/lib/module/VideoTrim.nitro.js.map +0 -1
- package/lib/typescript/src/VideoTrim.nitro.d.ts +0 -257
- package/lib/typescript/src/VideoTrim.nitro.d.ts.map +0 -1
- package/nitrogen/generated/android/c++/JEditorConfig.hpp +0 -237
- package/nitrogen/generated/android/c++/JFileValidationResult.hpp +0 -61
- package/nitrogen/generated/android/c++/JFunc_void.hpp +0 -74
- package/nitrogen/generated/android/c++/JFunc_void_std__string_std__unordered_map_std__string__std__string_.hpp +0 -89
- package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.cpp +0 -151
- package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.hpp +0 -68
- package/nitrogen/generated/android/c++/JTrimOptions.hpp +0 -109
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/EditorConfig.kt +0 -72
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/FileValidationResult.kt +0 -28
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void.kt +0 -80
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void_std__string_std__unordered_map_std__string__std__string_.kt +0 -80
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/HybridVideoTrimSpec.kt +0 -86
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/TrimOptions.kt +0 -40
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/videotrimOnLoad.kt +0 -35
- package/nitrogen/generated/android/videotrim+autolinking.cmake +0 -78
- package/nitrogen/generated/android/videotrim+autolinking.gradle +0 -27
- package/nitrogen/generated/android/videotrimOnLoad.cpp +0 -50
- package/nitrogen/generated/android/videotrimOnLoad.hpp +0 -25
- package/nitrogen/generated/ios/VideoTrim+autolinking.rb +0 -60
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.cpp +0 -96
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.hpp +0 -374
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Umbrella.hpp +0 -56
- package/nitrogen/generated/ios/VideoTrimAutolinking.mm +0 -33
- package/nitrogen/generated/ios/VideoTrimAutolinking.swift +0 -25
- package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.cpp +0 -11
- package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.hpp +0 -127
- package/nitrogen/generated/ios/swift/EditorConfig.swift +0 -541
- package/nitrogen/generated/ios/swift/FileValidationResult.swift +0 -57
- package/nitrogen/generated/ios/swift/Func_void.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_FileValidationResult.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_double.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_std__string_std__unordered_map_std__string__std__string_.swift +0 -54
- package/nitrogen/generated/ios/swift/Func_void_std__vector_std__string_.swift +0 -46
- package/nitrogen/generated/ios/swift/HybridVideoTrimSpec.swift +0 -54
- package/nitrogen/generated/ios/swift/HybridVideoTrimSpec_cxx.swift +0 -241
- package/nitrogen/generated/ios/swift/TrimOptions.swift +0 -189
- package/nitrogen/generated/shared/c++/EditorConfig.hpp +0 -253
- package/nitrogen/generated/shared/c++/FileValidationResult.hpp +0 -77
- package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.cpp +0 -27
- package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.hpp +0 -80
- package/nitrogen/generated/shared/c++/TrimOptions.hpp +0 -125
- package/src/VideoTrim.nitro.ts +0 -263
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
package com.
|
|
1
|
+
package com.videotrim
|
|
2
2
|
|
|
3
3
|
import android.R.attr.progressBarStyleHorizontal
|
|
4
4
|
import android.R.attr.selectableItemBackground
|
|
5
5
|
import android.R.color.holo_red_light
|
|
6
6
|
import android.R.style.Theme_Black_NoTitleBar_Fullscreen
|
|
7
|
-
import android.annotation.SuppressLint
|
|
8
7
|
import android.app.Activity
|
|
9
8
|
import android.content.Context
|
|
10
9
|
import android.content.DialogInterface
|
|
11
10
|
import android.content.Intent
|
|
12
11
|
import android.content.pm.PackageManager
|
|
13
12
|
import android.content.res.ColorStateList
|
|
13
|
+
import android.os.Build
|
|
14
14
|
import android.util.Log
|
|
15
15
|
import android.util.TypedValue
|
|
16
16
|
import android.view.Gravity
|
|
@@ -25,19 +25,17 @@ import androidx.core.content.ContextCompat
|
|
|
25
25
|
import androidx.core.content.FileProvider
|
|
26
26
|
import androidx.core.graphics.toColorInt
|
|
27
27
|
import androidx.core.net.toUri
|
|
28
|
+
import androidx.core.view.ViewCompat
|
|
29
|
+
import androidx.core.view.WindowInsetsCompat
|
|
28
30
|
import com.arthenica.ffmpegkit.FFmpegKit
|
|
29
|
-
import com.arthenica.ffmpegkit.ReturnCode
|
|
30
|
-
import com.facebook.
|
|
31
|
-
import com.facebook.react.
|
|
32
|
-
import com.
|
|
33
|
-
import com.
|
|
34
|
-
import com.
|
|
35
|
-
import com.
|
|
36
|
-
import com.
|
|
37
|
-
import com.margelo.nitro.videotrim.interfaces.VideoTrimListener
|
|
38
|
-
import com.margelo.nitro.videotrim.utils.MediaMetadataUtil
|
|
39
|
-
import com.margelo.nitro.videotrim.utils.StorageUtil
|
|
40
|
-
import com.margelo.nitro.videotrim.widgets.VideoTrimmerView
|
|
31
|
+
import com.arthenica.ffmpegkit.ReturnCode
|
|
32
|
+
import com.facebook.react.bridge.*
|
|
33
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
34
|
+
import com.videotrim.enums.ErrorCode
|
|
35
|
+
import com.videotrim.interfaces.VideoTrimListener
|
|
36
|
+
import com.videotrim.utils.MediaMetadataUtil
|
|
37
|
+
import com.videotrim.utils.StorageUtil
|
|
38
|
+
import com.videotrim.widgets.VideoTrimmerView
|
|
41
39
|
import iknow.android.utils.BaseUtils
|
|
42
40
|
import java.io.File
|
|
43
41
|
import java.io.FileInputStream
|
|
@@ -45,12 +43,11 @@ import java.io.IOException
|
|
|
45
43
|
import java.text.SimpleDateFormat
|
|
46
44
|
import java.util.Date
|
|
47
45
|
import java.util.TimeZone
|
|
48
|
-
import kotlin.coroutines.resume
|
|
49
|
-
import kotlin.coroutines.resumeWithException
|
|
50
|
-
import kotlin.coroutines.suspendCoroutine
|
|
51
46
|
|
|
52
|
-
@
|
|
53
|
-
class
|
|
47
|
+
@ReactModule(name = VideoTrimModule.NAME)
|
|
48
|
+
class VideoTrimModule(reactContext: ReactApplicationContext) :
|
|
49
|
+
NativeVideoTrimSpec(reactContext), VideoTrimListener, LifecycleEventListener {
|
|
50
|
+
|
|
54
51
|
private var isInit: Boolean = false
|
|
55
52
|
private var trimmerView: VideoTrimmerView? = null
|
|
56
53
|
private var alertDialog: AlertDialog? = null
|
|
@@ -59,10 +56,8 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
59
56
|
private var mProgressBar: ProgressBar? = null
|
|
60
57
|
private var outputFile: String? = null
|
|
61
58
|
private var isVideoType = true
|
|
62
|
-
private var editorConfig:
|
|
63
|
-
private var trimOptions:
|
|
64
|
-
private var onEvent: ((eventName: String, payload: Map<String, String>) -> Unit)? = null
|
|
65
|
-
private var onComplete: (() -> Unit)? = null
|
|
59
|
+
private var editorConfig: ReadableMap? = null
|
|
60
|
+
private var trimOptions: ReadableMap? = null
|
|
66
61
|
|
|
67
62
|
init {
|
|
68
63
|
val mActivityEventListener = object : BaseActivityEventListener() {
|
|
@@ -76,7 +71,7 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
76
71
|
|
|
77
72
|
val uri = intent?.data ?: return
|
|
78
73
|
try {
|
|
79
|
-
|
|
74
|
+
reactApplicationContext.contentResolver?.openOutputStream(uri)
|
|
80
75
|
?.use { outputStream ->
|
|
81
76
|
FileInputStream(outputFile).use { fileInputStream ->
|
|
82
77
|
val buffer = ByteArray(1024)
|
|
@@ -88,9 +83,14 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
88
83
|
} ?: return
|
|
89
84
|
// File saved successfully
|
|
90
85
|
Log.d(TAG, "File saved successfully to $uri")
|
|
91
|
-
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
editorConfig?.getBoolean("removeAfterSavedToDocuments") == true ||
|
|
89
|
+
trimOptions?.getBoolean("removeAfterFailedToSaveDocuments") == true
|
|
90
|
+
) {
|
|
92
91
|
StorageUtil.deleteFile(outputFile)
|
|
93
92
|
}
|
|
93
|
+
|
|
94
94
|
} catch (e: Exception) {
|
|
95
95
|
e.printStackTrace()
|
|
96
96
|
// Handle the error
|
|
@@ -98,7 +98,7 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
98
98
|
"Failed to save edited video to Documents: ${e.localizedMessage}",
|
|
99
99
|
ErrorCode.FAIL_TO_SAVE_TO_DOCUMENTS
|
|
100
100
|
)
|
|
101
|
-
if (editorConfig?.removeAfterFailedToSaveDocuments == true || trimOptions?.removeAfterFailedToSaveDocuments == true) {
|
|
101
|
+
if (editorConfig?.getBoolean("removeAfterFailedToSaveDocuments") == true || trimOptions?.getBoolean("removeAfterFailedToSaveDocuments") == true) {
|
|
102
102
|
StorageUtil.deleteFile(outputFile)
|
|
103
103
|
}
|
|
104
104
|
} finally {
|
|
@@ -107,23 +107,22 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
|
|
110
|
+
reactApplicationContext.addActivityEventListener(mActivityEventListener)
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
override fun showEditor(
|
|
114
114
|
filePath: String,
|
|
115
|
-
config:
|
|
116
|
-
onEvent: (eventName: String, payload: Map<String, String>) -> Unit
|
|
115
|
+
config: ReadableMap,
|
|
117
116
|
) {
|
|
118
117
|
if (trimmerView != null || alertDialog != null) {
|
|
119
118
|
return
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
this.editorConfig = config
|
|
123
|
-
this.onEvent = onEvent
|
|
124
|
-
this.isVideoType = config.type == "video"
|
|
125
122
|
|
|
126
|
-
|
|
123
|
+
this.isVideoType = config.hasKey("type") && config.getString("type") == "video"
|
|
124
|
+
|
|
125
|
+
val activity = reactApplicationContext.currentActivity
|
|
127
126
|
if (!isInit) {
|
|
128
127
|
init()
|
|
129
128
|
isInit = true
|
|
@@ -131,7 +130,7 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
131
130
|
|
|
132
131
|
// here is NOT main thread, we need to create VideoTrimmerView on UI thread, so that later we can update it using same thread
|
|
133
132
|
UiThreadUtil.runOnUiThread {
|
|
134
|
-
trimmerView = VideoTrimmerView(
|
|
133
|
+
trimmerView = VideoTrimmerView(reactApplicationContext, editorConfig, null)
|
|
135
134
|
trimmerView?.setOnTrimVideoListener(this)
|
|
136
135
|
trimmerView?.initByURI(filePath.toUri())
|
|
137
136
|
|
|
@@ -141,7 +140,13 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
141
140
|
builder.setCancelable(false)
|
|
142
141
|
alertDialog = builder.create()
|
|
143
142
|
alertDialog?.setView(trimmerView)
|
|
144
|
-
|
|
143
|
+
|
|
144
|
+
// Apply safe area handling after the dialog is shown
|
|
145
|
+
alertDialog?.setOnShowListener {
|
|
146
|
+
applySafeAreaToDialog(alertDialog!!, trimmerView!!)
|
|
147
|
+
|
|
148
|
+
emitOnShow()
|
|
149
|
+
}
|
|
145
150
|
|
|
146
151
|
// 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)
|
|
147
152
|
alertDialog!!.setOnDismissListener {
|
|
@@ -151,16 +156,44 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
151
156
|
trimmerView = null
|
|
152
157
|
}
|
|
153
158
|
hideDialog(true)
|
|
154
|
-
|
|
159
|
+
emitOnHide()
|
|
155
160
|
}
|
|
156
|
-
|
|
161
|
+
|
|
162
|
+
alertDialog?.show()
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// because trimmerView is rendered within the dialog, we need to apply safe area insets to the dialog
|
|
167
|
+
// else setOnApplyWindowInsetsListener will not fire
|
|
168
|
+
private fun applySafeAreaToDialog(dialog: AlertDialog, trimmerView: VideoTrimmerView) {
|
|
169
|
+
val window = dialog.window
|
|
170
|
+
if (window != null) {
|
|
171
|
+
// Enable edge-to-edge for the dialog window
|
|
172
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
173
|
+
window.setDecorFitsSystemWindows(false)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Get the dialog's decorView and apply insets listener
|
|
177
|
+
val decorView = window.decorView
|
|
178
|
+
ViewCompat.setOnApplyWindowInsetsListener(decorView) { _, windowInsets ->
|
|
179
|
+
val insets = windowInsets.getInsets(
|
|
180
|
+
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
|
|
181
|
+
)
|
|
182
|
+
Log.d(TAG, "Dialog insets: top=${insets.top}, left=${insets.left}, bottom=${insets.bottom}, right=${insets.right}")
|
|
183
|
+
|
|
184
|
+
// Apply padding to the trimmer view
|
|
185
|
+
trimmerView.setPadding(insets.left, insets.top, insets.right, insets.bottom)
|
|
186
|
+
|
|
187
|
+
WindowInsetsCompat.CONSUMED
|
|
188
|
+
}
|
|
189
|
+
ViewCompat.requestApplyInsets(decorView)
|
|
157
190
|
}
|
|
158
191
|
}
|
|
159
192
|
|
|
160
193
|
private fun init() {
|
|
161
194
|
isInit = true
|
|
162
195
|
// we have to init this before create videoTrimmerView
|
|
163
|
-
BaseUtils.init(
|
|
196
|
+
BaseUtils.init(reactApplicationContext)
|
|
164
197
|
}
|
|
165
198
|
|
|
166
199
|
override fun onHostResume() {
|
|
@@ -178,8 +211,15 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
178
211
|
hideDialog(true)
|
|
179
212
|
}
|
|
180
213
|
|
|
214
|
+
override fun invalidate() {
|
|
215
|
+
super.invalidate()
|
|
216
|
+
hideDialog(true)
|
|
217
|
+
}
|
|
218
|
+
|
|
181
219
|
override fun onLoad(duration: Int) {
|
|
182
|
-
|
|
220
|
+
val map = Arguments.createMap()
|
|
221
|
+
map.putInt("duration", duration)
|
|
222
|
+
emitOnLoad(map)
|
|
183
223
|
}
|
|
184
224
|
|
|
185
225
|
override fun onTrimmingProgress(percentage: Int) {
|
|
@@ -196,20 +236,18 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
196
236
|
// save output file to use in other places
|
|
197
237
|
outputFile = out
|
|
198
238
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
)
|
|
206
|
-
)
|
|
239
|
+
val map = Arguments.createMap()
|
|
240
|
+
map.putString("outputPath", outputFile)
|
|
241
|
+
map.putInt("duration", duration)
|
|
242
|
+
map.putDouble("startTime", startTime.toDouble())
|
|
243
|
+
map.putDouble("endTime", endTime.toDouble())
|
|
244
|
+
emitOnFinishTrimming(map)
|
|
207
245
|
|
|
208
|
-
if (editorConfig
|
|
246
|
+
if (editorConfig?.getBoolean("saveToPhoto") == true && isVideoType) {
|
|
209
247
|
try {
|
|
210
|
-
StorageUtil.saveVideoToGallery(
|
|
248
|
+
StorageUtil.saveVideoToGallery(reactApplicationContext, outputFile)
|
|
211
249
|
Log.d(TAG, "Edited video saved to Photo Library successfully.")
|
|
212
|
-
if (editorConfig
|
|
250
|
+
if (editorConfig?.getBoolean("removeAfterSavedToPhoto") == true) {
|
|
213
251
|
StorageUtil.deleteFile(outputFile)
|
|
214
252
|
}
|
|
215
253
|
} catch (e: IOException) {
|
|
@@ -218,56 +256,52 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
218
256
|
"Failed to save edited video to Photo Library: " + e.localizedMessage,
|
|
219
257
|
ErrorCode.FAIL_TO_SAVE_TO_PHOTO
|
|
220
258
|
)
|
|
221
|
-
if (editorConfig
|
|
259
|
+
if (editorConfig?.getBoolean("removeAfterFailedToSavePhoto") == true) {
|
|
222
260
|
StorageUtil.deleteFile(outputFile)
|
|
223
261
|
}
|
|
224
262
|
} finally {
|
|
225
|
-
hideDialog(editorConfig
|
|
263
|
+
hideDialog(editorConfig?.getBoolean("closeWhenFinish") ?: true)
|
|
226
264
|
}
|
|
227
|
-
} else if (editorConfig
|
|
265
|
+
} else if (editorConfig?.getBoolean("openDocumentsOnFinish") == true) {
|
|
228
266
|
saveFileToExternalStorage(File(outputFile!!))
|
|
229
|
-
} else if (editorConfig
|
|
230
|
-
hideDialog(editorConfig
|
|
231
|
-
shareFile(
|
|
267
|
+
} else if (editorConfig?.getBoolean("openShareSheetOnFinish") == true) {
|
|
268
|
+
hideDialog(editorConfig?.getBoolean("closeWhenFinish") ?: true)
|
|
269
|
+
shareFile(reactApplicationContext, File(outputFile!!))
|
|
232
270
|
} else {
|
|
233
|
-
hideDialog(editorConfig
|
|
271
|
+
hideDialog(editorConfig?.getBoolean("closeWhenFinish") ?: true)
|
|
234
272
|
}
|
|
235
273
|
}
|
|
236
274
|
|
|
237
275
|
override fun onCancelTrim() {
|
|
238
|
-
|
|
276
|
+
emitOnCancelTrimming()
|
|
239
277
|
}
|
|
240
278
|
|
|
241
279
|
override fun onError(errorMessage: String?, errorCode: ErrorCode) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
)
|
|
247
|
-
)
|
|
280
|
+
val map = Arguments.createMap()
|
|
281
|
+
map.putString("message", errorMessage)
|
|
282
|
+
map.putString("errorCode", errorCode.name)
|
|
283
|
+
emitOnError(map)
|
|
248
284
|
}
|
|
249
285
|
|
|
250
286
|
override fun onCancel() {
|
|
251
|
-
if (!editorConfig
|
|
252
|
-
|
|
287
|
+
if (!editorConfig?.getBoolean("enableCancelDialog")!!) {
|
|
288
|
+
emitOnCancel()
|
|
253
289
|
hideDialog(true)
|
|
254
290
|
return
|
|
255
291
|
}
|
|
256
292
|
|
|
257
|
-
val builder = AlertDialog.Builder(
|
|
258
|
-
|
|
259
|
-
)
|
|
260
|
-
builder.setMessage(editorConfig!!.cancelDialogMessage)
|
|
261
|
-
builder.setTitle(editorConfig!!.cancelDialogTitle)
|
|
293
|
+
val builder = AlertDialog.Builder(reactApplicationContext.currentActivity!!)
|
|
294
|
+
builder.setMessage(editorConfig?.getString("cancelDialogMessage"))
|
|
295
|
+
builder.setTitle(editorConfig?.getString("cancelDialogTitle"))
|
|
262
296
|
builder.setCancelable(false)
|
|
263
|
-
builder.setPositiveButton(editorConfig
|
|
297
|
+
builder.setPositiveButton(editorConfig?.getString("cancelDialogConfirmText")) { dialog: DialogInterface, _: Int ->
|
|
264
298
|
dialog.cancel()
|
|
265
|
-
|
|
299
|
+
emitOnCancel()
|
|
266
300
|
hideDialog(true)
|
|
267
301
|
}
|
|
268
302
|
builder.setNegativeButton(
|
|
269
|
-
editorConfig
|
|
270
|
-
) { dialog: DialogInterface,
|
|
303
|
+
editorConfig?.getString("cancelDialogCancelText") ?: "Cancel"
|
|
304
|
+
) { dialog: DialogInterface, _: Int ->
|
|
271
305
|
dialog.cancel()
|
|
272
306
|
}
|
|
273
307
|
val alertDialog = builder.create()
|
|
@@ -275,23 +309,21 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
275
309
|
}
|
|
276
310
|
|
|
277
311
|
override fun onSave() {
|
|
278
|
-
if (!editorConfig
|
|
312
|
+
if (!editorConfig?.getBoolean("enableSaveDialog")!!) {
|
|
279
313
|
startTrim()
|
|
280
314
|
return
|
|
281
315
|
}
|
|
282
316
|
|
|
283
|
-
val builder = AlertDialog.Builder(
|
|
284
|
-
|
|
285
|
-
)
|
|
286
|
-
builder.setMessage(editorConfig!!.saveDialogMessage)
|
|
287
|
-
builder.setTitle(editorConfig!!.saveDialogTitle)
|
|
317
|
+
val builder = AlertDialog.Builder(reactApplicationContext.currentActivity!!)
|
|
318
|
+
builder.setMessage(editorConfig?.getString("saveDialogMessage"))
|
|
319
|
+
builder.setTitle(editorConfig?.getString("saveDialogTitle"))
|
|
288
320
|
builder.setCancelable(false)
|
|
289
|
-
builder.setPositiveButton(editorConfig
|
|
321
|
+
builder.setPositiveButton(editorConfig?.getString("saveDialogConfirmText")) { dialog: DialogInterface, _: Int ->
|
|
290
322
|
dialog.cancel()
|
|
291
323
|
startTrim()
|
|
292
324
|
}
|
|
293
325
|
builder.setNegativeButton(
|
|
294
|
-
editorConfig
|
|
326
|
+
editorConfig?.getString("saveDialogCancelText") ?: "Cancel"
|
|
295
327
|
) { dialog: DialogInterface, _: Int ->
|
|
296
328
|
dialog.cancel()
|
|
297
329
|
}
|
|
@@ -299,16 +331,16 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
299
331
|
alertDialog.show()
|
|
300
332
|
}
|
|
301
333
|
|
|
302
|
-
override fun onLog(log:
|
|
303
|
-
|
|
334
|
+
override fun onLog(log: ReadableMap) {
|
|
335
|
+
emitOnLog( log)
|
|
304
336
|
}
|
|
305
337
|
|
|
306
|
-
override fun onStatistics(statistics:
|
|
307
|
-
|
|
338
|
+
override fun onStatistics(statistics: ReadableMap) {
|
|
339
|
+
emitOnStatistics(statistics)
|
|
308
340
|
}
|
|
309
341
|
|
|
310
342
|
private fun startTrim() {
|
|
311
|
-
val activity =
|
|
343
|
+
val activity = reactApplicationContext.currentActivity
|
|
312
344
|
// Create the parent layout for the dialog
|
|
313
345
|
val layout = LinearLayout(activity)
|
|
314
346
|
layout.layoutParams = ViewGroup.LayoutParams(
|
|
@@ -325,7 +357,8 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
325
357
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
326
358
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
327
359
|
)
|
|
328
|
-
textView.text = editorConfig
|
|
360
|
+
textView.text = editorConfig?.getString("trimmingText")
|
|
361
|
+
?: "Trimming in progress..."
|
|
329
362
|
textView.gravity = Gravity.CENTER
|
|
330
363
|
textView.textSize = 18f
|
|
331
364
|
layout.addView(textView)
|
|
@@ -340,14 +373,15 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
340
373
|
layout.addView(mProgressBar)
|
|
341
374
|
|
|
342
375
|
// Create button
|
|
343
|
-
if (editorConfig
|
|
376
|
+
if (editorConfig?.getBoolean("enableCancelTrimming") == true) {
|
|
344
377
|
val button = Button(activity)
|
|
345
378
|
button.layoutParams = ViewGroup.LayoutParams(
|
|
346
379
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
347
380
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
348
381
|
)
|
|
349
382
|
// Set the text and style it like a text button
|
|
350
|
-
button.text = editorConfig
|
|
383
|
+
button.text = editorConfig?.getString("cancelTrimmingText")
|
|
384
|
+
?: "Cancel Trimming"
|
|
351
385
|
button.setTextColor(
|
|
352
386
|
ContextCompat.getColor(
|
|
353
387
|
activity!!,
|
|
@@ -359,15 +393,15 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
359
393
|
val outValue = TypedValue()
|
|
360
394
|
activity.theme.resolveAttribute(selectableItemBackground, outValue, true)
|
|
361
395
|
button.setBackgroundResource(outValue.resourceId)
|
|
362
|
-
button.setOnClickListener {
|
|
363
|
-
if (editorConfig
|
|
396
|
+
button.setOnClickListener { _: View? ->
|
|
397
|
+
if (editorConfig?.getBoolean("enableCancelTrimmingDialog") == true) {
|
|
364
398
|
val builder = AlertDialog.Builder(
|
|
365
399
|
activity
|
|
366
400
|
)
|
|
367
|
-
builder.setMessage(editorConfig
|
|
368
|
-
builder.setTitle(editorConfig
|
|
401
|
+
builder.setMessage(editorConfig?.getString("cancelTrimmingDialogMessage"))
|
|
402
|
+
builder.setTitle(editorConfig?.getString("cancelTrimmingDialogTitle"))
|
|
369
403
|
builder.setCancelable(false)
|
|
370
|
-
builder.setPositiveButton(editorConfig
|
|
404
|
+
builder.setPositiveButton(editorConfig?.getString("cancelTrimmingDialogConfirmText")) { _: DialogInterface?, _: Int ->
|
|
371
405
|
if (trimmerView != null) {
|
|
372
406
|
trimmerView!!.onCancelTrimClicked()
|
|
373
407
|
}
|
|
@@ -376,8 +410,8 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
376
410
|
}
|
|
377
411
|
}
|
|
378
412
|
builder.setNegativeButton(
|
|
379
|
-
editorConfig
|
|
380
|
-
) { dialog: DialogInterface,
|
|
413
|
+
editorConfig?.getString("cancelTrimmingDialogCancelText") ?: "Close"
|
|
414
|
+
) { dialog: DialogInterface, _: Int ->
|
|
381
415
|
dialog.cancel()
|
|
382
416
|
}
|
|
383
417
|
cancelTrimmingConfirmDialog = builder.create()
|
|
@@ -406,7 +440,7 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
406
440
|
mProgressDialog = builder.create()
|
|
407
441
|
|
|
408
442
|
mProgressDialog!!.setOnShowListener {
|
|
409
|
-
|
|
443
|
+
emitOnStartTrimming()
|
|
410
444
|
if (trimmerView != null) {
|
|
411
445
|
trimmerView!!.onSaveClicked()
|
|
412
446
|
}
|
|
@@ -440,27 +474,24 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
440
474
|
}
|
|
441
475
|
}
|
|
442
476
|
|
|
443
|
-
private fun sendEvent(
|
|
444
|
-
eventName: String,
|
|
445
|
-
params: Map<String, String>
|
|
446
|
-
) {
|
|
447
|
-
onEvent?.let { it(eventName, params) }
|
|
448
|
-
|
|
449
|
-
if (eventName == "onHide" && onComplete != null) {
|
|
450
|
-
onComplete?.let { it() }
|
|
451
|
-
onComplete = null // Clear the callback after invoking it
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
override fun listFiles(
|
|
456
|
-
|
|
457
|
-
StorageUtil.listFiles(NitroModules.applicationContext)
|
|
458
|
-
}
|
|
477
|
+
// private fun sendEvent(
|
|
478
|
+
// eventName: String,
|
|
479
|
+
// params: Map<String, String>
|
|
480
|
+
// ) {
|
|
481
|
+
// onEvent?.let { it(eventName, params) }
|
|
482
|
+
//
|
|
483
|
+
// if (eventName == "onHide" && onComplete != null) {
|
|
484
|
+
// onComplete?.let { it() }
|
|
485
|
+
// onComplete = null // Clear the callback after invoking it
|
|
486
|
+
// }
|
|
487
|
+
// }
|
|
488
|
+
|
|
489
|
+
override fun listFiles(promise: Promise) {
|
|
490
|
+
promise.resolve(StorageUtil.listFiles(reactApplicationContext))
|
|
459
491
|
}
|
|
460
492
|
|
|
461
|
-
override fun cleanFiles(
|
|
462
|
-
|
|
463
|
-
val files = StorageUtil.listFiles(NitroModules.applicationContext)
|
|
493
|
+
override fun cleanFiles(promise: Promise) {
|
|
494
|
+
val files = StorageUtil.listFiles(reactApplicationContext)
|
|
464
495
|
var successCount = 0
|
|
465
496
|
for (file in files) {
|
|
466
497
|
val state = StorageUtil.deleteFile(file)
|
|
@@ -469,143 +500,127 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
469
500
|
}
|
|
470
501
|
}
|
|
471
502
|
|
|
472
|
-
successCount.toDouble()
|
|
473
|
-
}
|
|
503
|
+
promise.resolve(successCount.toDouble())
|
|
474
504
|
}
|
|
475
505
|
|
|
476
|
-
override fun deleteFile(filePath: String
|
|
477
|
-
|
|
478
|
-
StorageUtil.deleteFile(filePath)
|
|
479
|
-
}
|
|
506
|
+
override fun deleteFile(filePath: String?, promise: Promise) {
|
|
507
|
+
promise.resolve(StorageUtil.deleteFile(filePath))
|
|
480
508
|
}
|
|
481
509
|
|
|
482
|
-
override fun closeEditor(
|
|
483
|
-
this.onComplete = onComplete
|
|
510
|
+
override fun closeEditor() {
|
|
484
511
|
hideDialog(true)
|
|
512
|
+
emitOnHide()
|
|
485
513
|
}
|
|
486
514
|
|
|
487
|
-
override fun isValidFile(url: String
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
Log.d(TAG, "Valid $fileType file with duration: $duration milliseconds")
|
|
494
|
-
} else {
|
|
495
|
-
Log.d(TAG, "Invalid file")
|
|
496
|
-
}
|
|
497
|
-
// Create a FileValidationResult object
|
|
498
|
-
val result = FileValidationResult(
|
|
499
|
-
isValid = isValid,
|
|
500
|
-
fileType = fileType,
|
|
501
|
-
duration = duration.toDouble() // Convert Long to Double
|
|
502
|
-
)
|
|
503
|
-
continuation.resume(result)
|
|
504
|
-
}
|
|
515
|
+
override fun isValidFile(url: String, promise: Promise) {
|
|
516
|
+
MediaMetadataUtil.checkFileValidity(url) { isValid: Boolean, fileType: String, duration: Long ->
|
|
517
|
+
if (isValid) {
|
|
518
|
+
Log.d(TAG, "Valid $fileType file with duration: $duration milliseconds")
|
|
519
|
+
} else {
|
|
520
|
+
Log.d(TAG, "Invalid file")
|
|
505
521
|
}
|
|
506
|
-
//
|
|
507
|
-
getValidationResult()
|
|
508
|
-
}
|
|
509
|
-
}
|
|
522
|
+
// Create a FileValidationResult object
|
|
510
523
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
524
|
+
val result = Arguments.createMap()
|
|
525
|
+
result.putBoolean("isValid", isValid)
|
|
526
|
+
result.putString("fileType", fileType)
|
|
527
|
+
result.putDouble("duration", duration.toDouble())
|
|
514
528
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
|
|
529
|
+
promise.resolve(result)
|
|
530
|
+
}
|
|
531
|
+
}
|
|
519
532
|
|
|
520
|
-
|
|
521
|
-
|
|
533
|
+
override fun trim(url: String, options: ReadableMap?, promise: Promise) {
|
|
534
|
+
trimOptions = options
|
|
522
535
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
"${options.startTime}ms",
|
|
526
|
-
"-to",
|
|
527
|
-
"${options.endTime}ms",
|
|
528
|
-
)
|
|
536
|
+
val currentDate = Date()
|
|
537
|
+
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
|
|
529
538
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
}
|
|
539
|
+
dateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
|
540
|
+
val formattedDateTime = dateFormat.format(currentDate)
|
|
533
541
|
|
|
534
|
-
|
|
542
|
+
var cmds = arrayOf(
|
|
543
|
+
"-ss",
|
|
544
|
+
"${options?.getDouble("startTime") ?: 0 }ms",
|
|
545
|
+
"-to",
|
|
546
|
+
"${options?.getDouble("endTime") ?: 1000}ms",
|
|
547
|
+
)
|
|
535
548
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
"-c",
|
|
540
|
-
"copy",
|
|
541
|
-
"-metadata",
|
|
542
|
-
"creation_time=$formattedDateTime",
|
|
543
|
-
outputFile!!
|
|
544
|
-
)
|
|
549
|
+
if (options?.getBoolean("enableRotation") == true) {
|
|
550
|
+
cmds += arrayOf("-display_rotation", "${options.getDouble("rotationAngle")}")
|
|
551
|
+
}
|
|
545
552
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
FFmpegKit.executeWithArgumentsAsync(cmds, { session ->
|
|
549
|
-
val state = session.state
|
|
550
|
-
val returnCode = session.returnCode
|
|
551
|
-
when {
|
|
552
|
-
ReturnCode.isSuccess(returnCode) -> {
|
|
553
|
-
// SUCCESS
|
|
554
|
-
if (options.saveToPhoto && options.type == "video") {
|
|
555
|
-
try {
|
|
556
|
-
StorageUtil.saveVideoToGallery(NitroModules.applicationContext, outputFile)
|
|
557
|
-
Log.d(TAG, "Edited video saved to Photo Library successfully.")
|
|
558
|
-
if (options.removeAfterSavedToPhoto) {
|
|
559
|
-
StorageUtil.deleteFile(outputFile)
|
|
560
|
-
}
|
|
553
|
+
outputFile = StorageUtil.getOutputPath(reactApplicationContext, options?.getString("outputExt") ?: "mp4")
|
|
561
554
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
555
|
+
cmds += arrayOf(
|
|
556
|
+
"-i",
|
|
557
|
+
url,
|
|
558
|
+
"-c",
|
|
559
|
+
"copy",
|
|
560
|
+
"-metadata",
|
|
561
|
+
"creation_time=$formattedDateTime",
|
|
562
|
+
outputFile!!
|
|
563
|
+
)
|
|
565
564
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
565
|
+
Log.d(TAG, "Command: ${cmds.joinToString(",")}")
|
|
566
|
+
|
|
567
|
+
FFmpegKit.executeWithArgumentsAsync(cmds, { session ->
|
|
568
|
+
val state = session.state
|
|
569
|
+
val returnCode = session.returnCode
|
|
570
|
+
when {
|
|
571
|
+
ReturnCode.isSuccess(returnCode) -> {
|
|
572
|
+
// SUCCESS
|
|
573
|
+
if (options?.getBoolean("saveToPhoto") == true && options.getString("type") == "video") {
|
|
574
|
+
try {
|
|
575
|
+
StorageUtil.saveVideoToGallery(reactApplicationContext, outputFile)
|
|
576
|
+
Log.d(TAG, "Edited video saved to Photo Library successfully.")
|
|
577
|
+
if (options.getBoolean("removeAfterSavedToPhoto")) {
|
|
578
|
+
StorageUtil.deleteFile(outputFile)
|
|
579
|
+
}
|
|
569
580
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
} else {
|
|
575
|
-
if (options.openDocumentsOnFinish) {
|
|
576
|
-
saveFileToExternalStorage(File(outputFile!!))
|
|
577
|
-
} else if (options.openShareSheetOnFinish) {
|
|
578
|
-
shareFile(NitroModules.applicationContext!!, File(outputFile!!))
|
|
579
|
-
}
|
|
581
|
+
promise.resolve(outputFile)
|
|
582
|
+
} catch (e: IOException) {
|
|
583
|
+
e.printStackTrace()
|
|
580
584
|
|
|
581
|
-
|
|
582
|
-
|
|
585
|
+
if (options.getBoolean("removeAfterFailedToSavePhoto")) {
|
|
586
|
+
StorageUtil.deleteFile(outputFile)
|
|
583
587
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
continuation.resumeWithException(
|
|
588
|
-
Exception("FFmpeg command was cancelled")
|
|
588
|
+
|
|
589
|
+
promise.reject(
|
|
590
|
+
Exception("Failed to save edited video to Photo Library: " + e.localizedMessage)
|
|
589
591
|
)
|
|
590
592
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
Exception(errorMessage)
|
|
597
|
-
)
|
|
593
|
+
} else {
|
|
594
|
+
if (options?.getBoolean("openDocumentsOnFinish") == true) {
|
|
595
|
+
saveFileToExternalStorage(File(outputFile!!))
|
|
596
|
+
} else if (options?.getBoolean("openShareSheetOnFinish") == true) {
|
|
597
|
+
shareFile(reactApplicationContext, File(outputFile!!))
|
|
598
598
|
}
|
|
599
|
+
|
|
600
|
+
promise.resolve(outputFile)
|
|
599
601
|
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
602
|
+
}
|
|
603
|
+
ReturnCode.isCancel(returnCode) -> {
|
|
604
|
+
// CANCEL
|
|
605
|
+
println("FFmpeg command was cancelled")
|
|
606
|
+
promise.reject(
|
|
607
|
+
Exception("FFmpeg command was cancelled")
|
|
608
|
+
)
|
|
609
|
+
}
|
|
610
|
+
else -> {
|
|
611
|
+
// FAILURE
|
|
612
|
+
val errorMessage = String.format("Command failed with state %s and rc %s.%s", state, returnCode, session.getFailStackTrace());
|
|
613
|
+
println(errorMessage)
|
|
614
|
+
promise.reject(
|
|
615
|
+
Exception(errorMessage)
|
|
616
|
+
)
|
|
617
|
+
}
|
|
605
618
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
619
|
+
}, { log ->
|
|
620
|
+
Log.d(TAG, "FFmpeg process started with log ${log.message}")
|
|
621
|
+
}, { statistics ->
|
|
622
|
+
// Handle statistics if needed
|
|
623
|
+
})
|
|
609
624
|
}
|
|
610
625
|
|
|
611
626
|
private fun saveFileToExternalStorage(file: File) {
|
|
@@ -613,8 +628,7 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
613
628
|
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
|
614
629
|
intent.setType("*/*") // Change MIME type as needed
|
|
615
630
|
intent.putExtra(Intent.EXTRA_TITLE, file.name)
|
|
616
|
-
|
|
617
|
-
.startActivityForResult(intent, REQUEST_CODE_SAVE_FILE)
|
|
631
|
+
reactApplicationContext.currentActivity?.startActivityForResult(intent, REQUEST_CODE_SAVE_FILE)
|
|
618
632
|
}
|
|
619
633
|
|
|
620
634
|
private fun shareFile(context: Context, file: File) {
|
|
@@ -635,11 +649,11 @@ class VideoTrim : HybridVideoTrimSpec(), VideoTrimListener, LifecycleEventListen
|
|
|
635
649
|
}
|
|
636
650
|
|
|
637
651
|
// directly use context.startActivity(shareIntent) will cause crash
|
|
638
|
-
|
|
639
|
-
.startActivity(Intent.createChooser(shareIntent, "Share file"))
|
|
652
|
+
reactApplicationContext.currentActivity?.startActivity(Intent.createChooser(shareIntent, "Share file"))
|
|
640
653
|
}
|
|
641
654
|
|
|
642
655
|
companion object {
|
|
656
|
+
const val NAME = "VideoTrim"
|
|
643
657
|
const val TAG = "VideoTrimModule"
|
|
644
658
|
const val REQUEST_CODE_SAVE_FILE = 1
|
|
645
659
|
}
|