From ed93a901a4698a184c51321a9c89a020b4cb96ca Mon Sep 17 00:00:00 2001 From: HermanKhodyrevBanubaTechSupport Date: Fri, 18 Oct 2024 11:43:51 +0300 Subject: [PATCH 1/2] Export Background Example --- App.js | 2 + .../EmptyExportNotificationManger.kt | 56 +++++++++++++++++++ .../SdkEditorModule.kt | 13 ++++- .../VideoEditorIntegrationModule.kt | 22 ++++++++ ios/SdkEditorModule.swift | 22 +------- 5 files changed, 94 insertions(+), 21 deletions(-) create mode 100644 android/app/src/main/java/com/vesdkreactnativecliintegrationsample/EmptyExportNotificationManger.kt diff --git a/App.js b/App.js index 53dded8..d55001c 100644 --- a/App.js +++ b/App.js @@ -75,6 +75,8 @@ export default class App extends Component { message = "Host Activity or ViewController does not exist!"; case 'ERR_VIDEO_EXPORT_CANCEL': message = "Video export is canceled"; + case 'ERR_FAILED_BACKGROUND_EXPORT': + message = "Failed to export video in the background"; default: message = ''; console.log( diff --git a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/EmptyExportNotificationManger.kt b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/EmptyExportNotificationManger.kt new file mode 100644 index 0000000..408f53c --- /dev/null +++ b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/EmptyExportNotificationManger.kt @@ -0,0 +1,56 @@ +package com.vesdkreactnativecliintegrationsample + +import com.banuba.sdk.export.data.ExportNotificationManager +import com.banuba.sdk.export.data.ExportResult +import com.facebook.react.bridge.* +import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.WritableMap +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.ReactApplicationContext +import android.util.Log + + +class EmptyExportNotificationManger() : ExportNotificationManager { + + companion object { + const val TAG = "ExportNotificationManger" + + // Errors + private const val ERR_FAILED_BACKGROUND_EXPORT = "ERR_FAILED_BACKGROUND_EXPORT" + } + + private var isExportInProgress = false + + fun isExportInProgress(): Boolean { + return isExportInProgress + } + + private var resultPromise: Promise? = null + + fun setResultPromise(promise: Promise?) { + resultPromise = promise + } + + override fun showExportStartedNotification(){ + isExportInProgress = true + Log.d(TAG, "Starting background export") + } + override fun showSuccessfulExportNotification(result: ExportResult.Success){ + isExportInProgress = false + val videoUri = result.videoList.firstOrNull()?.sourceUri + val previewUri = result.preview + if (videoUri == null) { + resultPromise?.reject("ERR_MISSING_EXPORT_RESULT", "") + } else { + val arguments: WritableMap = Arguments.createMap() + arguments.putString("videoUri", videoUri.toString()) + arguments.putString("previewUri", previewUri.toString()) + resultPromise?.resolve(arguments) + } + } + override fun showFailedExportExportNotification(){ + isExportInProgress = false + Log.d(TAG, "Failed background export") + resultPromise?.reject(ERR_FAILED_BACKGROUND_EXPORT, "") + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/SdkEditorModule.kt b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/SdkEditorModule.kt index 59f783c..bb5d72d 100644 --- a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/SdkEditorModule.kt +++ b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/SdkEditorModule.kt @@ -26,6 +26,7 @@ import com.banuba.sdk.core.EditorUtilityManager import com.banuba.sdk.ve.ext.VideoEditorUtils.getKoin import org.koin.core.context.stopKoin import org.koin.core.error.InstanceCreationException +import com.banuba.sdk.export.data.ExportNotificationManager class SdkEditorModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { @@ -82,7 +83,17 @@ class SdkEditorModule(reactContext: ReactApplicationContext) : ReactContextBaseJ } } - resultCode == Activity.RESULT_CANCELED -> resultPromise?.reject("ERR_VIDEO_EXPORT_CANCEL", "") + resultCode == Activity.RESULT_CANCELED -> { + val exportNotificationManager = getKoin().get() as? EmptyExportNotificationManger + + if (exportNotificationManager != null) { + if (exportNotificationManager.isExportInProgress()) { + exportNotificationManager.setResultPromise(resultPromise) + } else { + resultPromise?.reject("ERR_VIDEO_EXPORT_CANCEL", "") + } + } + } } resultPromise = null } else if (requestCode == OPEN_PHOTO_EDITOR_REQUEST_CODE) { diff --git a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/VideoEditorIntegrationModule.kt b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/VideoEditorIntegrationModule.kt index a760324..dd5e6cb 100644 --- a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/VideoEditorIntegrationModule.kt +++ b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/VideoEditorIntegrationModule.kt @@ -22,6 +22,10 @@ import org.koin.core.context.startKoin import org.koin.core.qualifier.named import org.koin.dsl.module +import com.banuba.sdk.export.data.BackgroundExportFlowManager +import com.banuba.sdk.export.data.ExportFlowManager +import com.banuba.sdk.export.data.ExportNotificationManager + class VideoEditorIntegrationModule { companion object { @@ -82,6 +86,24 @@ private class SampleModule { AudioBrowserMusicProvider() } } + + single { + BackgroundExportFlowManager( + exportDataProvider = get(), + exportSessionHelper = get(), + exportNotificationManager = get(), + exportDir = get(named("exportDir")), + shouldClearSessionOnFinish = true, + publishManager = get(), + errorParser = get(), + exportBundleProvider = get(), + eventConverter = get() + ) + } + + single { + EmptyExportNotificationManger() + } } } diff --git a/ios/SdkEditorModule.swift b/ios/SdkEditorModule.swift index fe8c755..823c8c7 100644 --- a/ios/SdkEditorModule.swift +++ b/ios/SdkEditorModule.swift @@ -393,13 +393,6 @@ class SdkEditorModule: NSObject, RCTBridgeModule { extension SdkEditorModule { func exportVideo() { guard let videoEditorSDK else { return } - let progressViewController = createProgressViewController() - progressViewController.cancelHandler = { videoEditorSDK.stopExport() } - guard let presentedVC = RCTPresentedViewController() else { - return - } - presentedVC.present(progressViewController, animated: true) - let manager = FileManager.default // File name let firstFileURL = manager.temporaryDirectory.appendingPathComponent("tmp1.mov") @@ -427,18 +420,13 @@ extension SdkEditorModule { // Export func videoEditorSDK.export( using: exportConfiguration, - exportProgress: { [weak progressViewController] progress in - DispatchQueue.main.async { - progressViewController?.updateProgressView(with: Float(progress)) - } - } + exportProgress: nil ) { [weak self] (error, previewImageInfo) in let success = error == nil // Export Callback DispatchQueue.main.async { if success { // Result urls. You could interact with your own implementation. - progressViewController.dismiss(animated: true) let previewImageData = previewImageInfo?.coverImage?.pngData() let previewImageUrl = FileManager.default.temporaryDirectory.appendingPathComponent("\(UUID().uuidString).png") try? previewImageData?.write(to: previewImageUrl) @@ -457,7 +445,7 @@ extension SdkEditorModule { NOT REQUIRED FOR INTEGRATION Added for playing exported video file. */ - self?.demoPlayExportedVideo(videoURL: firstFileURL) +// self?.demoPlayExportedVideo(videoURL: firstFileURL) } else { self?.currentReject?("ERR_MISSING_EXPORT_RESULT", error?.errorMessage, nil) // clear video editor session data and remove strong reference to video editor sdk instance @@ -470,12 +458,6 @@ extension SdkEditorModule { } } } - - func createProgressViewController() -> ProgressViewController { - let progressViewController = ProgressViewController.makeViewController() - progressViewController.message = "Exporting" - return progressViewController - } } // MARK: - BanubaVideoEditorSDKDelegate From 897d6014f775585226e8e6c0d4fa1c3c9c36fd95 Mon Sep 17 00:00:00 2001 From: HermanKhodyrevBanubaTechSupport Date: Fri, 18 Oct 2024 16:49:15 +0300 Subject: [PATCH 2/2] Add Alert after export --- App.js | 23 +++++++++++++++---- ... => BackgroundExportNotificationManger.kt} | 7 ++++-- .../SdkEditorModule.kt | 13 +++++------ .../VideoEditorIntegrationModule.kt | 2 +- 4 files changed, 30 insertions(+), 15 deletions(-) rename android/app/src/main/java/com/vesdkreactnativecliintegrationsample/{EmptyExportNotificationManger.kt => BackgroundExportNotificationManger.kt} (88%) diff --git a/App.js b/App.js index d55001c..c84451d 100644 --- a/App.js +++ b/App.js @@ -6,7 +6,8 @@ import { View, Platform, NativeModules, - TouchableOpacity + TouchableOpacity, + Alert } from 'react-native'; const {SdkEditorModule} = NativeModules; @@ -53,9 +54,20 @@ export default class App extends Component { }; } + showBackgroundExportResult(message) { + Alert.alert( + 'Background export result', + message, + [{ text: 'OK' }] + ); + } + handleVideoExport(response) { - console.log('Export completed successfully: video = ' + response?.videoUri + '; videoPreview = ' - + response?.previewUri); + const message = `Export completed successfully: video = ${response?.videoUri}; videoPreview = ${response?.previewUri}`; + + console.log(message); + + this.showBackgroundExportResult(message) } handleSdkError(e) { @@ -73,10 +85,11 @@ export default class App extends Component { message = 'Missing video export result!'; case 'ERR_CODE_NO_HOST_CONTROLLER': message = "Host Activity or ViewController does not exist!"; - case 'ERR_VIDEO_EXPORT_CANCEL': - message = "Video export is canceled"; case 'ERR_FAILED_BACKGROUND_EXPORT': message = "Failed to export video in the background"; + this.showBackgroundExportResult(message); + case 'ERR_VIDEO_EXPORT_CANCEL': + message = "Video export is canceled"; default: message = ''; console.log( diff --git a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/EmptyExportNotificationManger.kt b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/BackgroundExportNotificationManger.kt similarity index 88% rename from android/app/src/main/java/com/vesdkreactnativecliintegrationsample/EmptyExportNotificationManger.kt rename to android/app/src/main/java/com/vesdkreactnativecliintegrationsample/BackgroundExportNotificationManger.kt index 408f53c..964e212 100644 --- a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/EmptyExportNotificationManger.kt +++ b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/BackgroundExportNotificationManger.kt @@ -10,7 +10,7 @@ import com.facebook.react.bridge.ReactApplicationContext import android.util.Log -class EmptyExportNotificationManger() : ExportNotificationManager { +class BackgroundExportNotificationManger() : ExportNotificationManager { companion object { const val TAG = "ExportNotificationManger" @@ -29,6 +29,9 @@ class EmptyExportNotificationManger() : ExportNotificationManager { fun setResultPromise(promise: Promise?) { resultPromise = promise + if (!isExportInProgress){ + resultPromise?.reject("ERR_VIDEO_EXPORT_CANCEL", "") + } } override fun showExportStartedNotification(){ @@ -50,7 +53,7 @@ class EmptyExportNotificationManger() : ExportNotificationManager { } override fun showFailedExportExportNotification(){ isExportInProgress = false - Log.d(TAG, "Failed background export") + Log.w(TAG, "Failed background export") resultPromise?.reject(ERR_FAILED_BACKGROUND_EXPORT, "") } } \ No newline at end of file diff --git a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/SdkEditorModule.kt b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/SdkEditorModule.kt index bb5d72d..8d55acd 100644 --- a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/SdkEditorModule.kt +++ b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/SdkEditorModule.kt @@ -84,14 +84,13 @@ class SdkEditorModule(reactContext: ReactApplicationContext) : ReactContextBaseJ } resultCode == Activity.RESULT_CANCELED -> { - val exportNotificationManager = getKoin().get() as? EmptyExportNotificationManger - + val exportNotificationManager = getKoin().get() as? BackgroundExportNotificationManger + if (exportNotificationManager != null) { - if (exportNotificationManager.isExportInProgress()) { - exportNotificationManager.setResultPromise(resultPromise) - } else { - resultPromise?.reject("ERR_VIDEO_EXPORT_CANCEL", "") - } + exportNotificationManager.setResultPromise(resultPromise) + } else { + // For Foreground export + resultPromise?.reject("ERR_VIDEO_EXPORT_CANCEL", "") } } } diff --git a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/VideoEditorIntegrationModule.kt b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/VideoEditorIntegrationModule.kt index dd5e6cb..1cec895 100644 --- a/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/VideoEditorIntegrationModule.kt +++ b/android/app/src/main/java/com/vesdkreactnativecliintegrationsample/VideoEditorIntegrationModule.kt @@ -102,7 +102,7 @@ private class SampleModule { } single { - EmptyExportNotificationManger() + BackgroundExportNotificationManger() } } }