md2link

Quick Start — Tera Beauty SDK (15 minutes)

DraftJul 20, 2026

Quick Start — Tera Beauty SDK (15 minutes)

Integrate the on-device Beauty Editor into an existing app: one repository, one dependency, one Activity call. For the full reference see partner-guide.md.


Step 1 — Repository + credentials (the step people get wrong)

The dependency line is trivial; wiring the Artifactory repo with credentials is what trips most consumers. In your settings.gradle.kts:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
        maven {
            url = uri("https://artifactory.apero.vn/artifactory/gradle-release/")
            credentials {
                username = providers.gradleProperty("artifactoryUser").orNull
                    ?: System.getenv("ARTIFACTORY_USER")
                password = providers.gradleProperty("artifactoryPassword").orNull
                    ?: System.getenv("ARTIFACTORY_PASSWORD")
            }
        }
    }
}

Put the credentials in ~/.gradle/gradle.properties (never commit them):

artifactoryUser=<your-user>
artifactoryPassword=<your-token>

Contact the Apero team for credentials.


Step 2 — One dependency

In your app module's build.gradle.kts:

dependencies {
    implementation("tera-soft:tera-sdk-beauty:1.1.0")
}

That's the only tera-soft:* line you need — the 8 engine artifacts (beauty-core, beauty-renderer, beauty-detection, beauty-face, beauty-body, beauty-adjust, beauty-retouch, beauty-sdk) resolve transitively.

Requirements: minSdk 24, compileSdk 36, Java 11.


Step 3 — Launch the editor and read the result

The API mirrors ML Kit: getClient(options) → build an IntentSender → launch it with StartIntentSenderForResult → parse the result.

class MainActivity : AppCompatActivity() {

    // 1) Launcher that receives the edited image (null Intent = user cancelled).
    private val editorLauncher =
        registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
            val edited = BeautyEditingResult.fromActivityResultIntent(result.data) ?: return@registerForActivityResult
            // edited.imageUri / edited.imagePath — the saved result
            binding.preview.setImageURI(edited.imageUri)
        }

    private fun launchEditor(input: BeautyInput) {
        // 2) Configure
        val options = BeautyEditorOptions.Builder()
            .setEnabledTools(Tool.CROP, Tool.ADJUST, Tool.FACE_EDIT, Tool.BODY, Tool.MAKEUP, Tool.AUTO_RETOUCH)
            .setOutputFormat(OutputFormat.JPEG)
            .setOutputQuality(95)
            .setThemeMode(ThemeMode.DARK)   // DARK (default) / LIGHT / SYSTEM — see Step 4
            .build()

        // 3) Build the intent and launch
        BeautyEditing.getClient(options)
            .getStartEditingIntent(this, input)
            .addOnSuccessListener { editorLauncher.launch(IntentSenderRequest.Builder(it).build()) }
            .addOnFailureListener { e -> Log.e("Beauty", "Failed to build editor intent", e) }
    }
}

Passing the input image — BeautyInput

The input is a BeautyInput. Build it with the factory matching your source:

BeautyInput.fromUri(uri)        // content:// , file:// or a path — used as-is (no copy)
BeautyInput.fromFile(file)      // a File on disk — used as-is
BeautyInput.fromBitmap(bitmap)  // in-memory bitmap (e.g. an AI-generated image) — encoded to PNG
BeautyInput.fromByteArray(bytes)// encoded image bytes (e.g. a network/AI response) — written verbatim

The editor is a separate Activity that must survive process death, so in-memory inputs (fromBitmap / fromByteArray) are materialized to a private cache file by the SDK for you — you no longer copy anything by hand. fromUri / fromFile pass through untouched.

// System picker → content:// . Hand it straight to the editor:
launchEditor(BeautyInput.fromUri(pickedContentUri))

// Already have a generated Bitmap? No cache dance needed:
launchEditor(BeautyInput.fromBitmap(generatedBitmap))

For a content:// from another app, make sure it is still readable when the editor launches (a short-lived picker grant can expire). If in doubt, copy into your cache and use BeautyInput.fromFile(...). A legacy getStartEditingIntent(activity, inputUri) overload is also kept for backward compatibility.


Step 4 (optional) — Open straight into one tool

To wire a Home-screen "Makeup" button directly to the Makeup tool, keep all tools enabled and set the initial tool:

val options = BeautyEditorOptions.Builder()
    .setInitialTool(Tool.MAKEUP)   // opens on Makeup; closing it returns to the full tool bar
    .build()

null (the default) opens on the full tool bar.


Step 5 (optional) — Light / Dark theme

The editor ships both palettes; pick which one renders with setThemeMode(...):

val options = BeautyEditorOptions.Builder()
    .setThemeMode(ThemeMode.SYSTEM)   // follow the device's day/night setting
    .build()
Mode Behaviour
ThemeMode.DARK Always dark — the SDK's original look. Default (existing integrations unchanged).
ThemeMode.LIGHT Always light.
ThemeMode.SYSTEM Follows the device's dark/light setting.

The mode is chosen at runtime — not by the device unless you pass SYSTEM. To recolor either palette to your brand, see theming-guide.md (values/ = light, values-night/ = dark).