Search Docs
Loading...
Skip to content

Actions

Configure action-style workflows in Android editors by dispatching EditorEvents and handling them with EditorConfiguration callbacks.

5 mins
estimated time
GitHub

Android does not expose the Web cesdk.actions registry or cesdk.utils API. Use the Android editor configuration instead: UI components send events, and callbacks own export, upload, analytics, and app-specific workflow steps.

The snippets below use the editor-provided editorContext.engine; the editor owns engine initialization and lifecycle.

Action Hooks on Android#

Action-style work on Android is split between callback hooks and event dispatch. Configure the hooks in EditorConfiguration.remember.

Hook When it runs Typical use
onCreate When the editor and engine are created Load or build a scene and prepare asset sources.
onLoaded After onCreate finishes on first launch or after process recreation Collect editor flows or run setup that depends on a loaded editor.
onExport After EditorEvent.Export.Start() is sent Export, validate, upload, or hand the result to your app.
onUpload After the user selects a file for an upload asset source Replace temporary local URIs with permanent app storage URIs.
onEvent Whenever an editor or custom event is sent Reduce events into UI state, analytics, or follow-up work.
onClose After EditorEvent.OnClose is sent or the system back action is unhandled Confirm close behavior or route the close request to your app UI.
onError When the editor captures a Throwable Inspect the Throwable and route failures to your app UI.

The default export callback only logs a warning. Add onExport whenever an export action should produce, save, share, or upload a file.

Trigger an Action from UI#

Send EditorEvent.Export.Start() from your own Compose UI to start the export workflow. The editor’s built-in export controls use the same event path.

Button(
modifier =
Modifier
.align(Alignment.BottomCenter)
.padding(24.dp),
enabled = !exportState.isExporting,
onClick = {
editorContext.eventHandler.send(EditorEvent.Export.Start())
},
) {
Text("Export")
}

The event is routed through the editor event handler and invokes onExport. When your UI shows progress, send EditorEvent.Export.Cancel() to cancel the running export job.

TextButton(
onClick = {
editorContext.eventHandler.send(EditorEvent.Export.Cancel())
},
) {
Text("Cancel")
}

Handle Exports#

Keep custom events small and specific to the workflow state you want to display or track.

object ActionsExportStarted : EditorEvent
data class ActionsExportCompleted(
val fileName: String,
) : EditorEvent
data class ActionsExportFailed(
val message: String,
) : EditorEvent

Implement onExport inside EditorConfiguration.remember. This sample exports the current scene to PDF, writes it to app cache, and sends a success or failure event back to the editor.

onExport = {
editorContext.eventHandler.send(ActionsExportStarted)
try {
val file = exportCurrentSceneToCache(
engine = editorContext.engine,
directory = editorContext.activity.cacheDir,
)
editorContext.eventHandler.send(ActionsExportCompleted(file.name))
} catch (throwable: CancellationException) {
throw throwable
} catch (throwable: Throwable) {
editorContext.eventHandler.send(
ActionsExportFailed(throwable.message ?: throwable.toString()),
)
}
}

Those custom events are reduced in onEvent below. The reducer turns ActionsExportStarted, ActionsExportCompleted, and ActionsExportFailed into state that the overlay uses to show progress, completion, or error dialogs.

The export helper reads the active scene from the editor engine and exports it with engine.block.export(...).

private suspend fun exportCurrentSceneToCache(
engine: Engine,
directory: File,
): File {
val scene = requireNotNull(engine.scene.get()) { "No scene loaded for export." }
val buffer = engine.block.export(
block = scene,
mimeType = MimeType.PDF,
)
return writeToCacheFile(
byteBuffer = buffer,
directory = directory,
mimeType = MimeType.PDF,
)
}

Write the returned ByteBuffer to app-controlled storage before sharing, uploading, or handing it to another Android component.

private suspend fun writeToCacheFile(
byteBuffer: ByteBuffer,
directory: File,
mimeType: MimeType,
): File = withContext(Dispatchers.IO) {
val extension = when (mimeType) {
MimeType.PNG -> "png"
MimeType.JPEG -> "jpg"
MimeType.TGA -> "tga"
MimeType.SVG -> "svg"
MimeType.MP4 -> "mp4"
MimeType.BINARY -> "bin"
MimeType.PDF -> "pdf"
}
val outputFile = File.createTempFile(UUID.randomUUID().toString(), ".$extension", directory)
val sourceBuffer = byteBuffer.asReadOnlyBuffer()
FileOutputStream(outputFile).channel.use { channel ->
while (sourceBuffer.hasRemaining()) {
channel.write(sourceBuffer)
}
}
check(outputFile.length() > 0L) { "Exported file is empty." }
outputFile
}

Reflect Results in the UI#

Represent action state with a small data class. Store it with editorContext.mutableStateOf(...) so it survives configuration changes while the editor is open; use editorContext.stateOf(...) when another custom component needs read-only access to the same keyed state. Then reduce editor events into that state in onEvent.

data class ActionsExportState(
val isExporting: Boolean = false,
val exportedFileName: String? = null,
val errorMessage: String? = null,
)
var exportState by editorContext.mutableStateOf(
key = "actions.export.state",
initial = ActionsExportState(),
)
onEvent = { event ->
exportState = when (event) {
is ActionsExportStarted -> {
ActionsExportState(isExporting = true)
}
is ActionsExportCompleted -> {
ActionsExportState(exportedFileName = event.fileName)
}
is ActionsExportFailed -> {
ActionsExportState(errorMessage = event.message)
}
is EditorEvent.Export.Cancel -> {
ActionsExportState()
}
else -> exportState
}
}

onEvent receives both built-in editor events and your custom event classes. Use it for lightweight state updates, analytics, or follow-up workflow routing.

Customize Uploads#

Use onUpload to replace the Web uploadFile action pattern on Android. The callback receives the generated AssetDefinition and the upload source that triggered it.

onUpload = { assetDefinition, uploadSource ->
uploadTransientResource(
assetDefinition = assetDefinition,
uploadSource = uploadSource,
)
}

Upload the local URI to your own storage client, then return an updated AssetDefinition with permanent metadata. If the asset has a separate thumbUri, upload that thumbnail separately instead of pointing it at the media file.

private suspend fun uploadTransientResource(
assetDefinition: AssetDefinition,
uploadSource: UploadAssetSourceType,
): AssetDefinition {
val meta = assetDefinition.meta ?: return assetDefinition
val localUri = meta["uri"] ?: return assetDefinition
val permanentUri = uploadToPermanentStorage(
uri = localUri,
sourceId = uploadSource.sourceId,
)
val permanentMeta = meta.toMutableMap()
permanentMeta["uri"] = permanentUri
meta["thumbUri"]?.let { thumbnailUri ->
permanentMeta["thumbUri"] = if (thumbnailUri == localUri) {
permanentUri
} else {
uploadToPermanentStorage(
uri = thumbnailUri,
sourceId = uploadSource.sourceId,
)
}
}
return assetDefinition.copy(
meta = permanentMeta,
)
}
private suspend fun uploadToPermanentStorage(
uri: String,
sourceId: String,
): String {
check(sourceId.isNotBlank()) { "Upload source id is required." }
// Replace this with your app's storage client and return its permanent URI.
return uri
}

API Reference#

API Purpose
EditorConfiguration.remember { onExport = { ... } } Handles export actions triggered by EditorEvent.Export.Start().
EditorConfiguration.remember { onEvent = { event -> ... } } Observes built-in and custom editor events.
EditorConfiguration.remember { onUpload = { assetDefinition, uploadSource -> ... } } Customizes assets selected through upload sources.
editorContext.mutableStateOf(key=_, initial=_) Stores editor-scoped Compose state that survives configuration changes.
editorContext.stateOf(key=_) Reads state previously declared with editorContext.mutableStateOf(...).
editorContext.eventHandler.send(event=_) Dispatches an EditorEvent from callbacks or editor UI components.
editorContext.engine Provides the editor-owned engine for export work inside callbacks.
editorContext.activity Provides the Android Activity, used here for cacheDir.
EditorEvent.Export.Start() Starts the configured export workflow.
EditorEvent.Export.Cancel() Cancels the running export job, if one exists.
engine.scene.get() Reads the currently loaded scene block.
engine.block.export(block=_, mimeType=_) Exports a scene, page, or block to the requested MIME type.
assetDefinition.meta Reads the uploaded asset metadata generated by the editor.
uploadSource.sourceId Identifies the upload asset source that triggered onUpload.
AssetDefinition.copy(meta=_) Returns an updated asset definition from onUpload.

Related types:

Type Role
EditorEvent Base interface for built-in and custom editor events.
UploadAssetSourceType Describes the upload asset source that invoked onUpload.
MimeType Provides Android constants such as MimeType.PDF.

Next Steps#

  • Automation Actions - build a complete editor-driven automation flow with callbacks and custom events.
  • To PDF - customize PDF export behavior from the editor.
  • UI Events - observe editor events and callback lifecycle details.