From 392c497e2fd10a3a04df2bb30f93e4d1569a1e56 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Thu, 21 Apr 2022 13:20:06 +0200 Subject: [PATCH 01/13] Add version 2.13.0 --- CHANGELOG.md | 13 +++ README.md | 76 ++++++++----- android/build.gradle | 8 +- .../vesdk/RNVideoEditorSDKModule.kt | 107 ++++++++---------- package.json | 4 +- 5 files changed, 115 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2709ab..73c0f32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## [2.13.0] + +### Changed + +* 🚨 With this version you might need to create symlinks when using Android Gradle Plugin version `4.x`. Please refer to the new [known issues](https://github.com/imgly/vesdk-react-native#known-issues) section of the README for details. +* 🚨 This version requires `minSdkVersion` `21` for Android. Please refer to the new step 3 in the [getting started](https://github.com/imgly/vesdk-react-native#android) section of the README for instructions on how to adjust it. +* [react-native-videoeditorsdk] Raised minimum VideoEditor SDK for Android version to 10.0.1. See the [changelog](https://github.com/imgly/vesdk-android-demo/blob/master/CHANGELOG.md) for more information. +* [react-native-photoeditorsdk] Raised minimum PhotoEditor SDK for Android version to 10.0.1. See the [changelog](https://github.com/imgly/pesdk-android-demo/blob/master/CHANGELOG.md) for more information. + +### Added + +* [react-native-imglysdk] Added support to specify a custom `buildToolsVersion`, `minSdkVersion`, `compileSdkVersion`, `targetSdkVersion`, and `kotlinGradlePluginVersion` for Android with the Expo config plugin. + ## [2.12.0] ### Changed diff --git a/README.md b/README.md index 7ddf294..4ca8b5f 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,27 @@ Check out our [video tutorial](https://img.ly/blog/a-photo-and-video-editor-for- ## Getting started +### Known Issues + +With version `2.13.0`, we recommend using `compileSdkVersion` not lower than `31.0.0` for Android. However, this might interfere with your application's Android Gradle Plugin version if this is set to `4.x`. + +If you don't use a newer Android Gradle Plugin version, e.g., by updating at least to RN 0.68.0, you'll most likely encounter a build error similar to: +``` +FAILURE: Build failed with an exception. + +* What went wrong: +A problem occurred configuring project ':react-native-videoeditorsdk'. +> com.android.builder.errors.EvalIssueException: Installed Build Tools revision 31.0.0 is corrupted. Remove and install again using the SDK Manager. + +* Try: +Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. + +* Get more help at https://help.gradle.org +``` +As a workaround you can create the following symlinks: + 1. Inside `/Users/YOUR-USERNAME/Library/Android/sdk/build-tools/31.0.0/`: Create a `dx` symlink for the `d8` file with `ln -s d8 dx`. + 2. From there, go to `./lib/` and create a `dx.jar` symlink for the `d8.jar` file with `ln -s d8.jar dx.jar`. + ### Expo CLI #### Limitations @@ -47,7 +68,7 @@ In order to use this module with the Expo CLI you can make use of our integrated } ``` - If needed, you can also use a specific version of our native library for Android as well as define explicitly the included modules. By default, all modules for both PhotoEditor SDK and VideoEditor SDK are included. + If needed, you can also use a specific version of our native library for Android as well as define explicitly the included modules. By default, all modules for both PhotoEditor SDK and VideoEditor SDK are included. Furthermore, you can configure the `buildToolsVersion`, `minSdkVersion`, `compileSdkVersion`, `targetSdkVersion`, and `kotlinGradlePluginVersion`. ```json { @@ -56,13 +77,18 @@ In order to use this module with the Expo CLI you can make use of our integrated "react-native-imglysdk", { "android": { - "version": "9.2.0", + "version": "10.0.1", "modules": [ "ui:core", "ui:transform", "ui:filter", "assets:filter-basic" - ] + ], + "buildToolsVersion": "31.0.0", + "minSdkVersion": "21", + "compileSdkVersion": "31", + "targetSdkVersion": "30", + "kotlinGradlePluginVersion": "1.5.32" } } ] @@ -114,25 +140,7 @@ For older React Native versions autolinking is not available and VideoEditor SDK #### Android -1. Because VideoEditor SDK for Android is quite large, there is a high chance that you will need to enable [Multidex](https://developer.android.com/studio/build/multidex) for your project as follows: - - 1. Open the `android/app/build.gradle` file (**not** `android/build.gradle`) and add these lines at the end: - ```groovy - android { - defaultConfig { - multiDexEnabled true - } - } - dependencies { - implementation 'androidx.multidex:multidex:2.0.1' - } - ``` - 2. Open the `android/app/src/main/java/.../MainApplication.java` file and change the superclass of your `MainApplication` class from `Application` to `androidx.multidex.MultiDexApplication`, e.g.: - ```java - public class MainApplication extends androidx.multidex.MultiDexApplication implements ReactApplication { - ``` - -2. Add the img.ly repository and plugin by opening the `android/build.gradle` file (**not** `android/app/build.gradle`) and adding these lines at the top: +1. Add the img.ly repository and plugin by opening the `android/build.gradle` file (**not** `android/app/build.gradle`) and adding these lines at the top: ```groovy buildscript { repositories { @@ -140,14 +148,14 @@ For older React Native versions autolinking is not available and VideoEditor SDK maven { url "https://artifactory.img.ly/artifactory/imgly" } } dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10" - classpath 'ly.img.android.sdk:plugin:9.2.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.32" + classpath 'ly.img.android.sdk:plugin:10.0.1' } } ``` - In order to update VideoEditor SDK for Android replace the version string `9.2.0` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). + In order to update VideoEditor SDK for Android replace the version string `10.0.1` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). -3. Still in the `android/build.gradle` file (**not** `android/app/build.gradle`), add these lines at the bottom: +2. Still in the `android/build.gradle` file (**not** `android/app/build.gradle`), add these lines at the bottom: ```groovy allprojects { @@ -157,6 +165,22 @@ For older React Native versions autolinking is not available and VideoEditor SDK } ``` +3. In the same file, you will need to modify the `minSdkVersion` to at least `21`. We also recommend to update the `buildToolsVersion` to `31.0.0` or higher as well as the `compileSdkVersion` to `31` or higher: + + ```diff + buildscript { + ext { + - buildToolsVersion = "30.0.2" + + buildToolsVersion = "31.0.0" + - minSdkVersion = 19 + + minSdkVersion = 21 + - compileSdkVersion = 30 + + compileSdkVersion = 31 + targetSdkVersion = 30 + } + } + ``` + 4. Configure VideoEditor SDK for Android by opening the `android/app/build.gradle` file (**not** `android/build.gradle`) and adding the following lines under `apply plugin: "com.android.application"`: ```groovy apply plugin: 'ly.img.android.sdk' diff --git a/android/build.gradle b/android/build.gradle index 3db2ac5..a151358 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,7 +20,7 @@ imglyConfig { } } -def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "9.2.0" +def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "10.0.1" task checkVersion { if (imglyConfig.convertToVersionNumber(imglyConfig.getVersion()) < imglyConfig.convertToVersionNumber(MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION)) { @@ -39,11 +39,11 @@ task checkVersion { preBuild.dependsOn checkVersion android { - compileSdkVersion safeExtGet('compileSdkVersion', 30) - buildToolsVersion safeExtGet('buildToolsVersion', '30.0.3') + compileSdkVersion safeExtGet('compileSdkVersion', 31) + buildToolsVersion safeExtGet('buildToolsVersion', '31.0.0') defaultConfig { - minSdkVersion safeExtGet('minSdkVersion', 19) + minSdkVersion safeExtGet('minSdkVersion', 21) targetSdkVersion safeExtGet('targetSdkVersion', 30) versionCode 1 versionName "1.0" diff --git a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt index 9085751..552ce53 100644 --- a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt +++ b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt @@ -2,9 +2,7 @@ package ly.img.react_native.vesdk import android.app.Activity import android.content.Intent -import android.content.pm.PackageManager import android.net.Uri -import android.os.Build import android.util.Log import com.facebook.react.bridge.* import ly.img.android.IMGLY @@ -39,7 +37,6 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte reactContext.addActivityEventListener(this) } - private var currentSettingsList: VideoEditorSettingsList? = null private var currentPromise: Promise? = null private var currentConfig: Configuration? = null @@ -68,9 +65,9 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte val resultPath = data.resultUri val serializationConfig = currentConfig?.export?.serialization - val settingsList = data.settingsList val serialization: Any? = if (serializationConfig?.enabled == true) { + val settingsList = data.settingsList skipIfNotExists { settingsList.let { settingsList -> if (serializationConfig.embedSourceImage == true) { @@ -99,6 +96,7 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte Log.i("ImgLySdk", "You need to include 'backend:serializer' Module, to use serialisation!") null } + settingsList.release() } else { null } @@ -123,75 +121,61 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte @ReactMethod fun present(video: String, config: ReadableMap?, serialization: String?, promise: Promise) { - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - val settingsList = VideoEditorSettingsList() - - currentSettingsList = settingsList - currentConfig = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()).also { - it.applyOn(settingsList) - } - currentPromise = promise - - - settingsList.configure { loadSettings -> - loadSettings.source = retrieveURI(video) - } - - readSerialisation(settingsList, serialization, false) - - startEditor(settingsList) - } else { - promise.reject("VESDK", "The video editor is only available in Android 4.3 and later.") + val configuration = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()) + val settingsList = VideoEditorSettingsList(configuration.export?.serialization?.enabled == true) + configuration.applyOn(settingsList) + currentConfig = configuration + currentPromise = promise + + settingsList.configure { loadSettings -> + loadSettings.source = retrieveURI(video) } + + readSerialisation(settingsList, serialization, false) + startEditor(settingsList) } @ReactMethod fun presentComposition(videos: ReadableArray, config: ReadableMap?, serialization: String?, size: ReadableMap?, promise: Promise) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - val array = videos.toArrayList() - val videoArray = array.filterIsInstance().takeIf { it.size == array.size } ?: arrayListOf() - val settingsList = VideoEditorSettingsList() - var source = resolveSize(size) - - currentSettingsList = settingsList - currentConfig = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()).also { - it.applyOn(settingsList) - } - currentPromise = promise - - if (videoArray.count() > 0) { - if (source == null) { - if (size != null) { - promise.reject("VESDK", "Invalid video size: width and height must be greater than zero.") - return - } - val video = videoArray.first() - source = retrieveURI(video) - } - - settingsList.configure { loadSettings -> - videoArray.forEach { - val resolvedSource = retrieveURI(it) - loadSettings.addCompositionPart(VideoCompositionSettings.VideoPart(resolvedSource)) - } - } - } else { - if (source == null) { - promise.reject("VESDK", "The editor requires a valid size when initialized without a video.") + val array = videos.toArrayList() + val videoArray = array.filterIsInstance().takeIf { it.size == array.size } ?: arrayListOf() + var source = resolveSize(size) + + val configuration = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()) + val settingsList = VideoEditorSettingsList(configuration.export?.serialization?.enabled == true) + configuration.applyOn(settingsList) + currentConfig = configuration + currentPromise = promise + + if (videoArray.count() > 0) { + if (source == null) { + if (size != null) { + promise.reject("VESDK", "Invalid video size: width and height must be greater than zero.") return } + val video = videoArray.first() + source = retrieveURI(video) } - settingsList.configure { - it.source = source + settingsList.configure { loadSettings -> + videoArray.forEach { + val resolvedSource = retrieveURI(it) + loadSettings.addCompositionPart(VideoCompositionSettings.VideoPart(resolvedSource)) + } } - - readSerialisation(settingsList, serialization, false) - startEditor(settingsList) } else { - promise.reject("VESDK", "The video editor is only available in Android 4.3 and later.") + if (source == null) { + promise.reject("VESDK", "The editor requires a valid size when initialized without a video.") + return + } } + + settingsList.configure { + it.source = source + } + + readSerialisation(settingsList, serialization, false) + startEditor(settingsList) } private fun retrieveURI(source: String) : Uri { @@ -234,6 +218,7 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte VideoEditorBuilder(currentActivity) .setSettingsList(settingsList) .startActivityForResult(currentActivity, EDITOR_RESULT_ID) + settingsList.release() }() } } diff --git a/package.json b/package.json index b14553f..625450c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "2.12.0", + "version": "2.13.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "2.12.0" + "react-native-imglysdk": "2.13.0" } } From 7d0eb09026df0c6002f8143bd2b91c29a3110349 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Fri, 22 Apr 2022 12:25:46 +0200 Subject: [PATCH 02/13] Add version 2.13.1 --- CHANGELOG.md | 6 ++++++ .../img/react_native/vesdk/RNVideoEditorSDKModule.kt | 10 ++++------ package.json | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73c0f32..45a5d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [2.13.1] + +### Fixed + +* Fixed enabling serialization would crash the application on Android when exporting. + ## [2.13.0] ### Changed diff --git a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt index 552ce53..335984c 100644 --- a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt +++ b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt @@ -66,14 +66,15 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte val serializationConfig = currentConfig?.export?.serialization - val serialization: Any? = if (serializationConfig?.enabled == true) { + var serialization: Any? = null + if (serializationConfig?.enabled == true) { val settingsList = data.settingsList skipIfNotExists { settingsList.let { settingsList -> if (serializationConfig.embedSourceImage == true) { Log.i("ImgLySdk", "EmbedSourceImage is currently not supported by the Android SDK") } - when (serializationConfig.exportType) { + serialization = when (serializationConfig.exportType) { SerializationExportType.FILE_URL -> { val uri = serializationConfig.filename?.let { Uri.parse("$it.json") @@ -92,13 +93,10 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte } } } + settingsList.release() } ?: run { Log.i("ImgLySdk", "You need to include 'backend:serializer' Module, to use serialisation!") - null } - settingsList.release() - } else { - null } currentPromise?.resolve( diff --git a/package.json b/package.json index 625450c..6234e92 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "2.13.0", + "version": "2.13.1", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "2.13.0" + "react-native-imglysdk": "2.13.1" } } From f444a8263b837d239cf0163ed5eafcc483ee40d9 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Wed, 8 Jun 2022 10:49:18 +0200 Subject: [PATCH 03/13] Add version 2.14.0 --- CHANGELOG.md | 14 ++++++++ README.md | 9 ++--- RNVideoEditorSDK.podspec | 2 +- android/build.gradle | 2 +- .../vesdk/RNVideoEditorSDKModule.kt | 2 +- configuration.ts | 34 ++++++++++++++++++- index.d.ts | 4 +-- index.js | 2 +- package.json | 4 +-- 9 files changed, 60 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45a5d8c..be86e57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## [2.14.0] + +### Added + +* [react-native-videoeditorsdk] Added implementation and documentation for GIPHY sticker integration. + +### Fixed + +* [react-native-videoeditorsdk] Fixed `VESDK.openEditor` return type declaration and API documentation to return `Promise` instead of just `Promise`. +* [react-native-videoeditorsdk] Fixed height and width of specified composition size would be flipped on Android. +* [react-native-photoeditorsdk] Fixed `PESDK.openEditor` return type declaration and API documentation to return `Promise` instead of just `Promise`. +* [react-native-photoeditorsdk] Fixed deprecation warning for `RCTBridge.imageLoader` on iOS. + + ## [2.13.1] ### Fixed diff --git a/README.md b/README.md index 4ca8b5f..ecca533 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Check out our [video tutorial](https://img.ly/blog/a-photo-and-video-editor-for- ### Known Issues -With version `2.13.0`, we recommend using `compileSdkVersion` not lower than `31.0.0` for Android. However, this might interfere with your application's Android Gradle Plugin version if this is set to `4.x`. +With version `2.13.0`, we recommend using `compileSdkVersion` not lower than `31` for Android. However, this might interfere with your application's Android Gradle Plugin version if this is set to `4.x`. If you don't use a newer Android Gradle Plugin version, e.g., by updating at least to RN 0.68.0, you'll most likely encounter a build error similar to: ``` @@ -77,7 +77,7 @@ In order to use this module with the Expo CLI you can make use of our integrated "react-native-imglysdk", { "android": { - "version": "10.0.1", + "version": "10.1.1", "modules": [ "ui:core", "ui:transform", @@ -149,11 +149,11 @@ For older React Native versions autolinking is not available and VideoEditor SDK } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.32" - classpath 'ly.img.android.sdk:plugin:10.0.1' + classpath 'ly.img.android.sdk:plugin:10.1.1' } } ``` - In order to update VideoEditor SDK for Android replace the version string `10.0.1` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). + In order to update VideoEditor SDK for Android replace the version string `10.1.1` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). 2. Still in the `android/build.gradle` file (**not** `android/app/build.gradle`), add these lines at the bottom: @@ -203,6 +203,7 @@ For older React Native versions autolinking is not available and VideoEditor SDK include 'ui:video-library' include 'ui:video-composition' include 'ui:audio-composition' + include 'ui:giphy-sticker' // This module is big, remove the serializer if you don't need that feature. include 'backend:serializer' diff --git a/RNVideoEditorSDK.podspec b/RNVideoEditorSDK.podspec index b27608c..3c101f7 100644 --- a/RNVideoEditorSDK.podspec +++ b/RNVideoEditorSDK.podspec @@ -18,5 +18,5 @@ Pod::Spec.new do |s| s.dependency 'React' s.dependency 'React-RCTImage' - s.dependency 'VideoEditorSDK', '~> 10.29' + s.dependency 'VideoEditorSDK', '~> 10.30' end diff --git a/android/build.gradle b/android/build.gradle index a151358..f78e5c7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,7 +20,7 @@ imglyConfig { } } -def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "10.0.1" +def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "10.1.1" task checkVersion { if (imglyConfig.convertToVersionNumber(imglyConfig.getVersion()) < imglyConfig.convertToVersionNumber(MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION)) { diff --git a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt index 335984c..08cb2a0 100644 --- a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt +++ b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt @@ -196,7 +196,7 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte if (height == 0.0 || width == 0.0) { return null } - return LoadSettings.compositionSource(height.toInt(), width.toInt(), 60) + return LoadSettings.compositionSource(width.toInt(), height.toInt(), 60) } private fun readSerialisation(settingsList: SettingsList, serialization: String?, readImage: Boolean) { diff --git a/configuration.ts b/configuration.ts index f49db10..1014164 100644 --- a/configuration.ts +++ b/configuration.ts @@ -576,7 +576,7 @@ export interface Configuration { * ]}, * ] */ - categories?: (StickerCategory | ExistingStickerCategory)[]; + categories?: (StickerCategory | ExistingStickerCategory | ExistingStickerProviderCategory)[]; /** * Defines all available colors that can be applied to stickers with a `tintMode` other than `TintMode.NONE`. * The color pipette is always added. @@ -1571,6 +1571,38 @@ export interface StickerCategory extends NamedItem { items?: (Sticker | ExistingItem)[]; } +/** An existing sticker provider category. */ +export interface ExistingStickerProviderCategory extends ExistingItem { + /** + * The used sticker provider that must match the category's identifier. + */ + provider: GiphyStickerProvider; +} + +/** + * A GIPHY sticker provider. + * @note This sticker provider requires to use the identifier `imgly_sticker_category_giphy` for its `ExistingStickerProviderCategory`. + */ +export interface GiphyStickerProvider { + /** + * The key used to authorize API requests, obtained from GIPHY. + */ + apiKey: string; + /** + * The default language for regional content in 2-letter ISO 639-1 language code. + * If `null` the language setting of the current locale is used. + * @example // Defaults to: + * null + */ + language?: string; + /** + * The audience category used for content filtering. Available values are `"g"`, `"pg"`, `"pg-13"`, `"r"`. + * @example // Defaults to: + * "g" + */ + rating?: string; +} + /** A sticker. */ export interface Sticker extends NamedItem { /** diff --git a/index.d.ts b/index.d.ts index 6d4620a..3b32133 100644 --- a/index.d.ts +++ b/index.d.ts @@ -42,7 +42,7 @@ declare class VESDK { * This overrides the natural dimensions of the video(s) passed to the editor. All videos will * be fitted to the `videoSize` aspect by adding black bars on the left and right side or top and bottom. * - * @return {Promise} Returns a `VideoEditorResult` or `null` if the editor + * @return {Promise} Returns a `VideoEditorResult` or `null` if the editor * is dismissed without exporting the edited video. */ static openEditor( @@ -50,7 +50,7 @@ declare class VESDK { configuration?: Configuration, serialization?: object, videoSize?: Size - ): Promise + ): Promise /** * Unlock VideoEditor SDK with a license. diff --git a/index.js b/index.js index 58e1d8a..b293346 100644 --- a/index.js +++ b/index.js @@ -138,7 +138,7 @@ class VESDK { * This overrides the natural dimensions of the video(s) passed to the editor. All videos will * be fitted to the `videoSize` aspect by adding black bars on the left and right side or top and bottom. * - * @return {Promise} Returns a `VideoEditorResult` or `null` if the editor + * @return {Promise} Returns a `VideoEditorResult` or `null` if the editor * is dismissed without exporting the edited video. */ static openEditor(video, configuration = null, serialization = null, videoSize = null) { diff --git a/package.json b/package.json index 6234e92..ca71f0a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "2.13.1", + "version": "2.14.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "2.13.1" + "react-native-imglysdk": "2.14.0" } } From c6ecc280fc049698cd07d0cc61abc8c9ba9fb9a0 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Thu, 30 Jun 2022 10:48:50 +0200 Subject: [PATCH 04/13] Add version 2.15.0 --- CHANGELOG.md | 12 +++++ README.md | 11 ++++- RNVideoEditorSDK.podspec | 4 +- configuration.ts | 7 ++- ios/RNVideoEditorSDK.m | 99 +++++++++++++++++++++------------------- package.json | 4 +- 6 files changed, 83 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be86e57..4be6bfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [2.15.0] + +### Changed + +* 🚨 Bumped iOS deployment target to 13.0. +* [react-native-videoeditorsdk] Raised minimum VideoEditor SDK for iOS version to 11.1.0. See the [changelog](https://github.com/imgly/vesdk-ios-build/blob/master/CHANGELOG.md) for more information. +* [react-native-photoeditorsdk] Raised minimum PhotoEditor SDK for iOS version to 11.1.0. See the [changelog](https://github.com/imgly/pesdk-ios-build/blob/master/CHANGELOG.md) for more information. + +### Added + +* Added implementation and documentation for background removal. + ## [2.14.0] ### Added diff --git a/README.md b/README.md index ecca533..1f89e4a 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ Check out our [video tutorial](https://img.ly/blog/a-photo-and-video-editor-for-your-react-native-apps/) for a step-by-step integration guide which also details advanced SDK features, such as serializing and reusing previously applied editing operations. +## System requirements + +- React Native: 0.60 +- iOS: 13 +- Android: 5 (SDK 21) + ## Getting started ### Known Issues @@ -100,7 +106,9 @@ In order to use this module with the Expo CLI you can make use of our integrated **Please note that the `react-native-imglysdk` module manages both VideoEditor SDK as well as PhotoEditor SDK so you only need to add the Expo config plugin once even when using both SDKs.** -3. The changes will be applied on `expo prebuild` or during the prebuild phase of `eas build`. +3. From version `2.15.0` the iOS deployment target needs to be set to at least iOS 13. You can use the `expo-build-properties` config plugin for this. Please refer to the [official Expo docs](https://docs.expo.dev/versions/v45.0.0/sdk/build-properties/). + +4. The changes will be applied on `expo prebuild` or during the prebuild phase of `eas build`. For further information on how to integrate Expo config plugins please also refer to the official [docs](https://docs.expo.dev/guides/config-plugins/#using-a-plugin-in-your-app). @@ -219,6 +227,7 @@ For older React Native versions autolinking is not available and VideoEditor SDK include 'backend:sticker-animated' include 'backend:sticker-smart' + include 'backend:background-removal' } } ``` diff --git a/RNVideoEditorSDK.podspec b/RNVideoEditorSDK.podspec index 3c101f7..db84714 100644 --- a/RNVideoEditorSDK.podspec +++ b/RNVideoEditorSDK.podspec @@ -10,7 +10,7 @@ Pod::Spec.new do |s| s.homepage = package['homepage'] s.license = { :type => package['license'], :file => package['licenseFilename'] } s.author = { package['author']['name'] => package['author']['email'] } - s.platform = :ios, '9.0' + s.platform = :ios, '13.0' s.source = { :git => package['repository']['url'], :tag => "#{s.version}" } s.source_files = 'ios/**/*.{h,m,swift}' s.public_header_files = ['ios/RNVideoEditorSDK.h', 'ios/RNImglyKit.h'] @@ -18,5 +18,5 @@ Pod::Spec.new do |s| s.dependency 'React' s.dependency 'React-RCTImage' - s.dependency 'VideoEditorSDK', '~> 10.30' + s.dependency 'VideoEditorSDK', '~> 11.1' end diff --git a/configuration.ts b/configuration.ts index 1014164..b72d824 100644 --- a/configuration.ts +++ b/configuration.ts @@ -27,11 +27,13 @@ export interface Configuration { /** * Defines all allowed actions for the main screen that are displayed as overlay buttons on the canvas. * Only buttons for allowed actions are visible. + * @note The `CanvasAction.REMOVE_BACKGROUND` action is only shown when editing photos where a person could be detected. This feature is only supported on devices running iOS 15+. * @note The `CanvasAction.SOUND_ON_OFF` and `CanvasAction.PLAY_PAUSE` action is only shown when editing videos. * @example // Defaults to: * [CanvasAction.SOUND_ON_OFF, CanvasAction.PLAY_PAUSE, CanvasAction.UNDO, CanvasAction.REDO] */ mainCanvasActions?: Array< + CanvasAction.REMOVE_BACKGROUND | CanvasAction.SOUND_ON_OFF | CanvasAction.PLAY_PAUSE | CanvasAction.UNDO | @@ -603,8 +605,9 @@ export interface Configuration { colors?: ColorPalette; /** * Defines all allowed actions for the sticker tool menu. Only buttons for allowed actions are visible and shown in the given order. + * @note The `StickerAction.REMOVE_BACKGROUND` action is only shown for personal and external (non-animated) stickers where a person could be detected. This feature is only supported on devices running iOS 15+. * @example // Defaults to: - * [StickerAction.REPLACE, StickerAction.OPACITY, StickerAction.COLOR] + * [StickerAction.REPLACE, StickerAction.OPACITY, StickerAction.COLOR, StickerAction.REMOVE_BACKGROUND] */ actions?: StickerAction[]; /** @@ -1297,6 +1300,7 @@ export enum CanvasAction { INVERT = "invert", SOUND_ON_OFF = "soundonoff", PLAY_PAUSE = "playpause", + REMOVE_BACKGROUND = "removebackground", } /** A sticker action. */ @@ -1308,6 +1312,7 @@ export enum StickerAction { SATURATION = "saturation", REPLACE = "replace", OPACITY = "opacity", + REMOVE_BACKGROUND = "removebackground", } /** A text action. */ diff --git a/ios/RNVideoEditorSDK.m b/ios/RNVideoEditorSDK.m index 2afe432..255fe38 100644 --- a/ios/RNVideoEditorSDK.m +++ b/ios/RNVideoEditorSDK.m @@ -29,6 +29,13 @@ + (void)setWillPresentVideoEditViewController:(RNVESDKWillPresentBlock)willPrese _willPresentVideoEditViewController = willPresentBlock; } +- (void)handleError:(nonnull PESDKVideoEditViewController *)videoEditViewController code:(nullable NSString *)code message:(nullable NSString *)message error:(nullable NSError *)error { + RCTPromiseRejectBlock reject = self.reject; + [self dismiss:videoEditViewController animated:YES completion:^{ + reject(code, message, error); + }]; +} + - (void)present:(nonnull PESDKVideo *)video withConfiguration:(nullable NSDictionary *)dictionary andSerialization:(nullable NSDictionary *)state resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { @@ -113,49 +120,49 @@ - (void)present:(nonnull PESDKVideo *)video withConfiguration:(nullable NSDictio resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { - NSMutableArray *assets = [NSMutableArray new]; - - if (requests.count > 0) { - for (NSURLRequest *request in requests) { - if (request.URL.isFileURL) { - if (![[NSFileManager defaultManager] fileExistsAtPath:request.URL.path]) { - reject(RN_IMGLY.kErrorUnableToLoad, @"File does not exist", nil); - return; - } - } - - AVAsset *asset = [AVAsset assetWithURL:request.URL]; - [assets addObject:asset]; + NSMutableArray *assets = [NSMutableArray new]; + + if (requests.count > 0) { + for (NSURLRequest *request in requests) { + if (request.URL.isFileURL) { + if (![[NSFileManager defaultManager] fileExistsAtPath:request.URL.path]) { + reject(RN_IMGLY.kErrorUnableToLoad, @"File does not exist", nil); + return; } + } + + AVAsset *asset = [AVAsset assetWithURL:request.URL]; + [assets addObject:asset]; } + } - PESDKVideo *video; + PESDKVideo *video; - if (CGSizeEqualToSize(videoSize, CGSizeZero)) { - if (assets.count == 0) { - RCTLogError(@"A video without assets must have a specific size."); - reject(RN_IMGLY.kErrorUnableToLoad, @"The editor requires a valid size when initialized without a video.", nil); - return; - } - video = [[PESDKVideo alloc] initWithAssets:assets]; - } else { - if (videoSize.height <= 0 || videoSize.width <= 0) { - RCTLogError(@"Invalid video size: width and height must be greater than zero"); - reject(RN_IMGLY.kErrorUnableToLoad, @"Invalid video size: width and height must be greater than zero", nil); - return; - } - if (assets.count == 0) { - video = [[PESDKVideo alloc] initWithSize:videoSize]; - } - video = [[PESDKVideo alloc] initWithAssets:assets size:videoSize]; + if (CGSizeEqualToSize(videoSize, CGSizeZero)) { + if (assets.count == 0) { + RCTLogError(@"A video without assets must have a specific size."); + reject(RN_IMGLY.kErrorUnableToLoad, @"The editor requires a valid size when initialized without a video.", nil); + return; + } + video = [[PESDKVideo alloc] initWithAssets:assets]; + } else { + if (videoSize.height <= 0 || videoSize.width <= 0) { + RCTLogError(@"Invalid video size: width and height must be greater than zero"); + reject(RN_IMGLY.kErrorUnableToLoad, @"Invalid video size: width and height must be greater than zero", nil); + return; + } + if (assets.count == 0) { + video = [[PESDKVideo alloc] initWithSize:videoSize]; } + video = [[PESDKVideo alloc] initWithAssets:assets size:videoSize]; + } - [self present:video withConfiguration:configuration andSerialization:state resolve:resolve reject:reject]; + [self present:video withConfiguration:configuration andSerialization:state resolve:resolve reject:reject]; } #pragma mark - PESDKVideoEditViewControllerDelegate -- (void)videoEditViewController:(nonnull PESDKVideoEditViewController *)videoEditViewController didFinishWithVideoAtURL:(nullable NSURL *)url { +- (void)videoEditViewControllerDidFinish:(nonnull PESDKVideoEditViewController *)videoEditViewController result:(nonnull PESDKVideoEditorResult *)result { NSError *error = nil; id serialization = nil; @@ -171,17 +178,16 @@ - (void)videoEditViewController:(nonnull PESDKVideoEditViewController *)videoEdi } } - RCTPromiseResolveBlock resolve = self.resolve; - RCTPromiseRejectBlock reject = self.reject; - [self dismiss:videoEditViewController animated:YES completion:^{ - if (error == nil) { - resolve(@{ @"video": (url != nil) ? url.absoluteString : [NSNull null], - @"hasChanges": @(videoEditViewController.hasChanges), + if (error == nil) { + RCTPromiseResolveBlock resolve = self.resolve; + [self dismiss:videoEditViewController animated:YES completion:^{ + resolve(@{ @"video": (result.output.url != nil) ? result.output.url.absoluteString : [NSNull null], + @"hasChanges": @(result.status == VESDKVideoEditorStatusRenderedWithChanges), @"serialization": (serialization != nil) ? serialization : [NSNull null] }); - } else { - reject(RN_IMGLY.kErrorUnableToExport, [NSString RN_IMGLY_string:@"Unable to export video or serialization." withError:error], error); - } - }]; + }]; + } else { + [self handleError:videoEditViewController code:RN_IMGLY.kErrorUnableToExport message:[NSString RN_IMGLY_string:@"Unable to export video or serialization." withError:error] error:error]; + } } - (void)videoEditViewControllerDidCancel:(nonnull PESDKVideoEditViewController *)videoEditViewController { @@ -191,11 +197,8 @@ - (void)videoEditViewControllerDidCancel:(nonnull PESDKVideoEditViewController * }]; } -- (void)videoEditViewControllerDidFailToGenerateVideo:(nonnull PESDKVideoEditViewController *)videoEditViewController { - RCTPromiseRejectBlock reject = self.reject; - [self dismiss:videoEditViewController animated:YES completion:^{ - reject(RN_IMGLY.kErrorUnableToExport, @"Unable to generate video", nil); - }]; +- (void)videoEditViewControllerDidFail:(nonnull PESDKVideoEditViewController *)videoEditViewController error:(PESDKVideoEditorError *)error { + [self handleError:videoEditViewController code:RN_IMGLY.kErrorUnableToExport message:@"Unable to generate video" error:error]; } @end diff --git a/package.json b/package.json index ca71f0a..f750aa6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "2.14.0", + "version": "2.15.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "2.14.0" + "react-native-imglysdk": "2.15.0" } } From 8bf294feb57a2776557e8354cb3f63ac0c04fb26 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Tue, 25 Oct 2022 15:02:01 +0200 Subject: [PATCH 05/13] Add version 2.16.0 --- CHANGELOG.md | 7 + README.md | 34 ++-- RNVideoEditorSDK.podspec | 2 +- android/build.gradle | 2 +- .../vesdk/RNVideoEditorSDKModule.kt | 148 +++++++++++++++--- configuration.ts | 18 ++- index.d.ts | 80 +++++++--- index.js | 59 +++++-- ios/RNImglyKit.m | 34 ++-- ios/RNImglyKitSubclass.h | 2 + ios/RNVideoEditorSDK.m | 112 ++++++++++--- package.json | 4 +- 12 files changed, 393 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4be6bfb..650f61e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [2.16.0] + +### Added + +* [react-native-videoeditorsdk] Added duration action for text and stickers. +* [react-native-videoeditorsdk] Added `VideoEditorResult.segments`, `VideoEditorResult.videoSize`, and `VideoEditorResult.release()` which enable serialization of the individual video composition components if `configuration.export.video.segments` is enabled. + ## [2.15.0] ### Changed diff --git a/README.md b/README.md index 1f89e4a..7e94b45 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Check out our [video tutorial](https://img.ly/blog/a-photo-and-video-editor-for- With version `2.13.0`, we recommend using `compileSdkVersion` not lower than `31` for Android. However, this might interfere with your application's Android Gradle Plugin version if this is set to `4.x`. If you don't use a newer Android Gradle Plugin version, e.g., by updating at least to RN 0.68.0, you'll most likely encounter a build error similar to: + ``` FAILURE: Build failed with an exception. @@ -44,9 +45,11 @@ Run with --stacktrace option to get the stack trace. Run with --info or --debug * Get more help at https://help.gradle.org ``` + As a workaround you can create the following symlinks: - 1. Inside `/Users/YOUR-USERNAME/Library/Android/sdk/build-tools/31.0.0/`: Create a `dx` symlink for the `d8` file with `ln -s d8 dx`. - 2. From there, go to `./lib/` and create a `dx.jar` symlink for the `d8.jar` file with `ln -s d8.jar dx.jar`. + +1. Inside `/Users/YOUR-USERNAME/Library/Android/sdk/build-tools/31.0.0/`: Create a `dx` symlink for the `d8` file with `ln -s d8 dx`. +2. From there, go to `./lib/` and create a `dx.jar` symlink for the `d8.jar` file with `ln -s d8.jar dx.jar`. ### Expo CLI @@ -63,7 +66,7 @@ In order to use this module with the Expo CLI you can make use of our integrated ```sh expo install react-native-videoeditorsdk ``` - + This will automatically install [`react-native-imglysdk`](https://npmjs.org/package/react-native-imglysdk) which you can use to configure your application with our Expo config plugin. 2. Inside your app's `app.json` or `app.config.js` add our config plugin: @@ -83,7 +86,7 @@ In order to use this module with the Expo CLI you can make use of our integrated "react-native-imglysdk", { "android": { - "version": "10.1.1", + "version": "10.4.0", "modules": [ "ui:core", "ui:transform", @@ -103,7 +106,7 @@ In order to use this module with the Expo CLI you can make use of our integrated ``` For further information on the available modules, please refer to step 4 of the React Native CLI [Android](#android) guide below. - + **Please note that the `react-native-imglysdk` module manages both VideoEditor SDK as well as PhotoEditor SDK so you only need to add the Expo config plugin once even when using both SDKs.** 3. From version `2.15.0` the iOS deployment target needs to be set to at least iOS 13. You can use the `expo-build-properties` config plugin for this. Please refer to the [official Expo docs](https://docs.expo.dev/versions/v45.0.0/sdk/build-properties/). @@ -149,6 +152,7 @@ For older React Native versions autolinking is not available and VideoEditor SDK #### Android 1. Add the img.ly repository and plugin by opening the `android/build.gradle` file (**not** `android/app/build.gradle`) and adding these lines at the top: + ```groovy buildscript { repositories { @@ -157,11 +161,12 @@ For older React Native versions autolinking is not available and VideoEditor SDK } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.32" - classpath 'ly.img.android.sdk:plugin:10.1.1' + classpath 'ly.img.android.sdk:plugin:10.4.0' } } ``` - In order to update VideoEditor SDK for Android replace the version string `10.1.1` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). + + In order to update VideoEditor SDK for Android replace the version string `10.4.0` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). 2. Still in the `android/build.gradle` file (**not** `android/app/build.gradle`), add these lines at the bottom: @@ -189,7 +194,8 @@ For older React Native versions autolinking is not available and VideoEditor SDK } ``` -4. Configure VideoEditor SDK for Android by opening the `android/app/build.gradle` file (**not** `android/build.gradle`) and adding the following lines under `apply plugin: "com.android.application"`: +4. Configure VideoEditor SDK for Android by opening the `android/app/build.gradle` file (**not** `android/build.gradle`) and adding the following lines under `apply plugin: "com.android.application"`: + ```groovy apply plugin: 'ly.img.android.sdk' apply plugin: 'kotlin-android' @@ -237,25 +243,29 @@ For older React Native versions autolinking is not available and VideoEditor SDK Import the module in your `App.js`: ```js -import {VESDK, VideoEditorModal, Configuration} from 'react-native-videoeditorsdk'; +import { + VESDK, + VideoEditorModal, + Configuration, +} from "react-native-videoeditorsdk"; ``` Each platform requires a separate license file. [Unlock VideoEditor SDK](./index.d.ts#L41-L53) automatically for both platforms with a single line of code via [platform-specific file extensions](https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions): ```js -VESDK.unlockWithLicense(require('./vesdk_license')); +VESDK.unlockWithLicense(require("./vesdk_license")); ``` Open the editor with a video: ```js -VESDK.openEditor(require('./video.mp4')); +VESDK.openEditor(require("./video.mp4")); ``` Or use the component to open the editor: ```jsx - + ``` Please see the [code documentation](./index.d.ts) for more details and additional [customization and configuration options](./configuration.ts). diff --git a/RNVideoEditorSDK.podspec b/RNVideoEditorSDK.podspec index db84714..c988442 100644 --- a/RNVideoEditorSDK.podspec +++ b/RNVideoEditorSDK.podspec @@ -18,5 +18,5 @@ Pod::Spec.new do |s| s.dependency 'React' s.dependency 'React-RCTImage' - s.dependency 'VideoEditorSDK', '~> 11.1' + s.dependency 'VideoEditorSDK', '~> 11.3' end diff --git a/android/build.gradle b/android/build.gradle index f78e5c7..213f989 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,7 +20,7 @@ imglyConfig { } } -def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "10.1.1" +def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "10.4.0" task checkVersion { if (imglyConfig.convertToVersionNumber(imglyConfig.getVersion()) < imglyConfig.convertToVersionNumber(MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION)) { diff --git a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt index 08cb2a0..f54c610 100644 --- a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt +++ b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt @@ -22,6 +22,8 @@ import org.json.JSONObject import java.io.File import ly.img.android.pesdk.backend.encoder.Encoder import ly.img.android.pesdk.backend.model.EditorSDKResult +import ly.img.android.pesdk.backend.model.VideoPart +import ly.img.android.pesdk.backend.model.state.LoadState import ly.img.android.pesdk.backend.model.state.VideoCompositionSettings import ly.img.android.serializer._3.IMGLYFileReader import ly.img.android.serializer._3.IMGLYFileWriter @@ -39,6 +41,9 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte private var currentPromise: Promise? = null private var currentConfig: Configuration? = null + private var resolveManually: Boolean = false + private var currentEditorUID: String = UUID.randomUUID().toString() + private var settingsLists: MutableMap = mutableMapOf() @ReactMethod fun unlockWithLicense(license: String) { @@ -46,6 +51,15 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte IMGLY.authorize() } + @ReactMethod + fun releaseTemporaryData(identifier: String) { + val settingsList = settingsLists[identifier] + if (settingsList != null) { + settingsList.release() + settingsLists.remove(identifier) + } + } + override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, intent: Intent?) { val data = try { intent?.let { EditorSDKResult(it) } @@ -67,45 +81,78 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte val serializationConfig = currentConfig?.export?.serialization var serialization: Any? = null + val settingsList = data.settingsList + if (serializationConfig?.enabled == true) { - val settingsList = data.settingsList skipIfNotExists { settingsList.let { settingsList -> if (serializationConfig.embedSourceImage == true) { - Log.i("ImgLySdk", "EmbedSourceImage is currently not supported by the Android SDK") + Log.i( + "ImgLySdk", + "EmbedSourceImage is currently not supported by the Android SDK" + ) } serialization = when (serializationConfig.exportType) { SerializationExportType.FILE_URL -> { val uri = serializationConfig.filename?.let { Uri.parse("$it.json") - } ?: Uri.fromFile(File.createTempFile("serialization-" + UUID.randomUUID().toString(), ".json")) - Encoder.createOutputStream(uri).use { outputStream -> - IMGLYFileWriter(settingsList).writeJson(outputStream) - } + } ?: Uri.fromFile( + File.createTempFile( + "serialization-" + UUID.randomUUID() + .toString(), ".json" + ) + ) + Encoder.createOutputStream(uri) + .use { outputStream -> + IMGLYFileWriter(settingsList).writeJson( + outputStream + ) + } uri.toString() } SerializationExportType.OBJECT -> { ReactJSON.convertJsonToMap( - JSONObject( - IMGLYFileWriter(settingsList).writeJsonAsString() - ) + JSONObject( + IMGLYFileWriter(settingsList).writeJsonAsString() + ) ) } } } - settingsList.release() } ?: run { - Log.i("ImgLySdk", "You need to include 'backend:serializer' Module, to use serialisation!") + Log.i( + "ImgLySdk", + "You need to include 'backend:serializer' Module, to use serialisation!" + ) } } + var segments: ReadableArray? = null + val canvasSize = settingsList[LoadState::class].sourceSize + val serializedSize = reactMap( + "height" to canvasSize.height, + "width" to canvasSize.width + ) + + if (resolveManually) { + settingsLists[currentEditorUID] = settingsList + segments = serializeVideoSegments(settingsList) + } + currentPromise?.resolve( - reactMap( - "video" to resultPath?.toString(), - "hasChanges" to (sourcePath?.path != resultPath?.path), - "serialization" to serialization - ) + reactMap( + "video" to resultPath?.toString(), + "hasChanges" to (sourcePath?.path != resultPath?.path), + "serialization" to serialization, + "segments" to segments, + "identifier" to currentEditorUID, + "videoSize" to serializedSize + ) ) + if (!resolveManually) { + settingsList.release() + } + resolveManually = false }() } } @@ -113,14 +160,17 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte } } - override fun onNewIntent(intent: Intent?) { } @ReactMethod fun present(video: String, config: ReadableMap?, serialization: String?, promise: Promise) { val configuration = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()) - val settingsList = VideoEditorSettingsList(configuration.export?.serialization?.enabled == true) + val exportVideoSegments = config?.getMap("export")?.getMap("video")?.getBoolean("segments") == true + val createTemporaryFiles = configuration.export?.serialization?.enabled == true || exportVideoSegments + resolveManually = exportVideoSegments + + val settingsList = VideoEditorSettingsList(createTemporaryFiles) configuration.applyOn(settingsList) currentConfig = configuration currentPromise = promise @@ -135,30 +185,33 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte @ReactMethod fun presentComposition(videos: ReadableArray, config: ReadableMap?, serialization: String?, size: ReadableMap?, promise: Promise) { - val array = videos.toArrayList() - val videoArray = array.filterIsInstance().takeIf { it.size == array.size } ?: arrayListOf() + val videoArray = deserializeVideoParts(videos) var source = resolveSize(size) val configuration = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()) - val settingsList = VideoEditorSettingsList(configuration.export?.serialization?.enabled == true) + + val exportVideoSegments = config?.getMap("export")?.getMap("video")?.getBoolean("segments") == true + val createTemporaryFiles = configuration.export?.serialization?.enabled == true || exportVideoSegments + resolveManually = exportVideoSegments + + val settingsList = VideoEditorSettingsList(createTemporaryFiles) configuration.applyOn(settingsList) currentConfig = configuration currentPromise = promise - if (videoArray.count() > 0) { + if (videoArray.isNotEmpty()) { if (source == null) { if (size != null) { promise.reject("VESDK", "Invalid video size: width and height must be greater than zero.") return } val video = videoArray.first() - source = retrieveURI(video) + source = video.videoSource.getSourceAsUri() } settingsList.configure { loadSettings -> videoArray.forEach { - val resolvedSource = retrieveURI(it) - loadSettings.addCompositionPart(VideoCompositionSettings.VideoPart(resolvedSource)) + loadSettings.addCompositionPart(it) } } } else { @@ -176,6 +229,50 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte startEditor(settingsList) } + private fun serializeVideoSegments(settingsList: SettingsList): ReadableArray { + val compositionParts = WritableNativeArray() + settingsList[VideoCompositionSettings::class].videos.forEach { + val source = it.videoSource.getSourceAsUri().toString() + val trimStart = it.trimStartInNano / 1000000000.0f + val trimEnd = it.trimEndInNano / 1000000000.0f + + val videoPart = reactMap( + "videoURI" to source, + "startTime" to trimStart.toDouble(), + "endTime" to trimEnd.toDouble() + ) + compositionParts.pushMap(videoPart) + } + return compositionParts + } + + private fun deserializeVideoParts(videos: ReadableArray) : List { + val parts = emptyList().toMutableList() + + videos.toArrayList().forEach { + if (it is String) { + val videoPart = VideoPart(retrieveURI(it)) + parts.add(videoPart) + } else if (it is Map<*, *>) { + val uri = it["videoURI"] as String? + val trimStart = it["startTime"] as Double? + val trimEnd = it["endTime"] as Double? + + if (uri != null) { + val videoPart = VideoPart(retrieveURI(uri)) + if (trimStart != null) { + videoPart.trimStartInNanoseconds = (trimStart * 1000000000.0f).toLong() + } + if (trimEnd != null) { + videoPart.trimEndInNanoseconds = (trimEnd * 1000000000.0f).toLong() + } + parts.add(videoPart) + } + } + } + return parts + } + private fun retrieveURI(source: String) : Uri { return if (source.startsWith("data:")) { UriHelper.createFromBase64String(source.substringAfter("base64,")) @@ -211,6 +308,7 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte private fun startEditor(settingsList: VideoEditorSettingsList?) { val currentActivity = this.currentActivity ?: throw RuntimeException("Can't start the Editor because there is no current activity") + currentEditorUID = UUID.randomUUID().toString() if (settingsList != null) { MainThreadRunnable { VideoEditorBuilder(currentActivity) diff --git a/configuration.ts b/configuration.ts index b72d824..e10b785 100644 --- a/configuration.ts +++ b/configuration.ts @@ -606,8 +606,9 @@ export interface Configuration { /** * Defines all allowed actions for the sticker tool menu. Only buttons for allowed actions are visible and shown in the given order. * @note The `StickerAction.REMOVE_BACKGROUND` action is only shown for personal and external (non-animated) stickers where a person could be detected. This feature is only supported on devices running iOS 15+. + * @note The `StickerAction.DURATION` action is only shown when editing videos. * @example // Defaults to: - * [StickerAction.REPLACE, StickerAction.OPACITY, StickerAction.COLOR, StickerAction.REMOVE_BACKGROUND] + * [StickerAction.DURATION, StickerAction.REPLACE, StickerAction.OPACITY, StickerAction.COLOR, StickerAction.REMOVE_BACKGROUND] */ actions?: StickerAction[]; /** @@ -685,8 +686,9 @@ export interface Configuration { fonts?: (Font | ExistingItem)[]; /** * Defines all allowed actions for the text tool menu. Only buttons for allowed actions are visible and shown in the given order. + * @note The `TextAction.DURATION` action is only shown when editing videos. * @example // Defaults to: - * [TextAction.FONT, TextAction.COLOR, TextAction.BACKGROUND_COLOR, TextAction.ALIGNMENT] + * [TextAction.DURATION, TextAction.FONT, TextAction.COLOR, TextAction.BACKGROUND_COLOR, TextAction.ALIGNMENT] */ actions?: TextAction[]; /** @@ -1030,6 +1032,16 @@ export interface Configuration { * null */ bitRate?: number | null; + /** + * Whether the video editor should include the video segments of the composition + * in the `VideoEditorResult`. + * @note If enabled, you need to release the result via `VideoEditorResult.release()` + * after processing the video segments in order to prevent memory leaks. + * + * @example // Defaults to: + * false + */ + segments?: boolean; } /** * The filename for the exported data if the `exportType` is not `ImageExportType.DATA_URL`. @@ -1313,6 +1325,7 @@ export enum StickerAction { REPLACE = "replace", OPACITY = "opacity", REMOVE_BACKGROUND = "removebackground", + DURATION = "duration" } /** A text action. */ @@ -1321,6 +1334,7 @@ export enum TextAction { COLOR = "color", BACKGROUND_COLOR = "backgroundcolor", ALIGNMENT = "alignment", + DURATION = "duration" } /** A frame action. */ diff --git a/index.d.ts b/index.d.ts index 3b32133..0b04cda 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,5 +1,5 @@ -import { Component } from 'react'; -import { AssetURI, Configuration } from './configuration'; +import { Component } from "react"; +import { AssetURI, Configuration } from "./configuration"; /** * The result of an export. @@ -11,6 +11,12 @@ interface VideoEditorResult { hasChanges: boolean; /** All modifications applied to the input video if `export.serialization.enabled` of the `Configuration` was set to `true`. */ serialization?: string | object; + /** The used input video segments that compose the edited `video`. Returned if `export.video.segments` of the `Configuration` was set to `true`. */ + segments?: [VideoSegment]; + /** The size of the **untransformed** video. */ + videoSize: Size; + /** Releases the result. Needed if `export.video.segments` of the `Configuration` was set to `true`. */ + release?(): void; } /** An object that contains width and height values. */ @@ -21,18 +27,45 @@ interface Size { height: number; } +/** + * A video segment with optional trimming. + * + * It is used to initialize the editor with a video composition and to retrieve the edited video composition on export. + * A video composition (edited asset) in combination with a serialization (edit model) can be used to restore the state of the editor. + */ +interface VideoSegment { + /** + * A URI for the video segment. + * @note Remote resources are not optimized and therefore should be downloaded + * in advance and then passed to the editor as local resources. + */ + videoURI: AssetURI; + /** + * The start time in seconds. + * @example // Defaults to: + * null + */ + startTime?: number; + /** + * The end time in seconds. + * @example // Defaults to: + * null + */ + endTime?: number; +} + declare class VESDK { /** * Modally present a video editor. * @note Remote resources are not optimized and therefore should be downloaded * in advance and then passed to the editor as local resources. * - * @param {AssetURI | [AssetURI] | {uri: string}} video The source of the video to be edited. - * Can be either an URI (local only), an object with a member `uri`, or an asset reference + * @param {AssetURI | [AssetURI] | [VideoSegment] | {uri: string}} video The source of the video to be edited. + * Can be either a URI, an object with a member `uri`, or an asset reference * which can be optained by, e.g., `require('./video.mp4')` as `number`. * * For video compositions an array of video sources is accepted as input. If an empty array is - * passed to the editor `videoSize` must be set. You need to obtain a **valid license** for this + * passed to the editor `videoSize` must be set. You need to obtain a **valid license** for this * feature to work. * @param {Configuration} configuration The configuration used to initialize the editor. * @param {object} serialization The serialization used to initialize the editor. This @@ -41,16 +74,25 @@ declare class VESDK { * @param {Size} videoSize **Video composition only:** The size of the video in pixels that is about to be edited. * This overrides the natural dimensions of the video(s) passed to the editor. All videos will * be fitted to the `videoSize` aspect by adding black bars on the left and right side or top and bottom. - * * @return {Promise} Returns a `VideoEditorResult` or `null` if the editor * is dismissed without exporting the edited video. */ static openEditor( - video: AssetURI | [AssetURI] | {uri: string}, + video: AssetURI | [AssetURI] | [VideoSegment] | { uri: string }, configuration?: Configuration, serialization?: object, videoSize?: Size - ): Promise + ): Promise; + + /** + * Releases the result from the editor and deletes the temporary files. + * @note This function needs to be called in case `configuration.export.video.segments` + * is set to `true`. + * + * @param {string} identifier The identifier of the `VideoEditorResult` to release + * the temporary data of. + */ + static releaseTemporaryData(identifier: string): void; /** * Unlock VideoEditor SDK with a license. @@ -62,9 +104,7 @@ declare class VESDK { * and `vesdk_license.android.json` for the Android license file in order to get automatically * resolved by the packager. */ - static unlockWithLicense( - license: string | object - ): void + static unlockWithLicense(license: string | object): void; } /** @@ -78,17 +118,15 @@ interface VideoEditorModalProps { /** * This prop determines the source of the video to be edited. - * Can be either an URI (local only), an object with a member `uri`, or an asset reference + * Can be either a URI, an object with a member `uri`, or an asset reference * which can be optained by, e.g., `require('./video.mp4')` as `number`. * For video compositions an array of video sources is accepted as input. If an empty array is * passed to the editor `videoSize` must be set. * - * @note Edited videos from remote resources can be previewed in the editor but their export will - * fail! Remote video resources are currently supported for debugging purposes only, e.g., when - * loading videos with `require('./video.mp4')` for debug builds static video assets will be - * resolved to remote URLs served by the development packager. + * @note Remote resources are not optimized and therefore should be downloaded + * in advance and then passed to the editor as local resources. */ - video: AssetURI | [AssetURI] | {uri: string}; + video: AssetURI | [AssetURI] | [VideoSegment] | { uri: string }; /** * This prop determines the configuration used to initialize the editor. @@ -139,7 +177,11 @@ interface VideoEditorModalState { /** * A component that wraps the `VESDK.openEditor` function to modally present a video editor. */ -declare class VideoEditorModal extends Component {} +declare class VideoEditorModal extends Component< + VideoEditorModalProps, + VideoEditorModalState +> {} +export * from "./configuration"; export { VESDK, VideoEditorModal }; -export * from './configuration'; + diff --git a/index.js b/index.js index b293346..6596386 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ import { Component } from 'react'; -import { NativeModules, Image, Platform } from 'react-native'; +import { Image, NativeModules, Platform } from 'react-native'; import { Configuration } from './configuration'; const { RNVideoEditorSDK } = NativeModules; @@ -123,8 +123,8 @@ class VESDK { * @note Remote resources are not optimized and therefore should be downloaded * in advance and then passed to the editor as local resources. * - * @param {AssetURI | [AssetURI] | {uri: string}} video The source of the video to be edited. - * Can be either an URI (local only), an object with a member `uri`, or an asset reference + * @param {AssetURI | [AssetURI] | [VideoSegment] | {uri: string}} video The source of the video to be edited. + * Can be either a URI, an object with a member `uri`, or an asset reference * which can be optained by, e.g., `require('./video.mp4')` as `number`. * * For video compositions an array of video sources is accepted as input. If an empty array is @@ -137,29 +137,59 @@ class VESDK { * @param {Size} videoSize **Video composition only:** The size of the video in pixels that is about to be edited. * This overrides the natural dimensions of the video(s) passed to the editor. All videos will * be fitted to the `videoSize` aspect by adding black bars on the left and right side or top and bottom. - * * @return {Promise} Returns a `VideoEditorResult` or `null` if the editor * is dismissed without exporting the edited video. */ - static openEditor(video, configuration = null, serialization = null, videoSize = null) { + static async openEditor(video, configuration = null, serialization = null, videoSize = null) { resolveStaticAssets(configuration) - const videoDimensions = videoSize == null ? (Platform.OS == 'android' ? null : {height: 0, width: 0}) : videoSize; - const resolvedSerialization = Platform.OS == 'android' ? (serialization != null ? JSON.stringify(serialization) : null) : serialization; - + const isAndroid = Platform.OS == 'android'; + const videoDimensions = videoSize == null ? (isAndroid ? null : {height: 0, width: 0}) : videoSize; + const resolvedSerialization = isAndroid ? (serialization != null ? JSON.stringify(serialization) : null) : serialization; + var result = null; + var source = null; + if (Array.isArray(video)) { - var source = []; + source = []; video.forEach((videoClip) => { - source.push(resolveStaticAsset(videoClip, Platform.OS == 'android')); + if (videoClip.videoURI != null) { + const resolvedSource = resolveStaticAsset(videoClip.videoURI, isAndroid); + const resolvedVideo = {...videoClip}; + resolvedVideo.videoURI = resolvedSource + source.push(resolvedVideo); + } else { + const resolvedSource = resolveStaticAsset(videoClip, isAndroid); + source.push(resolvedSource); + } }); - return RNVideoEditorSDK.presentComposition(source, configuration, resolvedSerialization, videoDimensions); + if (isAndroid) { + result = await RNVideoEditorSDK.presentComposition(source, configuration, resolvedSerialization, videoDimensions); + } else { + result = await RNVideoEditorSDK.presentVideoSegments(source, configuration, resolvedSerialization, videoDimensions); + } } else { if (videoSize != null) { console.warn("Ignoring the video size. This parameter can only be used in combination with video compositions. If your license includes the video composition feature please wrap your video source into an array instead.") } - const resolvedVideo = resolveStaticAsset(video, Platform.OS == 'android'); - return RNVideoEditorSDK.present(resolvedVideo, configuration, resolvedSerialization); + source = resolveStaticAsset(video, isAndroid); + result = await RNVideoEditorSDK.present(source, configuration, resolvedSerialization); } + const resolvedResult = {...result} + + if (configuration.export.video.segments == true) { + const release = () => { + if (isAndroid) { + return RNVideoEditorSDK.releaseTemporaryData(result.identifier); + } + return; + } + resolvedResult.release = release; + } else { + delete resolvedResult.segments; + } + + delete resolvedResult.identifier; + return resolvedResult; } /** @@ -212,5 +242,6 @@ class VideoEditorModal extends Component { } } -export { VESDK, VideoEditorModal }; export * from './configuration'; +export { VESDK, VideoEditorModal }; + diff --git a/ios/RNImglyKit.m b/ios/RNImglyKit.m index 510f3f1..5150cf4 100644 --- a/ios/RNImglyKit.m +++ b/ios/RNImglyKit.m @@ -86,6 +86,7 @@ - (void)present:(nonnull IMGLYMediaEditViewControllerBlock)createMediaEditViewCo id valueSerializationType = [NSDictionary RN_IMGLY_dictionary:dictionary valueForKeyPath:@"export.serialization.exportType" default:RN_IMGLY.kExportTypeFileURL]; id valueSerializationFile = [NSDictionary RN_IMGLY_dictionary:dictionary valueForKeyPath:@"export.serialization.filename" default:valueExportFile]; id valueSerializationEmbedImage = [NSDictionary RN_IMGLY_dictionary:dictionary valueForKeyPath:@"export.serialization.embedSourceImage" default:@(NO)]; + id valueExportVideoSegments = [NSDictionary RN_IMGLY_dictionary:dictionary valueForKeyPath:@"export.video.segments" default:@(NO)]; NSString *exportType = [RCTConvert NSString:valueExportType]; NSURL *exportFile = [RCTConvert RN_IMGLY_ExportFileURL:valueExportFile withExpectedUTI:getUTI(configuration)]; @@ -93,6 +94,7 @@ - (void)present:(nonnull IMGLYMediaEditViewControllerBlock)createMediaEditViewCo NSString *serializationType = [RCTConvert NSString:valueSerializationType]; NSURL *serializationFile = [RCTConvert RN_IMGLY_ExportFileURL:valueSerializationFile withExpectedUTI:kUTTypeJSON]; BOOL serializationEmbedImage = [RCTConvert BOOL:valueSerializationEmbedImage]; + BOOL exportVideoSegments = [RCTConvert BOOL:valueExportVideoSegments]; // Make sure that the export settings are valid if ((exportType == nil) || @@ -138,6 +140,7 @@ - (void)present:(nonnull IMGLYMediaEditViewControllerBlock)createMediaEditViewCo self.resolve = resolve; self.reject = reject; self.mediaEditViewController = mediaEditViewController; + self.exportVideoSegments = exportVideoSegments; UIViewController *currentViewController = RCTPresentedViewController(); [currentViewController presentViewController:self.mediaEditViewController animated:YES completion:NULL]; @@ -159,6 +162,7 @@ - (void)dismiss:(nullable PESDKMediaEditViewController *)mediaEditViewController self.resolve = nil; self.reject = nil; self.mediaEditViewController = nil; + self.exportVideoSegments = nil; dispatch_async(dispatch_get_main_queue(), ^{ [mediaEditViewController.presentingViewController dismissViewControllerAnimated:animated completion:completion]; @@ -351,22 +355,22 @@ + (nullable RN_IMGLY_ExportFileURL *)RN_IMGLY_ExportFileURL:(nullable id)json wi + (nullable RN_IMGLY_URLRequestArray *)RN_IMGLY_URLRequestArray:(nullable id)json { - NSArray *array = [RCTConvert NSArray:json]; - NSMutableArray *requests = [NSMutableArray new]; - if (array.count == 0) { return [requests copy]; } - for (id value in array) { - if (value == (id)[NSNull null]) { - RCTLogConvertError(json, @"a valid NSArray"); - return nil; - } - NSURLRequest *request = [RCTConvert NSURLRequest:value]; - if (request == nil) { - RCTLogConvertError(value, @"a valid NSURLRequest"); - return nil; - } - [requests addObject:request]; + NSArray *array = [RCTConvert NSArray:json]; + NSMutableArray *requests = [NSMutableArray new]; + if (array.count == 0) { return [requests copy]; } + for (id value in array) { + if (value == (id)[NSNull null]) { + RCTLogConvertError(json, @"a valid NSArray"); + return nil; } - return [requests copy]; + NSURLRequest *request = [RCTConvert NSURLRequest:value]; + if (request == nil) { + RCTLogConvertError(value, @"a valid NSURLRequest"); + return nil; + } + [requests addObject:request]; + } + return [requests copy]; } @end diff --git a/ios/RNImglyKitSubclass.h b/ios/RNImglyKitSubclass.h index 5386312..1007a7f 100644 --- a/ios/RNImglyKitSubclass.h +++ b/ios/RNImglyKitSubclass.h @@ -60,6 +60,8 @@ typedef void (^IMGLYCompletionBlock)(void); @property (strong, atomic, nullable) RCTPromiseResolveBlock resolve; @property (strong, atomic, nullable) RCTPromiseRejectBlock reject; @property (strong, atomic, nullable) PESDKMediaEditViewController* mediaEditViewController; +@property (atomic) BOOL exportVideoSegments; +@property (strong, atomic, nullable) NSString* uuid; - (void)present:(nonnull IMGLYMediaEditViewControllerBlock)createMediaEditViewController withUTI:(nonnull IMGLYUTIBlock)getUTI configuration:(nullable NSDictionary *)dictionary serialization:(nullable NSDictionary *)state diff --git a/ios/RNVideoEditorSDK.m b/ios/RNVideoEditorSDK.m index 255fe38..6a2003b 100644 --- a/ios/RNVideoEditorSDK.m +++ b/ios/RNVideoEditorSDK.m @@ -40,6 +40,7 @@ - (void)present:(nonnull PESDKVideo *)video withConfiguration:(nullable NSDictio resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [self present:^PESDKMediaEditViewController * _Nullable(PESDKConfiguration * _Nonnull configuration, NSData * _Nullable serializationData) { + self.uuid = [[NSUUID new] UUIDString]; PESDKPhotoEditModel *photoEditModel = [[PESDKPhotoEditModel alloc] init]; @@ -103,16 +104,24 @@ - (void)present:(nonnull PESDKVideo *)video withConfiguration:(nullable NSDictio reject:(RCTPromiseRejectBlock)reject) { // TODO: Handle React Native URLs from camera roll. - if (request.URL.isFileURL) { - if (![[NSFileManager defaultManager] fileExistsAtPath:request.URL.path]) { - reject(RN_IMGLY.kErrorUnableToLoad, @"File does not exist", nil); - return; - } + if (![self isValidURL:request.URL]) { + reject(RN_IMGLY.kErrorUnableToLoad, @"File does not exist", nil); + return; } PESDKVideo *video = [[PESDKVideo alloc] initWithURL:request.URL]; [self present:video withConfiguration:configuration andSerialization:state resolve:resolve reject:reject]; } +RCT_EXPORT_METHOD(presentVideoSegments:(nonnull NSArray *)requests + configuration:(nullable NSDictionary *)configuration + serialization:(nullable NSDictionary *)state + videoSize:(CGSize)videoSize + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + [self openEditorFromVideos:requests videoSize:videoSize configuration:configuration serialization:state resolve:resolve reject:reject]; +} + RCT_EXPORT_METHOD(presentComposition:(nonnull RN_IMGLY_URLRequestArray *)requests configuration:(nullable NSDictionary *)configuration serialization:(nullable NSDictionary *)state @@ -120,46 +129,103 @@ - (void)present:(nonnull PESDKVideo *)video withConfiguration:(nullable NSDictio resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { - NSMutableArray *assets = [NSMutableArray new]; + [self openEditorFromVideos:requests videoSize:videoSize configuration:configuration serialization:state resolve:resolve reject:reject]; +} + +- (void)openEditorFromVideos:(nonnull NSArray *)videos videoSize:(CGSize)videoSize configuration:(nullable NSDictionary *)configuration serialization:(nullable NSDictionary *)state resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock) reject +{ + NSMutableArray *segments = [NSMutableArray new]; + + for (id video in videos) { + if ([video isKindOfClass:[NSURLRequest class]]) { + NSURLRequest *request = video; + if (![self isValidURL:request.URL]) { + reject(RN_IMGLY.kErrorUnableToLoad, @"File does not exist", nil); + return; + } + PESDKVideoSegment *videoSegment = [[PESDKVideoSegment alloc] initWithURL:request.URL]; + [segments addObject: videoSegment]; + } else { + NSURLRequest *request = [RCTConvert NSURLRequest:video]; + if (request == nil) { + // Segment. + NSURLRequest *segmentRequest = [RCTConvert NSURLRequest:[video valueForKey:@"videoURI"]]; + NSNumber *startTime; + NSNumber *endTime; + + id start = [video valueForKey:@"startTime"]; + id end = [video valueForKey:@"endTime"]; + + if (![start isKindOfClass:[NSNull class]]) { + startTime = [RCTConvert NSNumber:start]; + } + if (![end isKindOfClass:[NSNull class]]) { + endTime = [RCTConvert NSNumber:end]; + } - if (requests.count > 0) { - for (NSURLRequest *request in requests) { - if (request.URL.isFileURL) { - if (![[NSFileManager defaultManager] fileExistsAtPath:request.URL.path]) { + if (![self isValidURL:segmentRequest.URL]) { + reject(RN_IMGLY.kErrorUnableToLoad, @"File does not exist", nil); + return; + } + PESDKVideoSegment *videoSegment = [[PESDKVideoSegment alloc] initWithURL:segmentRequest.URL startTime:startTime endTime:endTime]; + [segments addObject: videoSegment]; + } else { + if (![self isValidURL:request.URL]) { reject(RN_IMGLY.kErrorUnableToLoad, @"File does not exist", nil); return; } + PESDKVideoSegment *videoSegment = [[PESDKVideoSegment alloc] initWithURL:request.URL]; + [segments addObject: videoSegment]; } - - AVAsset *asset = [AVAsset assetWithURL:request.URL]; - [assets addObject:asset]; } } PESDKVideo *video; if (CGSizeEqualToSize(videoSize, CGSizeZero)) { - if (assets.count == 0) { + if (segments.count == 0) { RCTLogError(@"A video without assets must have a specific size."); reject(RN_IMGLY.kErrorUnableToLoad, @"The editor requires a valid size when initialized without a video.", nil); return; } - video = [[PESDKVideo alloc] initWithAssets:assets]; + video = [[PESDKVideo alloc] initWithSegments:segments]; } else { if (videoSize.height <= 0 || videoSize.width <= 0) { RCTLogError(@"Invalid video size: width and height must be greater than zero"); reject(RN_IMGLY.kErrorUnableToLoad, @"Invalid video size: width and height must be greater than zero", nil); return; } - if (assets.count == 0) { + if (segments.count == 0) { video = [[PESDKVideo alloc] initWithSize:videoSize]; } - video = [[PESDKVideo alloc] initWithAssets:assets size:videoSize]; + video = [[PESDKVideo alloc] initWithSegments:segments size:videoSize]; } [self present:video withConfiguration:configuration andSerialization:state resolve:resolve reject:reject]; } +- (BOOL)isValidURL:(nonnull NSURL*)url { + if (url.isFileURL) { + if (![[NSFileManager defaultManager] fileExistsAtPath:url.path]) { + return false; + } + } + return true; +} + +- (NSArray *)serializeVideoSegments:(nonnull NSArray *)segments { + NSMutableArray *videoSegments = [NSMutableArray new]; + for (PESDKVideoSegment *segment in segments) { + NSDictionary *videoSegment = @{ + @"videoURI": segment.url.absoluteString, + @"startTime": (segment.startTime != nil) ? segment.startTime : [NSNull null], + @"endTime": (segment.endTime != nil) ? segment.endTime : [NSNull null] + }; + [videoSegments addObject: videoSegment]; + } + return [videoSegments copy]; +} + #pragma mark - PESDKVideoEditViewControllerDelegate - (void)videoEditViewControllerDidFinish:(nonnull PESDKVideoEditViewController *)videoEditViewController result:(nonnull PESDKVideoEditorResult *)result { @@ -180,10 +246,20 @@ - (void)videoEditViewControllerDidFinish:(nonnull PESDKVideoEditViewController * if (error == nil) { RCTPromiseResolveBlock resolve = self.resolve; + NSArray *segments; + + if (self.exportVideoSegments) { + segments = [self serializeVideoSegments:result.task.video.segments]; + } + [self dismiss:videoEditViewController animated:YES completion:^{ resolve(@{ @"video": (result.output.url != nil) ? result.output.url.absoluteString : [NSNull null], @"hasChanges": @(result.status == VESDKVideoEditorStatusRenderedWithChanges), - @"serialization": (serialization != nil) ? serialization : [NSNull null] }); + @"serialization": (serialization != nil) ? serialization : [NSNull null], + @"segments": (segments != nil) ? segments : [NSNull null], + @"videoSize": @{@"height": @(result.task.video.size.height), @"width": @(result.task.video.size.height)}, + @"identifier": self.uuid + }); }]; } else { [self handleError:videoEditViewController code:RN_IMGLY.kErrorUnableToExport message:[NSString RN_IMGLY_string:@"Unable to export video or serialization." withError:error] error:error]; diff --git a/package.json b/package.json index f750aa6..b4fe003 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "2.15.0", + "version": "2.16.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "2.15.0" + "react-native-imglysdk": "2.16.0" } } From 4753e305a5ddf0b991d92d438622d1d40d03462b Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Wed, 26 Oct 2022 14:35:53 +0200 Subject: [PATCH 06/13] Add version 2.16.1 --- CHANGELOG.md | 8 ++++++++ index.d.ts | 6 +++--- index.js | 8 ++++---- package.json | 4 ++-- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 650f61e..7a5b085 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [2.16.1] + +### Fixed + +* [react-native-videoeditorsdk] Fixed error when cancelling the editor. +* [react-native-videoeditorsdk] Fixed missing export of `VideoSegment` type. +* [react-native-videoeditorsdk] Fixed wrong types for `VESDK.openEditor`. + ## [2.16.0] ### Added diff --git a/index.d.ts b/index.d.ts index 0b04cda..7338388 100644 --- a/index.d.ts +++ b/index.d.ts @@ -60,7 +60,7 @@ declare class VESDK { * @note Remote resources are not optimized and therefore should be downloaded * in advance and then passed to the editor as local resources. * - * @param {AssetURI | [AssetURI] | [VideoSegment] | {uri: string}} video The source of the video to be edited. + * @param {AssetURI | AssetURI[] | VideoSegment[] | {uri: string}} video The source of the video to be edited. * Can be either a URI, an object with a member `uri`, or an asset reference * which can be optained by, e.g., `require('./video.mp4')` as `number`. * @@ -78,7 +78,7 @@ declare class VESDK { * is dismissed without exporting the edited video. */ static openEditor( - video: AssetURI | [AssetURI] | [VideoSegment] | { uri: string }, + video: AssetURI | AssetURI[] | VideoSegment[] | { uri: string }, configuration?: Configuration, serialization?: object, videoSize?: Size @@ -183,5 +183,5 @@ declare class VideoEditorModal extends Component< > {} export * from "./configuration"; -export { VESDK, VideoEditorModal }; +export { VESDK, VideoEditorModal, VideoSegment }; diff --git a/index.js b/index.js index 6596386..deffd3e 100644 --- a/index.js +++ b/index.js @@ -123,7 +123,7 @@ class VESDK { * @note Remote resources are not optimized and therefore should be downloaded * in advance and then passed to the editor as local resources. * - * @param {AssetURI | [AssetURI] | [VideoSegment] | {uri: string}} video The source of the video to be edited. + * @param {AssetURI | AssetURI[] | VideoSegment[] | {uri: string}} video The source of the video to be edited. * Can be either a URI, an object with a member `uri`, or an asset reference * which can be optained by, e.g., `require('./video.mp4')` as `number`. * @@ -174,9 +174,9 @@ class VESDK { source = resolveStaticAsset(video, isAndroid); result = await RNVideoEditorSDK.present(source, configuration, resolvedSerialization); } - const resolvedResult = {...result} - - if (configuration.export.video.segments == true) { + if (result == null) return null; + const resolvedResult = {...result}; + if (configuration?.export?.video?.segments == true) { const release = () => { if (isAndroid) { return RNVideoEditorSDK.releaseTemporaryData(result.identifier); diff --git a/package.json b/package.json index b4fe003..c1b8b89 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "2.16.0", + "version": "2.16.1", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "2.16.0" + "react-native-imglysdk": "2.16.1" } } From fc3852f5add4d5225c7e6cb30fbd69c756ff2986 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Tue, 18 Apr 2023 15:04:40 +0200 Subject: [PATCH 07/13] Add version 2.17.0 --- CHANGELOG.md | 13 ++++ LICENSE.md | 2 +- README.md | 6 +- RNVideoEditorSDK.podspec | 2 +- android/build.gradle | 2 +- android/src/main/AndroidManifest.xml | 1 + .../vesdk/RNVideoEditorSDKModule.kt | 59 +++++++++++++------ configuration.ts | 9 +++ package.json | 4 +- 9 files changed, 72 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a5b085..b92de53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## [2.17.0] + +### Added + +* Added `configuration.singleToolMode` that skips main menu if only one tool is used. +* [react-native-videoeditorsdk] Added `RNVideoEditorSDKModule.editorWillOpenClosure` and `RNVideoEditorSDKModule.editorWillExportClosure` which allow further native configuration on Android. +* [react-native-photoeditorsdk] Added `RNPhotoEditorSDKModule.editorWillOpenClosure` and `RNPhotoEditorSDKModule.editorWillExportClosure` which allow further native configuration on Android. + +### Fixed + +* [react-native-videoeditorsdk] Fixed `VideoEditorResult.videoSize` would always be zero. +* [react-native-videoeditorsdk] Fixed error when not setting `Configuration.export.video.segments`. + ## [2.16.1] ### Fixed diff --git a/LICENSE.md b/LICENSE.md index d86fc14..e6acf57 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -4,7 +4,7 @@ In order to run any samples or use any wrapper without a watermark, you'll have to purchase a commercial PhotoEditor SDK or VideoEditor SDK license. Visit https://img.ly for more details. -Copyright (c) 2014-2022, img.ly GmbH +Copyright (c) 2014-2023, img.ly GmbH All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 7e94b45..f942ce1 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ In order to use this module with the Expo CLI you can make use of our integrated "react-native-imglysdk", { "android": { - "version": "10.4.0", + "version": "10.4.1", "modules": [ "ui:core", "ui:transform", @@ -161,12 +161,12 @@ For older React Native versions autolinking is not available and VideoEditor SDK } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.32" - classpath 'ly.img.android.sdk:plugin:10.4.0' + classpath 'ly.img.android.sdk:plugin:10.4.1' } } ``` - In order to update VideoEditor SDK for Android replace the version string `10.4.0` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). + In order to update VideoEditor SDK for Android replace the version string `10.4.1` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). 2. Still in the `android/build.gradle` file (**not** `android/app/build.gradle`), add these lines at the bottom: diff --git a/RNVideoEditorSDK.podspec b/RNVideoEditorSDK.podspec index c988442..6359197 100644 --- a/RNVideoEditorSDK.podspec +++ b/RNVideoEditorSDK.podspec @@ -18,5 +18,5 @@ Pod::Spec.new do |s| s.dependency 'React' s.dependency 'React-RCTImage' - s.dependency 'VideoEditorSDK', '~> 11.3' + s.dependency 'VideoEditorSDK', '~> 11.4' end diff --git a/android/build.gradle b/android/build.gradle index 213f989..a3b5802 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,7 +20,7 @@ imglyConfig { } } -def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "10.4.0" +def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "10.4.1" task checkVersion { if (imglyConfig.convertToVersionNumber(imglyConfig.getVersion()) < imglyConfig.convertToVersionNumber(MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION)) { diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 333a25c..e4eef02 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ android:exported="false" android:enabled="false" android:name="ly.img.android.IMGLYAutoInit" /> + \ No newline at end of file diff --git a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt index f54c610..97956ca 100644 --- a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt +++ b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt @@ -4,10 +4,12 @@ import android.app.Activity import android.content.Intent import android.net.Uri import android.util.Log +import androidx.annotation.WorkerThread import com.facebook.react.bridge.* import ly.img.android.IMGLY import ly.img.android.VESDK import ly.img.android.pesdk.VideoEditorSettingsList +import ly.img.android.pesdk.backend.decoder.VideoSource import ly.img.android.pesdk.backend.model.state.LoadSettings import ly.img.android.pesdk.backend.model.state.manager.SettingsList import ly.img.android.pesdk.kotlin_extension.continueWithExceptions @@ -23,8 +25,9 @@ import java.io.File import ly.img.android.pesdk.backend.encoder.Encoder import ly.img.android.pesdk.backend.model.EditorSDKResult import ly.img.android.pesdk.backend.model.VideoPart -import ly.img.android.pesdk.backend.model.state.LoadState import ly.img.android.pesdk.backend.model.state.VideoCompositionSettings +import ly.img.android.pesdk.backend.model.state.manager.StateHandler +import ly.img.android.pesdk.ui.activity.VideoEditorActivity import ly.img.android.serializer._3.IMGLYFileReader import ly.img.android.serializer._3.IMGLYFileWriter import java.util.UUID @@ -32,7 +35,13 @@ import java.util.UUID class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), ActivityEventListener { companion object { // This number must be unique. It is public to allow client code to change it if the same value is used elsewhere. - var EDITOR_RESULT_ID = 29065 + @JvmField var EDITOR_RESULT_ID = 29065 + + /** A closure to modify a *VideoEditorSettingsList* before the editor is opened. */ + @JvmField var editorWillOpenClosure: ((settingsList: VideoEditorSettingsList) -> Unit)? = null + + /** A closure allowing access to the *StateHandler* before the editor is exporting. */ + @JvmField var editorWillExportClosure: ((stateHandler: StateHandler) -> Unit)? = null } init { @@ -62,9 +71,9 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, intent: Intent?) { val data = try { - intent?.let { EditorSDKResult(it) } + intent?.let { EditorSDKResult(it) } } catch (e: EditorSDKResult.NotAnImglyResultException) { - null + null } ?: return // If data is null the result is not from us. when (requestCode) { @@ -128,10 +137,10 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte } var segments: ReadableArray? = null - val canvasSize = settingsList[LoadState::class].sourceSize + val canvasSize = sourcePath?.let { VideoSource.create(it).fetchFormatInfo()?.size } val serializedSize = reactMap( - "height" to canvasSize.height, - "width" to canvasSize.width + "height" to canvasSize?.height, + "width" to canvasSize?.width ) if (resolveManually) { @@ -166,8 +175,9 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte @ReactMethod fun present(video: String, config: ReadableMap?, serialization: String?, promise: Promise) { val configuration = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()) - val exportVideoSegments = config?.getMap("export")?.getMap("video")?.getBoolean("segments") == true - val createTemporaryFiles = configuration.export?.serialization?.enabled == true || exportVideoSegments + val serializationEnabled = configuration.export?.serialization?.enabled == true + val exportVideoSegments = configuration.export?.video?.segments == true + val createTemporaryFiles = serializationEnabled || exportVideoSegments resolveManually = exportVideoSegments val settingsList = VideoEditorSettingsList(createTemporaryFiles) @@ -189,9 +199,9 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte var source = resolveSize(size) val configuration = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()) - - val exportVideoSegments = config?.getMap("export")?.getMap("video")?.getBoolean("segments") == true - val createTemporaryFiles = configuration.export?.serialization?.enabled == true || exportVideoSegments + val serializationEnabled = configuration.export?.serialization?.enabled == true + val exportVideoSegments = configuration.export?.video?.segments == true + val createTemporaryFiles = serializationEnabled || exportVideoSegments resolveManually = exportVideoSegments val settingsList = VideoEditorSettingsList(createTemporaryFiles) @@ -296,7 +306,9 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte return LoadSettings.compositionSource(width.toInt(), height.toInt(), 60) } - private fun readSerialisation(settingsList: SettingsList, serialization: String?, readImage: Boolean) { + private fun readSerialisation(settingsList: VideoEditorSettingsList, serialization: String?, readImage: Boolean) { + editorWillOpenClosure?.invoke(settingsList) + if (serialization != null) { skipIfNotExists { IMGLYFileReader(settingsList).also { @@ -308,12 +320,13 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte private fun startEditor(settingsList: VideoEditorSettingsList?) { val currentActivity = this.currentActivity ?: throw RuntimeException("Can't start the Editor because there is no current activity") - currentEditorUID = UUID.randomUUID().toString() if (settingsList != null) { + currentEditorUID = UUID.randomUUID().toString() + MainThreadRunnable { - VideoEditorBuilder(currentActivity) - .setSettingsList(settingsList) - .startActivityForResult(currentActivity, EDITOR_RESULT_ID) + VideoEditorBuilder(currentActivity, RNVideoEditorSDKActivity::class.java) + .setSettingsList(settingsList) + .startActivityForResult(currentActivity, EDITOR_RESULT_ID) settingsList.release() }() } @@ -417,4 +430,14 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte } override fun getName() = "RNVideoEditorSDK" -} \ No newline at end of file +} + +/** A *VideoEditorActivity* used for the native interfaces. */ +class RNVideoEditorSDKActivity: VideoEditorActivity() { + @WorkerThread + override fun onExportStart(stateHandler: StateHandler) { + RNVideoEditorSDKModule.editorWillExportClosure?.invoke(stateHandler) + + super.onExportStart(stateHandler) + } +} diff --git a/configuration.ts b/configuration.ts index e10b785..dda7297 100644 --- a/configuration.ts +++ b/configuration.ts @@ -24,6 +24,15 @@ export interface Configuration { */ forceCrop?: boolean; + /** + * Controls if the editor is used in single tool mode. + * Prerequisite is that only one tool is in `tools`. + * + * @example // Defaults to: + * true + */ + singleToolMode?: boolean; + /** * Defines all allowed actions for the main screen that are displayed as overlay buttons on the canvas. * Only buttons for allowed actions are visible. diff --git a/package.json b/package.json index c1b8b89..f27332b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "2.16.1", + "version": "2.17.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "2.16.1" + "react-native-imglysdk": "2.17.0" } } From 9ea589bb35e8527bb85fd86dcb07a5760960ac4a Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Thu, 20 Apr 2023 12:16:54 +0200 Subject: [PATCH 08/13] Add version 2.17.1 --- CHANGELOG.md | 6 ++++++ package.json | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b92de53..782b76e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [2.17.1] + +### Fixed + +* [react-native-imglysdk] Fixed wrong default SDK version for Android. + ## [2.17.0] ### Added diff --git a/package.json b/package.json index f27332b..a659e8e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "2.17.0", + "version": "2.17.1", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "2.17.0" + "react-native-imglysdk": "2.17.1" } } From 14eb997ec171aed56be1995b3e4af97cc6249a50 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Tue, 2 May 2023 15:13:56 +0200 Subject: [PATCH 09/13] Add version 3.0.0 --- CHANGELOG.md | 12 ++++++ .../vesdk/RNVideoEditorSDKModule.kt | 23 ++++++++--- index.d.ts | 12 +----- index.js | 4 +- ios/RNImglyKit.m | 41 ++++++------------- ios/RNImglyKitSubclass.h | 11 +++-- ios/RNVideoEditorSDK.m | 16 ++++---- package.json | 4 +- 8 files changed, 61 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 782b76e..542b66a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [3.0.0] + +### Changed + +* Changed and aligned the error codes for the modules. +* [react-native-videoeditorsdk] Unlocking the SDK via `VESDK.unlockWithLicense` now returns a `Promise`. +* [react-native-photoeditorsdk] Unlocking the SDK via `PESDK.unlockWithLicense` now returns a `Promise`. + +### Fixed + +* [react-native-videoeditorsdk] Fixed unused types exports. + ## [2.17.1] ### Fixed diff --git a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt index 97956ca..675b6bb 100644 --- a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt +++ b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt @@ -6,6 +6,7 @@ import android.net.Uri import android.util.Log import androidx.annotation.WorkerThread import com.facebook.react.bridge.* +import ly.img.android.AuthorizationException import ly.img.android.IMGLY import ly.img.android.VESDK import ly.img.android.pesdk.VideoEditorSettingsList @@ -48,6 +49,12 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte reactContext.addActivityEventListener(this) } + /** IMGLY constants for the plugin use. */ + object IMGLYConstants { + const val K_ERROR_UNABLE_TO_UNLOCK = "E_UNABLE_TO_UNLOCK" + const val K_ERROR_UNABLE_TO_LOAD = "E_UNABLE_TO_LOAD" + } + private var currentPromise: Promise? = null private var currentConfig: Configuration? = null private var resolveManually: Boolean = false @@ -55,9 +62,15 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte private var settingsLists: MutableMap = mutableMapOf() @ReactMethod - fun unlockWithLicense(license: String) { - VESDK.initSDKWithLicenseData(license) - IMGLY.authorize() + fun unlockWithLicense(license: String, promise: Promise) { + try { + VESDK.initSDKWithLicenseData(license) + IMGLY.authorize() + promise.resolve(null) + } catch (e: AuthorizationException) { + promise.reject(IMGLYConstants.K_ERROR_UNABLE_TO_UNLOCK, "Unlocking the SDK failed due to: ${e.message}.") + } + } @ReactMethod @@ -212,7 +225,7 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte if (videoArray.isNotEmpty()) { if (source == null) { if (size != null) { - promise.reject("VESDK", "Invalid video size: width and height must be greater than zero.") + promise.reject(IMGLYConstants.K_ERROR_UNABLE_TO_LOAD, "Invalid video size: width and height must be greater than zero.") return } val video = videoArray.first() @@ -226,7 +239,7 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte } } else { if (source == null) { - promise.reject("VESDK", "The editor requires a valid size when initialized without a video.") + promise.reject(IMGLYConstants.K_ERROR_UNABLE_TO_LOAD, "The editor requires a valid size when initialized without a video.") return } } diff --git a/index.d.ts b/index.d.ts index 7338388..7e8af8d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -84,16 +84,6 @@ declare class VESDK { videoSize?: Size ): Promise; - /** - * Releases the result from the editor and deletes the temporary files. - * @note This function needs to be called in case `configuration.export.video.segments` - * is set to `true`. - * - * @param {string} identifier The identifier of the `VideoEditorResult` to release - * the temporary data of. - */ - static releaseTemporaryData(identifier: string): void; - /** * Unlock VideoEditor SDK with a license. * @@ -104,7 +94,7 @@ declare class VESDK { * and `vesdk_license.android.json` for the Android license file in order to get automatically * resolved by the packager. */ - static unlockWithLicense(license: string | object): void; + static unlockWithLicense(license: string | object): Promise; } /** diff --git a/index.js b/index.js index deffd3e..3b1187f 100644 --- a/index.js +++ b/index.js @@ -204,9 +204,9 @@ class VESDK { */ static unlockWithLicense(license) { if (Platform.OS == 'android') { - RNVideoEditorSDK.unlockWithLicense(JSON.stringify(license)); + return RNVideoEditorSDK.unlockWithLicense(JSON.stringify(license)); } else { - RNVideoEditorSDK.unlockWithLicense(license); + return RNVideoEditorSDK.unlockWithLicense(license); } } } diff --git a/ios/RNImglyKit.m b/ios/RNImglyKit.m index 5150cf4..114d46e 100644 --- a/ios/RNImglyKit.m +++ b/ios/RNImglyKit.m @@ -63,11 +63,6 @@ - (void)present:(nonnull IMGLYMediaEditViewControllerBlock)createMediaEditViewCo } dispatch_async(dispatch_get_main_queue(), ^{ - if (self.licenseError != nil) { - reject(RN_IMGLY.kErrorUnableToUnlock, [NSString RN_IMGLY_string:@"Unable to unlock with license." withError:self.licenseError], self.licenseError); - return; - } - PESDKAssetCatalog *assetCatalog = PESDKAssetCatalog.defaultItems; PESDKConfiguration *configuration = [[PESDKConfiguration alloc] initWithBuilder:^(PESDKConfigurationBuilder * _Nonnull builder) { builder.assetCatalog = assetCatalog; @@ -169,34 +164,24 @@ - (void)dismiss:(nullable PESDKMediaEditViewController *)mediaEditViewController }); } -- (void)handleLicenseError:(nullable NSError *)error +- (void)handleLicenseError:(nullable NSError *)error resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject { - self.licenseError = nil; if (error != nil) { - if ([error.domain isEqualToString:@"ImglyKit.IMGLY.Error"]) { - switch (error.code) { - case 3: - RCTLogWarn(@"%@: %@", NSStringFromClass(self.class), error.localizedDescription); - break; - default: - self.licenseError = error; - RCTLogError(@"%@: %@", NSStringFromClass(self.class), error.localizedDescription); - break; - } - } else { - self.licenseError = error; - RCTLogError(@"Error while unlocking with license: %@", error); - } + reject(RN_IMGLY.kErrorUnableToUnlock, [NSString RN_IMGLY_string:@"Unable to unlock with license." withError:error], error); + return; + } else { + resolve(nil); + return; } } -- (void)unlockWithLicenseURL:(nonnull NSURL *)url {} +- (void)unlockWithLicenseURL:(nonnull NSURL *)url resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {} -- (void)unlockWithLicenseString:(nonnull NSString *)string {} +- (void)unlockWithLicenseString:(nonnull NSString *)string resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {} -- (void)unlockWithLicenseObject:(nonnull NSDictionary *)dictionary {} +- (void)unlockWithLicenseObject:(nonnull NSDictionary *)dictionary resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {} -- (void)unlockWithLicense:(nonnull id)json +- (void)unlockWithLicense:(nonnull id)json resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject { NSString *string = nil; NSURL *url = nil; @@ -222,14 +207,14 @@ - (void)unlockWithLicense:(nonnull id)json } if (url != nil) { - [self unlockWithLicenseURL:url]; + [self unlockWithLicenseURL:url resolve:resolve reject:reject]; } else if (string != nil) { - [self unlockWithLicenseString:string]; + [self unlockWithLicenseString:string resolve:resolve reject:reject]; } else if ([json isKindOfClass:[NSDictionary class]]) { NSDictionary *dictionary = json; - [self unlockWithLicenseObject:dictionary]; + [self unlockWithLicenseObject:dictionary resolve:resolve reject:reject]; } else if (json) { RCTLogConvertError(json, @"a valid license format"); diff --git a/ios/RNImglyKitSubclass.h b/ios/RNImglyKitSubclass.h index 1007a7f..e42f7e0 100644 --- a/ios/RNImglyKitSubclass.h +++ b/ios/RNImglyKitSubclass.h @@ -50,7 +50,6 @@ typedef void (^IMGLYCompletionBlock)(void); @property (class, strong, atomic, nullable) IMGLYConfigurationBlock configureWithBuilder; -@property (strong, atomic, nullable) NSError* licenseError; @property (strong, atomic, nullable) NSString* exportType; @property (strong, atomic, nullable) NSURL* exportFile; @property (atomic) BOOL serializationEnabled; @@ -68,11 +67,11 @@ typedef void (^IMGLYCompletionBlock)(void); resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject; - (void)dismiss:(nullable PESDKMediaEditViewController *)mediaEditViewController animated:(BOOL)animated completion:(nullable IMGLYCompletionBlock)completion; -- (void)handleLicenseError:(nullable NSError *)error; -- (void)unlockWithLicenseURL:(nonnull NSURL *)url; -- (void)unlockWithLicenseString:(nonnull NSString *)string; -- (void)unlockWithLicenseObject:(nonnull NSDictionary *)dictionary; -- (void)unlockWithLicense:(nonnull id)json; +- (void)handleLicenseError:(nullable NSError *)error resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject; +- (void)unlockWithLicenseURL:(nonnull NSURL *)url resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject; +- (void)unlockWithLicenseString:(nonnull NSString *)string resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject; +- (void)unlockWithLicenseObject:(nonnull NSDictionary *)dictionary resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject; +- (void)unlockWithLicense:(nonnull id)json resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject; extern const struct RN_IMGLY_Constants { diff --git a/ios/RNVideoEditorSDK.m b/ios/RNVideoEditorSDK.m index 6a2003b..6e96763 100644 --- a/ios/RNVideoEditorSDK.m +++ b/ios/RNVideoEditorSDK.m @@ -65,36 +65,36 @@ - (void)present:(nonnull PESDKVideo *)video withConfiguration:(nullable NSDictio } configuration:dictionary serialization:state resolve:resolve reject:reject]; } -RCT_EXPORT_METHOD(unlockWithLicenseURL:(nonnull NSURL *)url) +RCT_EXPORT_METHOD(unlockWithLicenseURL:(nonnull NSURL *)url resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_main_queue(), ^{ NSError *error = nil; [VESDK unlockWithLicenseFromURL:url error:&error]; - [self handleLicenseError:error]; + [self handleLicenseError:error resolve:resolve reject:reject]; }); } -RCT_EXPORT_METHOD(unlockWithLicenseString:(nonnull NSString *)string) +RCT_EXPORT_METHOD(unlockWithLicenseString:(nonnull NSString *)string resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_main_queue(), ^{ NSError *error = nil; [VESDK unlockWithLicenseFromString:string error:&error]; - [self handleLicenseError:error]; + [self handleLicenseError:error resolve:resolve reject:reject]; }); } -RCT_EXPORT_METHOD(unlockWithLicenseObject:(nonnull NSDictionary *)dictionary) +RCT_EXPORT_METHOD(unlockWithLicenseObject:(nonnull NSDictionary *)dictionary resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject) { dispatch_async(dispatch_get_main_queue(), ^{ NSError *error = nil; [VESDK unlockWithLicenseFromDictionary:dictionary error:&error]; - [self handleLicenseError:error]; + [self handleLicenseError:error resolve:resolve reject:reject]; }); } -RCT_EXPORT_METHOD(unlockWithLicense:(nonnull id)json) +RCT_EXPORT_METHOD(unlockWithLicense:(nonnull id)json resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject) { - [super unlockWithLicense:json]; + [super unlockWithLicense:json resolve:resolve reject:reject]; } RCT_EXPORT_METHOD(present:(nonnull NSURLRequest *)request diff --git a/package.json b/package.json index a659e8e..fb67d3b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "2.17.1", + "version": "3.0.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "2.17.1" + "react-native-imglysdk": "3.0.0" } } From e158930cfe5b493965227795551c22b5ada91bc0 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Thu, 30 Nov 2023 22:46:51 +0100 Subject: [PATCH 10/13] Add version 3.1.0 --- CHANGELOG.md | 6 ++++++ RNVideoEditorSDK.podspec | 2 +- ios/RNImglyKit.h | 2 +- ios/RNImglyKitSubclass.h | 3 ++- ios/RNVideoEditorSDK.h | 2 +- package.json | 4 ++-- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 542b66a..6d4d827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [3.1.0] + +### Added + +* Added support for Expo 49. (#1811) + ## [3.0.0] ### Changed diff --git a/RNVideoEditorSDK.podspec b/RNVideoEditorSDK.podspec index 6359197..17ae17a 100644 --- a/RNVideoEditorSDK.podspec +++ b/RNVideoEditorSDK.podspec @@ -18,5 +18,5 @@ Pod::Spec.new do |s| s.dependency 'React' s.dependency 'React-RCTImage' - s.dependency 'VideoEditorSDK', '~> 11.4' + s.dependency 'VideoEditorSDK', '~> 11.8' end diff --git a/ios/RNImglyKit.h b/ios/RNImglyKit.h index d8f9cc7..68e8d6c 100644 --- a/ios/RNImglyKit.h +++ b/ios/RNImglyKit.h @@ -1,4 +1,4 @@ -@import Foundation; +#import @interface RNVESDKImglyKit : NSObject diff --git a/ios/RNImglyKitSubclass.h b/ios/RNImglyKitSubclass.h index e42f7e0..a85f07e 100644 --- a/ios/RNImglyKitSubclass.h +++ b/ios/RNImglyKitSubclass.h @@ -39,7 +39,8 @@ #define RN_IMGLY_dictionary_HELPER(prefix) RN_IMGLY_CONCATENATE(prefix, _dictionary) #define RN_IMGLY_dictionary RN_IMGLY_dictionary_HELPER(RN_IMGLY) -@import ImglyKit; +#import +#import @interface RN_IMGLY_ImglyKit () diff --git a/ios/RNVideoEditorSDK.h b/ios/RNVideoEditorSDK.h index 45a5c1c..bf57cc8 100644 --- a/ios/RNVideoEditorSDK.h +++ b/ios/RNVideoEditorSDK.h @@ -1,7 +1,7 @@ #import #import "RNImglyKit.h" -@import VideoEditorSDK; +#import /// The React Native module for VideoEditor SDK @interface RNVideoEditorSDK : RNVESDKImglyKit diff --git a/package.json b/package.json index fb67d3b..44210bd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "3.0.0", + "version": "3.1.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "3.0.0" + "react-native-imglysdk": "3.1.0" } } From 2fd43564ad48b24fd2374045110aa2db2f5c73f0 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Wed, 3 Apr 2024 09:52:38 +0200 Subject: [PATCH 11/13] Add version 3.2.0 --- CHANGELOG.md | 23 ++++++++++- LICENSE.md | 4 +- README.md | 62 ++++++++++------------------ RNVideoEditorSDK.podspec | 2 +- android/build.gradle | 11 ++--- android/src/main/AndroidManifest.xml | 4 +- index.d.ts | 2 +- package.json | 6 +-- 8 files changed, 60 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d4d827..6f1ddd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +## [3.2.0] + +### Changed + +* [react-native-imglysdk] Updated `@expo/config-plugins` dependency to `7.2`. +* [react-native-photoeditorsdk] Raised minimum PhotoEditor SDK for iOS version to 11.9.0. +* [react-native-photoeditorsdk] Raised minimum PhotoEditor SDK for Android version to 10.9.0. See the [migration guide](https://img.ly/docs/pesdk/react-native/getting-started/migration-guides/3-2-0/) for more information. +* [react-native-videoeditorsdk] Raised minimum VideoEditor SDK for iOS version to 11.9.0. +* [react-native-videoeditorsdk] Raised minimum VideoEditor SDK for Android version to 10.9.0. See the [migration guide](https://img.ly/docs/vesdk/react-native/getting-started/migration-guides/3-2-0/) for more information. + +### Added + +* [react-native-imglysdk] Added `kspVersion` parameter. +* [react-native-videoeditorsdk] Added `VideoEditorResult` to types export. +* [react-native-photoeditorsdk] Added `PhotoEditorResult` to types export. + +### Fixed + +* Fixed compiling issues when using native customizations on iOS. +* Fixed potential crash on Android: `IllegalStateException "You need to use a Theme.AppCompat theme (or descendant) with this activity."`. + ## [3.1.0] ### Added @@ -117,7 +138,7 @@ ### Changed -* 🚨 The img.ly maven repository is no longer automatically added to your project by the plugin for Android. Please refer to the new step 3 in the [getting started](https://github.com/imgly/vesdk-react-native#android) section of the README for instructions on how to add it. +* 🚨 The IMG.LY maven repository is no longer automatically added to your project by the plugin for Android. Please refer to the new step 3 in the [getting started](https://github.com/imgly/vesdk-react-native#android) section of the README for instructions on how to add it. * [react-native-videoeditorsdk] Added support for VideoEditor SDK for Android version 9. * [react-native-photoeditorsdk] Added support for PhotoEditor SDK for Android version 9. diff --git a/LICENSE.md b/LICENSE.md index e6acf57..8e7cc2c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -4,7 +4,7 @@ In order to run any samples or use any wrapper without a watermark, you'll have to purchase a commercial PhotoEditor SDK or VideoEditor SDK license. Visit https://img.ly for more details. -Copyright (c) 2014-2023, img.ly GmbH +Copyright (c) 2014-2024, IMG.LY GmbH All rights reserved. Redistribution and use in source and binary forms, with or without @@ -17,7 +17,7 @@ modification, are permitted provided that the following conditions are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name img.ly GmbH, img.ly, PhotoEditor SDK, VideoEditor SDK +3. Neither the name IMG.LY GmbH, IMG.LY, PhotoEditor SDK, VideoEditor SDK nor the names of its developers may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/README.md b/README.md index f942ce1..e9c1fa7 100644 --- a/README.md +++ b/README.md @@ -27,30 +27,6 @@ Check out our [video tutorial](https://img.ly/blog/a-photo-and-video-editor-for- ## Getting started -### Known Issues - -With version `2.13.0`, we recommend using `compileSdkVersion` not lower than `31` for Android. However, this might interfere with your application's Android Gradle Plugin version if this is set to `4.x`. - -If you don't use a newer Android Gradle Plugin version, e.g., by updating at least to RN 0.68.0, you'll most likely encounter a build error similar to: - -``` -FAILURE: Build failed with an exception. - -* What went wrong: -A problem occurred configuring project ':react-native-videoeditorsdk'. -> com.android.builder.errors.EvalIssueException: Installed Build Tools revision 31.0.0 is corrupted. Remove and install again using the SDK Manager. - -* Try: -Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. - -* Get more help at https://help.gradle.org -``` - -As a workaround you can create the following symlinks: - -1. Inside `/Users/YOUR-USERNAME/Library/Android/sdk/build-tools/31.0.0/`: Create a `dx` symlink for the `d8` file with `ln -s d8 dx`. -2. From there, go to `./lib/` and create a `dx.jar` symlink for the `d8.jar` file with `ln -s d8.jar dx.jar`. - ### Expo CLI #### Limitations @@ -77,7 +53,9 @@ In order to use this module with the Expo CLI you can make use of our integrated } ``` - If needed, you can also use a specific version of our native library for Android as well as define explicitly the included modules. By default, all modules for both PhotoEditor SDK and VideoEditor SDK are included. Furthermore, you can configure the `buildToolsVersion`, `minSdkVersion`, `compileSdkVersion`, `targetSdkVersion`, and `kotlinGradlePluginVersion`. + If needed, you can also use a specific version of our native library for Android as well as define explicitly the included modules. By default, all modules for both PhotoEditor SDK and VideoEditor SDK are included. Further, you can alternate the KSP version with the `kspVersion` parameter based on the Kotlin version you are using. Please take a look [here](#android) on further details. + + For Expo version < 45, you can configure the `buildToolsVersion`, `minSdkVersion`, `compileSdkVersion`, `targetSdkVersion`, and `kotlinGradlePluginVersion`. From version 45+ we recommend setting these properties using the `expo-build-properties` config plugin directly. ```json { @@ -86,18 +64,19 @@ In order to use this module with the Expo CLI you can make use of our integrated "react-native-imglysdk", { "android": { - "version": "10.4.1", + "version": "10.9.0", + "kspVersion": "1.8.0-1.0.9", "modules": [ "ui:core", "ui:transform", "ui:filter", "assets:filter-basic" ], - "buildToolsVersion": "31.0.0", + "buildToolsVersion": "34.0.0", "minSdkVersion": "21", - "compileSdkVersion": "31", - "targetSdkVersion": "30", - "kotlinGradlePluginVersion": "1.5.32" + "compileSdkVersion": "34", + "targetSdkVersion": "34", + "kotlinGradlePluginVersion": "1.8.0" } } ] @@ -151,7 +130,7 @@ For older React Native versions autolinking is not available and VideoEditor SDK #### Android -1. Add the img.ly repository and plugin by opening the `android/build.gradle` file (**not** `android/app/build.gradle`) and adding these lines at the top: +1. Add the IMG.LY repository and plugin by opening the `android/build.gradle` file (**not** `android/app/build.gradle`) and adding these lines at the top: ```groovy buildscript { @@ -160,13 +139,16 @@ For older React Native versions autolinking is not available and VideoEditor SDK maven { url "https://artifactory.img.ly/artifactory/imgly" } } dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.32" - classpath 'ly.img.android.sdk:plugin:10.4.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0" + classpath 'com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:1.8.0-1.0.9' // KSP version is depending on your Kotlin version. + classpath 'ly.img.android.sdk:plugin:10.9.0' } } ``` - In order to update VideoEditor SDK for Android replace the version string `10.4.1` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). + The KSP version depends on the Kotlin version that you are using. In order to find the correct version, please visit the [official KSP release page](https://github.com/google/ksp/releases?page=1). + + In order to update VideoEditor SDK for Android replace the version string `10.9.0` with a [newer release](https://github.com/imgly/vesdk-android-demo/releases). 2. Still in the `android/build.gradle` file (**not** `android/app/build.gradle`), add these lines at the bottom: @@ -178,18 +160,18 @@ For older React Native versions autolinking is not available and VideoEditor SDK } ``` -3. In the same file, you will need to modify the `minSdkVersion` to at least `21`. We also recommend to update the `buildToolsVersion` to `31.0.0` or higher as well as the `compileSdkVersion` to `31` or higher: +3. In the same file, you will need to modify the `minSdkVersion` to at least `21`. We also recommend to update the `buildToolsVersion` to `34.0.0` or higher as well as the `compileSdkVersion` to `34` or higher but this is not mandatory: ```diff buildscript { ext { - buildToolsVersion = "30.0.2" - + buildToolsVersion = "31.0.0" + + buildToolsVersion = "34.0.0" - minSdkVersion = 19 + minSdkVersion = 21 - - compileSdkVersion = 30 - + compileSdkVersion = 31 - targetSdkVersion = 30 + - compileSdkVersion = 34 + + compileSdkVersion = 34 + targetSdkVersion = 34 } } ``` @@ -201,7 +183,7 @@ For older React Native versions autolinking is not available and VideoEditor SDK apply plugin: 'kotlin-android' // Comment out the modules you don't need, to save size. - imglyConfig { + IMGLY.configure { modules { include 'ui:text' include 'ui:focus' diff --git a/RNVideoEditorSDK.podspec b/RNVideoEditorSDK.podspec index 17ae17a..e6f1a3c 100644 --- a/RNVideoEditorSDK.podspec +++ b/RNVideoEditorSDK.podspec @@ -18,5 +18,5 @@ Pod::Spec.new do |s| s.dependency 'React' s.dependency 'React-RCTImage' - s.dependency 'VideoEditorSDK', '~> 11.8' + s.dependency 'VideoEditorSDK', '~> 11.9' end diff --git a/android/build.gradle b/android/build.gradle index a3b5802..67f8e1b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,8 @@ apply plugin: 'com.android.library' // Apply the ImgLy-Plugin apply plugin: 'ly.img.android.sdk' apply plugin: 'kotlin-android' -imglyConfig { + +IMGLY.configure { vesdk { enabled true } @@ -20,7 +21,7 @@ imglyConfig { } } -def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "10.4.1" +def MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION = "10.9.0" task checkVersion { if (imglyConfig.convertToVersionNumber(imglyConfig.getVersion()) < imglyConfig.convertToVersionNumber(MIN_LY_IMG_ANDROID_SDK_PLUGIN_VERSION)) { @@ -39,12 +40,12 @@ task checkVersion { preBuild.dependsOn checkVersion android { - compileSdkVersion safeExtGet('compileSdkVersion', 31) - buildToolsVersion safeExtGet('buildToolsVersion', '31.0.0') + compileSdkVersion safeExtGet('compileSdkVersion', 34) + buildToolsVersion safeExtGet('buildToolsVersion', '34.0.0') defaultConfig { minSdkVersion safeExtGet('minSdkVersion', 21) - targetSdkVersion safeExtGet('targetSdkVersion', 30) + targetSdkVersion safeExtGet('targetSdkVersion', 34) versionCode 1 versionName "1.0" } diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index e4eef02..d43ec93 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -8,7 +8,9 @@ android:exported="false" android:enabled="false" android:name="ly.img.android.IMGLYAutoInit" /> - + \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index 7e8af8d..134f226 100644 --- a/index.d.ts +++ b/index.d.ts @@ -173,5 +173,5 @@ declare class VideoEditorModal extends Component< > {} export * from "./configuration"; -export { VESDK, VideoEditorModal, VideoSegment }; +export { VESDK, VideoEditorModal, VideoEditorResult, VideoSegment }; diff --git a/package.json b/package.json index 44210bd..f50da47 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "3.1.0", + "version": "3.2.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -25,7 +25,7 @@ "android" ], "author": { - "name": "img.ly GmbH", + "name": "IMG.LY GmbH", "email": "contact@img.ly" }, "license": "BSD-3-Clause", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "3.1.0" + "react-native-imglysdk": "3.2.0" } } From e953310363134e637fb9481659c86ad9086d0c71 Mon Sep 17 00:00:00 2001 From: Leon Dudlik Date: Mon, 6 Jan 2025 14:18:37 +0100 Subject: [PATCH 12/13] Add version 3.3.0 --- CHANGELOG.md | 10 ++++++++++ index.js | 6 +----- package.json | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f1ddd0..ca3f70c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [3.3.0] + +### Changed + +* [react-native-imglysdk] Removed `@expo/config-plugins` dependency. + +### Fixed + +* Fixed potential crash from `unlockWithLicense` if new architecture is enabled. + ## [3.2.0] ### Changed diff --git a/index.js b/index.js index 3b1187f..718096b 100644 --- a/index.js +++ b/index.js @@ -203,11 +203,7 @@ class VESDK { * resolved by the packager. */ static unlockWithLicense(license) { - if (Platform.OS == 'android') { - return RNVideoEditorSDK.unlockWithLicense(JSON.stringify(license)); - } else { - return RNVideoEditorSDK.unlockWithLicense(license); - } + return RNVideoEditorSDK.unlockWithLicense(JSON.stringify(license)); } } diff --git a/package.json b/package.json index f50da47..1e4abfa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "3.2.0", + "version": "3.3.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "3.2.0" + "react-native-imglysdk": "3.3.0" } } From 1928ba546a4777afab0486c08dc1e48e0b822de1 Mon Sep 17 00:00:00 2001 From: Jonas Hartwig Date: Mon, 8 Sep 2025 13:10:24 +0200 Subject: [PATCH 13/13] Add version 3.4.0 --- CHANGELOG.md | 10 +++++++++ README.md | 22 +++++++++---------- android/build.gradle | 6 ++--- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../vesdk/RNVideoEditorSDKModule.kt | 13 ++++++++--- package.json | 4 ++-- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca3f70c..2bbb40b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [3.4.0] + +### Changed + +* [react-native-photoeditorsdk] Raised minimum PhotoEditor Build SDK for Android version to 35 +* [react-native-photoeditorsdk] Raised Kotlin version to 2.1 and KSP to 2.1.0-1.0.28 +* [react-native-videoeditorsdk] Raised minimum PhotoEditor Build SDK for Android version to 35 +* [react-native-videoeditorsdk] Raised Kotlin version to 2.1 and KSP to 2.1.0-1.0.28 + + ## [3.3.0] ### Changed diff --git a/README.md b/README.md index e9c1fa7..35527f5 100644 --- a/README.md +++ b/README.md @@ -72,11 +72,11 @@ In order to use this module with the Expo CLI you can make use of our integrated "ui:filter", "assets:filter-basic" ], - "buildToolsVersion": "34.0.0", + "buildToolsVersion": "35.0.0", "minSdkVersion": "21", - "compileSdkVersion": "34", - "targetSdkVersion": "34", - "kotlinGradlePluginVersion": "1.8.0" + "compileSdkVersion": "35", + "targetSdkVersion": "35", + "kotlinGradlePluginVersion": "2.1.0" } } ] @@ -139,8 +139,8 @@ For older React Native versions autolinking is not available and VideoEditor SDK maven { url "https://artifactory.img.ly/artifactory/imgly" } } dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0" - classpath 'com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:1.8.0-1.0.9' // KSP version is depending on your Kotlin version. + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.0" + classpath 'com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.1.0-1.0.28' // KSP version is depending on your Kotlin version. classpath 'ly.img.android.sdk:plugin:10.9.0' } } @@ -160,18 +160,18 @@ For older React Native versions autolinking is not available and VideoEditor SDK } ``` -3. In the same file, you will need to modify the `minSdkVersion` to at least `21`. We also recommend to update the `buildToolsVersion` to `34.0.0` or higher as well as the `compileSdkVersion` to `34` or higher but this is not mandatory: +3. In the same file, you will need to modify the `minSdkVersion` to at least `21`. We also recommend to update the `buildToolsVersion` to `35.0.0` or higher as well as the `compileSdkVersion` to `35` or higher but this is not mandatory: ```diff buildscript { ext { - buildToolsVersion = "30.0.2" - + buildToolsVersion = "34.0.0" + + buildToolsVersion = "35.0.0" - minSdkVersion = 19 + minSdkVersion = 21 - - compileSdkVersion = 34 - + compileSdkVersion = 34 - targetSdkVersion = 34 + - compileSdkVersion = 30 + + compileSdkVersion = 35 + targetSdkVersion = 35 } } ``` diff --git a/android/build.gradle b/android/build.gradle index 67f8e1b..5987786 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -40,12 +40,12 @@ task checkVersion { preBuild.dependsOn checkVersion android { - compileSdkVersion safeExtGet('compileSdkVersion', 34) - buildToolsVersion safeExtGet('buildToolsVersion', '34.0.0') + compileSdkVersion safeExtGet('compileSdkVersion', 35) + buildToolsVersion safeExtGet('buildToolsVersion', '35.0.0') defaultConfig { minSdkVersion safeExtGet('minSdkVersion', 21) - targetSdkVersion safeExtGet('targetSdkVersion', 34) + targetSdkVersion safeExtGet('targetSdkVersion', 35) versionCode 1 versionName "1.0" } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 361d45c..6105f47 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip diff --git a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt index 675b6bb..d8ab7ee 100644 --- a/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt +++ b/android/src/main/java/ly/img/react_native/vesdk/RNVideoEditorSDKModule.kt @@ -182,12 +182,14 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte } } - override fun onNewIntent(intent: Intent?) { + override fun onNewIntent(intent: Intent) { } @ReactMethod fun present(video: String, config: ReadableMap?, serialization: String?, promise: Promise) { - val configuration = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()) + val rawConfig: Map = config?.toHashMap() ?: emptyMap() + val cleanConfig: Map = rawConfig.filterValues { it != null }.mapValues { it.value as Any } + val configuration = ConfigLoader.readFrom(cleanConfig) val serializationEnabled = configuration.export?.serialization?.enabled == true val exportVideoSegments = configuration.export?.video?.segments == true val createTemporaryFiles = serializationEnabled || exportVideoSegments @@ -211,7 +213,12 @@ class RNVideoEditorSDKModule(reactContext: ReactApplicationContext) : ReactConte val videoArray = deserializeVideoParts(videos) var source = resolveSize(size) - val configuration = ConfigLoader.readFrom(config?.toHashMap() ?: mapOf()) + val rawConfig: Map = config?.toHashMap() ?: emptyMap() + val cleanConfig: MutableMap = mutableMapOf() + for ((key, value) in rawConfig) { + if (value != null) cleanConfig[key] = value + } + val configuration = ConfigLoader.readFrom(cleanConfig) val serializationEnabled = configuration.export?.serialization?.enabled == true val exportVideoSegments = configuration.export?.video?.segments == true val createTemporaryFiles = serializationEnabled || exportVideoSegments diff --git a/package.json b/package.json index 1e4abfa..827f0b3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-videoeditorsdk", "title": "React Native module for VideoEditor SDK", - "version": "3.3.0", + "version": "3.4.0", "description": "A React Native module for VideoEditor SDK. Integrate the video editor into your own HTML5, iOS or Android app - in minutes!", "main": "index.js", "typings": "index.d.ts", @@ -35,6 +35,6 @@ "react-native": ">=0.60.0 <1.0.x" }, "dependencies": { - "react-native-imglysdk": "3.3.0" + "react-native-imglysdk": "3.4.0" } }