diff --git a/App.js b/App.js
index 744633e..332a3ab 100644
--- a/App.js
+++ b/App.js
@@ -1,4 +1,8 @@
import React, {Component} from 'react';
+
+// Use to access video files https://github.com/react-native-image-picker/react-native-image-picker/tree/main
+import {launchImageLibrary} from 'react-native-image-picker';
+
import {
StyleSheet,
Text,
@@ -6,6 +10,7 @@ import {
View,
Platform,
NativeModules,
+ PermissionsAndroid,
} from 'react-native';
const {SdkEditorModule} = NativeModules;
@@ -23,12 +28,68 @@ async function openVideoEditor() {
async function openVideoEditorPIP() {
initSDK();
- return await SdkEditorModule.openVideoEditorPIP();
+
+ // PLEASE GRANT ALL PERMISSIONS TO PROCEED
+ // The implementation below is for demonstration purposes to show how to use vide for PIP mode
+ await grantMediaPermissions()
+
+ const videoOptions: ImageLibraryOptions = {
+ mediaType: 'video',
+ videoQuality: 'high',
+ formatAsMp4: false,
+ quality: 1,
+ includeBase64: false,
+ selectionLimit: 1,
+ durationLimit: 0,
+ };
+
+ const result = await launchImageLibrary(videoOptions);
+
+ const videoPath = result.assets[0].originalPath
+ const videoUri = result.assets[0].uri
+ console.log('Open video editor in pip mode with video: path = ' + videoPath + ', uri = ' + videoUri);
+
+ if (Platform.OS === 'android') {
+ // IMPORTANT requirements
+ // There are 2 types of videoPath - external(/storage/emulated/0/Movies/sample.mp4) and internal (/data/data/$applicationId/...)
+ // Access to media is required for external - please grant all permissions.
+ return await SdkEditorModule.openVideoEditorPIP(videoPath);
+ } else {
+ return await SdkEditorModule.openVideoEditorPIP(videoUri);
+ }
}
async function openVideoEditorTrimmer() {
- initSDK();
- return await SdkEditorModule.openVideoEditorTrimmer();
+ initSDK();
+
+ // PLEASE GRANT ALL PERMISSIONS TO PROCEED
+ // The implementation below is for demonstration purposes to show how to use vide for PIP mode
+ await grantMediaPermissions()
+
+ const videoOptions: ImageLibraryOptions = {
+ mediaType: 'video',
+ videoQuality: 'high',
+ formatAsMp4: false,
+ quality: 1,
+ includeBase64: false,
+ selectionLimit: 1,
+ durationLimit: 0,
+ };
+
+ const result = await launchImageLibrary(videoOptions);
+
+ const videoPath = result.assets[0].originalPath
+ const videoUri = result.assets[0].uri
+ console.log('Open video editor in Trimmer mode with video: path = ' + videoPath + ', uri = ' + videoUri);
+
+ if (Platform.OS === 'android') {
+ // IMPORTANT requirements
+ // There are 2 types of videoPath - external(/storage/emulated/0/Movies/sample.mp4) and internal (/data/data/$applicationId/...)
+ // Access to media is required for external - please grant all permissions.
+ return await SdkEditorModule.openVideoEditorTrimmer(videoPath);
+ } else {
+ return await SdkEditorModule.openVideoEditorTrimmer(videoUri);
+ }
}
async function openIosPhotoEditor() {
@@ -41,6 +102,22 @@ async function openAndroidPhotoEditor() {
return await SdkEditorModule.openPhotoEditor();
}
+// It is expected that the user grants all permissions.
+// We do not check status here for simplicity
+const grantMediaPermissions = async () => {
+ if (Platform.OS === 'ios') {
+ return
+ }
+
+ const status = await PermissionsAndroid.requestMultiple([
+ PermissionsAndroid.PERMISSIONS.ACCESS_MEDIA_LOCATION,
+ PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
+ PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO
+ ]);
+
+ return status
+};
+
export default class App extends Component {
constructor() {
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 46ef1f7..1511ce9 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -20,6 +20,8 @@
+
+
if (isValid) {
// ✅ The license is active
@@ -192,21 +192,23 @@ class SdkEditorModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
if (hostActivity == null) {
promise.reject(ERR_CODE_NO_HOST_CONTROLLER, "")
} else {
- // sample_video.mp4 file is hardcoded for demonstrating how to open video editor sdk in the simplest case.
- // Please provide valid video URL to open Video Editor in PIP.
- val sampleVideoFileName = "sample_video.mp4"
- val filesStorage: File = hostActivity.applicationContext.filesDir
- val assets: AssetManager = hostActivity.applicationContext.assets
- val sampleVideoFile = prepareMediaFile(assets, filesStorage, sampleVideoFileName)
-
this.resultPromise = promise
- val intent = VideoCreationActivity.startFromCamera(
+
+ MediaScannerConnection.scanFile(
hostActivity,
- PipConfig(video = sampleVideoFile.toUri(), openPipSettings = false),
- null,
- null
- )
- hostActivity.startActivityForResult(intent, OPEN_VIDEO_EDITOR_REQUEST_CODE)
+ arrayOf(File(pipVideoPath).absolutePath),
+ arrayOf()
+ ) { path, uri ->
+ Log.d(TAG, "Found path = $path, uri = $uri")
+
+ val intent = VideoCreationActivity.startFromCamera(
+ hostActivity,
+ PipConfig(video = uri, openPipSettings = false),
+ null,
+ null
+ )
+ hostActivity.startActivityForResult(intent, OPEN_VIDEO_EDITOR_REQUEST_CODE)
+ }
}
} else {
// ❌ Use of SDK is restricted: the license is revoked or expired
@@ -216,7 +218,7 @@ class SdkEditorModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
}
@ReactMethod
- fun openVideoEditorTrimmer(promise: Promise) {
+ fun openVideoEditorTrimmer(videoPath: String, promise: Promise) {
checkLicense(callback = { isValid ->
if (isValid) {
// ✅ The license is active
@@ -224,21 +226,23 @@ class SdkEditorModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
if (hostActivity == null) {
promise.reject(ERR_CODE_NO_HOST_CONTROLLER, "")
} else {
- // sample_video.mp4 file is hardcoded for demonstrating how to open video editor sdk in the simplest case.
- // Please provide valid video URL to open Video Editor in trimmer.
- val sampleVideoFileName = "sample_video.mp4"
- val filesStorage: File = hostActivity.applicationContext.filesDir
- val assets: AssetManager = hostActivity.applicationContext.assets
- val sampleVideoFile = prepareMediaFile(assets, filesStorage, sampleVideoFileName)
-
this.resultPromise = promise
- val intent = VideoCreationActivity.startFromTrimmer(
+
+ MediaScannerConnection.scanFile(
hostActivity,
- arrayOf(sampleVideoFile.toUri()),
- null,
- null
- )
- hostActivity.startActivityForResult(intent, OPEN_VIDEO_EDITOR_REQUEST_CODE)
+ arrayOf(File(videoPath).absolutePath),
+ arrayOf()
+ ) { path, uri ->
+ Log.d(TAG, "Found path = $path, uri = $uri")
+
+ val intent = VideoCreationActivity.startFromTrimmer(
+ hostActivity,
+ arrayOf(uri),
+ null,
+ null
+ )
+ hostActivity.startActivityForResult(intent, OPEN_VIDEO_EDITOR_REQUEST_CODE)
+ }
}
} else {
// ❌ Use of SDK is restricted: the license is revoked or expired
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index d290772..92fd326 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -958,6 +958,10 @@ PODS:
- React-Mapbuffer (0.73.0):
- glog
- React-debug
+ - react-native-image-picker (7.1.1):
+ - glog
+ - RCT-Folly (= 2022.05.16.00)
+ - React-Core
- React-nativeconfig (0.73.0)
- React-NativeModulesApple (0.73.0):
- glog
@@ -1174,6 +1178,7 @@ DEPENDENCIES:
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
+ - react-native-image-picker (from `../node_modules/react-native-image-picker`)
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -1291,6 +1296,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/logger"
React-Mapbuffer:
:path: "../node_modules/react-native/ReactCommon"
+ react-native-image-picker:
+ :path: "../node_modules/react-native-image-picker"
React-nativeconfig:
:path: "../node_modules/react-native/ReactCommon"
React-NativeModulesApple:
@@ -1387,6 +1394,7 @@ SPEC CHECKSUMS:
React-jsinspector: 9f6fb9ed9f03a0fb961ab8dc2e0e0ee0dc729e77
React-logger: 008caec0d6a587abc1e71be21bfac5ba1662fe6a
React-Mapbuffer: 58fe558faf52ecde6705376700f848d0293d1cef
+ react-native-image-picker: 1a7cd3224036e080fe46bcb955f2eb42fcbf7acc
React-nativeconfig: a063483672b8add47a4875b0281e202908ff6747
React-NativeModulesApple: 169506a5fd708ab22811f76ee06a976595c367a1
React-perflogger: b61e5db8e5167f5e70366e820766c492847c082e
diff --git a/ios/SdkEditorModule.swift b/ios/SdkEditorModule.swift
index 24581a0..bfcb8bf 100644
--- a/ios/SdkEditorModule.swift
+++ b/ios/SdkEditorModule.swift
@@ -80,19 +80,21 @@ class SdkEditorModule: NSObject, RCTBridgeModule {
}
}
- @objc func openVideoEditorPIP(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
+ @objc func openVideoEditorPIP(_ pipVideoPath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
self.currentResolve = resolve
self.currentReject = reject
prepareAudioBrowser()
DispatchQueue.main.async {
guard let presentedVC = RCTPresentedViewController() else {
+ reject("RCTPresentedViewController returned nil", nil, nil)
return
}
- // sample_pip_video.mp4 file is hardcoded for demonstrating how to open video editor sdk in the simplest case.
- // Please provide valid video URL to open Video Editor in PIP.
- let pipVideoURL = Bundle.main.url(forResource: "sample_video", withExtension: "mp4")
+ guard let pipVideoURL = URL(string: pipVideoPath) else {
+ reject("Failed to instantiate URL from String", nil, nil)
+ return
+ }
let pipLaunchConfig = VideoEditorLaunchConfig(
entryPoint: .pip,
@@ -106,7 +108,7 @@ class SdkEditorModule: NSObject, RCTBridgeModule {
}
}
- @objc func openVideoEditorTrimmer(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
+ @objc func openVideoEditorTrimmer(_ trimmerVideoPath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
self.currentResolve = resolve
self.currentReject = reject
@@ -114,21 +116,19 @@ class SdkEditorModule: NSObject, RCTBridgeModule {
DispatchQueue.main.async {
guard let presentedVC = RCTPresentedViewController() else {
+ reject("RCTPresentedViewController returned nil", nil, nil)
return
}
- // sample_video.mp4 file is hardcoded for demonstrating how to open video editor sdk in the simplest case.
- // Please provide valid video URL to open Video Editor in Trimmer.
- let trimmerVideoURL = Bundle.main.url(forResource: "sample_video", withExtension: "mp4")!
- let fileManager = FileManager.default
- let tmpURL = fileManager.temporaryDirectory.appendingPathComponent("sample_video.mp4")
- try? fileManager.removeItem(at: tmpURL)
- try? fileManager.copyItem(at: trimmerVideoURL, to: tmpURL)
+ guard let trimmerVideoURL = URL(string: trimmerVideoPath) else {
+ reject("Failed to instantiate URL from String", nil, nil)
+ return
+ }
let trimmerLaunchConfig = VideoEditorLaunchConfig(
entryPoint: .trimmer,
hostController: presentedVC,
- videoItems: [tmpURL],
+ videoItems: [trimmerVideoURL],
musicTrack: nil,
animated: true
)
diff --git a/ios/SdkEditorModuleBridge.m b/ios/SdkEditorModuleBridge.m
index 6f6796b..89befc7 100644
--- a/ios/SdkEditorModuleBridge.m
+++ b/ios/SdkEditorModuleBridge.m
@@ -18,9 +18,9 @@ @interface RCT_EXTERN_MODULE(SdkEditorModule, NSObject)
RCT_EXTERN_METHOD(closeAudioBrowser: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
-RCT_EXTERN_METHOD(openVideoEditorPIP: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
+RCT_EXTERN_METHOD(openVideoEditorPIP:(NSString *)pipVideoPath resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
-RCT_EXTERN_METHOD(openVideoEditorTrimmer: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
+RCT_EXTERN_METHOD(openVideoEditorTrimmer:(NSString *)trimmerVideoPath resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(initPhotoEditor:(NSString *) token resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
diff --git a/ios/vesdkreactnativecliintegrationsample.xcodeproj/project.pbxproj b/ios/vesdkreactnativecliintegrationsample.xcodeproj/project.pbxproj
index 462161b..4d462fa 100644
--- a/ios/vesdkreactnativecliintegrationsample.xcodeproj/project.pbxproj
+++ b/ios/vesdkreactnativecliintegrationsample.xcodeproj/project.pbxproj
@@ -21,7 +21,6 @@
C2A6917D294C9CE6005DFA75 /* AudioBrowserModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2A6917C294C9CE6005DFA75 /* AudioBrowserModule.swift */; };
C2A69180294C9D1C005DFA75 /* CustomViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2A6917F294C9D1C005DFA75 /* CustomViewControllerFactory.swift */; };
C2A69183294CA0AB005DFA75 /* sample_audio.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C2A69182294CA0AB005DFA75 /* sample_audio.mp3 */; };
- C2B80BDF294DFBFA006D11FA /* sample_video.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = C2B80BDE294DFBFA006D11FA /* sample_video.mp4 */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -46,7 +45,6 @@
C2A6917C294C9CE6005DFA75 /* AudioBrowserModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioBrowserModule.swift; sourceTree = ""; };
C2A6917F294C9D1C005DFA75 /* CustomViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomViewControllerFactory.swift; sourceTree = ""; };
C2A69182294CA0AB005DFA75 /* sample_audio.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = sample_audio.mp3; path = ../android/app/src/main/assets/sample_audio.mp3; sourceTree = ""; };
- C2B80BDE294DFBFA006D11FA /* sample_video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; name = sample_video.mp4; path = ../android/app/src/main/assets/sample_video.mp4; sourceTree = ""; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F3D506A8DB72071BDDDE58E8 /* libPods-vesdkreactnativecliintegrationsample-vesdkreactnativecliintegrationsampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-vesdkreactnativecliintegrationsample-vesdkreactnativecliintegrationsampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -104,7 +102,6 @@
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
- C2B80BDE294DFBFA006D11FA /* sample_video.mp4 */,
7837EBDA28AF8E3C00174DDB /* Localizable.strings */,
13B07FAE1A68108700A75B9A /* vesdkreactnativecliintegrationsample */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
@@ -199,7 +196,6 @@
buildActionMask = 2147483647;
files = (
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
- C2B80BDF294DFBFA006D11FA /* sample_video.mp4 in Resources */,
C2A69183294CA0AB005DFA75 /* sample_audio.mp3 in Resources */,
7837EBDB28AF8E3C00174DDB /* Localizable.strings in Resources */,
7837EBDD28AF91FD00174DDB /* Music in Resources */,
diff --git a/package.json b/package.json
index d5d7c32..b1e50c5 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"eslint": "^7.32.0",
"jest": "^29.6.3",
"metro-react-native-babel-preset": "^0.76.9",
+ "react-native-image-picker": "7.1.1",
"react-test-renderer": "18.0.0",
"typescript": "5.0.4"
},
diff --git a/yarn.lock b/yarn.lock
index a87353d..5226191 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7107,6 +7107,16 @@ __metadata:
languageName: node
linkType: hard
+"react-native-image-picker@npm:7.1.1":
+ version: 7.1.1
+ resolution: "react-native-image-picker@npm:7.1.1"
+ peerDependencies:
+ react: "*"
+ react-native: "*"
+ checksum: 10/85a8d946da5c7e3187a1229bb1910e0b3d0a8664d591fcf322e1c7fa38997d2f182f0ea26c68bc7a0cb17ad78a6cb2987a2d54d1f8268e94c5a0745c65b4281f
+ languageName: node
+ linkType: hard
+
"react-native@npm:0.73.0":
version: 0.73.0
resolution: "react-native@npm:0.73.0"
@@ -8501,6 +8511,7 @@ __metadata:
react: "npm:18.2.6"
react-native: "npm:0.73.0"
react-native-gradle-plugin: "npm:^0.71.19"
+ react-native-image-picker: "npm:7.1.1"
react-test-renderer: "npm:18.0.0"
typescript: "npm:5.0.4"
languageName: unknown