diff --git a/app/build.gradle b/app/build.gradle index e1352257..53ee1b37 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -64,21 +64,11 @@ dependencies { // Banuba Video Editor SDK dependencies def banubaSdkVersion = '1.39.1' - implementation "com.banuba.sdk:ffmpeg:5.1.3" - implementation "com.banuba.sdk:camera-sdk:${banubaSdkVersion}" - implementation "com.banuba.sdk:camera-ui-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:core-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:core-ui-sdk:${banubaSdkVersion}" - implementation "com.banuba.sdk:ve-flow-sdk:${banubaSdkVersion}" - implementation "com.banuba.sdk:ve-sdk:${banubaSdkVersion}" - implementation "com.banuba.sdk:ve-ui-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-gallery-sdk:${banubaSdkVersion}" - implementation "com.banuba.sdk:ve-effects-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:effect-player-adapter:${banubaSdkVersion}" implementation "com.banuba.sdk:ar-cloud:${banubaSdkVersion}" - implementation "com.banuba.sdk:ve-audio-browser-sdk:${banubaSdkVersion}" - implementation "com.banuba.sdk:ve-export-sdk:${banubaSdkVersion}" - implementation "com.banuba.sdk:ve-playback-sdk:${banubaSdkVersion}" // Photo Editor SDK dependency // WARNING! diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1c592396..f0a13024 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,12 +24,6 @@ - - // The dialog is used only to demonstrate and play an exported video file. - - // Release Video Editor SDK after exporting video and closing the latest SDK screen - //(application as? SampleApp)?.releaseVideoEditor() - Log.d(SampleApp.TAG, "Video Editor export result = $result") - - if (result is ExportResult.Success) { - AlertDialog.Builder(this).setMessage("Export result") - .setPositiveButton("Play Video") { _, _ -> - playExportedVideo(result) - } - .setNeutralButton("Close") { _, _ -> - }.create().show() - } - } - - private fun playExportedVideo(exportResult: ExportResult.Success) { - exportResult.videoList.firstOrNull()?.let { - Utils.playExportedVideo(this@MainActivity, it.sourceUri) - } - } - - // Opens system app to pick video for PIP - private val requestVideoOpenPIP = registerForActivityResult( - ActivityResultContracts.GetContent() - ) { videoPipUri -> - if (videoPipUri != Uri.EMPTY && videoPipUri != null) { - openVideoEditor(pipVideo = videoPipUri) - } - } - - // Opens system app to pick image for Trimmer - private val requestImageOpenTrimmer = registerForActivityResult( - ActivityResultContracts.GetContent() - ) { imageUri -> - if (imageUri != Uri.EMPTY && imageUri != null) { - val slideShowList = - Utils.createSlideShowList( - applicationContext, - lifecycleScope, - imageUri - ) - openVideoEditorTrimmerWithSlideShow(slideShowList) - } - } - private val requestImageOpenPhotoEditor = registerForActivityResult( ActivityResultContracts.GetContent() ) { imageUri -> @@ -121,8 +66,8 @@ class MainActivity : AppCompatActivity() { setContentView(binding.root) // Handle Video Editor license flow - val videoEditor = sampleApp.videoEditor - if (videoEditor == null) { + val photoEditor = sampleApp.photoEditor + if (photoEditor == null) { binding.licenseStateView.visible() binding.licenseStateView.text = ERR_SDK_NOT_INITIALIZED return @@ -130,23 +75,9 @@ class MainActivity : AppCompatActivity() { // Might take up to 1 sec in the worst case. // Please optimize use of this function in your project to bring the best user experience - videoEditor.getLicenseState { isValid -> + photoEditor.getLicenseState { isValid -> if (isValid) { // ✅ License is active, all good - // You can show button that opens Video Editor or - // Start Video Editor right away - binding.btnVideoEditor.setOnClickListener { - openVideoEditor() - } - binding.btnPiPVideoEditor.setOnClickListener { - requestVideoOpenPIP.launch("video/*") - } - binding.btnDraftsVideoEditor.setOnClickListener { - openVideoEditorDrafts() - } - binding.btnSlideShowVideoEditorTrimmer.setOnClickListener { - requestImageOpenTrimmer.launch("image/*") - } binding.btnOpenPhotoEditor.setOnClickListener { // Start Photo Editor SDK startPhotoEditor(PhotoCreationActivity.startFromGallery(this@MainActivity)) @@ -161,11 +92,6 @@ class MainActivity : AppCompatActivity() { binding.licenseStateView.text = ERR_LICENSE_REVOKED Log.w(SampleApp.TAG, ERR_LICENSE_REVOKED) - binding.licenseStateView.visible() - binding.btnVideoEditor.isEnabled = false - binding.btnPiPVideoEditor.isEnabled = false - binding.btnDraftsVideoEditor.isEnabled = false - binding.btnSlideShowVideoEditorTrimmer.isEnabled = false binding.btnOpenPhotoEditor.isEnabled = false } } @@ -176,42 +102,6 @@ class MainActivity : AppCompatActivity() { _binding = null } - private fun openVideoEditor(pipVideo: Uri = Uri.EMPTY) { - val videoCreationIntent = VideoCreationActivity.startFromCamera( - context = this, - // set PiP video configuration - pictureInPictureConfig = PipConfig( - video = pipVideo, - openPipSettings = false - ), - // setup what kind of action you want to do with VideoCreationActivity - // setup data that will be acceptable during export flow - additionalExportData = null, - // set TrackData object if you open VideoCreationActivity with preselected music track - audioTrackData = null - ) - startVideoEditor(videoCreationIntent) - } - - private fun openVideoEditorDrafts() { - startVideoEditor(VideoCreationActivity.startFromDrafts(this)) - } - - private fun openVideoEditorTrimmerWithSlideShow(videos: List) { - startVideoEditor( - VideoCreationActivity.startFromTrimmer( - this, - videos.toTypedArray() - ) - ) - } - - private fun startVideoEditor(intent: Intent) { - sampleApp.prepareVideoEditor() - - videoEditorExportResult.launch(intent) - } - private fun startPhotoEditor(intent: Intent) { sampleApp.preparePhotoEditor() diff --git a/app/src/main/java/com/banuba/example/integrationapp/SampleApp.kt b/app/src/main/java/com/banuba/example/integrationapp/SampleApp.kt index b1b51a10..8ef2ca3d 100644 --- a/app/src/main/java/com/banuba/example/integrationapp/SampleApp.kt +++ b/app/src/main/java/com/banuba/example/integrationapp/SampleApp.kt @@ -22,75 +22,17 @@ class SampleApp : Application() { const val ERR_LICENSE_REVOKED = "License is revoked or expired. Please contact Banuba https://www.banuba.com/faq/kb-tickets/new" } - - // Manages Video Editor SDK - var videoEditor: BanubaVideoEditor? = null - // Manages Photo Editor SDK var photoEditor: BanubaPhotoEditor? = null override fun onCreate() { super.onCreate() - // Prepare Video Editor in the beginning for simplicity - prepareVideoEditor() - } - - fun prepareVideoEditor() { - // Video and Photo SDK share license management system. - // It is required to keep only one instance. - - if (photoEditor != null) { - BanubaPhotoEditor.release() - photoEditor = null - } - - if (videoEditor == null) { - Log.d(TAG, "Prepare Video Editor SDK") - - // Initialize Video Editor - VideoEditorModule().initialize(this@SampleApp) - - // Initialize Banuba Video Editor SDK - videoEditor = BanubaVideoEditor.initialize(LICENSE_TOKEN) - - if (videoEditor == null) { - // Token you provided is not correct - empty or truncated - Log.e(TAG, ERR_SDK_NOT_INITIALIZED) - } - } + preparePhotoEditor() } fun preparePhotoEditor() { - // Video and Photo SDK share license management system. - // It is required to keep only one instance. - - if (videoEditor != null) { - releaseVideoEditor() - } - Log.d(TAG, "Prepare Photo Editor SDK") // Photo Editor SDK auto releases its resources photoEditor = BanubaPhotoEditor.initialize(LICENSE_TOKEN) } - - // Call it to release VE SDK before opening PE - private fun releaseVideoEditor() { - Log.d(TAG, "Release Video Editor SDK") - releaseUtilityManager() - stopKoin() - videoEditor = null - } - - private fun releaseUtilityManager() { - val utilityManager = try { - // EditorUtilityManager is NULL when the token is expired or revoked. - // This dependency is not explicitly created in DI. - getKoin().getOrNull() - } catch (e: InstanceCreationException) { - Log.w(TAG, "EditorUtilityManager was not initialized!", e) - null - } - - utilityManager?.release() - } } diff --git a/app/src/main/java/com/banuba/example/integrationapp/Utils.kt b/app/src/main/java/com/banuba/example/integrationapp/Utils.kt index 4f6e76fa..92422f50 100644 --- a/app/src/main/java/com/banuba/example/integrationapp/Utils.kt +++ b/app/src/main/java/com/banuba/example/integrationapp/Utils.kt @@ -10,50 +10,11 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.FileProvider import androidx.lifecycle.LifecycleCoroutineScope import com.banuba.sdk.core.ext.obtainReadFileDescriptor -import com.banuba.sdk.ve.slideshow.SlideShowScaleMode -import com.banuba.sdk.ve.slideshow.SlideShowSource -import com.banuba.sdk.ve.slideshow.SlideShowTask import kotlinx.coroutines.launch import java.io.File // Helper methods not required for the SDK integration object Utils { - // Creates the list with 2 video sources - the first based on image taken from gallery and - // the second on image taken from assets - fun createSlideShowList( - context: Context, - lifecycleCoroutineScope: LifecycleCoroutineScope, - selectedImage: Uri - ): List { - val list = mutableListOf() - lifecycleCoroutineScope.launch { - val externalSlideshowDir = - File(context.getExternalFilesDir(null), "slideshow").apply { mkdirs() } - - list.add(buildSlideshowFromFile(context, selectedImage, externalSlideshowDir)) - list.add(buildSlideshowFromBitmap(context, externalSlideshowDir)) - } - return list - } - - // Plays exported video to demonstrate the result - fun playExportedVideo( - activity: AppCompatActivity, - videoUri: Uri - ) { - val intent = Intent(Intent.ACTION_VIEW).apply { - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - val uri = FileProvider.getUriForFile( - activity, - "${activity.packageName}.provider", - File(videoUri.encodedPath ?: "") - ) - setDataAndType(uri, "video/mp4") - } - activity.startActivity(intent) - } - - // Previews exported image to demonstrate the result fun previewExportedImage( activity: AppCompatActivity, uri: Uri @@ -68,64 +29,4 @@ object Utils { Log.e(SampleApp.TAG, "Can't handle intent") } } - - /** - * Helper function to create slideshow video from image taken from the gallery. - * It is highly recommended to execute in the background thread - */ - private suspend fun buildSlideshowFromFile( - context: Context, - source: Uri, - folder: File - ): Uri { - val slideshowFromFile = File(folder, "slideshowFromFile.mp4").apply { createNewFile() } - return context.obtainReadFileDescriptor(source)?.let { - val params = SlideShowTask.Params.create( - context = context, - size = Size(1280, 768), - destFile = slideshowFromFile, - sources = listOf( - SlideShowSource.File( - source = source, - durationMs = 3000 - ) - ), - animationEnabled = true, - debugEnabled = true, - scaleMode = SlideShowScaleMode.SCALE_BALANCED - ) - SlideShowTask.makeVideo(params) - Uri.fromFile(slideshowFromFile) - } ?: Uri.EMPTY - } - - /** - * Helper function to create slideshow video from image taken from assets. - * It is highly recommended to execute in the background thread - */ - private suspend fun buildSlideshowFromBitmap( - context: Context, - folder: File - ): Uri { - val slideshowFromBitmap = File(folder, "slideshowFromBitmap.mp4").apply { createNewFile() } - val bitmap = context.assets.open("image_sample.jpeg").run { - BitmapFactory.decodeStream(this) - } - val params = SlideShowTask.Params.create( - context = context, - size = Size(1280, 768), - destFile = slideshowFromBitmap, - sources = listOf( - SlideShowSource.Picture( - source = bitmap, - durationMs = 3000 - ) - ), - animationEnabled = true, - debugEnabled = false, - scaleMode = SlideShowScaleMode.SCALE_BALANCED - ) - SlideShowTask.makeVideo(params) - return Uri.fromFile(slideshowFromBitmap) - } } \ No newline at end of file diff --git a/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt b/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt index 3e13e3b8..d2aa3149 100644 --- a/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt +++ b/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt @@ -1,67 +1,67 @@ package com.banuba.example.integrationapp - -import android.content.Context -import androidx.fragment.app.Fragment -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.data.TrackData -import com.banuba.sdk.core.ui.ContentFeatureProvider -import com.banuba.sdk.effectplayer.adapter.BanubaEffectPlayerKoinModule -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.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.qualifier.named -import org.koin.dsl.module - -class VideoEditorModule { - - fun initialize(applicationContext: Context) { - startKoin { - androidContext(applicationContext) - allowOverride(true) - - // pass the customized Koin modules that implements required dependencies. Keep order of modules - modules( - VeSdkKoinModule().module, - VeExportKoinModule().module, - VePlaybackSdkKoinModule().module, - AudioBrowserKoinModule().module, - ArCloudKoinModule().module, - VeUiSdkKoinModule().module, - VeFlowKoinModule().module, - GalleryKoinModule().module, - BanubaEffectPlayerKoinModule().module, - SampleIntegrationKoinModule().module, - ) - } - } -} - -/** - * All dependencies mentioned in this module will override default - * implementations provided from SDK. - */ -private class SampleIntegrationKoinModule { - - val module = module { - single(createdAtStart = true) { - ArEffectsRepositoryProvider( - arEffectsRepository = get(named("backendArEffectsRepository")), - ioDispatcher = get(named("ioDispatcher")) - ) - } - - single>( - named("musicTrackProvider") - ) { - AudioBrowserMusicProvider() - } - } -} +// +//import android.content.Context +//import androidx.fragment.app.Fragment +//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.data.TrackData +//import com.banuba.sdk.core.ui.ContentFeatureProvider +//import com.banuba.sdk.effectplayer.adapter.BanubaEffectPlayerKoinModule +//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.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.qualifier.named +//import org.koin.dsl.module +// +//class VideoEditorModule { +// +// fun initialize(applicationContext: Context) { +// startKoin { +// androidContext(applicationContext) +// allowOverride(true) +// +// // pass the customized Koin modules that implements required dependencies. Keep order of modules +// modules( +// VeSdkKoinModule().module, +// VeExportKoinModule().module, +// VePlaybackSdkKoinModule().module, +// AudioBrowserKoinModule().module, +// ArCloudKoinModule().module, +// VeUiSdkKoinModule().module, +// VeFlowKoinModule().module, +// GalleryKoinModule().module, +// BanubaEffectPlayerKoinModule().module, +// SampleIntegrationKoinModule().module, +// ) +// } +// } +//} +// +///** +// * All dependencies mentioned in this module will override default +// * implementations provided from SDK. +// */ +//private class SampleIntegrationKoinModule { +// +// val module = module { +// single(createdAtStart = true) { +// ArEffectsRepositoryProvider( +// arEffectsRepository = get(named("backendArEffectsRepository")), +// ioDispatcher = get(named("ioDispatcher")) +// ) +// } +// +// single>( +// named("musicTrackProvider") +// ) { +// AudioBrowserMusicProvider() +// } +// } +//} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 10a81c4b..0361baed 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,75 +6,19 @@ android:layout_height="match_parent" tools:context=".MainActivity"> - - - - - - - - + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" /> + app:layout_constraintVertical_bias="0" + app:layout_constraintVertical_chainStyle="packed"/> @color/primaryBrandLight ?attr/colorPrimary - - -