Search Docs
Loading...
Skip to content

Actions

On Android, user-triggered automation in the prebuilt editor is wired through EditorConfiguration callbacks and EditorEvents. Use onExport to run the automated step, then route progress and results back into the UI through onEvent.

5 mins
estimated time
GitHub

What You’ll Learn#

  • Prepare a reusable scene in onCreate for an editor-driven automation workflow.
  • Trigger an automated PDF export by sending EditorEvent.Export.Start() from your own UI.
  • Send progress, success, and failure back into the editor overlay with custom EditorEvents.

Automation Hooks on Android#

Android’s prebuilt editor exposes action-style integration points through EditorConfiguration:

HookWhen it runsTypical automation use
onCreateWhen the editor and engine are createdLoad or build the scene, register asset sources, and apply editor settings.
onExportWhen an export action dispatches EditorEvent.Export.Start()Validate the design, export it, upload it, or hand it to another workflow.
onUploadAfter the user selects a file for an upload asset sourceReplace temporary local URIs with permanent server-backed URIs.
onEventWhenever an internal or custom EditorEvent is sentUpdate overlay state, analytics, or follow-up workflow steps.

Prepare the Scene#

Create or reuse the scene in onCreate. The callback can run again after process recreation, so check whether a scene already exists before rebuilding it.

onCreate = {
if (editorContext.engine.scene.get() == null) {
val scene = editorContext.engine.scene.create()
val page = editorContext.engine.block.create(DesignBlockType.Page)
editorContext.engine.block.setWidth(block = page, value = 1080F)
editorContext.engine.block.setHeight(block = page, value = 1080F)
editorContext.engine.block.appendChild(parent = scene, child = page)
val title = editorContext.engine.block.create(DesignBlockType.Text)
editorContext.engine.block.setWidthMode(title, mode = SizeMode.AUTO)
editorContext.engine.block.setHeightMode(title, mode = SizeMode.AUTO)
editorContext.engine.block.replaceText(title, text = "Quarterly Report")
editorContext.engine.block.appendChild(parent = page, child = title)
editorContext.engine.scene.zoomToBlock(page)
}
}

The sample creates a single square page and a text block so the export flow has deterministic content.

Trigger Automation from UI Actions#

Use a visible overlay action to send EditorEvent.Export.Start(). The editor’s built-in export button sends the same event, so the pattern stays the same if you later wire the automation flow to the default navigation bar.

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

That event then invokes EditorConfiguration.onExport, which is where the automation step should run.

onExport = {
editorContext.eventHandler.send(ShowExportProgress)
runCatching {
val buffer = editorContext.engine.block.export(
block = requireNotNull(editorContext.engine.scene.get()),
mimeType = MimeType.PDF,
)
writeToTempFile(
byteBuffer = buffer,
directory = { editorContext.activity.cacheDir },
)
}.onSuccess { file ->
editorContext.eventHandler.send(ExportFinished(file.name))
}.onFailure { throwable ->
editorContext.eventHandler.send(
ExportFailed(throwable.message ?: throwable.toString()),
)
}
}

This callback:

  • Runs in response to EditorEvent.Export.Start() dispatched from the UI.
  • Exports the current scene to PDF with block.export(...).
  • Sends success or failure back through custom events instead of mutating UI state directly inside the export branch.
private suspend fun writeToTempFile(
byteBuffer: ByteBuffer,
directory: () -> File,
mimeType: MimeType = MimeType.PDF,
): File = withContext(Dispatchers.IO) {
val extension = mimeType.key.substringAfterLast('/')
File
.createTempFile(UUID.randomUUID().toString(), ".$extension", directory())
.apply {
outputStream().use { it.channel.write(byteBuffer) }
}
}

The exported file is written to the app cache directory so your app can upload it, share it, or move it into permanent storage. Like the other editor callbacks, onExport runs in a coroutine that survives configuration changes and is cancelled only when the editor closes.

Reflect Progress in the UI#

Keep lightweight Compose state for the overlay, define custom events for the automation flow, and reduce those events in onEvent.

data class AutomationActionsState(
val isExporting: Boolean = false,
val exportedFileName: String? = null,
val errorMessage: String? = null,
)
var state by remember { mutableStateOf(AutomationActionsState()) }
object ShowExportProgress : EditorEvent
data class ExportFinished(
val fileName: String,
) : EditorEvent
data class ExportFailed(
val message: String,
) : EditorEvent
onEvent = { event ->
when (event) {
is EditorEvent.Export.Start -> {
state = state.copy(exportedFileName = null, errorMessage = null)
}
is ShowExportProgress -> {
state = state.copy(
isExporting = true,
exportedFileName = null,
errorMessage = null,
)
}
is ExportFinished -> {
state = state.copy(
isExporting = false,
exportedFileName = event.fileName,
)
}
is ExportFailed -> {
state = state.copy(
isExporting = false,
errorMessage = event.message,
)
}
}
}

In the sample, the overlay reacts to that state by showing a blocking progress spinner while export runs, then a result or error dialog when the automated step finishes.

overlay = {
EditorComponent.remember {
decoration = {
Box(
modifier = Modifier.fillMaxSize(),
) {
Button(
modifier =
Modifier
.align(Alignment.BottomCenter)
.padding(24.dp),
enabled = !state.isExporting,
onClick = {
editorContext.eventHandler.send(EditorEvent.Export.Start())
},
) {
Text("Export Document")
}
}
if (state.isExporting) {
Dialog(
onDismissRequest = {},
properties =
DialogProperties(
dismissOnBackPress = false,
dismissOnClickOutside = false,
),
) {
CircularProgressIndicator()
}
}
state.exportedFileName?.let { fileName ->
AlertDialog(
onDismissRequest = {
state = state.copy(exportedFileName = null)
},
title = {
Text("Automation complete")
},
text = {
Text("Created $fileName in the app cache directory.")
},
confirmButton = {
TextButton(
onClick = {
state = state.copy(exportedFileName = null)
},
) {
Text("OK")
}
},
)
}
state.errorMessage?.let { errorMessage ->
AlertDialog(
onDismissRequest = {
state = state.copy(errorMessage = null)
},
title = {
Text("Automation failed")
},
text = {
Text(errorMessage)
},
confirmButton = {
TextButton(
onClick = {
state = state.copy(errorMessage = null)
},
) {
Text("OK")
}
},
)
}
}
}
}

Next Steps#

  • Automation Overview - map this editor-driven pattern to the rest of CE.SDK’s automation workflows.
  • Export to PDF - customize the exported format and add PDF-specific options.
  • UI Events - learn the full lifecycle of editor callbacks and event handling on Android.