Skip to content

Open photo editor from video editor #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,6 @@ dependencies {
// Photo Editor SDK dependency
// WARNING!
// Remove this dependency if you only use Video Editor SDK
def banubaPESdkVersion = '1.1.0'
def banubaPESdkVersion = '1.2.0'
implementation "com.banuba.sdk:pe-sdk:${banubaPESdkVersion}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class MainActivity : FlutterActivity() {

// For Photo Editor
private const val PHOTO_EDITOR_REQUEST_CODE = 8888
const val EXTRA_EXPORTED_IMAGE = "EXTRA_EXPORTED_IMAGE"
private const val METHOD_START_PHOTO_EDITOR = "startPhotoEditor"

private const val ARG_EXPORTED_PHOTO_FILE = "argExportedPhotoFilePath"
Expand Down Expand Up @@ -210,6 +211,20 @@ class MainActivity : FlutterActivity() {
override fun onActivityResult(requestCode: Int, result: Int, intent: Intent?) {
super.onActivityResult(requestCode, result, intent)
if (requestCode == VIDEO_EDITOR_REQUEST_CODE && result == RESULT_OK) {
handleVideoEditorExportResult(intent)
} else if (requestCode == PHOTO_EDITOR_REQUEST_CODE && result == RESULT_OK) {
val data = preparePhotoExportData(intent)
exportResult?.success(data)
}
}

private fun handleVideoEditorExportResult(intent: Intent?) {
// The image taken on camera screen in Video Editor SDK
val exportImageResult =
intent?.getParcelableExtra("EXTRA_EXPORTED_IMAGE") as? ExportResult.Success

if (exportImageResult == null) {
// Handle export result from
val exportResult =
intent?.getParcelableExtra(EXTRA_EXPORTED_SUCCESS) as? ExportResult.Success
if (exportResult == null) {
Expand All @@ -222,10 +237,18 @@ class MainActivity : FlutterActivity() {
val data = prepareVideoExportData(exportResult)
this.exportResult?.success(data)
}

} else if (requestCode == PHOTO_EDITOR_REQUEST_CODE && result == RESULT_OK) {
val data = preparePhotoExportData(intent)
exportResult?.success(data)
} else {
// IMPORTANT! Release Video Editor SDK
videoEditorModule?.release()
videoEditorModule = null

activity.startActivityForResult(
PhotoCreationActivity.startFromEditor(
activity.applicationContext,
imageUri = exportImageResult.preview
),
PHOTO_EDITOR_REQUEST_CODE
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
package com.banuba.flutter.flutter_ve_sdk

import android.app.Activity
import android.app.Application
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import com.banuba.flutter.flutter_ve_sdk.MainActivity.Companion.EXTRA_EXPORTED_IMAGE
import com.banuba.sdk.arcloud.data.source.ArEffectsRepositoryProvider
import com.banuba.sdk.arcloud.di.ArCloudKoinModule
import com.banuba.sdk.audiobrowser.di.AudioBrowserKoinModule
import com.banuba.sdk.audiobrowser.domain.AudioBrowserMusicProvider
import com.banuba.sdk.core.EditorUtilityManager
import com.banuba.sdk.core.data.TrackData
import com.banuba.sdk.core.domain.MediaNavigationProcessor
import com.banuba.sdk.core.ui.ContentFeatureProvider
import com.banuba.sdk.effectplayer.adapter.BanubaEffectPlayerKoinModule
import com.banuba.sdk.export.data.ExportResult
import com.banuba.sdk.export.di.VeExportKoinModule
import com.banuba.sdk.gallery.di.GalleryKoinModule
import com.banuba.sdk.playback.di.VePlaybackSdkKoinModule
import com.banuba.sdk.ve.di.VeSdkKoinModule
import com.banuba.sdk.ve.ext.VideoEditorUtils.getKoin
import com.banuba.sdk.ve.flow.di.VeFlowKoinModule
import com.banuba.sdk.veui.di.VeUiSdkKoinModule
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.core.error.InstanceCreationException
import org.koin.core.qualifier.named
import org.koin.dsl.module

Expand Down Expand Up @@ -49,6 +61,19 @@ class VideoEditorModule {
)
}
}

fun release() {
val utilityManager = try {
// EditorUtilityManager is NULL when the token is expired or revoked.
// This dependency is not explicitly created in DI.
getKoin().getOrNull<EditorUtilityManager>()
} catch (e: InstanceCreationException) {
Log.w("CustomNavigation", "EditorUtilityManager was not initialized!", e)
null
}
utilityManager?.release()
stopKoin()
}
}

/**
Expand Down Expand Up @@ -79,5 +104,42 @@ private class SampleIntegrationVeKoinModule {
AudioBrowserMusicProvider()
}
}

// Provide custom implementation to process media received from Camera screen
single<MediaNavigationProcessor> {
OpenPhotoEditorFromVideoEditor()
}
}

private class OpenPhotoEditorFromVideoEditor() : MediaNavigationProcessor {

override fun process(activity: Activity, mediaList: List<Uri>): Boolean {
// Provide custom implementation to process mediaList received from Camera screen.
// Keep in mind that mediaList contains slideshow video as well.
Log.d("CustomMediaNavigation", "Process mediaList = $mediaList")
val imageUri = mediaList.find { it.path?.contains(".png") == true }

val stayInVideoEditorSDK = if (imageUri == null) {
true
} else {
val exportImageIntent = Intent().apply {
putExtra(EXTRA_EXPORTED_IMAGE ,
ExportResult.Success(
emptyList(),
imageUri,
Uri.EMPTY,
Bundle()
)
)
}

// Finish Video Editor SDK
activity.setResult(Activity.RESULT_OK, exportImageIntent)
activity.finish()
false // false - close and open app screen
}

return stayInVideoEditorSDK
}
}
}
8 changes: 8 additions & 0 deletions ios/Runner/PhotoEditorModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ class PhotoEditorModule: BanubaPhotoEditorDelegate {
})
}

func presentPhotoEditor(with launchConfig: PhotoEditorLaunchConfig) {
photoEditorSDK?.presentPhotoEditor(
withLaunchConfiguration: launchConfig,
completion: nil
)
}

// MARK: - PhotoEditorSDKDelegate
func photoEditorDidCancel(_ photoEditor: BanubaPhotoEditor) {
photoEditor.dismissPhotoEditor(animated: true) { [unowned self] in
Expand All @@ -66,6 +73,7 @@ class PhotoEditorModule: BanubaPhotoEditorDelegate {
let data = [
AppDelegate.argExportedPhotoFile: exportedPhotoFileUrl.path,
]
debugPrint("Photo exported = \(exportedPhotoFileUrl.path)")
photoEditor.dismissPhotoEditor(animated: true) { [unowned self] in
self.flutterResult?(data)
self.flutterResult = nil
Expand Down
60 changes: 60 additions & 0 deletions ios/Runner/VideoEditorModule.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation
import BanubaVideoEditorSDK
import BanubaAudioBrowserSDK
import BanubaPhotoEditorSDK
import VideoEditor
import VEExportSDK
import Flutter
Expand All @@ -18,11 +19,16 @@ protocol VideoEditor {
class VideoEditorModule: VideoEditor {

private var videoEditorSDK: BanubaVideoEditor?
// MARK: - PhotoEditorSDK
private var photoEditorModule: PhotoEditorModule?

private var flutterResult: FlutterResult?

// Use “true” if you want users could restore the last video editing session.
private let restoreLastVideoEditingSession: Bool = false

private var licenseToken: String = ""

func initVideoEditor(
token: String?,
flutterResult: @escaping FlutterResult
Expand All @@ -47,6 +53,8 @@ class VideoEditorModule: VideoEditor {
return
}

licenseToken = token!

videoEditorSDK?.delegate = self
flutterResult(nil)
}
Expand Down Expand Up @@ -249,6 +257,58 @@ extension VideoEditorModule: BanubaVideoEditorDelegate {
}
}

func videoEditor(_ videoEditor: BanubaVideoEditor, shouldProcessMediaUrls urls: [URL]) -> Bool {
// Implement your custom filter to take image
guard let jpegURL = urls.first(where: { $0.pathExtension.lowercased() == "jpeg" }),
let imageData = try? Data(contentsOf: jpegURL),
!imageData.isEmpty,
let resultImage = UIImage(data: imageData) else {
return true
}

videoEditor.dismissVideoEditor(animated: true) {
DispatchQueue.main.async { [weak self] in
guard let self else { return }
// Calling clearSessionData() also removes any files stored in urls array
videoEditorSDK?.clearSessionData()

let launchConfig = PhotoEditorLaunchConfig(
hostController: getTopViewController()!,
entryPoint: .editorWithImage(resultImage)
)
checkLicenseAndOpenPhotoEditor(with: launchConfig)
}
}

return false
}

private func checkLicenseAndOpenPhotoEditor(with launchConfig: PhotoEditorLaunchConfig) {
// Deallocate any active instances of both editors to free used resources
// and to prevent "You are trying to create the second instance of the singleton." crash
photoEditorModule = nil
videoEditorSDK = nil

photoEditorModule = PhotoEditorModule(token: licenseToken)

guard let photoEditorSDK = photoEditorModule?.photoEditorSDK else {
print("Banuba Photo Editor SDK is not initialized: license token is unknown or incorrect.\nPlease check your license token or contact Banuba")
return
}

photoEditorSDK.delegate = photoEditorModule

photoEditorSDK.getLicenseState(completion: { [weak self] isValid in
guard let self else { return }
if isValid {
print("✅ License is active, all good")
photoEditorModule?.presentPhotoEditor(with: launchConfig)
} else {
print("❌ License is either revoked or expired")
}
})
}

func videoEditorDone(_ videoEditor: BanubaVideoEditor) {
exportVideo()
}
Expand Down