Understand the asset system on Android, including how CE.SDK models asset data, exposes assets through sources, and turns those assets into blocks in a scene.
Images, videos, audio, fonts, stickers, and templates are all assets in CE.SDK. The Android engine gets access to them through asset sources. When you apply an asset, CE.SDK creates or updates a block so that the asset becomes visible in the scene.
The example accepts license: String?. Pass null to run in evaluation mode during development, then replace it with your
production key when you wire the code into your app.
This guide covers the core concepts of the asset system. For a concrete media workflow, see the Images guide. For related concepts, see Blocks and Resources.
Assets vs Blocks#
Assets are content definitions with metadata such as URIs, dimensions, tags, and grouping information. They exist outside the scene tree. Blocks are the visual elements in the scene that render or reference that content.
When you apply an asset, CE.SDK creates a block configured from the asset metadata or updates an existing block with new asset data. Multiple blocks can reuse the same asset definition, and an asset can exist in a source without being used in the scene yet.
The Asset Data Model#
On Android, findAssets() returns Asset objects and local sources accept AssetDefinition objects. They share the same core
ideas: IDs, localized labels, tags, groups, structured payload data, and meta entries that describe how the asset should be
handled.
Asset( id = "imgly-logo", context = AssetContext(sourceId = sourceId), label = "IMG.LY Logo", locale = "en", tags = listOf("logo", "brand", "header"), groups = listOf("logos"), meta = mapOf( "uri" to "https://img.ly/static/ubq_samples/imgly_logo.jpg", "thumbUri" to "https://img.ly/static/ubq_samples/imgly_logo.jpg", "mimeType" to "image/jpeg", "kind" to "image", "blockType" to DesignBlockType.Graphic.key, "fillType" to FillType.Image.key, "shapeType" to ShapeType.Rect.key, "width" to "640", "height" to "320", ),),Key properties include:
idfor the stable asset identifier.contextfor thesourceIdthat produced the asset.labelandlocalefor localized display text.tagsandgroupsfor search and filtering.metafor content-specific fields such asuri,thumbUri,mimeType,blockType,fillType,shapeType,width, andheight.payloadfor structured values such as colors, typefaces, source sets, or transform presets when plain string metadata is not enough.
Asset Sources#
Asset sources provide assets to the editor and the engine APIs. On Android, a custom source subclasses AssetSource and
implements at least findAssets(query) and getGroups().
override suspend fun getGroups(): List<String>? = brandedAssets.flatMap { it.groups.orEmpty() }.distinct()
override suspend fun findAssets(query: FindAssetsQuery): FindAssetsResult { val searchQuery = query.query val queryGroups = query.groups.orEmpty() val filteredAssets = brandedAssets.filter { asset -> val matchesQuery = searchQuery.isNullOrBlank() || buildList { asset.label?.let(::add) addAll(asset.tags.orEmpty()) }.any { value -> value.contains(searchQuery, ignoreCase = true) }
val matchesGroups = queryGroups.isEmpty() || asset.groups.orEmpty().any(queryGroups::contains)
matchesQuery && matchesGroups } val startIndex = query.page * query.perPage val pageAssets = filteredAssets.drop(startIndex).take(query.perPage) val nextPage = if (startIndex + pageAssets.size < filteredAssets.size) { query.page + 1 } else { -1 }
return FindAssetsResult( assets = pageAssets, currentPage = query.page, nextPage = nextPage, total = filteredAssets.size, )}
override suspend fun fetchAsset( id: String, options: FetchAssetOptions,): Asset? = brandedAssets.firstOrNull { it.id == id }The FindAssetsQuery object contains paging, text search, sorting, tag, and group filters. Your source responds with a
FindAssetsResult that contains the assets for the requested page, the total match count, and nextPage, which is -1 when
there are no more results.
Sources can also expose supportedMimeTypes, credits, license, fetchAsset(), and custom applyAsset() behavior when you
need more than the default block creation logic.
Querying Assets#
Use engine.asset.findAssets() to search a source. Android pages are zero-based, so the first request uses page = 0.
val queriedAssets = engine.asset.findAssets( sourceId = source.sourceId, query = FindAssetsQuery( perPage = 10, page = 0, query = "logo", groups = listOf("logos"), ),)val queriedAsset = queriedAssets.assets.first()val groups = engine.asset.getGroups(sourceId = source.sourceId)println("Found ${queriedAssets.total} assets in groups $groups")This is the point where you typically combine free-text search with groups, tags, or sorting. You can also call
engine.asset.getGroups() to inspect the filters that a source exposes before you build your own asset browser UI.
Applying Assets#
Use engine.asset.applyAssetSourceAsset() when you want the source’s custom apply behavior. If the source does not override
applyAsset(), CE.SDK falls back to defaultApplyAsset() and creates a block from the asset’s meta fields.
val appliedBlock = engine.asset.applyAssetSourceAsset( sourceId = source.sourceId, asset = queriedAsset,)if (appliedBlock != null) { engine.block.setPositionX(appliedBlock, 64F) engine.block.setPositionY(appliedBlock, 64F)}That block can then be positioned, resized, or otherwise modified through the regular block APIs.
Local Asset Sources#
Local asset sources keep their assets in memory and are ideal for uploads, generated media, or app-specific catalogs that you construct at runtime.
engine.asset.addLocalSource( sourceId = "my-local-images", supportedMimeTypes = listOf("image/jpeg"),)
val localAsset = AssetDefinition( id = "sunrise-poster", label = mapOf("en" to "Sunrise Poster"), tags = mapOf("en" to listOf("poster", "sunrise", "brand")), groups = listOf("posters"), meta = mapOf( "uri" to "https://img.ly/static/ubq_samples/sample_1.jpg", "thumbUri" to "https://img.ly/static/ubq_samples/sample_1.jpg", "mimeType" to "image/jpeg", "kind" to "image", "blockType" to DesignBlockType.Graphic.key, "fillType" to FillType.Image.key, "shapeType" to ShapeType.Rect.key, "width" to "1080", "height" to "1080", ),)engine.asset.addAsset(sourceId = "my-local-images", asset = localAsset)engine.asset.assetSourceContentsChanged(sourceId = "my-local-images")AssetDefinition uses localized label and tags maps, while meta carries the URI, MIME type, and block creation hints that
defaultApplyAsset() needs later on.
Source Events#
The asset API exposes Flow<String> streams for source lifecycle changes. These are useful when your UI needs to refresh its
filters or grid contents after sources are added, removed, or updated.
sourceEventJobs += engine.asset.onAssetSourceAdded() .onEach { println("Asset source added: $it") } .launchIn(this)
sourceEventJobs += engine.asset.onAssetSourceRemoved() .onEach { println("Asset source removed: $it") } .launchIn(this)
sourceEventJobs += engine.asset.onAssetSourceUpdated() .onEach { println("Asset source updated: $it") } .launchIn(this)After mutating a source, call engine.asset.assetSourceContentsChanged(sourceId) so subscribers know they should re-query the
source.