Create a custom media importer by connecting your app’s backend to CE.SDK’s
asset system with an Android AssetSource.
A custom importer is an asset source that translates media from your app into CE.SDK asset results. The source receives search and pagination requests from the Engine, fetches or filters media in your own data layer, and returns Asset objects with enough metadata for CE.SDK to preview and apply them.
This guide uses a deterministic in-memory media list to keep the sample runnable without network access. In production, replace that list with your API client and return permanent HTTPS URLs for the media and thumbnail fields.
Define an Asset Source#
Extend AssetSource with a stable sourceId. The source also declares the MIME types it can provide and optional source-level credits or license information.
class BackendImageAssetSource( private val mediaItems: List<BackendMediaItem>,) : AssetSource(sourceId = CUSTOM_IMPORTER_SOURCE_ID) { override val supportedMimeTypes = listOf(MimeType.JPEG.key)
override val credits = AssetCredits( name = "App media library", uri = null, )
override val license = AssetLicense( name = "App media license", uri = null, )
override suspend fun getGroups(): List<String> = withContext(Dispatchers.IO) { mediaItems.flatMap { item -> item.groups }.distinct() }findAssets and getGroups are the required callbacks. CE.SDK calls them when the asset library, your app, or any other Engine workflow needs to browse this source.
Return Paginated Results#
Implement findAssets by translating the incoming FindAssetsQuery into your backend query. Return only the requested page and set nextPage to -1 when there are no more results.
override suspend fun findAssets(query: FindAssetsQuery): FindAssetsResult = withContext(Dispatchers.IO) { val terms = query.query.orEmpty() .lowercase() .split(" ") .filter { term -> term.isNotBlank() } val requestedPage = query.page.coerceAtLeast(0) val pageSize = query.perPage.coerceAtLeast(1) val requestedGroups = query.groups.orEmpty()
val filteredItems = mediaItems.filter { item -> val matchesQuery = terms.isEmpty() || terms.all { term -> item.label.lowercase().contains(term) || item.tags.any { tag -> tag.lowercase().contains(term) } } val matchesGroup = requestedGroups.isEmpty() || item.groups.any { group -> group in requestedGroups } matchesQuery && matchesGroup }
val start = requestedPage * pageSize val pageItems = filteredItems.drop(start).take(pageSize)
FindAssetsResult( assets = pageItems.map { item -> item.toAsset(sourceId = sourceId) }, currentPage = requestedPage, nextPage = if (start + pageSize < filteredItems.size) requestedPage + 1 else -1, total = filteredItems.size, )}Run network or database work off the main dispatcher. The callback itself is suspend, so you can call your repository, cache, or HTTP client and then return a FindAssetsResult.
Map Backend Media to Assets#
Each backend item becomes an Asset. The required field is id, but media importers should also include labels, tags, groups, thumbnail URLs, content URLs, and type hints.
private fun BackendMediaItem.toAsset(sourceId: String) = Asset( id = id, context = AssetContext(sourceId = sourceId), label = label, locale = locale, tags = tags, groups = groups, meta = mapOf( "uri" to uri, "thumbUri" to thumbUri, "mimeType" to mimeType, "blockType" to DesignBlockType.Graphic.key, "fillType" to FillType.Image.key, "shapeType" to ShapeType.Rect.key, "kind" to "image", "width" to width.toString(), "height" to height.toString(), ), credits = AssetCredits( name = authorName, uri = authorUri?.let(Uri::parse), ), license = AssetLicense( name = licenseName, uri = licenseUri?.let(Uri::parse), ), utm = AssetUTM(source = "app-media", medium = "importer"),)The meta map drives the default apply behavior:
| Key | Purpose |
|---|---|
uri |
Permanent URL for the media file. |
thumbUri |
Preview image shown by asset browsing UI. |
mimeType |
MIME type used before CE.SDK loads the asset data. |
blockType |
Design block type to create when the asset is applied. |
fillType |
Fill type used for graphic media. |
shapeType |
Shape type attached to the graphic block. |
kind |
App-facing category stored on the resulting block. |
width / height |
Original media dimensions when known. |
The sample uses DesignBlockType.Graphic.key, FillType.Image.key, and ShapeType.Rect.key so CE.SDK can create an image graphic without guessing from the file.
Register and Query the Source#
Register the source once on the Engine before querying it or exposing it through an asset library entry.
engine.asset.addSource(source)You can then query the registered source directly. This is the same path CE.SDK uses when UI surfaces browse the source.
val groups = engine.asset.getGroups(sourceId = source.sourceId).orEmpty()val result = engine.asset.findAssets( sourceId = source.sourceId, query = FindAssetsQuery( query = "summer", page = 0, perPage = 2, locale = "en", ),)When your backend changes outside CE.SDK, notify the Engine so subscribers and UI surfaces can refresh their cached source contents.
engine.asset.assetSourceContentsChanged(sourceId = source.sourceId)API Reference#
| Method | Purpose |
|---|---|
AssetSource(sourceId=_) |
Defines a custom asset source with a stable source identifier. |
AssetSource.findAssets(query=_) |
Returns one page of assets for a search, group filter, locale, and pagination request. |
AssetSource.getGroups() |
Lists the groups exposed by the source. |
engine.asset.addSource(source=_) |
Registers the custom source with the Engine. |
engine.asset.findAssets(sourceId=_, query=_) |
Queries a registered source directly. |
engine.asset.getGroups(sourceId=_) |
Reads the groups exposed by a registered source. |
engine.asset.assetSourceContentsChanged(sourceId=_) |
Notifies CE.SDK that external source contents changed. |
Key Types#
| Type | Purpose |
|---|---|
FindAssetsQuery |
Search text, locale, group filters, sort options, page, and page size. |
FindAssetsResult |
Returned assets plus currentPage, nextPage, and total. |
Asset |
One result in a source, including labels, tags, groups, metadata, credits, and license data. |
AssetContext |
Connects an asset result to the source that produced it. |
DesignBlockType, FillType, ShapeType |
Type-safe constants for metadata values that drive default insertion. |
Next Steps#
- Asset Concepts - Understand how sources, assets, metadata, and UI surfaces fit together.
- Customize - Add custom sources to the Android asset library UI.