Skip to content
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

PE only #289

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
10 changes: 0 additions & 10 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down
6 changes: 0 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.banuba.sdk.ve.flow.VideoCreationActivity"
android:screenOrientation="portrait"
android:theme="@style/CustomIntegrationAppTheme"
android:windowSoftInputMode="adjustResize"
tools:replace="android:theme" />

<!-- Allows to play exported video -->
<provider
Expand Down
116 changes: 3 additions & 113 deletions app/src/main/java/com/banuba/example/integrationapp/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,73 +7,18 @@ import android.util.Log
import android.view.LayoutInflater
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.banuba.example.integrationapp.SampleApp.Companion.ERR_LICENSE_REVOKED
import com.banuba.example.integrationapp.SampleApp.Companion.ERR_SDK_NOT_INITIALIZED
import com.banuba.example.integrationapp.databinding.ActivityMainBinding
import com.banuba.sdk.cameraui.data.PipConfig
import com.banuba.sdk.core.ui.ext.visible
import com.banuba.sdk.export.data.ExportResult
import com.banuba.sdk.pe.BanubaPhotoEditor
import com.banuba.sdk.pe.PhotoCreationActivity
import com.banuba.sdk.pe.PhotoExportResultContract
import com.banuba.sdk.ve.flow.VideoCreationActivity
import com.banuba.sdk.ve.flow.VideoExportResultContract


class MainActivity : AppCompatActivity() {

// Handle Video Editor export results
private val videoEditorExportResult =
registerForActivityResult(VideoExportResultContract()) { result ->
// 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 ->
Expand Down Expand Up @@ -121,32 +66,18 @@ 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
}

// 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))
Expand All @@ -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
}
}
Expand All @@ -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<Uri>) {
startVideoEditor(
VideoCreationActivity.startFromTrimmer(
this,
videos.toTypedArray()
)
)
}

private fun startVideoEditor(intent: Intent) {
sampleApp.prepareVideoEditor()

videoEditorExportResult.launch(intent)
}

private fun startPhotoEditor(intent: Intent) {
sampleApp.preparePhotoEditor()

Expand Down
60 changes: 1 addition & 59 deletions app/src/main/java/com/banuba/example/integrationapp/SampleApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<EditorUtilityManager>()
} catch (e: InstanceCreationException) {
Log.w(TAG, "EditorUtilityManager was not initialized!", e)
null
}

utilityManager?.release()
}
}
99 changes: 0 additions & 99 deletions app/src/main/java/com/banuba/example/integrationapp/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<Uri> {
val list = mutableListOf<Uri>()
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
Expand All @@ -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)
}
}
Loading