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.
What You’ll Learn#
- Prepare a reusable scene in
onCreatefor 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:
| Hook | When it runs | Typical automation use |
|---|---|---|
onCreate | When the editor and engine are created | Load or build the scene, register asset sources, and apply editor settings. |
onExport | When an export action dispatches EditorEvent.Export.Start() | Validate the design, export it, upload it, or hand it to another workflow. |
onUpload | After the user selects a file for an upload asset source | Replace temporary local URIs with permanent server-backed URIs. |
onEvent | Whenever an internal or custom EditorEvent is sent | Update 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,) : EditorEventonEvent = { 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.