├── .gitignore ├── README.md ├── build.gradle ├── camera-guides-configuration └── ConfiguredCameraActivity.kt ├── camera-guides-quickstart ├── CameraActivity.kt ├── build.gradle └── settings.gradle ├── camera-guides-recordings └── RecordingsCameraActivity.kt ├── editor-guides-configuration-asset-library ├── AssetLibraryEditorSolution.kt ├── CustomAssetLibraryEditorSolution.kt └── DefaultAssetLibraryEditorSolution.kt ├── editor-guides-configuration-basics └── BasicEditorSolution.kt ├── editor-guides-configuration-callbacks └── CallbacksEditorSolution.kt ├── editor-guides-configuration-canvas-menu ├── CanvasMenuItems.kt ├── CanvasMenuListBuilders.kt ├── ModifyListBuilderCanvasMenuSolution.kt ├── NewListBuilderCanvasMenuSolution.kt └── SimpleCanvasMenuSolution.kt ├── editor-guides-configuration-color-palette └── ColorPaletteEditorSolution.kt ├── editor-guides-configuration-dock ├── DockItems.kt ├── DockListBuilders.kt ├── ModifyListBuilderDockSolution.kt ├── NewListBuilderDockSolution.kt └── SimpleDockSolution.kt ├── editor-guides-configuration-inspector-bar ├── InspectorBarItems.kt ├── InspectorBarListBuilders.kt ├── ModifyListBuilderInspectorBarSolution.kt ├── NewListBuilderInspectorBarSolution.kt └── SimpleInspectorBarSolution.kt ├── editor-guides-configuration-navigation-bar ├── ModifyListBuilderNavigationBarSolution.kt ├── NavigationBarItems.kt ├── NavigationBarListBuilders.kt ├── NewListBuilderNavigationBarSolution.kt └── SimpleNavigationBarSolution.kt ├── editor-guides-configuration-overlay ├── OverlayCustomState.kt └── OverlayEditorSolution.kt ├── editor-guides-configuration-theming └── ThemingEditorSolution.kt ├── editor-guides-configuration-ui-events └── UiEventsEditorSolution.kt ├── editor-guides-quickstart ├── EditorActivity.kt ├── EditorComposable.kt ├── EditorFragment.kt ├── build.gradle └── settings.gradle ├── editor-guides-solutions-apparel-editor └── ApparelEditorSolution.kt ├── editor-guides-solutions-design-editor └── DesignEditorSolution.kt ├── editor-guides-solutions-photo-editor └── PhotoEditorSolution.kt ├── editor-guides-solutions-postcard-editor └── PostcardEditorSolution.kt ├── editor-guides-solutions-video-editor └── VideoEditorSolution.kt ├── engine-guides-bool-ops └── BoolOps.kt ├── engine-guides-buffers └── Buffers.kt ├── engine-guides-captions └── Captions.kt ├── engine-guides-colors └── Colors.kt ├── engine-guides-create-scene-from-image-blob └── CreateSceneFromImageBlob.kt ├── engine-guides-create-scene-from-image-url └── CreateSceneFromImageURL.kt ├── engine-guides-create-scene-from-scratch └── CreateSceneFromScratch.kt ├── engine-guides-create-scene-from-video-url └── CreateSceneFromVideoURL.kt ├── engine-guides-custom-asset-source ├── RemoteAssetSource.kt └── UnsplashAssetSource.kt ├── engine-guides-custom-lut-filter └── CustomLUTFilter.kt ├── engine-guides-cutouts └── Cutouts.kt ├── engine-guides-exporting-blocks └── ExportingBlocks.kt ├── engine-guides-load-scene-from-blob └── LoadSceneFromBlob.kt ├── engine-guides-load-scene-from-remote └── LoadSceneFromRemote.kt ├── engine-guides-load-scene-from-string └── LoadSceneFromString.kt ├── engine-guides-modifying-scenes └── ModifyingScenes.kt ├── engine-guides-save-scene-to-archive └── SaveSceneToArchive.kt ├── engine-guides-save-scene-to-blob └── SaveSceneToBlob.kt ├── engine-guides-save-scene-to-string └── SaveSceneToString.kt ├── engine-guides-scopes └── Scopes.kt ├── engine-guides-setup ├── MyActivity.kt ├── MyApplication.kt ├── MyComposable.kt ├── build.gradle └── settings.gradle ├── engine-guides-source-sets └── SourceSets.kt ├── engine-guides-spot-colors └── SpotColors.kt ├── engine-guides-store-metadata └── StoreMetadata.kt ├── engine-guides-text-properties └── TextProperties.kt ├── engine-guides-text-with-emojis └── TextWithEmojis.kt ├── engine-guides-underlayer └── Underlayer.kt ├── engine-guides-uri-resolver └── UriResolver.kt ├── engine-guides-using-animations └── UsingAnimations.kt ├── engine-guides-using-camera ├── UsingCamera.kt └── build.gradle ├── engine-guides-using-effects └── UsingEffects.kt ├── engine-guides-using-fills └── UsingFills.kt ├── engine-guides-using-shapes └── UsingShapes.kt ├── engine-guides-video └── Video.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── showcases-app ├── .gitignore ├── .version ├── build.gradle ├── proguard-rules.pro └── src ├── internal └── res │ ├── drawable │ ├── ic_launcher_background.xml │ └── ic_launcher_foreground.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ └── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp └── main ├── AndroidManifest.xml ├── assets ├── images │ └── photo-ui-empty.png └── scenes │ ├── apparel-ui-b-1.scene │ ├── apparel-ui-b-2.scene │ ├── apparel-ui-b-3.scene │ ├── apparel-ui-b-4.scene │ ├── bonjour_paris.scene │ ├── design_ui_booklet.scene │ ├── design_ui_booklet_template.scene │ ├── design_ui_business_card.scene │ ├── design_ui_business_card_template.scene │ ├── design_ui_instagram_post.scene │ ├── design_ui_instagram_post_template.scene │ ├── design_ui_instagram_story.scene │ ├── design_ui_instagram_story_template.scene │ ├── design_ui_video_full_hd.scene │ ├── design_ui_video_full_hd_template.scene │ ├── design_ui_x_header.scene │ ├── design_ui_x_header_template.scene │ ├── design_ui_x_profile_picture.scene │ ├── design_ui_x_profile_picture_template.scene │ ├── merry_christmas.scene │ ├── milli-surf-school.scene │ ├── monthly-review.scene │ ├── my-plants.scene │ ├── red-dot.scene │ ├── thank_you.scene │ └── wish_you_were_here.scene ├── java └── ly │ └── img │ └── editor │ └── showcases │ ├── Secrets.kt │ ├── ShowcaseItem.kt │ ├── Showcases.kt │ ├── ShowcasesActivity.kt │ ├── ShowcasesApp.kt │ ├── ShowcasesViewModel.kt │ ├── asset │ └── RemoteAssetSource.kt │ ├── icon │ ├── Homeoutline.kt │ └── IconPack.kt │ ├── ui │ ├── components │ │ ├── QuickActionButton.kt │ │ └── VersionFooter.kt │ ├── preview │ │ └── PreviewTheme.kt │ ├── screens │ │ ├── EditCameraRecordings.kt │ │ ├── EditRecordedReaction.kt │ │ └── EditVideoFromUri.kt │ └── sections │ │ └── QuickActions.kt │ └── util │ └── CameraResultHandler.kt └── res ├── drawable-night └── ic_logo.xml ├── drawable-nodpi ├── apparel_ui_blank_dark.png ├── apparel_ui_blank_light.png ├── design_ui_booklet_dark.png ├── design_ui_booklet_light.png ├── design_ui_booklet_template.png ├── design_ui_business_card_dark.png ├── design_ui_business_card_light.png ├── design_ui_business_card_template.png ├── design_ui_instagram_post_dark.png ├── design_ui_instagram_post_light.png ├── design_ui_instagram_post_template.png ├── design_ui_instagram_story_dark.png ├── design_ui_instagram_story_light.png ├── design_ui_instagram_story_template.png ├── design_ui_open_design_dark.png ├── design_ui_open_design_light.png ├── design_ui_video_full_hd_dark.png ├── design_ui_video_full_hd_light.png ├── design_ui_video_full_hd_template.png ├── design_ui_x_header_dark.png ├── design_ui_x_header_light.png ├── design_ui_x_header_template.png ├── design_ui_x_profile_picture_dark.png ├── design_ui_x_profile_picture_light.png ├── design_ui_x_profile_picture_template.png ├── photo_ui_16_9_dark.png ├── photo_ui_16_9_light.png ├── photo_ui_1_1_dark.png ├── photo_ui_1_1_light.png ├── photo_ui_2_3_dark.png ├── photo_ui_2_3_light.png ├── photo_ui_3_2_dark.png ├── photo_ui_3_2_light.png ├── photo_ui_3_4_dark.png ├── photo_ui_3_4_light.png ├── photo_ui_4_3_dark.png ├── photo_ui_4_3_light.png ├── photo_ui_9_16_dark.png ├── photo_ui_9_16_light.png ├── photo_ui_choose_from_gallery_dark.png ├── photo_ui_choose_from_gallery_light.png ├── postcard_ui_blank_dark.png ├── postcard_ui_blank_light.png ├── thumbnail_apparel_ui_b_1.webp ├── thumbnail_apparel_ui_b_2.webp ├── thumbnail_apparel_ui_b_3.webp ├── thumbnail_apparel_ui_b_4.webp ├── thumbnail_bonjour_paris.webp ├── thumbnail_merry_christmas.webp ├── thumbnail_milli_surf_school.webp ├── thumbnail_monthly_review.webp ├── thumbnail_my_plants.webp ├── thumbnail_red_dot.webp ├── thumbnail_thank_you.webp ├── thumbnail_wish_you_were_here.webp ├── video_ui_blank_dark.png └── video_ui_blank_light.png ├── drawable ├── ic_logo.xml ├── quick_action_dual_camera.xml ├── quick_action_edit_media.xml ├── quick_action_gallery.xml ├── quick_action_react_to_video.xml └── quick_action_record_video.xml ├── raw ├── isrg_root_x1.der └── isrg_root_x2.der ├── values-night └── general_thumbnails.xml ├── values ├── general_thumbnails.xml └── strings.xml ├── xml-v25 └── network_security_config.xml └── xml └── network_security_config.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | !.idea 5 | .idea/* 6 | !.idea/codeStyles 7 | .DS_Store 8 | /captures 9 | .externalNativeBuild 10 | .cxx 11 | local.properties 12 | android-release.keystore 13 | **/build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Hero image showing the configuration abilities of IMGLYEngine](https://img.ly/static/cesdk_release_header_android.png) 2 | 3 | # IMGLY Creative Engine and UI - Android Examples 4 | 5 | This repository contains the Android examples for the IMG.LY `Engine`, the core of CE.SDK, as well 6 | as the source code of our mobile editor solutions. 7 | The `Engine` enables you to build any design editing UI, automation and creative workflow in Kotlin. 8 | It offers performant and robust graphics processing capabilities combining the best of layout, 9 | typography and image processing with advanced workflows centered around templating and adaptation. 10 | 11 | The `Engine` seamlessly integrates into any Android app whether you are building a photo editor, 12 | template-based design tool or scalable automation of content creation for your app. 13 | The mobile editor is fully built on top of the `Engine`. 14 | 15 | ## Documentation 16 | 17 | The full documentation of the [engine](https://img.ly/docs/cesdk/engine/quickstart?platform=android) 18 | and the [mobile editor](https://img.ly/docs/cesdk/mobile-editor/quickstart?platform=android) can be 19 | found on our website. 20 | There you will learn how to integrate and configure them for your use case. 21 | 22 | ## License 23 | 24 | The `Engine` is a commercial product. To use it you need to unlock the SDK with a license file. You 25 | can purchase a license at https://img.ly/pricing. 26 | 27 | In order to run the `Showcases` application that lives in the `showcases-app` module of this 28 | repository use the instructions below: 29 | 30 | 1. Get a free trial license at https://img.ly/forms/free-trial. 31 | 2. Copy the license key. 32 | 3. Include the license key in the `local.properties` file: 33 | 34 | ``` 35 | license=... 36 | ``` 37 | 38 | Note that failing to provide the license key-value pairing will display an error when opening any of 39 | the showcases. 40 | 41 | Source code of the mobile editor and camera can be 42 | found [here](https://github.com/imgly/cesdk-android). 43 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' version libs.versions.gradleAndroid apply false 3 | id 'com.android.library' version libs.versions.gradleAndroid apply false 4 | id 'org.jetbrains.kotlin.android' version libs.versions.kotlin apply false 5 | id 'com.google.gms.google-services' version libs.versions.googleServices apply false 6 | id 'com.dropbox.dropshots' version libs.versions.dropshots apply false 7 | id 'com.github.gmazzo.buildconfig' version libs.versions.buildConfig apply false 8 | id 'com.google.firebase.appdistribution' version libs.versions.appDistributionPlugin apply false 9 | id 'com.google.firebase.crashlytics' version libs.versions.gradleCrashlytics apply false 10 | alias(libs.plugins.compose.compiler) apply false 11 | } 12 | -------------------------------------------------------------------------------- /camera-guides-configuration/ConfiguredCameraActivity.kt: -------------------------------------------------------------------------------- 1 | import android.os.Bundle 2 | import android.util.Log 3 | import androidx.activity.compose.rememberLauncherForActivityResult 4 | import androidx.activity.compose.setContent 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.compose.material3.Button 7 | import androidx.compose.material3.Text 8 | import androidx.compose.ui.graphics.Color 9 | import ly.img.camera.core.CameraConfiguration 10 | import ly.img.camera.core.CameraMode 11 | import ly.img.camera.core.CaptureVideo 12 | import ly.img.camera.core.EngineConfiguration 13 | import kotlin.time.Duration.Companion.seconds 14 | 15 | private const val TAG = "ConfiguredCameraActivity" 16 | 17 | class ConfiguredCameraActivity : AppCompatActivity() { 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | 21 | val cameraInput = CaptureVideo.Input( 22 | // highlight-engine-configuration 23 | engineConfiguration = EngineConfiguration( 24 | // highlight-license 25 | license = "", 26 | // highlight-userId 27 | userId = "", 28 | ), 29 | // highlight-engine-configuration 30 | // highlight-camera-configuration 31 | cameraConfiguration = CameraConfiguration( 32 | // highlight-recording-color 33 | recordingColor = Color.Blue, 34 | // highlight-max-duration 35 | maxTotalDuration = 30.seconds, 36 | // highlight-allow-exceeding-max-duration 37 | allowExceedingMaxDuration = false, 38 | ), 39 | // highlight-camera-configuration 40 | // highlight-camera-mode 41 | cameraMode = CameraMode.Standard(), 42 | // highlight-camera-mode 43 | ) 44 | 45 | setContent { 46 | val cameraLauncher = rememberLauncherForActivityResult(contract = CaptureVideo()) { result -> 47 | result ?: run { 48 | Log.d(TAG, "Camera dismissed") 49 | return@rememberLauncherForActivityResult 50 | } 51 | Log.d(TAG, "Result: $result") 52 | } 53 | 54 | Button( 55 | onClick = { 56 | cameraLauncher.launch(cameraInput) 57 | }, 58 | ) { 59 | Text(text = "Open Camera") 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /camera-guides-quickstart/CameraActivity.kt: -------------------------------------------------------------------------------- 1 | import android.os.Bundle 2 | import android.util.Log 3 | import androidx.activity.compose.rememberLauncherForActivityResult 4 | import androidx.activity.compose.setContent 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.compose.material3.Button 7 | import androidx.compose.material3.Text 8 | import ly.img.camera.core.CameraResult 9 | import ly.img.camera.core.CaptureVideo 10 | import ly.img.camera.core.EngineConfiguration 11 | 12 | private const val TAG = "CameraActivity" 13 | 14 | class CameraActivity : AppCompatActivity() { 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | 18 | // highlight-initialization 19 | val cameraInput = CaptureVideo.Input( 20 | engineConfiguration = EngineConfiguration( 21 | license = "", 22 | userId = "", 23 | ), 24 | ) 25 | // highlight-initialization 26 | 27 | setContent { 28 | // highlight-launcher 29 | val cameraLauncher = rememberLauncherForActivityResult(contract = CaptureVideo()) { result -> 30 | // highlight-launcher 31 | // highlight-result 32 | result ?: run { 33 | Log.d(TAG, "Camera dismissed") 34 | return@rememberLauncherForActivityResult 35 | } 36 | when (result) { 37 | is CameraResult.Record -> { 38 | val recordedVideoUris = result.recordings.flatMap { it.videos.map { it.uri } } 39 | // Do something with the recorded videos 40 | Log.d(TAG, "Recorded videos: $recordedVideoUris") 41 | } 42 | 43 | else -> { 44 | Log.d(TAG, "Unhandled result") 45 | } 46 | } 47 | // highlight-result 48 | } 49 | 50 | Button( 51 | onClick = { 52 | // highlight-launch 53 | cameraLauncher.launch(cameraInput) 54 | // highlight-launch 55 | }, 56 | ) { 57 | Text(text = "Open Camera") 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /camera-guides-quickstart/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | namespace "ly.img.editor.showcase" 8 | compileSdk 35 9 | 10 | defaultConfig { 11 | applicationId "ly.img.editor.showcase" 12 | // highlight-minsdk 13 | minSdk 24 14 | // highlight-minsdk 15 | targetSdk 35 16 | versionCode 1 17 | versionName "1.0" 18 | // highlight-abi-filters 19 | ndk { 20 | abiFilters "arm64-v8a", "armeabi-v7a", "x86_64", "x86" 21 | } 22 | // highlight-abi-filters 23 | } 24 | 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | 34 | // highlight-buildFeatures 35 | buildFeatures { 36 | compose true 37 | } 38 | // highlight-buildFeatures 39 | 40 | // highlight-kotlinCompilerExtensionVersion 41 | composeOptions { 42 | kotlinCompilerExtensionVersion = "1.5.3" 43 | } 44 | // highlight-kotlinCompilerExtensionVersion 45 | } 46 | 47 | dependencies { 48 | // highlight-dependency 49 | implementation "ly.img:camera:1.52.0" 50 | // highlight-dependency 51 | // highlight-bom-version 52 | implementation(platform("androidx.compose:compose-bom:2023.05.01")) 53 | // highlight-bom-version 54 | implementation "androidx.activity:activity-compose:1.8.2" 55 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" 56 | } 57 | -------------------------------------------------------------------------------- /camera-guides-quickstart/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | dependencyResolutionManagement { 10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 11 | repositories { 12 | google() 13 | mavenCentral() 14 | // highlight-maven-dependency 15 | maven { 16 | name "IMG.LY Artifactory" 17 | url "https://artifactory.img.ly/artifactory/maven" 18 | mavenContent { 19 | includeGroup("ly.img") 20 | } 21 | } 22 | // highlight-maven-dependency 23 | } 24 | } 25 | 26 | rootProject.name = "My App" 27 | include ':app' 28 | -------------------------------------------------------------------------------- /camera-guides-recordings/RecordingsCameraActivity.kt: -------------------------------------------------------------------------------- 1 | import android.os.Bundle 2 | import android.util.Log 3 | import androidx.activity.compose.rememberLauncherForActivityResult 4 | import androidx.activity.compose.setContent 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.compose.material3.Button 7 | import androidx.compose.material3.Text 8 | import ly.img.camera.core.CameraResult 9 | import ly.img.camera.core.CaptureVideo 10 | import ly.img.camera.core.EngineConfiguration 11 | 12 | private const val TAG = "RecordingsCameraActivity" 13 | 14 | class RecordingsCameraActivity : AppCompatActivity() { 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | 18 | val cameraInput = CaptureVideo.Input( 19 | engineConfiguration = EngineConfiguration( 20 | license = "", 21 | userId = "", 22 | ), 23 | ) 24 | 25 | setContent { 26 | val cameraLauncher = rememberLauncherForActivityResult(contract = CaptureVideo()) { result -> 27 | // highlight-failure 28 | result ?: run { 29 | Log.d(TAG, "Camera dismissed") 30 | return@rememberLauncherForActivityResult 31 | } 32 | // highlight-failure 33 | // highlight-success 34 | when (result) { 35 | // highlight-standard 36 | is CameraResult.Record -> { 37 | for (recording in result.recordings) { 38 | Log.d(TAG, "Duration: ${recording.duration}") 39 | for (video in recording.videos) { 40 | Log.d(TAG, "Video Uri: ${video.uri} Video Rect: ${video.rect}") 41 | } 42 | } 43 | } 44 | // highlight-standard 45 | // highlight-reaction 46 | is CameraResult.Reaction -> { 47 | Log.d(TAG, "Video uri: ${result.video.uri}") 48 | for (reaction in result.reaction) { 49 | Log.d(TAG, "Duration: ${reaction.duration}") 50 | for (video in reaction.videos) { 51 | Log.d(TAG, "Video Uri: ${video.uri} Video Rect: ${video.rect}") 52 | } 53 | } 54 | } 55 | // highlight-reaction 56 | 57 | else -> { 58 | Log.d(TAG, "Unhandled result") 59 | } 60 | } 61 | // highlight-success 62 | } 63 | 64 | Button( 65 | onClick = { 66 | cameraLauncher.launch(cameraInput) 67 | }, 68 | ) { 69 | Text(text = "Open Camera") 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /editor-guides-configuration-asset-library/AssetLibraryEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.DesignEditor 4 | import ly.img.editor.EditorConfiguration 5 | import ly.img.editor.EngineConfiguration 6 | import ly.img.editor.core.library.AssetLibrary 7 | import ly.img.editor.rememberForDesign 8 | 9 | // Add this composable to your NavHost 10 | @Composable 11 | fun AssetLibraryEditorSolution(navController: NavHostController) { 12 | val engineConfiguration = EngineConfiguration.rememberForDesign( 13 | license = "", 14 | ) 15 | // highlight-configuration-asset-library 16 | val editorConfiguration = EditorConfiguration.rememberForDesign( 17 | assetLibrary = AssetLibrary.getDefault(), 18 | ) 19 | // highlight-configuration-asset-library 20 | DesignEditor( 21 | engineConfiguration = engineConfiguration, 22 | editorConfiguration = editorConfiguration, 23 | ) { 24 | // You can set result here 25 | navController.popBackStack() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /editor-guides-configuration-asset-library/CustomAssetLibraryEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.compose.runtime.remember 3 | import androidx.navigation.NavHostController 4 | import ly.img.editor.DesignEditor 5 | import ly.img.editor.EditorConfiguration 6 | import ly.img.editor.EditorDefaults 7 | import ly.img.editor.EngineConfiguration 8 | import ly.img.editor.core.R 9 | import ly.img.editor.core.iconpack.IconPack 10 | import ly.img.editor.core.iconpack.LibraryElements 11 | import ly.img.editor.core.library.AssetLibrary 12 | import ly.img.editor.core.library.AssetType 13 | import ly.img.editor.core.library.LibraryCategory 14 | import ly.img.editor.core.library.LibraryCategory.Companion.sourceTypes 15 | import ly.img.editor.core.library.LibraryContent 16 | import ly.img.editor.core.library.addSection 17 | import ly.img.editor.core.library.data.AssetSourceType 18 | import ly.img.editor.rememberForDesign 19 | import ly.img.editor.smoketests.R as SmokeTestR 20 | 21 | // Add this composable to your NavHost 22 | @Composable 23 | fun CustomAssetLibraryEditorSolution(navController: NavHostController) { 24 | val unsplashAssetSource = remember { 25 | UnsplashAssetSource("") 26 | } 27 | val engineConfiguration = EngineConfiguration.remember( 28 | license = "", 29 | onCreate = { 30 | EditorDefaults.onCreate( 31 | engine = editorContext.engine, 32 | sceneUri = EngineConfiguration.defaultDesignSceneUri, 33 | eventHandler = editorContext.eventHandler, 34 | ) { _, _ -> 35 | editorContext.engine.asset.addSource(unsplashAssetSource) 36 | } 37 | }, 38 | ) 39 | // highlight-configuration-custom-asset-library 40 | val assetLibrary = remember { 41 | // We create a custom tab with title "My Assets" that contains 2 sections: 42 | // 1. Stickers - expanding it opens the default stickers content 43 | // 2. Text - expanding it opens the default text content. Note that the title is skipped. 44 | val myAssetsCategory = LibraryCategory( 45 | tabTitleRes = SmokeTestR.string.my_assets, 46 | tabSelectedIcon = IconPack.LibraryElements, 47 | tabUnselectedIcon = IconPack.LibraryElements, 48 | content = LibraryContent.Sections( 49 | titleRes = SmokeTestR.string.my_assets, 50 | sections = listOf( 51 | LibraryContent.Section( 52 | titleRes = R.string.ly_img_editor_stickers, 53 | sourceTypes = LibraryContent.Stickers.sourceTypes, 54 | assetType = AssetType.Sticker, 55 | expandContent = LibraryContent.Stickers, 56 | ), 57 | LibraryContent.Section( 58 | sourceTypes = LibraryContent.Text.sourceTypes, 59 | assetType = AssetType.Text, 60 | expandContent = LibraryContent.Text, 61 | ), 62 | ), 63 | ), 64 | ) 65 | AssetLibrary( 66 | tabs = { 67 | listOf( 68 | myAssetsCategory, 69 | LibraryCategory.Images, 70 | ) 71 | }, 72 | images = { 73 | val unsplashSection = LibraryContent.Section( 74 | titleRes = SmokeTestR.string.unsplash, 75 | sourceTypes = listOf(AssetSourceType(sourceId = unsplashAssetSource.sourceId)), 76 | assetType = AssetType.Image, 77 | ) 78 | // See how the images is different in tabs and here 79 | LibraryCategory.Images.addSection(unsplashSection) 80 | }, 81 | ) 82 | } 83 | val editorConfiguration = EditorConfiguration.rememberForDesign( 84 | assetLibrary = assetLibrary, 85 | ) 86 | // highlight-configuration-custom-asset-library 87 | DesignEditor( 88 | engineConfiguration = engineConfiguration, 89 | editorConfiguration = editorConfiguration, 90 | ) { 91 | // You can set result here 92 | navController.popBackStack() 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /editor-guides-configuration-asset-library/DefaultAssetLibraryEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.compose.runtime.remember 3 | import androidx.navigation.NavHostController 4 | import ly.img.editor.DesignEditor 5 | import ly.img.editor.EditorConfiguration 6 | import ly.img.editor.EditorDefaults 7 | import ly.img.editor.EngineConfiguration 8 | import ly.img.editor.core.library.AssetLibrary 9 | import ly.img.editor.core.library.AssetType 10 | import ly.img.editor.core.library.LibraryCategory 11 | import ly.img.editor.core.library.LibraryContent 12 | import ly.img.editor.core.library.addSection 13 | import ly.img.editor.core.library.data.AssetSourceType 14 | import ly.img.editor.core.library.dropSection 15 | import ly.img.editor.core.library.replaceSection 16 | import ly.img.editor.rememberForDesign 17 | import ly.img.editor.smoketests.R 18 | 19 | // Add this composable to your NavHost 20 | @Composable 21 | fun DefaultAssetLibraryEditorSolution(navController: NavHostController) { 22 | // highlight-configuration-custom-asset-source 23 | val unsplashAssetSource = remember { 24 | UnsplashAssetSource("") 25 | } 26 | val engineConfiguration = EngineConfiguration.remember( 27 | license = "", 28 | onCreate = { 29 | EditorDefaults.onCreate( 30 | engine = editorContext.engine, 31 | sceneUri = EngineConfiguration.defaultDesignSceneUri, 32 | eventHandler = editorContext.eventHandler, 33 | ) { _, _ -> 34 | editorContext.engine.asset.addSource(unsplashAssetSource) 35 | } 36 | }, 37 | ) 38 | // highlight-configuration-custom-asset-source 39 | // highlight-configuration-default-asset-library 40 | val assetLibrary = remember { 41 | val unsplashSection = LibraryContent.Section( 42 | titleRes = R.string.unsplash, 43 | sourceTypes = listOf(AssetSourceType(sourceId = unsplashAssetSource.sourceId)), 44 | assetType = AssetType.Image, 45 | ) 46 | AssetLibrary.getDefault( 47 | tabs = listOf( 48 | AssetLibrary.Tab.IMAGES, 49 | AssetLibrary.Tab.SHAPES, 50 | AssetLibrary.Tab.STICKERS, 51 | AssetLibrary.Tab.TEXT, 52 | ), 53 | images = LibraryCategory.Images 54 | .replaceSection(index = 0) { 55 | // We replace the title: "Image Uploads" -> "Uploads" 56 | copy(titleRes = R.string.uploads) 57 | } 58 | .dropSection(index = 1) 59 | .addSection(unsplashSection), 60 | ) 61 | } 62 | val editorConfiguration = EditorConfiguration.rememberForDesign( 63 | assetLibrary = assetLibrary, 64 | ) 65 | // highlight-configuration-default-asset-library 66 | DesignEditor( 67 | engineConfiguration = engineConfiguration, 68 | editorConfiguration = editorConfiguration, 69 | ) { 70 | // You can set result here 71 | navController.popBackStack() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /editor-guides-configuration-basics/BasicEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import androidx.compose.runtime.Composable 3 | import androidx.navigation.NavHostController 4 | import ly.img.editor.DesignEditor 5 | import ly.img.editor.EngineConfiguration 6 | import ly.img.editor.core.engine.EngineRenderTarget 7 | import ly.img.editor.rememberForDesign 8 | 9 | // Add this composable to your NavHost 10 | @Composable 11 | fun BasicEditorSolution(navController: NavHostController) { 12 | val engineConfiguration = EngineConfiguration.rememberForDesign( 13 | // highlight-configuration-license 14 | license = "", 15 | // highlight-configuration-license 16 | // highlight-configuration-userId 17 | userId = "", 18 | // highlight-configuration-userId 19 | // highlight-configuration-baseUri 20 | baseUri = Uri.parse("file:///android_asset/"), 21 | // highlight-configuration-baseUri 22 | // highlight-configuration-sceneUri 23 | sceneUri = EngineConfiguration.defaultDesignSceneUri, 24 | // highlight-configuration-sceneUri 25 | // highlight-configuration-renderTarget 26 | renderTarget = EngineRenderTarget.SURFACE_VIEW, 27 | // highlight-configuration-renderTarget 28 | ) 29 | DesignEditor(engineConfiguration = engineConfiguration) { 30 | // You can set result here 31 | navController.popBackStack() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /editor-guides-configuration-canvas-menu/CanvasMenuListBuilders.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import ly.img.editor.core.component.CanvasMenu 3 | import ly.img.editor.core.component.rememberBringForward 4 | import ly.img.editor.core.component.rememberDelete 5 | import ly.img.editor.core.component.rememberDuplicate 6 | import ly.img.editor.core.component.rememberSendBackward 7 | 8 | // highlight-listBuilders 9 | @Composable 10 | fun CanvasMenu.ListBuilder.remember() = CanvasMenu.ListBuilder.remember { 11 | add { CanvasMenu.Button.rememberBringForward() } 12 | add { CanvasMenu.Button.rememberSendBackward() } 13 | add { CanvasMenu.Divider.remember(visible = { editorContext.canSelectionMove }) } 14 | add { CanvasMenu.Button.rememberDuplicate() } 15 | add { CanvasMenu.Button.rememberDelete() } 16 | } 17 | // highlight-listBuilders 18 | -------------------------------------------------------------------------------- /editor-guides-configuration-canvas-menu/NewListBuilderCanvasMenuSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.DesignEditor 4 | import ly.img.editor.EditorConfiguration 5 | import ly.img.editor.EngineConfiguration 6 | import ly.img.editor.core.component.CanvasMenu 7 | import ly.img.editor.core.component.EditorComponentId 8 | import ly.img.editor.core.component.rememberBringForward 9 | import ly.img.editor.core.component.rememberDuplicate 10 | import ly.img.editor.core.component.rememberSendBackward 11 | import ly.img.editor.rememberForDesign 12 | 13 | // Add this composable to your NavHost 14 | @Composable 15 | fun NewListBuilderCanvasMenuSolution(navController: NavHostController) { 16 | val engineConfiguration = EngineConfiguration.rememberForDesign( 17 | license = "", 18 | ) 19 | 20 | val editorConfiguration = EditorConfiguration.rememberForDesign( 21 | canvasMenu = { 22 | CanvasMenu.remember( 23 | // highlight-newListBuilder 24 | listBuilder = CanvasMenu.ListBuilder.remember { 25 | add { 26 | CanvasMenu.Button.remember( 27 | id = EditorComponentId("my.package.canvasMenu.button.custom"), 28 | onClick = {}, 29 | vectorIcon = null, 30 | text = { "Custom Button" }, 31 | ) 32 | } 33 | add { CanvasMenu.Button.rememberSendBackward() } 34 | add { CanvasMenu.Button.rememberBringForward() } 35 | add { CanvasMenu.Divider.remember(visible = { editorContext.canSelectionMove }) } 36 | add { CanvasMenu.Button.rememberDuplicate() } 37 | }, 38 | // highlight-newListBuilder 39 | ) 40 | }, 41 | ) 42 | DesignEditor( 43 | engineConfiguration = engineConfiguration, 44 | editorConfiguration = editorConfiguration, 45 | ) { 46 | // You can set result here 47 | navController.popBackStack() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /editor-guides-configuration-canvas-menu/SimpleCanvasMenuSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.animation.EnterTransition 2 | import androidx.compose.animation.ExitTransition 3 | import androidx.compose.foundation.layout.Box 4 | import androidx.compose.foundation.layout.padding 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.runtime.collectAsState 7 | import androidx.compose.runtime.getValue 8 | import androidx.compose.runtime.remember 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.unit.dp 11 | import androidx.navigation.NavHostController 12 | import ly.img.editor.DesignEditor 13 | import ly.img.editor.EditorConfiguration 14 | import ly.img.editor.EngineConfiguration 15 | import ly.img.editor.core.component.CanvasMenu 16 | import ly.img.editor.core.component.CanvasMenu.Companion.DefaultDecoration 17 | import ly.img.editor.rememberForDesign 18 | import ly.img.engine.DesignBlockType 19 | 20 | // Add this composable to your NavHost 21 | @Composable 22 | fun SimpleCanvasMenuSolution(navController: NavHostController) { 23 | val engineConfiguration = EngineConfiguration.rememberForDesign( 24 | license = "", 25 | ) 26 | 27 | // highlight-canvasMenuConfiguration 28 | val editorConfiguration = EditorConfiguration.rememberForDesign( 29 | canvasMenu = { 30 | CanvasMenu.remember( 31 | // highlight-canvasMenuConfiguration-scope 32 | // Implementation is too large, check the implementation of CanvasMenu.defaultScope 33 | scope = CanvasMenu.defaultScope, 34 | // highlight-canvasMenuConfiguration-scope 35 | // highlight-canvasMenuConfiguration-visible 36 | visible = { 37 | val editorState by editorContext.state.collectAsState() 38 | remember(this, editorState) { 39 | editorState.isTouchActive.not() && 40 | editorState.activeSheet == null && 41 | editorContext.safeSelection != null && 42 | editorContext.selection.type != DesignBlockType.Page && 43 | editorContext.selection.type != DesignBlockType.Audio && 44 | editorContext.engine.editor.getEditMode() != "Text" && 45 | editorContext.isScenePlaying.not() && 46 | editorContext.selection.isVisibleAtCurrentPlaybackTime 47 | } 48 | }, 49 | // highlight-canvasMenuConfiguration-visible 50 | // highlight-canvasMenuConfiguration-enterTransition 51 | enterTransition = { EnterTransition.None }, 52 | // highlight-canvasMenuConfiguration-exitTransition 53 | exitTransition = { ExitTransition.None }, 54 | // highlight-canvasMenuConfiguration-decoration 55 | // Implementation is too large, check the implementation of CanvasMenu.DefaultDecoration 56 | decoration = { DefaultDecoration { it() } }, 57 | // highlight-canvasMenuConfiguration-decoration 58 | // highlight-canvasMenuConfiguration-listBuilder 59 | listBuilder = CanvasMenu.ListBuilder.remember(), 60 | // highlight-canvasMenuConfiguration-itemDecoration 61 | // default value is { it() } 62 | itemDecoration = { 63 | Box(modifier = Modifier.padding(2.dp)) { 64 | it() 65 | } 66 | }, 67 | // highlight-canvasMenuConfiguration-itemDecoration 68 | ) 69 | }, 70 | ) 71 | // highlight-canvasMenuConfiguration 72 | DesignEditor( 73 | engineConfiguration = engineConfiguration, 74 | editorConfiguration = editorConfiguration, 75 | ) { 76 | // You can set result here 77 | navController.popBackStack() 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /editor-guides-configuration-color-palette/ColorPaletteEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.compose.runtime.remember 3 | import androidx.compose.ui.graphics.Color 4 | import androidx.navigation.NavHostController 5 | import ly.img.editor.DesignEditor 6 | import ly.img.editor.EditorConfiguration 7 | import ly.img.editor.EngineConfiguration 8 | import ly.img.editor.rememberForDesign 9 | 10 | // Add this composable to your NavHost 11 | @Composable 12 | fun ColorPaletteEditorSolution(navController: NavHostController) { 13 | val engineConfiguration = EngineConfiguration.rememberForDesign( 14 | license = "", 15 | ) 16 | val editorConfiguration = EditorConfiguration.rememberForDesign( 17 | // highlight-configuration-colorPalette 18 | colorPalette = remember { 19 | listOf( 20 | Color(0xFF4A67FF), 21 | Color(0xFFFFD333), 22 | Color(0xFFC41230), 23 | Color(0xFF000000), 24 | Color(0xFFFFFFFF), 25 | ) 26 | }, 27 | // highlight-configuration-colorPalette 28 | ) 29 | DesignEditor( 30 | engineConfiguration = engineConfiguration, 31 | editorConfiguration = editorConfiguration, 32 | ) { 33 | // You can set result here 34 | navController.popBackStack() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /editor-guides-configuration-dock/DockListBuilders.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import ly.img.camera.core.CaptureVideo 3 | import ly.img.editor.core.component.Dock 4 | import ly.img.editor.core.component.rememberAdjustments 5 | import ly.img.editor.core.component.rememberAudiosLibrary 6 | import ly.img.editor.core.component.rememberBlur 7 | import ly.img.editor.core.component.rememberCrop 8 | import ly.img.editor.core.component.rememberEffect 9 | import ly.img.editor.core.component.rememberElementsLibrary 10 | import ly.img.editor.core.component.rememberFilter 11 | import ly.img.editor.core.component.rememberImagesLibrary 12 | import ly.img.editor.core.component.rememberImglyCamera 13 | import ly.img.editor.core.component.rememberOverlaysLibrary 14 | import ly.img.editor.core.component.rememberReorder 15 | import ly.img.editor.core.component.rememberShapesLibrary 16 | import ly.img.editor.core.component.rememberStickersLibrary 17 | import ly.img.editor.core.component.rememberSystemCamera 18 | import ly.img.editor.core.component.rememberSystemGallery 19 | import ly.img.editor.core.component.rememberTextLibrary 20 | 21 | // highlight-listBuilders 22 | // highlight-listBuilders-design 23 | @Composable 24 | fun Dock.ListBuilder.rememberForDesign() = Dock.ListBuilder.remember { 25 | add { Dock.Button.rememberElementsLibrary() } 26 | add { Dock.Button.rememberSystemGallery() } 27 | add { Dock.Button.rememberSystemCamera() } 28 | add { Dock.Button.rememberImagesLibrary() } 29 | add { Dock.Button.rememberTextLibrary() } 30 | add { Dock.Button.rememberShapesLibrary() } 31 | add { Dock.Button.rememberStickersLibrary() } 32 | } 33 | // highlight-listBuilders-design 34 | 35 | // highlight-listBuilders-photo 36 | @Composable 37 | fun Dock.ListBuilder.rememberForPhoto() = Dock.ListBuilder.remember { 38 | add { Dock.Button.rememberAdjustments() } 39 | add { Dock.Button.rememberFilter() } 40 | add { Dock.Button.rememberEffect() } 41 | add { Dock.Button.rememberBlur() } 42 | add { Dock.Button.rememberCrop() } 43 | add { Dock.Button.rememberTextLibrary() } 44 | add { Dock.Button.rememberShapesLibrary() } 45 | add { Dock.Button.rememberStickersLibrary() } 46 | } 47 | // highlight-listBuilders-photo 48 | 49 | // highlight-listBuilders-video 50 | @Composable 51 | fun Dock.ListBuilder.rememberForVideo() = Dock.ListBuilder.remember { 52 | add { Dock.Button.rememberSystemGallery() } 53 | add { 54 | /* 55 | Make sure to add the gradle dependency of our camera library if you want to use the [rememberImglyCamera] button: 56 | implementation "ly.img:camera:". 57 | If the dependency is missing, then [rememberSystemCamera] is used. 58 | */ 59 | val isImglyCameraAvailable = androidx.compose.runtime.remember { 60 | runCatching { CaptureVideo() }.isSuccess 61 | } 62 | if (isImglyCameraAvailable) { 63 | Dock.Button.rememberImglyCamera() 64 | } else { 65 | Dock.Button.rememberSystemCamera() 66 | } 67 | } 68 | add { Dock.Button.rememberOverlaysLibrary() } 69 | add { Dock.Button.rememberTextLibrary() } 70 | add { Dock.Button.rememberStickersLibrary() } 71 | add { Dock.Button.rememberAudiosLibrary() } 72 | add { Dock.Button.rememberReorder() } 73 | } 74 | // highlight-listBuilders-video 75 | // highlight-listBuilders 76 | -------------------------------------------------------------------------------- /editor-guides-configuration-dock/NewListBuilderDockSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.DesignEditor 4 | import ly.img.editor.EditorConfiguration 5 | import ly.img.editor.EngineConfiguration 6 | import ly.img.editor.core.component.Dock 7 | import ly.img.editor.core.component.EditorComponentId 8 | import ly.img.editor.core.component.rememberElementsLibrary 9 | import ly.img.editor.core.component.rememberImagesLibrary 10 | import ly.img.editor.core.component.rememberStickersLibrary 11 | import ly.img.editor.core.component.rememberSystemCamera 12 | import ly.img.editor.core.component.rememberSystemGallery 13 | import ly.img.editor.core.component.rememberTextLibrary 14 | import ly.img.editor.rememberForDesign 15 | 16 | // Add this composable to your NavHost 17 | @Composable 18 | fun NewListBuilderDockSolution(navController: NavHostController) { 19 | val engineConfiguration = EngineConfiguration.rememberForDesign( 20 | license = "", 21 | ) 22 | 23 | val editorConfiguration = EditorConfiguration.rememberForDesign( 24 | dock = { 25 | Dock.remember( 26 | // highlight-newListBuilder 27 | listBuilder = Dock.ListBuilder.remember { 28 | add { 29 | Dock.Button.remember( 30 | id = EditorComponentId("my.package.dock.button.custom"), 31 | vectorIcon = null, 32 | text = { "Custom Button" }, 33 | onClick = {}, 34 | ) 35 | } 36 | add { Dock.Button.rememberSystemGallery() } 37 | add { Dock.Button.rememberSystemCamera() } 38 | add { Dock.Button.rememberElementsLibrary() } 39 | add { Dock.Button.rememberStickersLibrary() } 40 | add { Dock.Button.rememberImagesLibrary() } 41 | add { Dock.Button.rememberTextLibrary() } 42 | }, 43 | // highlight-newListBuilder 44 | ) 45 | }, 46 | ) 47 | DesignEditor( 48 | engineConfiguration = engineConfiguration, 49 | editorConfiguration = editorConfiguration, 50 | ) { 51 | // You can set result here 52 | navController.popBackStack() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /editor-guides-configuration-dock/SimpleDockSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.animation.EnterTransition 2 | import androidx.compose.animation.ExitTransition 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.material3.MaterialTheme 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.remember 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.unit.dp 13 | import androidx.navigation.NavHostController 14 | import ly.img.editor.DesignEditor 15 | import ly.img.editor.EditorConfiguration 16 | import ly.img.editor.EngineConfiguration 17 | import ly.img.editor.core.LocalEditorScope 18 | import ly.img.editor.core.component.Dock 19 | import ly.img.editor.core.theme.surface1 20 | import ly.img.editor.rememberForDesign 21 | 22 | // Add this composable to your NavHost 23 | @Composable 24 | fun SimpleDockSolution(navController: NavHostController) { 25 | val engineConfiguration = EngineConfiguration.rememberForDesign( 26 | license = "", 27 | ) 28 | 29 | // highlight-dockConfiguration 30 | val editorConfiguration = EditorConfiguration.rememberForDesign( 31 | dock = { 32 | Dock.remember( 33 | // highlight-dockConfiguration-scope 34 | scope = LocalEditorScope.current.run { 35 | remember(this) { Dock.Scope(parentScope = this) } 36 | }, 37 | // highlight-dockConfiguration-scope 38 | // highlight-dockConfiguration-visible 39 | visible = { true }, 40 | // highlight-dockConfiguration-enterTransition 41 | enterTransition = { EnterTransition.None }, 42 | // highlight-dockConfiguration-exitTransition 43 | exitTransition = { ExitTransition.None }, 44 | // highlight-dockConfiguration-decoration 45 | decoration = { 46 | // Also available via Dock.DefaultDecoration 47 | Box( 48 | modifier = Modifier 49 | .fillMaxWidth() 50 | .background(MaterialTheme.colorScheme.surface1.copy(alpha = 0.95f)) 51 | .padding(vertical = 10.dp), 52 | ) { 53 | it() 54 | } 55 | }, 56 | // highlight-dockConfiguration-decoration 57 | // highlight-dockConfiguration-listBuilder 58 | listBuilder = Dock.ListBuilder.remember { }, 59 | // highlight-dockConfiguration-horizontalArrangement 60 | horizontalArrangement = { Arrangement.SpaceEvenly }, 61 | // highlight-dockConfiguration-itemDecoration 62 | // default value is { it() } 63 | itemDecoration = { 64 | Box(modifier = Modifier.padding(2.dp)) { 65 | it() 66 | } 67 | }, 68 | // highlight-dockConfiguration-itemDecoration 69 | ) 70 | }, 71 | ) 72 | // highlight-dockConfiguration 73 | DesignEditor( 74 | engineConfiguration = engineConfiguration, 75 | editorConfiguration = editorConfiguration, 76 | ) { 77 | // You can set result here 78 | navController.popBackStack() 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /editor-guides-configuration-inspector-bar/InspectorBarListBuilders.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import ly.img.editor.core.component.InspectorBar 3 | import ly.img.editor.core.component.rememberAdjustments 4 | import ly.img.editor.core.component.rememberBlur 5 | import ly.img.editor.core.component.rememberCrop 6 | import ly.img.editor.core.component.rememberDelete 7 | import ly.img.editor.core.component.rememberDuplicate 8 | import ly.img.editor.core.component.rememberEditText 9 | import ly.img.editor.core.component.rememberEffect 10 | import ly.img.editor.core.component.rememberEnterGroup 11 | import ly.img.editor.core.component.rememberFillStroke 12 | import ly.img.editor.core.component.rememberFilter 13 | import ly.img.editor.core.component.rememberFormatText 14 | import ly.img.editor.core.component.rememberLayer 15 | import ly.img.editor.core.component.rememberMoveAsClip 16 | import ly.img.editor.core.component.rememberMoveAsOverlay 17 | import ly.img.editor.core.component.rememberReorder 18 | import ly.img.editor.core.component.rememberReplace 19 | import ly.img.editor.core.component.rememberSelectGroup 20 | import ly.img.editor.core.component.rememberShape 21 | import ly.img.editor.core.component.rememberSplit 22 | import ly.img.editor.core.component.rememberTextBackground 23 | import ly.img.editor.core.component.rememberVolume 24 | 25 | // highlight-listBuilders 26 | @Composable 27 | fun InspectorBar.ListBuilder.remember() = InspectorBar.ListBuilder.remember { 28 | add { InspectorBar.Button.rememberReplace() } // Video, Image, Sticker, Audio 29 | add { InspectorBar.Button.rememberEditText() } // Text 30 | add { InspectorBar.Button.rememberFormatText() } // Text 31 | add { InspectorBar.Button.rememberFillStroke() } // Page, Video, Image, Shape, Text 32 | add { InspectorBar.Button.rememberTextBackground() } // Text 33 | add { InspectorBar.Button.rememberVolume() } // Video, Audio 34 | add { InspectorBar.Button.rememberCrop() } // Video, Image 35 | add { InspectorBar.Button.rememberAdjustments() } // Video, Image 36 | add { InspectorBar.Button.rememberFilter() } // Video, Image 37 | add { InspectorBar.Button.rememberEffect() } // Video, Image 38 | add { InspectorBar.Button.rememberBlur() } // Video, Image 39 | add { InspectorBar.Button.rememberShape() } // Video, Image, Shape 40 | add { InspectorBar.Button.rememberSelectGroup() } // Video, Image, Sticker, Shape, Text 41 | add { InspectorBar.Button.rememberEnterGroup() } // Group 42 | add { InspectorBar.Button.rememberLayer() } // Video, Image, Sticker, Shape, Text 43 | add { InspectorBar.Button.rememberSplit() } // Video, Image, Sticker, Shape, Text, Audio 44 | add { InspectorBar.Button.rememberMoveAsClip() } // Video, Image, Sticker, Shape, Text 45 | add { InspectorBar.Button.rememberMoveAsOverlay() } // Video, Image, Sticker, Shape, Text 46 | add { InspectorBar.Button.rememberReorder() } // Video, Image, Sticker, Shape, Text 47 | add { InspectorBar.Button.rememberDuplicate() } // Video, Image, Sticker, Shape, Text, Audio 48 | add { InspectorBar.Button.rememberDelete() } // Video, Image, Sticker, Shape, Text, Audio 49 | } 50 | // highlight-listBuilders 51 | -------------------------------------------------------------------------------- /editor-guides-configuration-inspector-bar/NewListBuilderInspectorBarSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.DesignEditor 4 | import ly.img.editor.EditorConfiguration 5 | import ly.img.editor.EngineConfiguration 6 | import ly.img.editor.core.component.EditorComponentId 7 | import ly.img.editor.core.component.InspectorBar 8 | import ly.img.editor.core.component.rememberAdjustments 9 | import ly.img.editor.core.component.rememberBlur 10 | import ly.img.editor.core.component.rememberCrop 11 | import ly.img.editor.core.component.rememberDelete 12 | import ly.img.editor.core.component.rememberDuplicate 13 | import ly.img.editor.core.component.rememberEditText 14 | import ly.img.editor.core.component.rememberEffect 15 | import ly.img.editor.core.component.rememberFillStroke 16 | import ly.img.editor.core.component.rememberFormatText 17 | import ly.img.editor.core.component.rememberLayer 18 | import ly.img.editor.core.component.rememberMoveAsClip 19 | import ly.img.editor.core.component.rememberMoveAsOverlay 20 | import ly.img.editor.core.component.rememberReplace 21 | import ly.img.editor.core.component.rememberShape 22 | import ly.img.editor.core.component.rememberSplit 23 | import ly.img.editor.core.component.rememberTextBackground 24 | import ly.img.editor.core.component.rememberVolume 25 | import ly.img.editor.rememberForDesign 26 | 27 | // Add this composable to your NavHost 28 | @Composable 29 | fun NewListBuilderInspectorBarSolution(navController: NavHostController) { 30 | val engineConfiguration = EngineConfiguration.rememberForDesign( 31 | license = "", 32 | ) 33 | 34 | val editorConfiguration = EditorConfiguration.rememberForDesign( 35 | inspectorBar = { 36 | InspectorBar.remember( 37 | // highlight-newListBuilder 38 | listBuilder = InspectorBar.ListBuilder.remember { 39 | add { 40 | InspectorBar.Button.remember( 41 | id = EditorComponentId("my.package.inspectorBar.button.custom"), 42 | onClick = {}, 43 | vectorIcon = null, 44 | text = { "Custom Button" }, 45 | ) 46 | } 47 | add { InspectorBar.Button.rememberDuplicate() } 48 | add { InspectorBar.Button.rememberDelete() } 49 | add { InspectorBar.Button.rememberAdjustments() } 50 | add { InspectorBar.Button.rememberEffect() } 51 | add { InspectorBar.Button.rememberBlur() } 52 | add { InspectorBar.Button.rememberReplace() } 53 | add { InspectorBar.Button.rememberEditText() } 54 | add { InspectorBar.Button.rememberFormatText() } 55 | add { InspectorBar.Button.rememberFillStroke() } 56 | add { InspectorBar.Button.rememberTextBackground() } 57 | add { InspectorBar.Button.rememberVolume() } 58 | add { InspectorBar.Button.rememberCrop() } 59 | add { InspectorBar.Button.rememberShape() } 60 | add { InspectorBar.Button.rememberLayer() } 61 | add { InspectorBar.Button.rememberSplit() } 62 | add { InspectorBar.Button.rememberMoveAsClip() } 63 | add { InspectorBar.Button.rememberMoveAsOverlay() } 64 | }, 65 | // highlight-newListBuilder 66 | ) 67 | }, 68 | ) 69 | DesignEditor( 70 | engineConfiguration = engineConfiguration, 71 | editorConfiguration = editorConfiguration, 72 | ) { 73 | // You can set result here 74 | navController.popBackStack() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /editor-guides-configuration-navigation-bar/NavigationBarListBuilders.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.compose.ui.Alignment 3 | import androidx.compose.ui.res.stringResource 4 | import ly.img.editor.core.R 5 | import ly.img.editor.core.component.NavigationBar 6 | import ly.img.editor.core.component.rememberCloseEditor 7 | import ly.img.editor.core.component.rememberExport 8 | import ly.img.editor.core.component.rememberNextPage 9 | import ly.img.editor.core.component.rememberPreviousPage 10 | import ly.img.editor.core.component.rememberRedo 11 | import ly.img.editor.core.component.rememberTogglePagesMode 12 | import ly.img.editor.core.component.rememberTogglePreviewMode 13 | import ly.img.editor.core.component.rememberUndo 14 | 15 | // highlight-listBuilders 16 | // highlight-listBuilders-design 17 | @Composable 18 | fun NavigationBar.ListBuilder.rememberForDesign() = NavigationBar.ListBuilder.remember { 19 | aligned(alignment = Alignment.Start) { 20 | add { NavigationBar.Button.rememberCloseEditor() } 21 | } 22 | aligned(alignment = Alignment.End) { 23 | add { NavigationBar.Button.rememberUndo() } 24 | add { NavigationBar.Button.rememberRedo() } 25 | add { NavigationBar.Button.rememberTogglePagesMode() } 26 | add { NavigationBar.Button.rememberExport() } 27 | } 28 | } 29 | // highlight-listBuilders-design 30 | 31 | // highlight-listBuilders-photo 32 | @Composable 33 | fun NavigationBar.ListBuilder.rememberForPhoto() = NavigationBar.ListBuilder.remember { 34 | aligned(alignment = Alignment.Start) { 35 | add { NavigationBar.Button.rememberCloseEditor() } 36 | } 37 | aligned(alignment = Alignment.End) { 38 | add { NavigationBar.Button.rememberUndo() } 39 | add { NavigationBar.Button.rememberRedo() } 40 | add { NavigationBar.Button.rememberTogglePreviewMode() } 41 | add { NavigationBar.Button.rememberExport() } 42 | } 43 | } 44 | // highlight-listBuilders-photo 45 | 46 | // highlight-listBuilders-video 47 | @Composable 48 | fun NavigationBar.ListBuilder.rememberForVideo() = NavigationBar.ListBuilder.remember { 49 | aligned(alignment = Alignment.Start) { 50 | add { NavigationBar.Button.rememberCloseEditor() } 51 | } 52 | aligned(alignment = Alignment.End) { 53 | add { NavigationBar.Button.rememberUndo() } 54 | add { NavigationBar.Button.rememberRedo() } 55 | add { NavigationBar.Button.rememberExport() } 56 | } 57 | } 58 | // highlight-listBuilders-video 59 | 60 | // highlight-listBuilders-apparel 61 | @Composable 62 | fun NavigationBar.ListBuilder.rememberForApparel() = NavigationBar.ListBuilder.remember { 63 | aligned(alignment = Alignment.Start) { 64 | add { NavigationBar.Button.rememberCloseEditor() } 65 | } 66 | aligned(alignment = Alignment.End) { 67 | add { NavigationBar.Button.rememberUndo() } 68 | add { NavigationBar.Button.rememberRedo() } 69 | add { NavigationBar.Button.rememberTogglePreviewMode() } 70 | add { NavigationBar.Button.rememberExport() } 71 | } 72 | } 73 | // highlight-listBuilders-apparel 74 | 75 | // highlight-listBuilders-postcard 76 | @Composable 77 | fun NavigationBar.ListBuilder.rememberForPostcard() = NavigationBar.ListBuilder.remember { 78 | aligned(alignment = Alignment.Start) { 79 | add { NavigationBar.Button.rememberCloseEditor() } 80 | add { 81 | NavigationBar.Button.rememberPreviousPage( 82 | text = { stringResource(R.string.ly_img_editor_design) }, 83 | ) 84 | } 85 | } 86 | 87 | aligned(alignment = Alignment.CenterHorizontally) { 88 | add { NavigationBar.Button.rememberUndo() } 89 | add { NavigationBar.Button.rememberRedo() } 90 | add { NavigationBar.Button.rememberTogglePreviewMode() } 91 | } 92 | 93 | aligned(alignment = Alignment.End) { 94 | add { 95 | NavigationBar.Button.rememberNextPage( 96 | text = { stringResource(R.string.ly_img_editor_write) }, 97 | ) 98 | } 99 | add { NavigationBar.Button.rememberExport() } 100 | } 101 | } 102 | // highlight-listBuilders-postcard 103 | // highlight-listBuilders 104 | -------------------------------------------------------------------------------- /editor-guides-configuration-navigation-bar/NewListBuilderNavigationBarSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.foundation.layout.Arrangement 2 | import androidx.compose.runtime.Composable 3 | import androidx.compose.ui.Alignment 4 | import androidx.compose.ui.unit.dp 5 | import androidx.navigation.NavHostController 6 | import ly.img.editor.DesignEditor 7 | import ly.img.editor.EditorConfiguration 8 | import ly.img.editor.EngineConfiguration 9 | import ly.img.editor.core.component.EditorComponentId 10 | import ly.img.editor.core.component.NavigationBar 11 | import ly.img.editor.core.component.rememberCloseEditor 12 | import ly.img.editor.core.component.rememberExport 13 | import ly.img.editor.core.component.rememberRedo 14 | import ly.img.editor.core.component.rememberUndo 15 | import ly.img.editor.rememberForDesign 16 | 17 | // Add this composable to your NavHost 18 | @Composable 19 | fun NewListBuilderNavigationBarSolution(navController: NavHostController) { 20 | val engineConfiguration = EngineConfiguration.rememberForDesign( 21 | license = "", 22 | ) 23 | 24 | val editorConfiguration = EditorConfiguration.rememberForDesign( 25 | navigationBar = { 26 | NavigationBar.remember( 27 | // highlight-newListBuilder 28 | listBuilder = NavigationBar.ListBuilder.remember { 29 | aligned(alignment = Alignment.Start) { 30 | add { NavigationBar.Button.rememberCloseEditor() } 31 | } 32 | aligned(alignment = Alignment.CenterHorizontally) { 33 | add { 34 | NavigationBar.Button.remember( 35 | id = EditorComponentId("my.package.navigationBar.button.custom"), 36 | vectorIcon = null, 37 | text = { "Custom Button" }, 38 | onClick = {}, 39 | ) 40 | } 41 | } 42 | aligned( 43 | alignment = Alignment.End, 44 | arrangement = Arrangement.spacedBy(2.dp), 45 | ) { 46 | add { NavigationBar.Button.rememberExport() } 47 | add { NavigationBar.Button.rememberUndo() } 48 | add { NavigationBar.Button.rememberRedo() } 49 | } 50 | }, 51 | // highlight-newListBuilder 52 | ) 53 | }, 54 | ) 55 | DesignEditor( 56 | engineConfiguration = engineConfiguration, 57 | editorConfiguration = editorConfiguration, 58 | ) { 59 | // You can set result here 60 | navController.popBackStack() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /editor-guides-configuration-navigation-bar/SimpleNavigationBarSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.animation.EnterTransition 2 | import androidx.compose.animation.ExitTransition 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.PaddingValues 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.heightIn 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.material3.MaterialTheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.remember 13 | import androidx.compose.ui.Alignment 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.unit.dp 16 | import androidx.navigation.NavHostController 17 | import ly.img.editor.DesignEditor 18 | import ly.img.editor.EditorConfiguration 19 | import ly.img.editor.EngineConfiguration 20 | import ly.img.editor.core.LocalEditorScope 21 | import ly.img.editor.core.component.NavigationBar 22 | import ly.img.editor.rememberForDesign 23 | 24 | // Add this composable to your NavHost 25 | @Composable 26 | fun SimpleNavigationBarSolution(navController: NavHostController) { 27 | val engineConfiguration = EngineConfiguration.rememberForDesign( 28 | license = "", 29 | ) 30 | // highlight-navigationBarConfiguration 31 | val editorConfiguration = EditorConfiguration.rememberForDesign( 32 | navigationBar = { 33 | NavigationBar.remember( 34 | // highlight-navigationBarConfiguration-scope 35 | scope = LocalEditorScope.current.run { 36 | remember(this) { NavigationBar.Scope(parentScope = this) } 37 | }, 38 | // highlight-navigationBarConfiguration-scope 39 | // highlight-navigationBarConfiguration-visible 40 | visible = { true }, 41 | // highlight-navigationBarConfiguration-enterTransition 42 | enterTransition = { EnterTransition.None }, 43 | // highlight-navigationBarConfiguration-exitTransition 44 | exitTransition = { ExitTransition.None }, 45 | // highlight-navigationBarConfiguration-decoration 46 | decoration = { 47 | // Also available via NavigationBar.DefaultDecoration 48 | Box( 49 | modifier = 50 | Modifier 51 | .fillMaxWidth() 52 | .heightIn(min = 64.dp) 53 | .background(MaterialTheme.colorScheme.surface) 54 | .padding(PaddingValues(horizontal = 4.dp)), 55 | contentAlignment = Alignment.Center, 56 | ) { 57 | it() 58 | } 59 | }, 60 | // highlight-navigationBarConfiguration-decoration 61 | // highlight-navigationBarConfiguration-listBuilder 62 | listBuilder = NavigationBar.ListBuilder.remember { }, 63 | // highlight-navigationBarConfiguration-horizontalArrangement 64 | horizontalArrangement = { Arrangement.SpaceEvenly }, 65 | // highlight-navigationBarConfiguration-itemDecoration 66 | // default value is { it() } 67 | itemDecoration = { 68 | Box(modifier = Modifier.padding(2.dp)) { 69 | it() 70 | } 71 | }, 72 | // highlight-navigationBarConfiguration-itemDecoration 73 | ) 74 | }, 75 | ) 76 | // highlight-navigationBarConfiguration 77 | DesignEditor( 78 | engineConfiguration = engineConfiguration, 79 | editorConfiguration = editorConfiguration, 80 | ) { 81 | // You can set result here 82 | navController.popBackStack() 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /editor-guides-configuration-overlay/OverlayCustomState.kt: -------------------------------------------------------------------------------- 1 | import android.os.Parcel 2 | import android.os.Parcelable 3 | import androidx.core.os.ParcelCompat 4 | import ly.img.editor.EditorUiState 5 | 6 | // highlight-configuration-custom-state 7 | data class OverlayCustomState( 8 | // hide default loading so we can use custom loading 9 | val baseState: EditorUiState = EditorUiState(showLoading = false), 10 | val showCustomLoading: Boolean = true, 11 | ) : Parcelable { 12 | constructor(parcel: Parcel) : this( 13 | baseState = ParcelCompat.readParcelable(parcel, EditorUiState::class.java.classLoader, EditorUiState::class.java)!!, 14 | showCustomLoading = parcel.readByte() != 0.toByte(), 15 | ) 16 | 17 | override fun writeToParcel( 18 | parcel: Parcel, 19 | flags: Int, 20 | ) { 21 | parcel.writeParcelable(baseState, flags) 22 | parcel.writeByte(if (showCustomLoading) 1 else 0) 23 | } 24 | 25 | override fun describeContents() = 0 26 | 27 | companion object CREATOR : Parcelable.Creator { 28 | override fun createFromParcel(parcel: Parcel): OverlayCustomState = OverlayCustomState(parcel) 29 | 30 | override fun newArray(size: Int): Array = arrayOfNulls(size) 31 | } 32 | } 33 | // highlight-configuration-custom-state 34 | -------------------------------------------------------------------------------- /editor-guides-configuration-overlay/OverlayEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.material3.AlertDialog 2 | import androidx.compose.material3.Text 3 | import androidx.compose.material3.TextButton 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.ui.window.DialogProperties 6 | import androidx.navigation.NavHostController 7 | import ly.img.editor.DesignEditor 8 | import ly.img.editor.EditorConfiguration 9 | import ly.img.editor.EditorDefaults 10 | import ly.img.editor.EngineConfiguration 11 | import ly.img.editor.HideLoading 12 | import ly.img.editor.ShowLoading 13 | import ly.img.editor.core.event.EditorEvent 14 | import ly.img.editor.rememberForDesign 15 | 16 | // Add this composable to your NavHost 17 | @Composable 18 | fun OverlayEditorSolution(navController: NavHostController) { 19 | val engineConfiguration = EngineConfiguration.rememberForDesign( 20 | license = "", 21 | ) 22 | val editorConfiguration = EditorConfiguration.remember( 23 | initialState = OverlayCustomState(), 24 | // highlight-configuration-on-event 25 | onEvent = { state, event -> 26 | when (event) { 27 | is ShowLoading -> { 28 | state.copy(showCustomLoading = true) 29 | } 30 | is HideLoading -> { 31 | state.copy(showCustomLoading = false) 32 | } 33 | else -> { 34 | // handle other default events 35 | state.copy(baseState = EditorDefaults.onEvent(editorContext.activity, state.baseState, event)) 36 | } 37 | } 38 | }, 39 | // highlight-configuration-on-event 40 | // highlight-configuration-overlay 41 | overlay = { state -> 42 | if (state.showCustomLoading) { 43 | AlertDialog( 44 | onDismissRequest = { }, 45 | title = { 46 | Text(text = "Please wait. If you want to close the editor, click the button.") 47 | }, 48 | confirmButton = { 49 | TextButton( 50 | onClick = { 51 | editorContext.eventHandler.send(HideLoading) 52 | editorContext.eventHandler.send(EditorEvent.CloseEditor()) 53 | }, 54 | ) { 55 | Text(text = "Close") 56 | } 57 | }, 58 | properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false), 59 | ) 60 | } 61 | EditorDefaults.Overlay(state = state.baseState, eventHandler = editorContext.eventHandler) 62 | }, 63 | // highlight-configuration-overlay 64 | ) 65 | DesignEditor( 66 | engineConfiguration = engineConfiguration, 67 | editorConfiguration = editorConfiguration, 68 | ) { 69 | // You can set result here 70 | navController.popBackStack() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /editor-guides-configuration-theming/ThemingEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.DesignEditor 4 | import ly.img.editor.EditorConfiguration 5 | import ly.img.editor.EditorUiMode 6 | import ly.img.editor.EngineConfiguration 7 | import ly.img.editor.rememberForDesign 8 | 9 | // Add this composable to your NavHost 10 | @Composable 11 | fun ThemingEditorSolution(navController: NavHostController) { 12 | val engineConfiguration = EngineConfiguration.rememberForDesign( 13 | license = "", 14 | ) 15 | val editorConfiguration = EditorConfiguration.rememberForDesign( 16 | // highlight-configuration-uiMode 17 | uiMode = EditorUiMode.DARK, 18 | // highlight-configuration-uiMode 19 | ) 20 | DesignEditor( 21 | engineConfiguration = engineConfiguration, 22 | editorConfiguration = editorConfiguration, 23 | ) { 24 | // You can set result here 25 | navController.popBackStack() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /editor-guides-configuration-ui-events/UiEventsEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import android.widget.Toast 2 | import androidx.compose.runtime.Composable 3 | import androidx.navigation.NavHostController 4 | import ly.img.editor.DesignEditor 5 | import ly.img.editor.EditorConfiguration 6 | import ly.img.editor.EditorDefaults 7 | import ly.img.editor.EditorUiState 8 | import ly.img.editor.EngineConfiguration 9 | import ly.img.editor.ShowLoading 10 | import ly.img.editor.core.event.EditorEvent 11 | 12 | // highlight-configuration-custom-event 13 | data object OnCreateCustomEvent : EditorEvent 14 | // highlight-configuration-custom-event 15 | 16 | // Add this composable to your NavHost 17 | @Composable 18 | fun UiEventsEditorSolution(navController: NavHostController) { 19 | val engineConfiguration = EngineConfiguration.remember( 20 | license = "", 21 | // highlight-configuration-engine-callback 22 | onCreate = { 23 | EditorDefaults.onCreate( 24 | engine = editorContext.engine, 25 | sceneUri = EngineConfiguration.defaultDesignSceneUri, 26 | eventHandler = editorContext.eventHandler, 27 | ) 28 | // highlight-configuration-send-event 29 | editorContext.eventHandler.send(OnCreateCustomEvent) 30 | // highlight-configuration-send-event 31 | }, 32 | // highlight-configuration-engine-callback 33 | ) 34 | val editorConfiguration = EditorConfiguration.remember( 35 | initialState = EditorUiState(), 36 | // highlight-configuration-on-event 37 | onEvent = { state, event -> 38 | when (event) { 39 | // highlight-configuration-on-event-new 40 | OnCreateCustomEvent -> { 41 | Toast.makeText(editorContext.activity, "Editor is created!", Toast.LENGTH_SHORT).show() 42 | state 43 | } 44 | // highlight-configuration-on-event-new 45 | // highlight-configuration-on-event-default-override 46 | ShowLoading -> { 47 | state.copy(showLoading = true) 48 | } 49 | // highlight-configuration-on-event-default-override 50 | // highlight-configuration-on-event-default-remaining 51 | else -> { 52 | // handle other default events 53 | EditorDefaults.onEvent(editorContext.activity, state, event) 54 | } 55 | // highlight-configuration-on-event-default-remaining 56 | } 57 | }, 58 | // highlight-configuration-on-event 59 | ) 60 | DesignEditor( 61 | engineConfiguration = engineConfiguration, 62 | editorConfiguration = editorConfiguration, 63 | ) { 64 | // You can set result here 65 | navController.popBackStack() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /editor-guides-quickstart/EditorActivity.kt: -------------------------------------------------------------------------------- 1 | import android.os.Bundle 2 | import androidx.activity.compose.setContent 3 | import androidx.appcompat.app.AppCompatActivity 4 | import ly.img.editor.DesignEditor 5 | import ly.img.editor.EngineConfiguration 6 | import ly.img.editor.rememberForDesign 7 | 8 | // Launch this activity via intent 9 | class EditorActivity : AppCompatActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | setContent { 13 | // highlight-engine-configuration 14 | val engineConfiguration = EngineConfiguration.rememberForDesign( 15 | license = "", 16 | userId = "", 17 | ) 18 | // highlight-engine-configuration 19 | // highlight-editor-invoke 20 | DesignEditor(engineConfiguration = engineConfiguration) { 21 | // You can set result here 22 | finish() 23 | } 24 | // highlight-editor-invoke 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /editor-guides-quickstart/EditorComposable.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.DesignEditor 4 | import ly.img.editor.EngineConfiguration 5 | import ly.img.editor.rememberForDesign 6 | 7 | // Add this composable to your NavHost 8 | @Composable 9 | fun EditorComposable(navController: NavHostController) { 10 | // highlight-engine-configuration 11 | val engineConfiguration = EngineConfiguration.rememberForDesign( 12 | license = "", 13 | userId = "", 14 | ) 15 | // highlight-engine-configuration 16 | // highlight-editor-invoke 17 | DesignEditor(engineConfiguration = engineConfiguration) { 18 | // You can set result here 19 | navController.popBackStack() 20 | } 21 | // highlight-editor-invoke 22 | } 23 | -------------------------------------------------------------------------------- /editor-guides-quickstart/EditorFragment.kt: -------------------------------------------------------------------------------- 1 | import android.os.Bundle 2 | import android.view.LayoutInflater 3 | import android.view.View 4 | import android.view.ViewGroup 5 | import androidx.compose.ui.platform.ComposeView 6 | import androidx.fragment.app.Fragment 7 | import ly.img.editor.DesignEditor 8 | import ly.img.editor.EngineConfiguration 9 | import ly.img.editor.rememberForDesign 10 | 11 | // Add this fragment via fragmentManager API 12 | class EditorFragment : Fragment() { 13 | override fun onCreateView( 14 | inflater: LayoutInflater, 15 | container: ViewGroup?, 16 | savedInstanceState: Bundle?, 17 | ): View = ComposeView(requireContext()).apply { 18 | setContent { 19 | // highlight-engine-configuration 20 | val engineConfiguration = EngineConfiguration.rememberForDesign( 21 | license = "", 22 | userId = "", 23 | ) 24 | // highlight-engine-configuration 25 | // highlight-editor-invoke 26 | DesignEditor(engineConfiguration = engineConfiguration) { 27 | // You can set result here 28 | parentFragmentManager.popBackStack() 29 | } 30 | // highlight-editor-invoke 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /editor-guides-quickstart/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | namespace "ly.img.editor.showcase" 8 | compileSdk 35 9 | 10 | defaultConfig { 11 | applicationId "ly.img.editor.showcase" 12 | // highlight-minsdk 13 | minSdk 24 14 | // highlight-minsdk 15 | targetSdk 35 16 | versionCode 1 17 | versionName "1.0" 18 | // highlight-abi-filters 19 | ndk { 20 | abiFilters "arm64-v8a", "armeabi-v7a", "x86_64", "x86" 21 | } 22 | // highlight-abi-filters 23 | } 24 | 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | 34 | // highlight-buildFeatures 35 | buildFeatures { 36 | compose true 37 | } 38 | // highlight-buildFeatures 39 | 40 | // highlight-kotlinCompilerExtensionVersion 41 | composeOptions { 42 | kotlinCompilerExtensionVersion = "1.5.3" 43 | } 44 | // highlight-kotlinCompilerExtensionVersion 45 | } 46 | 47 | dependencies { 48 | // highlight-dependency 49 | implementation "ly.img:editor:1.52.0" 50 | // Add this as well if you want to use img.ly camera's powerful features. 51 | // If not, then system camera will be used everywhere. 52 | implementation "ly.img:camera:1.52.0" 53 | // highlight-dependency 54 | // highlight-bom-version 55 | implementation(platform("androidx.compose:compose-bom:2023.05.01")) 56 | // highlight-bom-version 57 | implementation "androidx.activity:activity-compose:1.8.2" 58 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" 59 | } 60 | -------------------------------------------------------------------------------- /editor-guides-quickstart/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | dependencyResolutionManagement { 10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 11 | repositories { 12 | google() 13 | mavenCentral() 14 | // highlight-maven-dependency 15 | maven { 16 | name "IMG.LY Artifactory" 17 | url "https://artifactory.img.ly/artifactory/maven" 18 | mavenContent { 19 | includeGroup("ly.img") 20 | } 21 | } 22 | // highlight-maven-dependency 23 | } 24 | } 25 | 26 | rootProject.name = "My App" 27 | include ':app' 28 | -------------------------------------------------------------------------------- /editor-guides-solutions-apparel-editor/ApparelEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.ApparelEditor 4 | import ly.img.editor.EditorConfiguration 5 | import ly.img.editor.EngineConfiguration 6 | import ly.img.editor.rememberForApparel 7 | 8 | // Add this composable to your NavHost 9 | @Composable 10 | fun ApparelEditorSolution(navController: NavHostController) { 11 | // highlight-engine-configuration 12 | val engineConfiguration = EngineConfiguration.rememberForApparel( 13 | license = "", 14 | userId = "", 15 | ) 16 | // highlight-engine-configuration 17 | // highlight-editor-configuration 18 | val editorConfiguration = EditorConfiguration.rememberForApparel() 19 | // highlight-editor-configuration 20 | // highlight-editor-initialization 21 | ApparelEditor( 22 | engineConfiguration = engineConfiguration, 23 | editorConfiguration = editorConfiguration, 24 | ) { 25 | // You can set result here 26 | navController.popBackStack() 27 | } 28 | // highlight-editor-initialization 29 | } 30 | -------------------------------------------------------------------------------- /editor-guides-solutions-design-editor/DesignEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.DesignEditor 4 | import ly.img.editor.EditorConfiguration 5 | import ly.img.editor.EngineConfiguration 6 | import ly.img.editor.rememberForDesign 7 | 8 | // Add this composable to your NavHost 9 | @Composable 10 | fun DesignEditorSolution(navController: NavHostController) { 11 | // highlight-engine-configuration 12 | val engineConfiguration = EngineConfiguration.rememberForDesign( 13 | license = "", 14 | userId = "", 15 | ) 16 | // highlight-engine-configuration 17 | // highlight-editor-configuration 18 | val editorConfiguration = EditorConfiguration.rememberForDesign() 19 | // highlight-editor-configuration 20 | // highlight-editor-initialization 21 | DesignEditor( 22 | engineConfiguration = engineConfiguration, 23 | editorConfiguration = editorConfiguration, 24 | ) { 25 | // You can set result here 26 | navController.popBackStack() 27 | } 28 | // highlight-editor-initialization 29 | } 30 | -------------------------------------------------------------------------------- /editor-guides-solutions-photo-editor/PhotoEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import android.util.SizeF 3 | import androidx.compose.runtime.Composable 4 | import androidx.navigation.NavHostController 5 | import ly.img.editor.EditorConfiguration 6 | import ly.img.editor.EngineConfiguration 7 | import ly.img.editor.PhotoEditor 8 | import ly.img.editor.rememberForPhoto 9 | 10 | // Add this composable to your NavHost 11 | @Composable 12 | fun PhotoEditorSolution(navController: NavHostController) { 13 | // highlight-engine-configuration 14 | val engineConfiguration = EngineConfiguration.rememberForPhoto( 15 | license = "", 16 | imageUri = Uri.parse("https://img.ly/static/ubq_samples/sample_4.jpg"), 17 | imageSize = SizeF(1080F, 1920F), 18 | userId = "", 19 | ) 20 | // highlight-engine-configuration 21 | // highlight-editor-configuration 22 | val editorConfiguration = EditorConfiguration.rememberForPhoto() 23 | // highlight-editor-configuration 24 | // highlight-editor-initialization 25 | PhotoEditor( 26 | engineConfiguration = engineConfiguration, 27 | editorConfiguration = editorConfiguration, 28 | ) { 29 | // You can set result here 30 | navController.popBackStack() 31 | } 32 | // highlight-editor-initialization 33 | } 34 | -------------------------------------------------------------------------------- /editor-guides-solutions-postcard-editor/PostcardEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.EditorConfiguration 4 | import ly.img.editor.EngineConfiguration 5 | import ly.img.editor.PostcardEditor 6 | import ly.img.editor.rememberForPostcard 7 | 8 | // Add this composable to your NavHost 9 | @Composable 10 | fun PostcardEditorSolution(navController: NavHostController) { 11 | // highlight-engine-configuration 12 | val engineConfiguration = EngineConfiguration.rememberForPostcard( 13 | license = "", 14 | userId = "", 15 | ) 16 | // highlight-engine-configuration 17 | // highlight-editor-configuration 18 | val editorConfiguration = EditorConfiguration.rememberForPostcard() 19 | // highlight-editor-configuration 20 | // highlight-editor-initialization 21 | PostcardEditor( 22 | engineConfiguration = engineConfiguration, 23 | editorConfiguration = editorConfiguration, 24 | ) { 25 | // You can set result here 26 | navController.popBackStack() 27 | } 28 | // highlight-editor-initialization 29 | } 30 | -------------------------------------------------------------------------------- /editor-guides-solutions-video-editor/VideoEditorSolution.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.runtime.Composable 2 | import androidx.navigation.NavHostController 3 | import ly.img.editor.EditorConfiguration 4 | import ly.img.editor.EngineConfiguration 5 | import ly.img.editor.VideoEditor 6 | import ly.img.editor.rememberForVideo 7 | 8 | // Add this composable to your NavHost 9 | @Composable 10 | fun VideoEditorSolution(navController: NavHostController) { 11 | // highlight-engine-configuration 12 | val engineConfiguration = EngineConfiguration.rememberForVideo( 13 | license = "", 14 | userId = "", 15 | ) 16 | // highlight-engine-configuration 17 | // highlight-editor-configuration 18 | val editorConfiguration = EditorConfiguration.rememberForVideo() 19 | // highlight-editor-configuration 20 | // highlight-editor-initialization 21 | VideoEditor( 22 | engineConfiguration = engineConfiguration, 23 | editorConfiguration = editorConfiguration, 24 | ) { 25 | // You can set result here 26 | navController.popBackStack() 27 | } 28 | // highlight-editor-initialization 29 | } 30 | -------------------------------------------------------------------------------- /engine-guides-bool-ops/BoolOps.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.BooleanOperation 5 | import ly.img.engine.DesignBlockType 6 | import ly.img.engine.Engine 7 | import ly.img.engine.FillType 8 | import ly.img.engine.ShapeType 9 | 10 | fun usingBoolOps( 11 | license: String, 12 | userId: String, 13 | ) = CoroutineScope(Dispatchers.Main).launch { 14 | val engine = Engine.getInstance(id = "ly.img.engine.example") 15 | engine.start(license = license, userId = userId) 16 | engine.bindOffscreen(width = 1080, height = 1920) 17 | 18 | // highlight-setup 19 | val scene = engine.scene.create() 20 | 21 | val page = engine.block.create(DesignBlockType.Page) 22 | engine.block.setWidth(page, value = 800F) 23 | engine.block.setHeight(page, value = 600F) 24 | engine.block.appendChild(parent = scene, child = page) 25 | // highlight-setup 26 | 27 | // highlight-combine-union 28 | val circle1 = engine.block.create(DesignBlockType.Graphic) 29 | engine.block.setShape(circle1, shape = engine.block.createShape(ShapeType.Ellipse)) 30 | engine.block.setFill(circle1, fill = engine.block.createFill(FillType.Color)) 31 | engine.block.setPositionX(circle1, value = 30F) 32 | engine.block.setPositionY(circle1, value = 30F) 33 | engine.block.setWidth(circle1, value = 40F) 34 | engine.block.setHeight(circle1, value = 40F) 35 | engine.block.appendChild(parent = page, child = circle1) 36 | 37 | val circle2 = engine.block.create(DesignBlockType.Graphic) 38 | engine.block.setShape(circle2, shape = engine.block.createShape(ShapeType.Ellipse)) 39 | engine.block.setFill(circle2, fill = engine.block.createFill(FillType.Color)) 40 | engine.block.setPositionX(circle2, value = 80F) 41 | engine.block.setPositionY(circle2, value = 30F) 42 | engine.block.setWidth(circle2, value = 40F) 43 | engine.block.setHeight(circle2, value = 40F) 44 | engine.block.appendChild(parent = page, child = circle2) 45 | 46 | val circle3 = engine.block.create(DesignBlockType.Graphic) 47 | engine.block.setShape(circle3, shape = engine.block.createShape(ShapeType.Ellipse)) 48 | engine.block.setFill(circle3, fill = engine.block.createFill(FillType.Color)) 49 | engine.block.setPositionX(circle3, value = 50F) 50 | engine.block.setPositionY(circle3, value = 50F) 51 | engine.block.setWidth(circle3, value = 50F) 52 | engine.block.setHeight(circle3, value = 50F) 53 | engine.block.appendChild(parent = page, child = circle3) 54 | 55 | engine.block.combine(listOf(circle1, circle2, circle3), op = BooleanOperation.UNION) 56 | // highlight-combine-unions 57 | 58 | // highlight-combine-difference 59 | val text = engine.block.create(DesignBlockType.Text) 60 | engine.block.replaceText(text, "Removed text") 61 | engine.block.setPositionX(text, value = 10F) 62 | engine.block.setPositionY(text, value = 40F) 63 | engine.block.setWidth(text, value = 80F) 64 | engine.block.setHeight(text, value = 10F) 65 | engine.block.appendChild(parent = page, child = text) 66 | 67 | val block = engine.block.create(DesignBlockType.Graphic) 68 | engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) 69 | val imageFill = engine.block.createFill(FillType.Image) 70 | engine.block.setFill(block = block, fill = imageFill) 71 | engine.block.setPositionX(block, value = 0F) 72 | engine.block.setPositionY(block, value = 0F) 73 | engine.block.setWidth(block, value = 100F) 74 | engine.block.setHeight(block, value = 100F) 75 | engine.block.setString( 76 | block = imageFill, 77 | property = "fill/image/imageFileURI", 78 | value = "https://img.ly/static/ubq_samples/sample_1.jpg", 79 | ) 80 | engine.block.appendChild(parent = page, child = block) 81 | 82 | engine.block.sendToBack(block) 83 | engine.block.forceLoadResources(listOf(block)) 84 | val difference = engine.block.combine(listOf(block, text), op = BooleanOperation.DIFFERENCE) 85 | // highlight-combine-difference 86 | 87 | engine.stop() 88 | } 89 | -------------------------------------------------------------------------------- /engine-guides-buffers/Buffers.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.DesignBlockType 5 | import ly.img.engine.Engine 6 | import java.nio.ByteBuffer 7 | import kotlin.math.PI 8 | import kotlin.math.sin 9 | 10 | fun buffers( 11 | license: String, 12 | userId: String, 13 | ) = CoroutineScope(Dispatchers.Main).launch { 14 | val engine = Engine.getInstance(id = "ly.img.engine.example") 15 | engine.start(license = license, userId = userId) 16 | engine.bindOffscreen(width = 1080, height = 1920) 17 | 18 | // highlight-setup 19 | val scene = engine.scene.create() 20 | val page = engine.block.create(DesignBlockType.Page) 21 | engine.block.appendChild(parent = scene, child = page) 22 | // highlight-setup 23 | 24 | // Create an audio block and append it to the page 25 | val audioBlock = engine.block.create(DesignBlockType.Audio) 26 | engine.block.appendChild(parent = page, child = audioBlock) 27 | 28 | // Create a buffer 29 | // highlight-EditorApi.createBuffer 30 | val audioBuffer = engine.editor.createBuffer() 31 | 32 | // Reference the audio buffer resource from the audio block 33 | engine.block.setUri( 34 | block = audioBlock, 35 | property = "audio/fileURI", 36 | value = audioBuffer, 37 | ) 38 | 39 | // highlight-EditorApi.setBufferData 40 | // Generate 10 seconds of stereo 48 kHz audio data 41 | val sampleCount = 10 * 48000 42 | val byteBuffer = ByteBuffer.allocateDirect(2 * 4 * sampleCount) // 2 channels, each 4 bytes 43 | repeat(sampleCount) { 44 | val sample = sin((440 * it * 2 * PI) / 48000).toFloat() 45 | byteBuffer.putFloat(sample) 46 | byteBuffer.putFloat(sample) 47 | } 48 | engine.editor.setBufferData(uri = audioBuffer, offset = 0, data = byteBuffer) 49 | // highlight-EditorApi.setBufferData 50 | 51 | // We can get subranges of the buffer data 52 | // highlight-EditorApi.getBufferData 53 | val chunk = engine.editor.getBufferData(uri = audioBuffer, offset = 0, length = 4096) 54 | 55 | // Get current length of the buffer in bytes 56 | // highlight-EditorApi.getBufferLength 57 | val length = engine.editor.getBufferLength(uri = audioBuffer) 58 | 59 | // Reduce the buffer to half its length, leading to 5 seconds worth of audio 60 | // highlight-EditorApi.setBufferLength 61 | engine.editor.setBufferLength(uri = audioBuffer, length = byteBuffer.capacity() / 2) 62 | 63 | // Free data 64 | // highlight-EditorApi.destroyBuffer 65 | engine.editor.destroyBuffer(uri = audioBuffer) 66 | } 67 | -------------------------------------------------------------------------------- /engine-guides-colors/Colors.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.Color 5 | import ly.img.engine.ColorSpace 6 | import ly.img.engine.DesignBlockType 7 | import ly.img.engine.Engine 8 | import ly.img.engine.FillType 9 | import ly.img.engine.ShapeType 10 | 11 | fun colors( 12 | license: String, 13 | userId: String, 14 | ) = CoroutineScope(Dispatchers.Main).launch { 15 | val engine = Engine.getInstance(id = "ly.img.engine.example") 16 | engine.start(license = license, userId = userId) 17 | engine.bindOffscreen(width = 1080, height = 1920) 18 | 19 | // highlight-setup 20 | val scene = engine.scene.create() 21 | 22 | val page = engine.block.create(DesignBlockType.Page) 23 | engine.block.setWidth(page, value = 800F) 24 | engine.block.setHeight(page, value = 600F) 25 | engine.block.appendChild(parent = scene, child = page) 26 | 27 | val block = engine.block.create(DesignBlockType.Graphic) 28 | engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) 29 | engine.block.setPositionX(block, value = 350F) 30 | engine.block.setPositionY(block, value = 400F) 31 | engine.block.setWidth(block, value = 100F) 32 | engine.block.setHeight(block, value = 100F) 33 | 34 | val fill = engine.block.createFill(FillType.Color) 35 | engine.block.setFill(block, fill = fill) 36 | // highlight-setup 37 | 38 | // highlight-create-colors 39 | val rgbaBlue = Color.fromRGBA(r = 0F, g = 0F, b = 1F, a = 1F) 40 | val cmykRed = Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F, tint = 1F) 41 | val cmykPartialRed = Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F, tint = 0.5F) 42 | 43 | engine.editor.setSpotColor( 44 | name = "Pink-Flamingo", 45 | Color.fromRGBA(r = 0.988F, g = 0.455F, b = 0.992F), 46 | ) 47 | engine.editor.setSpotColor(name = "Yellow", Color.fromCMYK(c = 0F, m = 0F, y = 1F, k = 0F)) 48 | val spotPinkFlamingo = Color.fromSpotColor( 49 | name = "Pink-Flamingo", 50 | tint = 1F, 51 | externalReference = "Crayola", 52 | ) 53 | val spotPartialYellow = Color.fromSpotColor(name = "Yellow", tint = 0.3F) 54 | // highlight-create-colors 55 | 56 | // highlight-apply-colors 57 | engine.block.setColor(fill, property = "fill/color/value", value = rgbaBlue) 58 | engine.block.setColor(fill, property = "fill/color/value", value = cmykRed) 59 | engine.block.setColor(block, property = "stroke/color", value = cmykPartialRed) 60 | engine.block.setColor(fill, property = "fill/color/value", value = spotPinkFlamingo) 61 | engine.block.setColor(block, property = "dropShadow/color", value = spotPartialYellow) 62 | // highlight-apply-colors 63 | 64 | // highlight-convert-color 65 | val cmykBlueConverted = engine.editor.convertColorToColorSpace( 66 | rgbaBlue, 67 | colorSpace = ColorSpace.CMYK, 68 | ) 69 | val rgbaPinkFlamingoConverted = engine.editor.convertColorToColorSpace( 70 | spotPinkFlamingo, 71 | colorSpace = ColorSpace.SRGB, 72 | ) 73 | // highlight-convert-color 74 | 75 | // highlight-find-spot 76 | engine.editor.findAllSpotColors() // ["Crayola-Pink-Flamingo", "Yellow"] 77 | // highlight-find-spot 78 | 79 | // highlight-change-spot 80 | engine.editor.setSpotColor("Yellow", Color.fromCMYK(c = 0.2F, m = 0F, y = 1F, k = 0F)) 81 | // highlight-change-spot 82 | 83 | // highlight-undefine-spot 84 | engine.editor.removeSpotColor("Yellow") 85 | // highlight-undefine-spot 86 | 87 | engine.stop() 88 | } 89 | -------------------------------------------------------------------------------- /engine-guides-create-scene-from-image-blob/CreateSceneFromImageBlob.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.withContext 6 | import ly.img.engine.DesignBlockType 7 | import ly.img.engine.Engine 8 | import java.io.ByteArrayOutputStream 9 | import java.io.File 10 | import java.net.URL 11 | import java.util.UUID 12 | 13 | fun createSceneFromImageBlob( 14 | license: String, 15 | userId: String, 16 | ) = CoroutineScope( 17 | Dispatchers.Main, 18 | ).launch { 19 | val engine = Engine.getInstance(id = "ly.img.engine.example") 20 | engine.start(license = license, userId = userId) 21 | engine.bindOffscreen(width = 1080, height = 1920) 22 | 23 | // highlight-blob 24 | val blobUrl = URL("https://img.ly/static/ubq_samples/sample_4.jpg") 25 | val blob = withContext(Dispatchers.IO) { 26 | val outputStream = ByteArrayOutputStream() 27 | blobUrl.openStream().use { inputStream -> 28 | outputStream.use(inputStream::copyTo) 29 | } 30 | outputStream.toByteArray() 31 | } 32 | // highlight-blob 33 | 34 | // highlight-objectURL 35 | val blobFile = withContext(Dispatchers.IO) { 36 | File.createTempFile(UUID.randomUUID().toString(), ".tmp").apply { 37 | outputStream().use { it.write(blob) } 38 | } 39 | } 40 | val blobUri = Uri.fromFile(blobFile) 41 | // highlight-objectURL 42 | 43 | // highlight-initialImageURL 44 | val scene = engine.scene.createFromImage(blobUri) 45 | // highlight-initialImageURL 46 | 47 | // highlight-findByType 48 | // Find the automatically added graphic block in the scene that contains the image fill. 49 | val block = engine.block.findByType(DesignBlockType.Graphic).first() 50 | // highlight-findByType 51 | 52 | // highlight-set-opacity 53 | // Change its opacity. 54 | engine.block.setOpacity(block, value = 0.5F) 55 | // highlight-set-opacity 56 | 57 | engine.stop() 58 | } 59 | -------------------------------------------------------------------------------- /engine-guides-create-scene-from-image-url/CreateSceneFromImageURL.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import ly.img.engine.DesignBlockType 6 | import ly.img.engine.Engine 7 | 8 | fun createSceneFromImageURL( 9 | license: String, 10 | userId: String, 11 | ) = CoroutineScope( 12 | Dispatchers.Main, 13 | ).launch { 14 | val engine = Engine.getInstance(id = "ly.img.engine.example") 15 | engine.start(license = license, userId = userId) 16 | engine.bindOffscreen(width = 1080, height = 1920) 17 | 18 | // highlight-createFromImage 19 | val imageRemoteUri = Uri.parse("https://img.ly/static/ubq_samples/sample_4.jpg") 20 | val scene = engine.scene.createFromImage(imageRemoteUri) 21 | // highlight-createFromImage 22 | 23 | // highlight-findByType 24 | // Find the automatically added graphic block in the scene that contains the image fill. 25 | val block = engine.block.findByType(DesignBlockType.Graphic).first() 26 | // highlight-findByType 27 | 28 | // highlight-setOpacity 29 | // Change its opacity. 30 | engine.block.setOpacity(block, value = 0.5F) 31 | // highlight-setOpacity 32 | 33 | engine.stop() 34 | } 35 | -------------------------------------------------------------------------------- /engine-guides-create-scene-from-scratch/CreateSceneFromScratch.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.DesignBlockType 5 | import ly.img.engine.Engine 6 | import ly.img.engine.FillType 7 | import ly.img.engine.ShapeType 8 | 9 | fun createSceneFromScratch( 10 | license: String, 11 | userId: String, 12 | ) = CoroutineScope( 13 | Dispatchers.Main, 14 | ).launch { 15 | val engine = Engine.getInstance(id = "ly.img.engine.example") 16 | engine.start(license = license, userId = userId) 17 | engine.bindOffscreen(width = 1080, height = 1920) 18 | 19 | // highlight-create 20 | val scene = engine.scene.create() 21 | // highlight-create 22 | 23 | // highlight-add-page 24 | val page = engine.block.create(DesignBlockType.Page) 25 | engine.block.appendChild(parent = scene, child = page) 26 | // highlight-add-page 27 | 28 | // highlight-add-block-with-star-shape 29 | val block = engine.block.create(DesignBlockType.Graphic) 30 | engine.block.setShape(block = block, shape = engine.block.createShape(ShapeType.Star)) 31 | engine.block.setFill(block = block, fill = engine.block.createFill(FillType.Color)) 32 | engine.block.appendChild(parent = page, child = block) 33 | // highlight-add-block-with-star-shape 34 | 35 | engine.stop() 36 | } 37 | -------------------------------------------------------------------------------- /engine-guides-create-scene-from-video-url/CreateSceneFromVideoURL.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import ly.img.engine.DesignBlockType 6 | import ly.img.engine.Engine 7 | 8 | fun createSceneFromVideoURL( 9 | license: String, 10 | userId: String, 11 | ) = CoroutineScope( 12 | Dispatchers.Main, 13 | ).launch { 14 | val engine = Engine.getInstance(id = "ly.img.engine.example") 15 | engine.start(license = license, userId = userId) 16 | engine.bindOffscreen(width = 1080, height = 1920) 17 | 18 | // highlight-createFromVideo 19 | val videoRemoteUri = Uri.parse("https://img.ly/static/ubq_video_samples/bbb.mp4") 20 | val scene = engine.scene.createFromVideo(videoRemoteUri) 21 | // highlight-createFromVideo 22 | 23 | // highlight-findByType 24 | // Find the automatically added graphic block in the scene that contains the video fill. 25 | val block = engine.block.findByType(DesignBlockType.Graphic).first() 26 | // highlight-findByType 27 | 28 | // highlight-setOpacity 29 | // Change its opacity. 30 | engine.block.setOpacity(block, value = 0.5F) 31 | // highlight-setOpacity 32 | 33 | engine.stop() 34 | } 35 | -------------------------------------------------------------------------------- /engine-guides-custom-lut-filter/CustomLUTFilter.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.DesignBlockType 5 | import ly.img.engine.EffectType 6 | import ly.img.engine.Engine 7 | import ly.img.engine.FillType 8 | import ly.img.engine.ShapeType 9 | 10 | fun customLUTFilter( 11 | license: String, 12 | userId: String, 13 | ) = CoroutineScope( 14 | Dispatchers.Main, 15 | ).launch { 16 | val engine = Engine.getInstance(id = "ly.img.engine.example") 17 | engine.start(license = license, userId = userId) 18 | engine.bindOffscreen(width = 1080, height = 1920) 19 | val scene = engine.scene.create() 20 | 21 | // highlight-load-scene 22 | val page = engine.block.create(DesignBlockType.Page) 23 | engine.block.setWidth(page, value = 100F) 24 | engine.block.setHeight(page, value = 100F) 25 | engine.block.appendChild(parent = scene, child = page) 26 | engine.scene.zoomToBlock( 27 | scene, 28 | paddingLeft = 40F, 29 | paddingTop = 40F, 30 | paddingRight = 40F, 31 | paddingBottom = 40F, 32 | ) 33 | // highlight-load-scene 34 | 35 | // highlight-create-rect 36 | val rect = engine.block.create(DesignBlockType.Graphic) 37 | engine.block.setShape(rect, shape = engine.block.createShape(ShapeType.Rect)) 38 | engine.block.setWidth(rect, value = 100F) 39 | engine.block.setHeight(rect, value = 100F) 40 | engine.block.appendChild(parent = page, child = rect) 41 | // highlight-create-rect 42 | 43 | // highlight-create-image-fill 44 | val imageFill = engine.block.createFill(FillType.Image) 45 | engine.block.setString( 46 | imageFill, 47 | property = "fill/image/imageFileURI", 48 | value = "https://img.ly/static/ubq_samples/sample_1.jpg", 49 | ) 50 | // highlight-create-image-fill 51 | 52 | // highlight-create-lut-filter 53 | val lutFilter = engine.block.createEffect(EffectType.LutFilter) 54 | engine.block.setBoolean(lutFilter, property = "effect/enabled", value = true) 55 | engine.block.setFloat(lutFilter, property = "effect/lut_filter/intensity", value = 0.9F) 56 | 57 | @Suppress("ktlint:standard:max-line-length") 58 | val lutUri = "https://cdn.img.ly/packages/imgly/cesdk-js/1.26.0/assets/extensions/ly.img.cesdk.filters.lut/LUTs/imgly_lut_ad1920_5_5_128.png" 59 | 60 | engine.block.setString( 61 | lutFilter, 62 | property = "effect/lut_filter/lutFileURI", 63 | value = lutUri, 64 | ) 65 | engine.block.setInt(lutFilter, property = "effect/lut_filter/verticalTileCount", value = 5) 66 | engine.block.setInt(lutFilter, property = "effect/lut_filter/horizontalTileCount", value = 5) 67 | // highlight-create-lut-filter 68 | 69 | // highlight-apply-lut-filter 70 | engine.block.appendEffect(rect, effectBlock = lutFilter) 71 | engine.block.setFill(rect, fill = imageFill) 72 | // highlight-apply-lut-filter 73 | 74 | engine.stop() 75 | } 76 | -------------------------------------------------------------------------------- /engine-guides-cutouts/Cutouts.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.Color 5 | import ly.img.engine.CutoutOperation 6 | import ly.img.engine.DesignBlockType 7 | import ly.img.engine.Engine 8 | 9 | fun cutouts( 10 | license: String, 11 | userId: String, 12 | ) = CoroutineScope(Dispatchers.Main).launch { 13 | val engine = Engine.getInstance(id = "ly.img.engine.example") 14 | engine.start(license = license, userId = userId) 15 | engine.bindOffscreen(width = 1080, height = 1920) 16 | 17 | // highlight-setup 18 | val scene = engine.scene.create() 19 | 20 | val page = engine.block.create(DesignBlockType.Page) 21 | engine.block.setWidth(page, value = 800F) 22 | engine.block.setHeight(page, value = 600F) 23 | engine.block.appendChild(parent = scene, child = page) 24 | // highlight-setup 25 | 26 | // highlight-create-cutouts 27 | val circle = engine.block.createCutoutFromPath( 28 | "M 0,25 a 25,25 0 1,1 50,0 a 25,25 0 1,1 -50,0 Z", 29 | ) 30 | engine.block.setFloat(circle, "cutout/offset", 3F) 31 | engine.block.setEnum(circle, "cutout/type", "Dashed") 32 | 33 | val square = engine.block.createCutoutFromPath("M 0,0 H 50 V 50 H 0 Z") 34 | engine.block.setFloat(square, "cutout/offset", 6F) 35 | // highlight-create-cutouts 36 | 37 | // highlight-cutout-union 38 | val union = engine.block.createCutoutFromOperation( 39 | listOf(circle, square), 40 | op = CutoutOperation.UNION, 41 | ) 42 | engine.block.destroy(circle) 43 | engine.block.destroy(square) 44 | // highlight-cutout-union 45 | 46 | // highlight-spot-color-solid 47 | engine.editor.setSpotColor("CutContour", Color.fromRGBA(r = 0F, g = 0F, b = 1F, a = 1F)) 48 | // highlight-spot-color-solid 49 | 50 | engine.stop() 51 | } 52 | -------------------------------------------------------------------------------- /engine-guides-exporting-blocks/ExportingBlocks.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.withContext 6 | import ly.img.engine.Engine 7 | import ly.img.engine.ExportOptions 8 | import ly.img.engine.MimeType 9 | import ly.img.engine.addDefaultAssetSources 10 | import java.io.File 11 | import java.util.UUID 12 | 13 | fun exportingBlocks( 14 | license: String, 15 | userId: String, 16 | ) = CoroutineScope(Dispatchers.Main).launch { 17 | val engine = Engine.getInstance(id = "ly.img.engine.example") 18 | engine.start(license = license, userId = userId) 19 | engine.bindOffscreen(width = 1080, height = 1920) 20 | engine.scene.create() 21 | engine.editor.setSettingString( 22 | "basePath", 23 | value = "https://cdn.img.ly/packages/imgly/cesdk-engine/1.23.0/assets", 24 | ) 25 | engine.addDefaultAssetSources() 26 | 27 | val sceneUri = Uri.parse( 28 | "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", 29 | ) 30 | val scene = engine.scene.load(sceneUri = sceneUri) 31 | 32 | // Export scene as PNG image. 33 | val mimeType = MimeType.PNG 34 | // Optionally, the maximum supported export size can be checked before exporting. 35 | val maxExportSizeInPixels = engine.editor.getMaxExportSize() 36 | // Optionally, the compression level and the target size can be specified. 37 | val options = ExportOptions(pngCompressionLevel = 9, targetWidth = null, targetHeight = null) 38 | val blob = engine.block.export(scene, mimeType = mimeType, options = options) 39 | 40 | // Save the blob to file 41 | withContext(Dispatchers.IO) { 42 | File.createTempFile(UUID.randomUUID().toString(), ".png").apply { 43 | outputStream().channel.write(blob) 44 | } 45 | } 46 | 47 | engine.stop() 48 | } 49 | -------------------------------------------------------------------------------- /engine-guides-load-scene-from-blob/LoadSceneFromBlob.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import kotlinx.coroutines.withContext 5 | import ly.img.engine.DesignBlockType 6 | import ly.img.engine.Engine 7 | import java.io.ByteArrayOutputStream 8 | import java.net.URL 9 | 10 | fun loadSceneFromBlob( 11 | license: String, 12 | userId: String, 13 | ) = CoroutineScope(Dispatchers.Main).launch { 14 | val engine = Engine.getInstance(id = "ly.img.engine.example") 15 | engine.start(license = license, userId = userId) 16 | engine.bindOffscreen(width = 1080, height = 1920) 17 | 18 | // highlight-fetch-blob 19 | val sceneUrl = URL("https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene") 20 | val sceneBlob = withContext(Dispatchers.IO) { 21 | val outputStream = ByteArrayOutputStream() 22 | sceneUrl.openStream().use { inputStream -> 23 | outputStream.use(inputStream::copyTo) 24 | } 25 | outputStream.toByteArray() 26 | } 27 | // highlight-fetch-blob 28 | 29 | // highlight-read-blob 30 | val blobString = String(sceneBlob, Charsets.UTF_8) 31 | // highlight-read-blob 32 | 33 | // highlight-load-blob 34 | val scene = engine.scene.load(scene = blobString) 35 | // highlight-load-blob 36 | 37 | // highlight-set-text-dropshadow 38 | val text = engine.block.findByType(DesignBlockType.Text).first() 39 | engine.block.setDropShadowEnabled(text, enabled = true) 40 | // highlight-set-text-dropshadow 41 | 42 | engine.stop() 43 | } 44 | -------------------------------------------------------------------------------- /engine-guides-load-scene-from-remote/LoadSceneFromRemote.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import ly.img.engine.DesignBlockType 6 | import ly.img.engine.Engine 7 | 8 | fun loadSceneFromRemote( 9 | license: String, 10 | userId: String, 11 | ) = CoroutineScope(Dispatchers.Main).launch { 12 | val engine = Engine.getInstance(id = "ly.img.engine.example") 13 | engine.start(license = license, userId = userId) 14 | engine.bindOffscreen(width = 1080, height = 1920) 15 | 16 | // highlight-url 17 | val sceneUri = Uri.parse( 18 | "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", 19 | ) 20 | // highlight-url 21 | 22 | // highlight-load 23 | val scene = engine.scene.load(sceneUri = sceneUri) 24 | // highlight-load 25 | 26 | // highlight-set-text-dropshadow 27 | val text = engine.block.findByType(DesignBlockType.Text).first() 28 | engine.block.setDropShadowEnabled(text, enabled = true) 29 | // highlight-set-text-dropshadow 30 | 31 | engine.stop() 32 | } 33 | -------------------------------------------------------------------------------- /engine-guides-load-scene-from-string/LoadSceneFromString.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import kotlinx.coroutines.withContext 5 | import ly.img.engine.DesignBlockType 6 | import ly.img.engine.Engine 7 | import java.io.ByteArrayOutputStream 8 | import java.net.URL 9 | 10 | fun loadSceneFromString( 11 | license: String, 12 | userId: String, 13 | ) = CoroutineScope(Dispatchers.Main).launch { 14 | val engine = Engine.getInstance(id = "ly.img.engine.example") 15 | engine.start(license = license, userId = userId) 16 | engine.bindOffscreen(width = 1080, height = 1920) 17 | 18 | // highlight-fetch-string 19 | val sceneUrl = URL("https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene") 20 | val sceneBlob = withContext(Dispatchers.IO) { 21 | val outputStream = ByteArrayOutputStream() 22 | sceneUrl.openStream().use { inputStream -> 23 | outputStream.use(inputStream::copyTo) 24 | } 25 | outputStream.toByteArray() 26 | } 27 | val blobString = String(sceneBlob, Charsets.UTF_8) 28 | // highlight-fetch-string 29 | 30 | // highlight-load-string 31 | val scene = engine.scene.load(scene = blobString) 32 | // highlight-load-string 33 | 34 | // highlight-set-text-dropshadow 35 | val text = engine.block.findByType(DesignBlockType.Text).first() 36 | engine.block.setDropShadowEnabled(text, enabled = true) 37 | // highlight-set-text-dropshadow 38 | 39 | engine.stop() 40 | } 41 | -------------------------------------------------------------------------------- /engine-guides-modifying-scenes/ModifyingScenes.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.DesignBlockType 5 | import ly.img.engine.Engine 6 | import ly.img.engine.FillType 7 | import ly.img.engine.ShapeType 8 | 9 | fun modifyingScenes( 10 | license: String, 11 | userId: String, 12 | ) = CoroutineScope(Dispatchers.Main).launch { 13 | val engine = Engine.getInstance(id = "ly.img.engine.example") 14 | engine.start(license = license, userId = userId) 15 | engine.bindOffscreen(width = 1080, height = 1920) 16 | 17 | // highlight-scene-get-create 18 | // In engine only mode we have to create our own scene and page. 19 | if (engine.scene.get() == null) { 20 | val scene = engine.scene.create() 21 | // highlight-scene-get-create 22 | // highlight-page-get-create 23 | val page = engine.block.create(DesignBlockType.Page) 24 | engine.block.appendChild(parent = scene, child = page) 25 | } 26 | 27 | // Find all pages in our scene. 28 | val pages = engine.block.findByType(DesignBlockType.Page) 29 | // Use the first page we found. 30 | val page = pages.first() 31 | // highlight-page-get-create 32 | 33 | // highlight-create-image 34 | // Create a graphic block and add it to the scene's page. 35 | val block = engine.block.create(DesignBlockType.Graphic) 36 | val fill = engine.block.createFill(FillType.Image) 37 | engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) 38 | engine.block.setFill(block = block, fill = fill) 39 | // highlight-create-image 40 | 41 | // highlight-image-properties 42 | engine.block.setString( 43 | block = fill, 44 | property = "fill/image/imageFileURI", 45 | value = "https://img.ly/static/ubq_samples/imgly_logo.jpg", 46 | ) 47 | 48 | // The content fill mode 'Contain' ensures the entire image is visible. 49 | engine.block.setEnum( 50 | block = block, 51 | property = "contentFill/mode", 52 | value = "Contain", 53 | ) 54 | // highlight-image-properties 55 | 56 | // highlight-image-append 57 | engine.block.appendChild(parent = page, child = block) 58 | // highlight-image-append 59 | 60 | // highlight-zoom-page 61 | // Zoom the scene's camera on our page. 62 | engine.scene.zoomToBlock(page) 63 | // highlight-zoom-page 64 | 65 | engine.stop() 66 | } 67 | -------------------------------------------------------------------------------- /engine-guides-save-scene-to-archive/SaveSceneToArchive.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.withContext 6 | import ly.img.engine.Engine 7 | import java.net.HttpURLConnection 8 | import java.net.URL 9 | import java.nio.channels.Channels 10 | 11 | fun saveSceneToArchive( 12 | license: String, 13 | userId: String, 14 | ) = CoroutineScope(Dispatchers.Main).launch { 15 | val engine = Engine.getInstance(id = "ly.img.engine.example") 16 | engine.start(license = license, userId = userId) 17 | engine.bindOffscreen(width = 1080, height = 1920) 18 | 19 | val sceneUri = Uri.parse( 20 | "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", 21 | ) 22 | val scene = engine.scene.load(sceneUri = sceneUri) 23 | 24 | // highlight-save 25 | val blob = engine.scene.saveToArchive(scene = scene) 26 | // highlight-save 27 | 28 | // highlight-create-form-data 29 | withContext(Dispatchers.IO) { 30 | val connection = URL("https://example.com/upload/").openConnection() as HttpURLConnection 31 | connection.requestMethod = "POST" 32 | connection.doOutput = true 33 | connection.outputStream.use { Channels.newChannel(it).write(blob) } 34 | connection.connect() 35 | } 36 | // highlight-create-form-data 37 | 38 | engine.stop() 39 | } 40 | -------------------------------------------------------------------------------- /engine-guides-save-scene-to-blob/SaveSceneToBlob.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.withContext 6 | import ly.img.engine.Engine 7 | import java.net.HttpURLConnection 8 | import java.net.URL 9 | 10 | fun saveSceneToBlob( 11 | license: String, 12 | userId: String, 13 | uploadUrl: String, 14 | ) = CoroutineScope( 15 | Dispatchers.Main, 16 | ).launch { 17 | val engine = Engine.getInstance(id = "ly.img.engine.example") 18 | engine.start(license = license, userId = userId) 19 | engine.bindOffscreen(width = 1080, height = 1920) 20 | 21 | val sceneUri = Uri.parse( 22 | "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", 23 | ) 24 | val scene = engine.scene.load(sceneUri = sceneUri) 25 | 26 | // highlight-save 27 | val savedSceneString = engine.scene.saveToString(scene = scene) 28 | // highlight-save 29 | 30 | // highlight-create-blob 31 | val blob = savedSceneString.toByteArray(Charsets.UTF_8) 32 | // highlight-create-blob 33 | 34 | // highlight-create-form-data 35 | runCatching { 36 | withContext(Dispatchers.IO) { 37 | val connection = URL(uploadUrl).openConnection() as HttpURLConnection 38 | connection.requestMethod = "POST" 39 | connection.doOutput = true 40 | connection.outputStream.use { it.write(blob) } 41 | connection.connect() 42 | } 43 | } 44 | // highlight-create-form-data 45 | 46 | engine.stop() 47 | } 48 | -------------------------------------------------------------------------------- /engine-guides-save-scene-to-string/SaveSceneToString.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import ly.img.engine.Engine 6 | 7 | fun saveSceneToString( 8 | license: String, 9 | userId: String, 10 | ) = CoroutineScope(Dispatchers.Main).launch { 11 | val engine = Engine.getInstance(id = "ly.img.engine.example") 12 | engine.start(license = license, userId = userId) 13 | engine.bindOffscreen(width = 1080, height = 1920) 14 | 15 | val sceneUri = Uri.parse( 16 | "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", 17 | ) 18 | val scene = engine.scene.load(sceneUri = sceneUri) 19 | 20 | // highlight-save 21 | val savedSceneString = engine.scene.saveToString(scene = scene) 22 | // highlight-save 23 | 24 | // highlight-result 25 | println(savedSceneString) 26 | // highlight-result 27 | 28 | engine.stop() 29 | } 30 | -------------------------------------------------------------------------------- /engine-guides-scopes/Scopes.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import ly.img.engine.DesignBlockType 6 | import ly.img.engine.Engine 7 | import ly.img.engine.GlobalScope 8 | 9 | fun scopes( 10 | license: String, 11 | userId: String, 12 | ) = CoroutineScope(Dispatchers.Main).launch { 13 | val engine = Engine.getInstance(id = "ly.img.engine.example") 14 | engine.start(license = license, userId = userId) 15 | engine.bindOffscreen(width = 1080, height = 1920) 16 | 17 | // highlight-setup 18 | engine.scene.createFromImage(Uri.parse("https://img.ly/static/ubq_samples/imgly_logo.jpg")) 19 | val block = engine.block.findByType(DesignBlockType.Graphic).first() 20 | // highlight-setup 21 | 22 | // highlight-findAllScopes 23 | val scopes = engine.editor.findAllScopes() 24 | // highlight-findAllScopes 25 | 26 | // highlight-setGlobalScope 27 | // Let the global scope defer to the block-level. 28 | engine.editor.setGlobalScope(key = "layer/move", globalScope = GlobalScope.DEFER) 29 | 30 | // Manipulation of layout properties of any block will fail at this point. 31 | try { 32 | engine.block.setPositionX(block, value = 100F) // Not allowed 33 | } catch (exception: Exception) { 34 | exception.printStackTrace() 35 | } 36 | // highlight-setGlobalScope 37 | 38 | // highlight-getGlobalScope 39 | // This will return `GlobalScope.DEFER`. 40 | engine.editor.getGlobalScope(key = "layer/move") 41 | // highlight-getGlobalScope 42 | 43 | // highlight-setScopeEnabled 44 | // Allow the user to control the layout properties of the image block. 45 | engine.block.setScopeEnabled(block, key = "layer/move", enabled = true) 46 | 47 | // Manipulation of layout properties of any block is now allowed. 48 | try { 49 | engine.block.setPositionX(block, value = 100F) // Allowed 50 | } catch (exception: Exception) { 51 | exception.printStackTrace() 52 | } 53 | // highlight-setScopeEnabled 54 | 55 | // highlight-isScopeEnabled 56 | // Verify that the "layer/move" scope is now enabled for the image block. 57 | engine.block.isScopeEnabled(block, key = "layer/move") 58 | // highlight-isScopeEnabled 59 | 60 | // highlight-isAllowedByScope 61 | // This will return true as well since the global scope is set to `GlobalScope.DEFER`. 62 | engine.block.isAllowedByScope(block, key = "layer/move") 63 | // highlight-isAllowedByScope 64 | 65 | engine.stop() 66 | } 67 | -------------------------------------------------------------------------------- /engine-guides-setup/MyActivity.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import android.os.Bundle 3 | import android.view.TextureView 4 | import androidx.appcompat.app.AppCompatActivity 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.Dispatchers 7 | import kotlinx.coroutines.launch 8 | import ly.img.engine.Engine 9 | 10 | class MyActivity : AppCompatActivity() { 11 | // highlight-setup 12 | private val engine = Engine.getInstance(id = "ly.img.engine.example") 13 | // highlight-setup 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | val textureView = TextureView(this) 18 | setContentView(textureView) 19 | CoroutineScope(Dispatchers.Main).launch { 20 | // highlight-start 21 | engine.start( 22 | license = "", 23 | userId = "", 24 | savedStateRegistryOwner = this@MyActivity, 25 | ) 26 | // highlight-start 27 | // highlight-bind 28 | engine.bindTextureView(textureView) 29 | /* 30 | // Or any of the following 31 | engine.bindSurfaceView(SurfaceView(this@MyActivity)) 32 | engine.bindOffscreen(100, 100) 33 | */ 34 | 35 | // highlight-bind 36 | // highlight-work 37 | // Check whether scene already exists before loading it again as it might have been restored in engine.start). 38 | engine.scene.get() ?: run { 39 | val sceneUri = Uri.parse("https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene") 40 | engine.scene.load(sceneUri) 41 | } 42 | // highlight-work 43 | } 44 | } 45 | 46 | override fun onDestroy() { 47 | // highlight-unbind 48 | engine.unbind() 49 | // highlight-unbind 50 | // highlight-stop 51 | if (isFinishing) { 52 | engine.stop() 53 | } 54 | // highlight-stop 55 | super.onDestroy() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /engine-guides-setup/MyApplication.kt: -------------------------------------------------------------------------------- 1 | import android.app.Application 2 | import ly.img.engine.Engine 3 | 4 | class MyApplication : Application() { 5 | override fun onCreate() { 6 | super.onCreate() 7 | // highlight-engine-init 8 | Engine.init(this) 9 | // highlight-engine-init 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /engine-guides-setup/MyComposable.kt: -------------------------------------------------------------------------------- 1 | import android.app.Activity 2 | import android.net.Uri 3 | import android.view.SurfaceView 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.DisposableEffect 6 | import androidx.compose.runtime.LaunchedEffect 7 | import androidx.compose.runtime.remember 8 | import androidx.compose.ui.platform.LocalContext 9 | import androidx.compose.ui.platform.LocalLifecycleOwner 10 | import androidx.compose.ui.platform.LocalSavedStateRegistryOwner 11 | import androidx.compose.ui.viewinterop.AndroidView 12 | import androidx.lifecycle.Lifecycle 13 | import androidx.lifecycle.LifecycleEventObserver 14 | import ly.img.engine.Engine 15 | 16 | @Composable 17 | fun MyComposable() { 18 | // highlight-setup 19 | val engine = remember { Engine.getInstance(id = "ly.img.engine.example") } 20 | // highlight-setup 21 | val activity = LocalContext.current as Activity 22 | val lifecycle = LocalLifecycleOwner.current.lifecycle 23 | val surfaceView = remember { SurfaceView(activity) } 24 | val savedStateRegistryOwner = LocalSavedStateRegistryOwner.current 25 | AndroidView(factory = { surfaceView }) 26 | LaunchedEffect(Unit) { 27 | // highlight-start 28 | engine.start( 29 | license = "", 30 | userId = "", 31 | savedStateRegistryOwner = savedStateRegistryOwner, 32 | ) 33 | // highlight-start 34 | // highlight-bind 35 | engine.bindSurfaceView(surfaceView) 36 | // highlight-bind 37 | // highlight-work 38 | engine.scene.get() ?: run { 39 | val sceneUri = Uri.parse("https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene") 40 | engine.scene.load(sceneUri) 41 | } 42 | // highlight-work 43 | } 44 | DisposableEffect(Unit) { 45 | val observer = LifecycleEventObserver { _, event -> 46 | when { 47 | // highlight-stop 48 | event == Lifecycle.Event.ON_DESTROY && !activity.isChangingConfigurations -> engine.stop() 49 | // highlight-stop 50 | // highlight-unbind 51 | event == Lifecycle.Event.ON_DESTROY -> engine.unbind() 52 | // highlight-unbind 53 | } 54 | } 55 | lifecycle.addObserver(observer) 56 | onDispose { lifecycle.removeObserver(observer) } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /engine-guides-setup/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | namespace "ly.img.editor.showcase" 8 | compileSdk 35 9 | 10 | defaultConfig { 11 | applicationId "ly.img.editor.showcase" 12 | minSdk 24 13 | targetSdk 35 14 | versionCode 1 15 | versionName "1.0" 16 | // highlight-abi-filters 17 | ndk { 18 | abiFilters "arm64-v8a", "armeabi-v7a", "x86_64", "x86" 19 | } 20 | // highlight-abi-filters 21 | } 22 | 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | 28 | kotlinOptions { 29 | jvmTarget = '1.8' 30 | } 31 | } 32 | 33 | dependencies { 34 | // highlight-dependency 35 | implementation "ly.img:engine:1.52.0" 36 | // highlight-dependency 37 | implementation "androidx.appcompat:appcompat:1.6.0" 38 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" 39 | } 40 | -------------------------------------------------------------------------------- /engine-guides-setup/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | dependencyResolutionManagement { 10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 11 | repositories { 12 | google() 13 | mavenCentral() 14 | // highlight-maven-dependency 15 | maven { 16 | name "IMG.LY Artifactory" 17 | url "https://artifactory.img.ly/artifactory/maven" 18 | mavenContent { 19 | includeGroup("ly.img") 20 | } 21 | } 22 | // highlight-maven-dependency 23 | } 24 | } 25 | 26 | rootProject.name = "My App" 27 | include ':app' 28 | -------------------------------------------------------------------------------- /engine-guides-store-metadata/StoreMetadata.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import ly.img.engine.DesignBlockType 6 | import ly.img.engine.Engine 7 | 8 | fun storeMetadata( 9 | license: String, 10 | userId: String, 11 | ) = CoroutineScope(Dispatchers.Main).launch { 12 | val engine = Engine.getInstance(id = "ly.img.engine.example") 13 | engine.start(license = license, userId = userId) 14 | engine.bindOffscreen(width = 1080, height = 1920) 15 | 16 | // highlight-setup 17 | var scene = engine.scene.createFromImage( 18 | Uri.parse("https://img.ly/static/ubq_samples/imgly_logo.jpg"), 19 | ) 20 | val block = engine.block.findByType(DesignBlockType.Graphic).first() 21 | // highlight-setup 22 | 23 | // highlight-setMetadata 24 | engine.block.setMetadata(scene, key = "author", value = "img.ly") 25 | engine.block.setMetadata(block, key = "customer_id", value = "1234567890") 26 | engine.block.setMetadata(block, key = "customer_name", value = "Name") 27 | // highlight-setMetadata 28 | 29 | // highlight-getMetadata 30 | // This will return "img.ly" 31 | engine.block.getMetadata(scene, key = "author") 32 | 33 | // This will return "1000000" 34 | engine.block.getMetadata(block, key = "customer_id") 35 | // highlight-getMetadata 36 | 37 | // highlight-findAllMetadata 38 | // This will return ["customer_id", "customer_name"] 39 | engine.block.findAllMetadata(block) 40 | // highlight-findAllMetadata 41 | 42 | // highlight-removeMetadata 43 | engine.block.removeMetadata(block, key = "customer_id") 44 | 45 | // This will return false 46 | engine.block.hasMetadata(block, key = "customer_id") 47 | // highlight-removeMetadata 48 | 49 | // highlight-persistence 50 | // We save our scene and reload it from scratch 51 | val sceneString = engine.scene.saveToString(scene) 52 | scene = engine.scene.load(scene = sceneString) 53 | 54 | // This still returns "img.ly" 55 | engine.block.getMetadata(scene, key = "author") 56 | 57 | // And this still returns "Name" 58 | engine.block.getMetadata(block, key = "customer_name") 59 | // highlight-persistence 60 | 61 | engine.stop() 62 | } 63 | -------------------------------------------------------------------------------- /engine-guides-text-with-emojis/TextWithEmojis.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.DesignBlockType 5 | import ly.img.engine.Engine 6 | 7 | fun textWithEmojis( 8 | license: String, 9 | userId: String, 10 | ) = CoroutineScope(Dispatchers.Main).launch { 11 | val engine = Engine.getInstance(id = "ly.img.engine.example") 12 | engine.start(license = license, userId = userId) 13 | engine.bindOffscreen(width = 1080, height = 1920) 14 | 15 | // highlight-change-default-emoji-font 16 | val uri = engine.editor.getSettingString(keypath = "ubq://defaultEmojiFontFileUri") 17 | // From a bundle 18 | engine.editor.setSettingString( 19 | keypath = "ubq://defaultEmojiFontFileUri", 20 | value = "file:///android_asset/ly.img.cesdk/fonts/NotoColorEmoji.ttf", 21 | ) 22 | // From a URL 23 | engine.editor.setSettingString( 24 | keypath = "ubq://defaultEmojiFontFileUri", 25 | value = "https://cdn.img.ly/assets/v3/emoji/NotoColorEmoji.ttf", 26 | ) 27 | // highlight-change-default-emoji-font 28 | 29 | // highlight-setup 30 | val scene = engine.scene.create() 31 | 32 | val page = engine.block.create(DesignBlockType.Page) 33 | engine.block.setWidth(page, value = 800F) 34 | engine.block.setHeight(page, value = 600F) 35 | engine.block.appendChild(parent = scene, child = page) 36 | 37 | engine.scene.zoomToBlock( 38 | page, 39 | paddingLeft = 40F, 40 | paddingTop = 40F, 41 | paddingRight = 40F, 42 | paddingBottom = 40F, 43 | ) 44 | // highlight-setup 45 | 46 | // highlight-add-text-with-emoji 47 | val text = engine.block.create(DesignBlockType.Text) 48 | engine.block.setString(text, property = "text/text", value = "Text with an emoji 🧐") 49 | engine.block.setWidth(text, value = 50F) 50 | engine.block.setHeight(text, value = 10F) 51 | engine.block.appendChild(parent = page, child = text) 52 | // highlight-add-text-with-emoji 53 | 54 | engine.stop() 55 | } 56 | -------------------------------------------------------------------------------- /engine-guides-underlayer/Underlayer.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import kotlinx.coroutines.withContext 5 | import ly.img.engine.Color 6 | import ly.img.engine.DesignBlockType 7 | import ly.img.engine.Engine 8 | import ly.img.engine.ExportOptions 9 | import ly.img.engine.FillType 10 | import ly.img.engine.MimeType 11 | import ly.img.engine.ShapeType 12 | import java.io.File 13 | 14 | fun underlayer( 15 | license: String, 16 | userId: String, 17 | ) = CoroutineScope(Dispatchers.Main).launch { 18 | val engine = Engine.getInstance(id = "ly.img.engine.example") 19 | engine.start(license = license, userId = userId) 20 | engine.bindOffscreen(width = 1080, height = 1920) 21 | 22 | // highlight-setup 23 | val scene = engine.scene.create() 24 | 25 | val page = engine.block.create(DesignBlockType.Page) 26 | engine.block.setWidth(page, value = 800F) 27 | engine.block.setHeight(page, value = 600F) 28 | engine.block.appendChild(parent = scene, child = page) 29 | 30 | val block = engine.block.create(DesignBlockType.Graphic) 31 | engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Star)) 32 | engine.block.setPositionX(block, value = 350F) 33 | engine.block.setPositionY(block, value = 400F) 34 | engine.block.setWidth(block, value = 100F) 35 | engine.block.setHeight(block, value = 100F) 36 | 37 | val fill = engine.block.createFill(FillType.Color) 38 | engine.block.setFill(block, fill = fill) 39 | val rgbaBlue = Color.fromRGBA(r = 0F, g = 0F, b = 1F, a = 1F) 40 | engine.block.setColor(fill, property = "fill/color/value", value = rgbaBlue) 41 | // highlight-setup 42 | 43 | // highlight-create-underlayer-spot-color 44 | engine.editor.setSpotColor(name = "RDG_WHITE", Color.fromRGBA(r = 0.8F, g = 0.8F, b = 0.8F)) 45 | // highlight-create-underlayer-spot-color 46 | 47 | // highlight-export-pdf-underlayer 48 | val mimeType = MimeType.PDF 49 | val options = ExportOptions( 50 | exportPdfWithUnderlayer = true, 51 | underlayerSpotColorName = "RDG_WHITE", 52 | underlayerOffset = -2.0F, 53 | ) 54 | val blob = engine.block.export(scene, mimeType = mimeType, options = options) 55 | withContext(Dispatchers.IO) { 56 | File.createTempFile("underlayer_example", ".pdf").apply { 57 | outputStream().channel.write(blob) 58 | } 59 | } 60 | // highlight-export-pdf-underlayer 61 | 62 | engine.stop() 63 | } 64 | -------------------------------------------------------------------------------- /engine-guides-uri-resolver/UriResolver.kt: -------------------------------------------------------------------------------- 1 | import android.net.Uri 2 | import kotlinx.coroutines.CoroutineScope 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import ly.img.engine.Engine 6 | 7 | fun uriResolver( 8 | license: String, 9 | userId: String, 10 | ) = CoroutineScope(Dispatchers.Main).launch { 11 | val engine = Engine.getInstance(id = "ly.img.engine.example") 12 | engine.start(license = license, userId = userId) 13 | engine.bindOffscreen(width = 1080, height = 1920) 14 | 15 | // highlight-get-absolute-base-path 16 | // This will return uri to "banana.jpg" asset file 17 | engine.editor.getAbsoluteUri(uri = Uri.parse("banana.jpg")) 18 | // This will return uri to remote resource "https://example.com/orange.png" 19 | engine.editor.getAbsoluteUri(uri = Uri.parse("https://example.com/orange.png")) 20 | // highlight-get-absolute-base-path 21 | 22 | // highlight-resolver 23 | // Replace all .jpg files with the IMG.LY logo! 24 | engine.editor.setUriResolver { 25 | if (it.toString().endsWith(".jpg")) { 26 | Uri.parse("https://img.ly/static/ubq_samples/imgly_logo.jpg") 27 | } else { 28 | engine.editor.defaultUriResolver(it) 29 | } 30 | } 31 | // highlight-resolver 32 | 33 | // highlight-get-absolute-custom 34 | // The custom resolver will return a path to the IMG.LY logo because the given path ends with ".jpg". 35 | // This applies regardless if the given path is relative or absolute. 36 | engine.editor.getAbsoluteUri(uri = Uri.parse("banana.jpg")) 37 | 38 | // The custom resolver will not modify this path because it ends with ".png". 39 | engine.editor.getAbsoluteUri(uri = Uri.parse("https://example.com/orange.png")) 40 | 41 | // Because a custom resolver is set, relative paths that the resolver does not transform remain unmodified! 42 | engine.editor.getAbsoluteUri(uri = Uri.parse("/orange.png")) 43 | // highlight-get-absolute-custom 44 | 45 | // highlight-remove-resolver 46 | // Removes the previously set resolver. 47 | engine.editor.setUriResolver(null) 48 | 49 | // Since we've removed the custom resolver, this will return 50 | // Uri.Asset("banana.jpg") like before. 51 | engine.editor.getAbsoluteUri(uri = Uri.parse("banana.jpg")) 52 | // highlight-remove-resolver 53 | 54 | engine.editor.setUriResolver { uri -> 55 | val path = uri.path 56 | if (uri.host == "localhost" && path != null && path.startsWith("/scenes") && !path.endsWith(".scene")) { 57 | // Apply custom logic here, e.g. redirect to a different server 58 | } 59 | engine.editor.defaultUriResolver(uri) 60 | } 61 | 62 | engine.stop() 63 | } 64 | -------------------------------------------------------------------------------- /engine-guides-using-camera/UsingCamera.kt: -------------------------------------------------------------------------------- 1 | import android.view.SurfaceView 2 | import androidx.appcompat.app.AppCompatActivity 3 | import androidx.camera.core.CameraSelector 4 | import androidx.camera.core.MirrorMode 5 | import androidx.camera.core.Preview 6 | import androidx.camera.lifecycle.ProcessCameraProvider 7 | import androidx.camera.video.FileOutputOptions 8 | import androidx.camera.video.Quality 9 | import androidx.camera.video.QualitySelector 10 | import androidx.camera.video.Recorder 11 | import androidx.camera.video.VideoCapture 12 | import androidx.camera.video.VideoRecordEvent 13 | import androidx.core.content.ContextCompat 14 | import androidx.core.net.toUri 15 | import kotlinx.coroutines.CoroutineScope 16 | import kotlinx.coroutines.Dispatchers 17 | import kotlinx.coroutines.delay 18 | import kotlinx.coroutines.launch 19 | import ly.img.engine.DesignBlockType 20 | import ly.img.engine.EffectType 21 | import ly.img.engine.Engine 22 | import ly.img.engine.FillType 23 | import ly.img.engine.camera.setCameraPreview 24 | import java.io.File 25 | 26 | fun usingCamera( 27 | activity: AppCompatActivity, 28 | surfaceView: SurfaceView, 29 | cameraProvider: ProcessCameraProvider, 30 | license: String, 31 | userId: String, 32 | ) = CoroutineScope(Dispatchers.Main).launch { 33 | val engine = Engine.getInstance(id = "ly.img.engine.example") 34 | engine.start(license = license, userId = userId) 35 | engine.bindSurfaceView(surfaceView) 36 | 37 | // highlight-setupCamera 38 | val cameraSelector = CameraSelector.Builder() 39 | .requireLensFacing(CameraSelector.LENS_FACING_FRONT) 40 | .build() 41 | val preview = Preview.Builder().build() 42 | val qualitySelector = QualitySelector.from(Quality.FHD) 43 | val recorder = Recorder.Builder() 44 | .setQualitySelector(qualitySelector) 45 | .build() 46 | val videoCapture = VideoCapture.Builder(recorder) 47 | .setMirrorMode(MirrorMode.MIRROR_MODE_ON_FRONT_ONLY) 48 | .build() 49 | 50 | cameraProvider.bindToLifecycle(activity, cameraSelector, preview, videoCapture) 51 | // highlight-setupCamera 52 | 53 | // highlight-setupScene 54 | val scene = engine.scene.createForVideo() 55 | val page = engine.block.create(DesignBlockType.Page) 56 | engine.block.appendChild(parent = scene, child = page) 57 | val pixelStreamFill = engine.block.createFill(FillType.PixelStream) 58 | engine.block.setFill(block = page, fill = pixelStreamFill) 59 | engine.setCameraPreview(pixelStreamFill, preview, mirrored = false) 60 | engine.block.appendEffect( 61 | block = page, 62 | effectBlock = engine.block.createEffect(EffectType.HalfTone), 63 | ) 64 | // highlight-setupScene 65 | 66 | // highlight-orientation 67 | // If camerax preview transformation info rotation is 90, this will return Left. If we passed mirrored = true, this would be LeftMirrored. 68 | val orientation = engine.block.getEnum( 69 | block = pixelStreamFill, 70 | property = "fill/pixelStream/orientation", 71 | ) 72 | // highlight-orientation 73 | 74 | // highlight-camera 75 | val recordingFile = File(surfaceView.context.filesDir, "temp.mp4") 76 | val fileOutputOptions = FileOutputOptions.Builder(recordingFile).build() 77 | val recording = videoCapture.output 78 | .prepareRecording(activity, fileOutputOptions) 79 | .start(ContextCompat.getMainExecutor(surfaceView.context)) { 80 | if (it !is VideoRecordEvent.Finalize) return@start 81 | val videoFill = engine.block.createFill(FillType.Video) 82 | engine.block.setFill(block = page, fill = videoFill) 83 | engine.block.setString( 84 | block = videoFill, 85 | property = "fill/video/fileURI", 86 | value = recordingFile.toUri().toString(), 87 | ) 88 | } 89 | delay(5000L) 90 | recording.stop() 91 | // highlight-camera 92 | } 93 | -------------------------------------------------------------------------------- /engine-guides-using-camera/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | namespace "ly.img.editor.camera" 8 | compileSdk 35 9 | 10 | defaultConfig { 11 | applicationId "ly.img.editor.camera" 12 | minSdk 24 13 | targetSdk 35 14 | versionCode 1 15 | versionName "1.0" 16 | ndk { 17 | abiFilters "arm64-v8a", "armeabi-v7a", "x86_64", "x86" 18 | } 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_8 23 | targetCompatibility JavaVersion.VERSION_1_8 24 | } 25 | 26 | kotlinOptions { 27 | jvmTarget = '1.8' 28 | } 29 | } 30 | 31 | dependencies { 32 | // highlight-dependencies 33 | implementation "ly.img:engine-camera:1.52.0" 34 | implementation "androidx.camera:camera-core:1.2.3" 35 | implementation "androidx.camera:camera-camera2:1.2.3" 36 | implementation "androidx.camera:camera-view:1.2.3" 37 | implementation "androidx.camera:camera-lifecycle:1.2.3" 38 | implementation "androidx.camera:camera-video:1.2.3" 39 | // highlight-dependencies 40 | implementation "ly.img:engine:1.52.0" 41 | implementation "androidx.appcompat:appcompat:1.6.0" 42 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" 43 | } 44 | -------------------------------------------------------------------------------- /engine-guides-using-effects/UsingEffects.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.DesignBlockType 5 | import ly.img.engine.EffectType 6 | import ly.img.engine.Engine 7 | import ly.img.engine.FillType 8 | import ly.img.engine.ShapeType 9 | 10 | fun usingEffects( 11 | license: String, 12 | userId: String, 13 | ) = CoroutineScope(Dispatchers.Main).launch { 14 | val engine = Engine.getInstance(id = "ly.img.engine.example") 15 | engine.start(license = license, userId = userId) 16 | engine.bindOffscreen(width = 1080, height = 1920) 17 | 18 | // highlight-setup 19 | val scene = engine.scene.create() 20 | 21 | val page = engine.block.create(DesignBlockType.Page) 22 | engine.block.setWidth(page, value = 800F) 23 | engine.block.setHeight(page, value = 600F) 24 | engine.block.appendChild(parent = scene, child = page) 25 | 26 | engine.scene.zoomToBlock( 27 | page, 28 | paddingLeft = 40F, 29 | paddingTop = 40F, 30 | paddingRight = 40F, 31 | paddingBottom = 40F, 32 | ) 33 | 34 | val block = engine.block.create(DesignBlockType.Graphic) 35 | engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) 36 | engine.block.setPositionX(block, value = 100F) 37 | engine.block.setPositionY(block, value = 50F) 38 | engine.block.setWidth(block, value = 300F) 39 | engine.block.setHeight(block, value = 300F) 40 | engine.block.appendChild(parent = page, child = block) 41 | val fill = engine.block.createFill(FillType.Image) 42 | engine.block.setString( 43 | block = fill, 44 | property = "fill/image/imageFileURI", 45 | value = "https://img.ly/static/ubq_samples/sample_1.jpg", 46 | ) 47 | engine.block.setFill(block, fill = fill) 48 | // highlight-setup 49 | 50 | // highlight-supportsEffects 51 | engine.block.supportsEffects(scene) // Returns false 52 | engine.block.supportsEffects(block) // Returns true 53 | // highlight-supportsEffects 54 | 55 | // highlight-createEffect 56 | val pixelize = engine.block.createEffect(type = EffectType.Pixelize) 57 | val adjustments = engine.block.createEffect(type = EffectType.Adjustments) 58 | // highlight-createEffect 59 | 60 | // highlight-addEffect 61 | engine.block.appendEffect(block, effectBlock = pixelize) 62 | engine.block.insertEffect(block, effectBlock = adjustments, index = 0) 63 | // engine.block.removeEffect(rect, index = 0) 64 | // highlight-addEffect 65 | 66 | // highlight-getEffects 67 | // This will return [adjustments, pixelize] 68 | val effectsList = engine.block.getEffects(block) 69 | // highlight-getEffects 70 | 71 | // highlight-destroyEffect 72 | val unusedEffect = engine.block.createEffect(type = EffectType.HalfTone) 73 | engine.block.destroy(unusedEffect) 74 | // highlight-destroyEffect 75 | 76 | // highlight-getProperties 77 | val allPixelizeProperties = engine.block.findAllProperties(pixelize) 78 | val allAdjustmentProperties = engine.block.findAllProperties(adjustments) 79 | // highlight-getProperties 80 | 81 | // highlight-modifyProperties 82 | engine.block.setInt(pixelize, property = "pixelize/horizontalPixelSize", value = 20) 83 | engine.block.setFloat(adjustments, property = "effect/adjustments/brightness", value = 0.2F) 84 | // highlight-modifyProperties 85 | 86 | // highlight-disableEffect 87 | engine.block.setEffectEnabled(pixelize, enabled = false) 88 | engine.block.setEffectEnabled(pixelize, !engine.block.isEffectEnabled(pixelize)) 89 | // highlight-disableEffect 90 | 91 | engine.stop() 92 | } 93 | -------------------------------------------------------------------------------- /engine-guides-using-shapes/UsingShapes.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.CoroutineScope 2 | import kotlinx.coroutines.Dispatchers 3 | import kotlinx.coroutines.launch 4 | import ly.img.engine.DesignBlockType 5 | import ly.img.engine.Engine 6 | import ly.img.engine.FillType 7 | import ly.img.engine.ShapeType 8 | 9 | fun usingShapes( 10 | license: String, 11 | userId: String, 12 | ) = CoroutineScope(Dispatchers.Main).launch { 13 | val engine = Engine.getInstance(id = "ly.img.engine.example") 14 | engine.start(license = license, userId = userId) 15 | engine.bindOffscreen(width = 1080, height = 1920) 16 | 17 | // highlight-setup 18 | val scene = engine.scene.create() 19 | 20 | val graphic = engine.block.create(DesignBlockType.Graphic) 21 | val imageFill = engine.block.createFill(FillType.Image) 22 | engine.block.setString( 23 | block = imageFill, 24 | property = "fill/image/imageFileURI", 25 | value = "https://img.ly/static/ubq_samples/sample_1.jpg", 26 | ) 27 | engine.block.setFill(graphic, fill = imageFill) 28 | engine.block.setWidth(graphic, value = 100F) 29 | engine.block.setHeight(graphic, value = 100F) 30 | engine.block.appendChild(parent = scene, child = graphic) 31 | 32 | engine.scene.zoomToBlock( 33 | graphic, 34 | paddingLeft = 40F, 35 | paddingTop = 40F, 36 | paddingRight = 40F, 37 | paddingBottom = 40F, 38 | ) 39 | 40 | // highlight-setup 41 | 42 | // highlight-supportsShape 43 | engine.block.supportsShape(graphic) // Returns true 44 | val text = engine.block.create(DesignBlockType.Text) 45 | engine.block.supportsShape(text) // Returns false 46 | // highlight-supportsShape 47 | 48 | // highlight-createShape 49 | val rectShape = engine.block.createShape(ShapeType.Rect) 50 | // highlight-setShape 51 | engine.block.setShape(graphic, shape = rectShape) 52 | // highlight-getShape 53 | val shape = engine.block.getShape(graphic) 54 | val shapeType = engine.block.getType(shape) 55 | // highlight-getShape 56 | 57 | // highlight-replaceShape 58 | val starShape = engine.block.createShape(ShapeType.Star) 59 | engine.block.destroy(engine.block.getShape(graphic)) 60 | engine.block.setShape(graphic, shape = starShape) 61 | // The following line would also destroy the currently attached starShape 62 | // engine.block.destroy(graphic) 63 | // highlight-replaceShape 64 | 65 | // highlight-getProperties 66 | val allShapeProperties = engine.block.findAllProperties(starShape) 67 | // highlight-getProperties 68 | // highlight-modifyProperties 69 | engine.block.setInt(starShape, property = "shape/star/points", value = 6) 70 | // highlight-modifyProperties 71 | 72 | engine.stop() 73 | } 74 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | androidMinSdk = "24" 3 | androidCompileSdk = "35" 4 | androidTargetSdk = "35" 5 | kotlin = "2.1.10" 6 | imgly = "1.52.0" 7 | buildConfig = "5.3.5" 8 | dropshots = "0.4.1" 9 | gradleAndroid = "8.4.2" 10 | googleServices = "4.4.2" 11 | gradleCrashlytics = "3.0.2" 12 | appDistributionPlugin = "5.0.0" 13 | agp = "8.4.2" 14 | coreKtx = "1.15.0" 15 | appcompat = "1.7.0" 16 | installer = "0.7.6" 17 | 18 | [libraries] 19 | imgly-editor = { module = "ly.img:editor", version.ref = "imgly" } 20 | imgly-camera = { module = "ly.img:camera", version.ref = "imgly" } 21 | editor-debug-menu = { module = "ly.img:editor-debug-menu", version.ref = "imgly" } 22 | editor-debug-menu-dummy = { module = "ly.img:editor-debug-menu-dummy", version.ref = "imgly" } 23 | 24 | compose-bom = "androidx.compose:compose-bom:2023.05.01" 25 | compose-material3 = { module = "androidx.compose.material3:material3" } 26 | compose-tooling = "androidx.compose.ui:ui-tooling:1.7.5" 27 | compose-preview = "androidx.compose.ui:ui-tooling-preview:1.7.5" 28 | compose-activity = "androidx.activity:activity-compose:1.6.1" 29 | compose-navigation = "androidx.navigation:navigation-compose:2.5.3" 30 | compose-viewmodel = "androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7" 31 | 32 | firebase-bom = "com.google.firebase:firebase-bom:33.6.0" 33 | firebase-dynamicLinks = { module = "com.google.firebase:firebase-dynamic-links" } 34 | firebase-analytics = { module = "com.google.firebase:firebase-analytics" } 35 | firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" } 36 | firebase-crashlytics-ndk = { module = "com.google.firebase:firebase-crashlytics-ndk" } 37 | 38 | coil-compose = "io.coil-kt:coil-compose:2.2.2" 39 | 40 | play-services-auth = "com.google.android.gms:play-services-auth:21.2.0" 41 | 42 | material = "com.google.android.material:material:1.12.0" 43 | 44 | installer = { module = "ru.solrudev.ackpine:ackpine-core", version.ref = "installer" } 45 | installer-ktx = { module = "ru.solrudev.ackpine:ackpine-ktx", version.ref = "installer" } 46 | 47 | core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } 48 | appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } 49 | 50 | [plugins] 51 | android-library = { id = "com.android.library", version.ref = "agp" } 52 | jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 53 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 54 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Oct 04 14:06:13 CEST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | dependencyResolutionManagement { 15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 16 | repositories { 17 | google() 18 | mavenCentral() 19 | maven { 20 | name "IMG.LY Artifactory" 21 | url "https://artifactory.img.ly/artifactory/maven" 22 | mavenContent { 23 | includeGroup("ly.img") 24 | } 25 | } 26 | } 27 | } 28 | 29 | rootProject.name = "cesdk_android_showcases" 30 | include ':showcases-app' 31 | include ':solutions:photo-editor' 32 | include ':solutions:solution-template' 33 | -------------------------------------------------------------------------------- /showcases-app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /showcases-app/.version: -------------------------------------------------------------------------------- 1 | #Mon Jun 02 07:45:44 CEST 2025 2 | code=188 3 | year=2025 4 | iteration=17 5 | -------------------------------------------------------------------------------- /showcases-app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /showcases-app/src/internal/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 14 | 17 | 18 | 19 | 25 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /showcases-app/src/internal/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/internal/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /showcases-app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /showcases-app/src/main/assets/images/photo-ui-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/assets/images/photo-ui-empty.png -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/Secrets.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases 2 | 3 | object Secrets { 4 | // Add "license" key value pair in local.properties file 5 | const val license = ShowcasesBuildConfig.LICENSE 6 | 7 | // Add "remote_asset_source_host" key value pair in local.properties file 8 | const val remoteAssetSourceHost = ShowcasesBuildConfig.REMOTE_ASSET_SOURCE_HOST 9 | } 10 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/ShowcaseItem.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases 2 | 3 | import androidx.annotation.DrawableRes 4 | import androidx.annotation.StringRes 5 | 6 | sealed class ShowcaseItem( 7 | open val actionScene: String? = null, 8 | open val actionScreen: Screen? = null, 9 | open val span: Int, 10 | val key: String, 11 | ) { 12 | data class Header( 13 | @StringRes val title: Int, 14 | override val actionScene: String? = null, 15 | override val actionScreen: Screen? = null, 16 | override val span: Int, 17 | val items: List = emptyList(), 18 | ) : ShowcaseItem( 19 | actionScene = actionScene, 20 | actionScreen = actionScreen, 21 | span = span, 22 | key = "item.header.$title", 23 | ) 24 | 25 | data class Content( 26 | @DrawableRes val thumbnailRes: Int, 27 | override val actionScene: String, 28 | override val actionScreen: Screen, 29 | val thumbnailAspectRatio: Float = 1F, 30 | ) : ShowcaseItem( 31 | actionScene = actionScene, 32 | actionScreen = actionScreen, 33 | span = 1, 34 | key = "item.content.$actionScene", 35 | ) 36 | 37 | class CarouselContent( 38 | @DrawableRes val iconRes: Int, 39 | @StringRes val label: Int, 40 | @StringRes val sublabel: Int?, 41 | val hasDotLine: Boolean = false, 42 | val clickAction: ClickAction = ClickAction.OPEN_SCENE, 43 | override val actionScene: String?, 44 | override val actionScreen: Screen, 45 | ) : ShowcaseItem( 46 | actionScene = actionScene, 47 | actionScreen = actionScreen, 48 | span = 1, 49 | key = "item.content.$actionScene", 50 | ) { 51 | enum class ClickAction { 52 | OPEN_SCENE, 53 | PICK_SCENE, 54 | PICK_IMAGE, 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/ShowcasesApp.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases 2 | 3 | import android.app.Application 4 | import android.os.Build 5 | import android.os.StrictMode 6 | import androidx.core.content.ContextCompat 7 | import com.google.firebase.Firebase 8 | import com.google.firebase.FirebaseApp 9 | import com.google.firebase.crashlytics.crashlytics 10 | 11 | class ShowcasesApp : Application() { 12 | override fun onCreate() { 13 | super.onCreate() 14 | 15 | val isFirebaseActive = FirebaseApp.getApps(this).isNotEmpty() 16 | if (isFirebaseActive) { 17 | Firebase.crashlytics.setCustomKey("build_name", ShowcasesBuildConfig.BUILD_NAME) 18 | } 19 | // Setting a penalty listener is only possible on API levels >= 28. 20 | // This should be enough to catch most issues anyway. 21 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 22 | StrictMode.setThreadPolicy( 23 | StrictMode.ThreadPolicy 24 | .Builder() 25 | .detectAll() 26 | .penaltyLog() 27 | .penaltyListener(ContextCompat.getMainExecutor(this)) { violation -> 28 | if (isFirebaseActive) { 29 | Firebase.crashlytics.recordException(violation) 30 | } 31 | }.build(), 32 | ) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/icon/Homeoutline.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases.icon 2 | 3 | import androidx.compose.ui.graphics.Color 4 | import androidx.compose.ui.graphics.PathFillType.Companion.NonZero 5 | import androidx.compose.ui.graphics.SolidColor 6 | import androidx.compose.ui.graphics.StrokeCap.Companion.Butt 7 | import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter 8 | import androidx.compose.ui.graphics.vector.ImageVector 9 | import androidx.compose.ui.graphics.vector.ImageVector.Builder 10 | import androidx.compose.ui.graphics.vector.path 11 | import androidx.compose.ui.unit.dp 12 | 13 | val IconPack.Homeoutline: ImageVector 14 | get() { 15 | if (homeoutline != null) { 16 | return homeoutline!! 17 | } 18 | homeoutline = Builder( 19 | name = "Homeoutline", 20 | defaultWidth = 24.0.dp, 21 | defaultHeight = 24.0.dp, 22 | viewportWidth = 24.0f, 23 | viewportHeight = 24.0f, 24 | ).apply { 25 | path( 26 | fill = SolidColor(Color(0xFF49454F)), 27 | stroke = null, 28 | strokeLineWidth = 0.0f, 29 | strokeLineCap = Butt, 30 | strokeLineJoin = Miter, 31 | strokeLineMiter = 4.0f, 32 | pathFillType = NonZero, 33 | ) { 34 | moveTo(4.0f, 21.0f) 35 | verticalLineTo(9.0f) 36 | lineTo(12.0f, 3.0f) 37 | lineTo(20.0f, 9.0f) 38 | verticalLineTo(21.0f) 39 | horizontalLineTo(14.0f) 40 | verticalLineTo(14.0f) 41 | horizontalLineTo(10.0f) 42 | verticalLineTo(21.0f) 43 | horizontalLineTo(4.0f) 44 | close() 45 | moveTo(6.0f, 19.0f) 46 | horizontalLineTo(8.0f) 47 | verticalLineTo(12.0f) 48 | horizontalLineTo(16.0f) 49 | verticalLineTo(19.0f) 50 | horizontalLineTo(18.0f) 51 | verticalLineTo(10.0f) 52 | lineTo(12.0f, 5.5f) 53 | lineTo(6.0f, 10.0f) 54 | verticalLineTo(19.0f) 55 | close() 56 | } 57 | }.build() 58 | return homeoutline!! 59 | } 60 | 61 | private var homeoutline: ImageVector? = null 62 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/icon/IconPack.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases.icon 2 | 3 | object IconPack 4 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/ui/components/QuickActionButton.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases.ui.components 2 | 3 | import androidx.annotation.DrawableRes 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.clickable 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Spacer 8 | import androidx.compose.foundation.layout.height 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.foundation.layout.size 11 | import androidx.compose.foundation.layout.width 12 | import androidx.compose.foundation.shape.CircleShape 13 | import androidx.compose.foundation.shape.RoundedCornerShape 14 | import androidx.compose.material3.Icon 15 | import androidx.compose.material3.MaterialTheme 16 | import androidx.compose.material3.Surface 17 | import androidx.compose.material3.Text 18 | import androidx.compose.runtime.Composable 19 | import androidx.compose.ui.Alignment 20 | import androidx.compose.ui.Modifier 21 | import androidx.compose.ui.draw.clip 22 | import androidx.compose.ui.graphics.Color 23 | import androidx.compose.ui.res.painterResource 24 | import androidx.compose.ui.text.style.TextAlign 25 | import androidx.compose.ui.tooling.preview.PreviewLightDark 26 | import androidx.compose.ui.unit.dp 27 | import androidx.compose.ui.unit.sp 28 | import ly.img.editor.core.theme.LocalIsDarkTheme 29 | import ly.img.editor.showcases.R 30 | import ly.img.editor.showcases.ui.preview.PreviewTheme 31 | 32 | @Composable 33 | fun QuickActionButton( 34 | title: String, 35 | @DrawableRes iconId: Int, 36 | onClick: () -> Unit, 37 | modifier: Modifier = Modifier, 38 | ) { 39 | Column( 40 | modifier = modifier 41 | .clip(RoundedCornerShape(8.dp)) 42 | .clickable(onClick = onClick) 43 | .padding(8.dp), 44 | horizontalAlignment = Alignment.CenterHorizontally, 45 | ) { 46 | Icon( 47 | painter = painterResource(id = iconId), 48 | contentDescription = title, 49 | tint = MaterialTheme.colorScheme.onSurface, 50 | modifier = Modifier 51 | .size(56.dp) 52 | .background( 53 | color = if (LocalIsDarkTheme.current) Color(0xFF_353438) else Color(0xFF_E4E1E6), 54 | shape = CircleShape, 55 | ) 56 | .padding(12.dp), 57 | ) 58 | Spacer(modifier = Modifier.height(6.dp)) 59 | Text( 60 | text = title, 61 | modifier = Modifier.width(56.dp), 62 | textAlign = TextAlign.Center, 63 | color = MaterialTheme.colorScheme.onSurface, 64 | style = MaterialTheme.typography.labelSmall.copy( 65 | lineHeight = 13.sp, 66 | ), 67 | ) 68 | } 69 | } 70 | 71 | @PreviewLightDark 72 | @Composable 73 | private fun QuickActionButtonPreview() { 74 | PreviewTheme { 75 | Surface { 76 | QuickActionButton( 77 | title = "Action Title", 78 | iconId = R.drawable.quick_action_record_video, 79 | onClick = {}, 80 | ) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/ui/components/VersionFooter.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases.ui.components 2 | 3 | import androidx.compose.foundation.ExperimentalFoundationApi 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.basicMarquee 6 | import androidx.compose.foundation.layout.Arrangement 7 | import androidx.compose.foundation.layout.Box 8 | import androidx.compose.foundation.layout.Row 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.requiredWidthIn 12 | import androidx.compose.foundation.layout.size 13 | import androidx.compose.foundation.lazy.LazyListScope 14 | import androidx.compose.foundation.shape.CircleShape 15 | import androidx.compose.material3.MaterialTheme 16 | import androidx.compose.material3.Text 17 | import androidx.compose.runtime.Composable 18 | import androidx.compose.ui.Alignment 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.draw.alpha 21 | import androidx.compose.ui.tooling.preview.PreviewLightDark 22 | import androidx.compose.ui.unit.dp 23 | import ly.img.editor.showcases.ShowcasesBuildConfig 24 | import ly.img.editor.showcases.ui.preview.PreviewTheme 25 | 26 | fun LazyListScope.versionFooterItem() = item { VersionFooter() } 27 | 28 | @OptIn(ExperimentalFoundationApi::class) 29 | @Composable 30 | fun VersionFooter( 31 | modifier: Modifier = Modifier, 32 | appVersion: String = ShowcasesBuildConfig.VERSION_NAME, 33 | sdkVersion: String = ShowcasesBuildConfig.ENGINE_VERSION, 34 | ) { 35 | Row( 36 | modifier = modifier 37 | .alpha(.4f) 38 | .fillMaxWidth() 39 | .padding(vertical = 24.dp), 40 | verticalAlignment = Alignment.CenterVertically, 41 | horizontalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.CenterHorizontally), 42 | ) { 43 | Text( 44 | text = "App v$appVersion", 45 | color = MaterialTheme.colorScheme.onSurface, 46 | style = MaterialTheme.typography.labelSmall, 47 | modifier = Modifier 48 | .requiredWidthIn(max = 100.dp) 49 | .basicMarquee(), 50 | ) 51 | Box( 52 | modifier = Modifier 53 | .size(2.dp) 54 | .background(color = MaterialTheme.colorScheme.onSurface, shape = CircleShape), 55 | ) 56 | Text( 57 | text = "SDK v$sdkVersion", 58 | style = MaterialTheme.typography.labelSmall, 59 | color = MaterialTheme.colorScheme.onSurface, 60 | ) 61 | } 62 | } 63 | 64 | @PreviewLightDark 65 | @Composable 66 | private fun VersionFooterPreview() { 67 | PreviewTheme { 68 | VersionFooter( 69 | appVersion = "2025.30", 70 | sdkVersion = "1.45.1", 71 | ) 72 | } 73 | } 74 | 75 | @PreviewLightDark 76 | @Composable 77 | private fun VersionFooterWithMarqueePreview() { 78 | PreviewTheme { 79 | VersionFooter( 80 | appVersion = "name/type-super-duper-extra-long-branch-name - 934t3459", 81 | sdkVersion = "1.45.1", 82 | ) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/ui/preview/PreviewTheme.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases.ui.preview 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material3.MaterialTheme 5 | import androidx.compose.material3.darkColorScheme 6 | import androidx.compose.material3.lightColorScheme 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.runtime.CompositionLocalProvider 9 | import ly.img.editor.core.theme.LocalIsDarkTheme 10 | 11 | @Composable 12 | fun PreviewTheme(content: @Composable () -> Unit) { 13 | val colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme() 14 | CompositionLocalProvider( 15 | LocalIsDarkTheme provides isSystemInDarkTheme(), 16 | ) { 17 | MaterialTheme( 18 | colorScheme = colorScheme, 19 | content = content, 20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/ui/screens/EditCameraRecordings.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases.ui.screens 2 | 3 | import androidx.compose.foundation.layout.Box 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.runtime.rememberCoroutineScope 7 | import androidx.compose.ui.Modifier 8 | import kotlinx.coroutines.android.awaitFrame 9 | import kotlinx.coroutines.launch 10 | import ly.img.camera.core.CameraResult 11 | import ly.img.editor.EditorConfiguration 12 | import ly.img.editor.EditorDefaults 13 | import ly.img.editor.EngineConfiguration 14 | import ly.img.editor.VideoEditor 15 | import ly.img.editor.core.event.EditorEvent 16 | import ly.img.editor.core.library.data.AssetSourceType 17 | import ly.img.editor.rememberForVideo 18 | import ly.img.editor.showcases.Secrets 19 | 20 | @Composable 21 | fun EditCameraRecordings( 22 | recording: CameraResult.Record, 23 | onBack: () -> Unit, 24 | ) { 25 | val scope = rememberCoroutineScope() 26 | Box( 27 | modifier = Modifier.fillMaxSize(), 28 | ) { 29 | val engineConfig = EngineConfiguration.remember( 30 | license = Secrets.license, 31 | onCreate = { 32 | val eventHandler = editorContext.eventHandler 33 | EditorDefaults.onCreate( 34 | engine = editorContext.engine, 35 | eventHandler = editorContext.eventHandler, 36 | sceneUri = EngineConfiguration.defaultVideoSceneUri, 37 | ) 38 | 39 | scope.launch { 40 | awaitFrame() 41 | eventHandler.send( 42 | EditorEvent.AddCameraRecordingsToScene( 43 | uploadAssetSourceType = AssetSourceType.VideoUploads, 44 | recordings = recording.recordings 45 | .flatMap { recording -> 46 | recording.videos 47 | .map { it.uri to recording.duration } 48 | }, 49 | ), 50 | ) 51 | } 52 | }, 53 | ) 54 | 55 | val editorConfig = EditorConfiguration.rememberForVideo() 56 | VideoEditor( 57 | engineConfiguration = engineConfig, 58 | editorConfiguration = editorConfig, 59 | ) { 60 | onBack() 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/ui/screens/EditRecordedReaction.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases.ui.screens 2 | 3 | import androidx.compose.foundation.layout.Box 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import ly.img.camera.core.CameraResult 8 | import ly.img.editor.EditorConfiguration 9 | import ly.img.editor.EngineConfiguration 10 | import ly.img.editor.VideoEditor 11 | import ly.img.editor.rememberForVideo 12 | import ly.img.editor.showcases.Secrets 13 | import ly.img.editor.showcases.util.createSceneFromReaction 14 | 15 | @Composable 16 | fun EditRecordedReaction( 17 | reaction: CameraResult.Reaction, 18 | onBack: () -> Unit, 19 | ) { 20 | Box( 21 | modifier = Modifier.fillMaxSize(), 22 | ) { 23 | val engineConfig = EngineConfiguration.remember( 24 | license = Secrets.license, 25 | onCreate = { 26 | val eventHandler = editorContext.eventHandler 27 | val engine = editorContext.engine 28 | engine.createSceneFromReaction( 29 | cameraResult = reaction, 30 | eventHandler = eventHandler, 31 | ) 32 | }, 33 | ) 34 | 35 | val editorConfig = EditorConfiguration.rememberForVideo() 36 | VideoEditor( 37 | engineConfiguration = engineConfig, 38 | editorConfiguration = editorConfig, 39 | ) { 40 | onBack() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /showcases-app/src/main/java/ly/img/editor/showcases/ui/screens/EditVideoFromUri.kt: -------------------------------------------------------------------------------- 1 | package ly.img.editor.showcases.ui.screens 2 | 3 | import android.media.MediaMetadataRetriever 4 | import android.net.Uri 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.runtime.remember 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.geometry.Size 11 | import androidx.compose.ui.geometry.isSpecified 12 | import androidx.compose.ui.platform.LocalContext 13 | import ly.img.editor.EditorConfiguration 14 | import ly.img.editor.EditorDefaults 15 | import ly.img.editor.EngineConfiguration 16 | import ly.img.editor.VideoEditor 17 | import ly.img.editor.core.event.EditorEvent 18 | import ly.img.editor.core.library.data.AssetSourceType 19 | import ly.img.editor.rememberForVideo 20 | import ly.img.editor.showcases.Secrets 21 | 22 | @Composable 23 | fun EditVideoFromUri( 24 | uri: Uri, 25 | onBack: () -> Unit, 26 | ) { 27 | val context = LocalContext.current 28 | val size: Size = 29 | remember { 30 | val retriever = MediaMetadataRetriever() 31 | try { 32 | retriever.setDataSource(context, uri) 33 | val width = retriever.frameAtTime!!.width 34 | val height = retriever.frameAtTime!!.height 35 | Size(width.toFloat(), height.toFloat()) 36 | } catch (e: Exception) { 37 | e.printStackTrace() 38 | Size.Unspecified 39 | } finally { 40 | retriever.release() 41 | } 42 | } 43 | Box( 44 | modifier = 45 | Modifier.fillMaxSize(), 46 | ) { 47 | val engineConfig = 48 | EngineConfiguration.remember( 49 | license = Secrets.license, 50 | onCreate = { 51 | val eventHandler = editorContext.eventHandler 52 | val engine = editorContext.engine 53 | EditorDefaults.onCreate( 54 | engine = engine, 55 | eventHandler = eventHandler, 56 | sceneUri = EngineConfiguration.defaultVideoSceneUri, 57 | ) 58 | 59 | val currentPage = engine.scene.getCurrentPage()!! 60 | if (size.isSpecified) { 61 | engine.block.setWidth(currentPage, size.width) 62 | engine.block.setHeight(currentPage, size.height) 63 | } 64 | 65 | eventHandler.send( 66 | EditorEvent.AddUriToScene( 67 | uploadAssetSourceType = AssetSourceType.VideoUploads, 68 | uri = uri, 69 | ), 70 | ) 71 | }, 72 | ) 73 | 74 | val editorConfig = EditorConfiguration.rememberForVideo() 75 | VideoEditor( 76 | engineConfiguration = engineConfig, 77 | editorConfiguration = editorConfig, 78 | ) { 79 | onBack() 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/apparel_ui_blank_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/apparel_ui_blank_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/apparel_ui_blank_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/apparel_ui_blank_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_booklet_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_booklet_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_booklet_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_booklet_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_booklet_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_booklet_template.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_business_card_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_business_card_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_business_card_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_business_card_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_business_card_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_business_card_template.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_post_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_post_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_post_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_post_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_post_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_post_template.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_story_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_story_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_story_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_story_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_story_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_instagram_story_template.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_open_design_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_open_design_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_open_design_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_open_design_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_video_full_hd_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_video_full_hd_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_video_full_hd_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_video_full_hd_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_video_full_hd_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_video_full_hd_template.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_x_header_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_x_header_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_x_header_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_x_header_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_x_header_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_x_header_template.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_x_profile_picture_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_x_profile_picture_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_x_profile_picture_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_x_profile_picture_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/design_ui_x_profile_picture_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/design_ui_x_profile_picture_template.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_16_9_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_16_9_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_16_9_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_16_9_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_1_1_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_1_1_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_1_1_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_1_1_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_2_3_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_2_3_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_2_3_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_2_3_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_3_2_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_3_2_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_3_2_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_3_2_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_3_4_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_3_4_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_3_4_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_3_4_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_4_3_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_4_3_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_4_3_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_4_3_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_9_16_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_9_16_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_9_16_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_9_16_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_choose_from_gallery_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_choose_from_gallery_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/photo_ui_choose_from_gallery_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/photo_ui_choose_from_gallery_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/postcard_ui_blank_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/postcard_ui_blank_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/postcard_ui_blank_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/postcard_ui_blank_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_apparel_ui_b_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_apparel_ui_b_1.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_apparel_ui_b_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_apparel_ui_b_2.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_apparel_ui_b_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_apparel_ui_b_3.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_apparel_ui_b_4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_apparel_ui_b_4.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_bonjour_paris.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_bonjour_paris.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_merry_christmas.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_merry_christmas.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_milli_surf_school.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_milli_surf_school.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_monthly_review.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_monthly_review.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_my_plants.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_my_plants.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_red_dot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_red_dot.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_thank_you.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_thank_you.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/thumbnail_wish_you_were_here.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/thumbnail_wish_you_were_here.webp -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/video_ui_blank_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/video_ui_blank_dark.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable-nodpi/video_ui_blank_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/drawable-nodpi/video_ui_blank_light.png -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable/quick_action_dual_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable/quick_action_edit_media.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable/quick_action_gallery.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable/quick_action_react_to_video.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/drawable/quick_action_record_video.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/raw/isrg_root_x1.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/raw/isrg_root_x1.der -------------------------------------------------------------------------------- /showcases-app/src/main/res/raw/isrg_root_x2.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgly/cesdk-android-examples/9913d9073aee9378c07a584534ca85a2ab3ab5f1/showcases-app/src/main/res/raw/isrg_root_x2.der -------------------------------------------------------------------------------- /showcases-app/src/main/res/values-night/general_thumbnails.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @drawable/design_ui_booklet_dark 4 | @drawable/design_ui_business_card_dark 5 | @drawable/design_ui_instagram_post_dark 6 | @drawable/design_ui_instagram_story_dark 7 | @drawable/design_ui_open_design_dark 8 | @drawable/design_ui_video_full_hd_dark 9 | @drawable/design_ui_x_header_dark 10 | @drawable/design_ui_x_profile_picture_dark 11 | 12 | 13 | @drawable/apparel_ui_blank_dark 14 | @drawable/postcard_ui_blank_dark 15 | @drawable/video_ui_blank_dark 16 | 17 | @drawable/photo_ui_choose_from_gallery_dark 18 | 19 | @drawable/photo_ui_1_1_dark 20 | @drawable/photo_ui_9_16_dark 21 | @drawable/photo_ui_16_9_dark 22 | @drawable/photo_ui_2_3_dark 23 | @drawable/photo_ui_3_2_dark 24 | @drawable/photo_ui_3_4_dark 25 | @drawable/photo_ui_4_3_dark 26 | 27 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/values/general_thumbnails.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @drawable/design_ui_booklet_light 4 | @drawable/design_ui_business_card_light 5 | @drawable/design_ui_instagram_post_light 6 | @drawable/design_ui_instagram_story_light 7 | @drawable/design_ui_open_design_light 8 | @drawable/design_ui_video_full_hd_light 9 | @drawable/design_ui_x_header_light 10 | @drawable/design_ui_x_profile_picture_light 11 | 12 | 13 | @drawable/apparel_ui_blank_light 14 | @drawable/postcard_ui_blank_light 15 | @drawable/video_ui_blank_light 16 | 17 | @drawable/photo_ui_choose_from_gallery_light 18 | 19 | @drawable/photo_ui_1_1_light 20 | @drawable/photo_ui_9_16_light 21 | @drawable/photo_ui_16_9_light 22 | @drawable/photo_ui_2_3_light 23 | @drawable/photo_ui_3_2_light 24 | @drawable/photo_ui_3_4_light 25 | @drawable/photo_ui_4_3_light 26 | 27 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | IMG.LY 3 | Photo Editor 4 | Design Editor 5 | Video Editor 6 | Apparel Editor 7 | Postcards Editor 8 | Create 9 | 10 | Unsplash 11 | Pexels 12 | Giphy 13 | Giphy Stickers 14 | Sketches 15 | Tape 16 | Marker 17 | 3D Stickers 18 | 19 | Choose Image from Gallery 20 | Instagram Story 21 | Instagram Post 22 | Booklet 23 | Business Card 24 | Full HD 25 | X Header 26 | X Profile Picture 27 | Import Design File 28 | 29 | Square · 1:1 30 | Full HD · 9:16 31 | Full HD · 16:9 32 | Photo · 2:3 33 | Photo · 3:2 34 | Photo · 3:4 35 | Photo · 4:3 36 | 37 | Blank Video 38 | Blank T-shirt 39 | Blank Postcard 40 | 41 | 1080 × 1920 px 42 | 1080 × 1080 px 43 | 210 × 297 mm 44 | 148 × 105 mm 45 | 85 × 55 mm 46 | 1920 × 1080 px 47 | 1500 × 500 px 48 | 400 × 400 px 49 | .scene Format 50 | 1080 × 1620 px 51 | 1620 × 1080 px 52 | 1080 × 1440 px 53 | 1440 × 1080 px 54 | 55 | Record Video 56 | React to Video 57 | Dual Camera 58 | Edit\nMedia 59 | 60 | 61 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/xml-v25/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /showcases-app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------