This guide covers what changed for Android in v1.77: an asset-source registration migration, and new structured engine error codes. Both are described below.
Asset sources#
Version 1.77 moves asset-source registration out of the engine binding and updates the default asset content. As part of this update, several built-in asset source IDs were renamed or merged.
Two things change for your Android app:
- The
Engine.addDefaultAssetSources(...)andEngine.addDemoAssetSources(...)helpers, theDefaultAssetSourceandDemoAssetSourceenums, and theEngine.Companion.assetBaseUriconstant are deprecated. Register sources directly withEngine.asset.addLocalSourceFromJSON(contentUri). - Because the default base URI now serves the updated content, the deprecated helpers can no longer load the renamed or merged source IDs. Those sources are skipped silently, so the affected asset-library panels appear empty until you migrate.
Do I need to migrate?#
You are affected if any of the following apply:
- You call
engine.addDefaultAssetSources()orengine.addDemoAssetSources(). - You reference a built-in source by one of the renamed IDs in the table below.
- You self-host the CE.SDK assets.
If you never registered asset sources yourself and do not self-host, the Starter Kits already register the current sources for you—bump the SDK and you are done.
Asset Source ID Changes#
Renamed sources#
| Old ID (up to v1.76) | New ID (v1.77+) |
|---|---|
ly.img.vectorpath | ly.img.vector.shape |
ly.img.colors.defaultPalette | ly.img.color.palette |
ly.img.textComponents | ly.img.text.components |
Merged sources#
| Old IDs (up to v1.76) | New ID (v1.77+) | Notes |
|---|---|---|
ly.img.filter.lut + ly.img.filter.duotone | ly.img.filter | A single source now covers LUT and duotone filters |
Newly registered sources#
The default starter kits now register three text sources from the CDN. ly.img.text replaces the previous in-app programmatic text source (ly.img.asset.source.text):
ly.img.text—plain text presets (title, headline, body).ly.img.text.styles—decorative text styles.ly.img.text.curves—curved text presets.
Scenario A: You use the default asset library#
If you relied on addDefaultAssetSources or addDemoAssetSources (directly, or through a pre-v1.73 solution view), replace those calls with explicit addLocalSourceFromJSON registrations that use the new IDs.
Before#
engine.addDefaultAssetSources()engine.addDemoAssetSources()Now#
val baseUri = Uri.parse("https://cdn.img.ly/packages/imgly/cesdk-android/1.77.1/assets")
// Default content sources, plus the demo image source.listOf( "ly.img.sticker", "ly.img.vector.shape", "ly.img.filter", "ly.img.color.palette", "ly.img.effect", "ly.img.blur", "ly.img.typeface", "ly.img.crop.presets", "ly.img.page.presets", "ly.img.text", "ly.img.text.styles", "ly.img.text.curves", "ly.img.text.components", "ly.img.image",).forEach { id -> engine.asset.addLocalSourceFromJSON(Uri.parse("$baseUri/$id/content.json"))}engine.asset.addLocalSource( sourceId = "ly.img.image.upload", supportedMimeTypes = listOf("image/jpeg", "image/png", "image/heic", "image/heif", "image/svg+xml", "image/gif", "image/apng", "image/bmp"),)
// A video editor also registers the video and audio sources (content + uploads):listOf("ly.img.video", "ly.img.audio").forEach { id -> engine.asset.addLocalSourceFromJSON(Uri.parse("$baseUri/$id/content.json"))}engine.asset.addLocalSource( sourceId = "ly.img.video.upload", supportedMimeTypes = listOf("video/mp4"),)engine.asset.addLocalSource( sourceId = "ly.img.audio.upload", supportedMimeTypes = listOf("audio/x-m4a", "audio/mp3", "audio/mpeg"),)If you use a Starter Kit, this registration already lives in the starter kit’s asset-loading callback and is updated for you. See Serve Assets for the complete, current source list and registration reference.
Scenario B: You self-host your assets#
If you serve the assets from your own location, download the updated asset bundle and point your base URI at it:
- Download
https://cdn.img.ly/packages/imgly/cesdk-android/1.77.1/imgly-assets.zipand copy theassets/directory to your hosting location. - Update your
baseUrito your hosted path. - Register the renamed and newly added source IDs from the tables above. The old IDs no longer resolve.
Why is my asset library empty or showing “Cannot connect to service”?#
After upgrading, an empty shapes, filter, color-palette, or text panel means a source ID no longer resolves at the base URI. This happens when:
- The deprecated
addDefaultAssetSourcesoraddDemoAssetSourceshelpers requested an old ID (for examplely.img.vectorpath) that the CDN no longer serves. Register the new ID instead, following Scenario A. - You self-host and still serve content from v1.76 or earlier. Serve the updated assets and register the new IDs, following Scenario B.
Engine error codes#
Engine errors now carry a stable catalog code, a developer-facing hint, typed args, and an optional docs anchor. Existing code that reads EngineException.message keeps working; new code can branch on the stable code.
EngineException (still in ly.img.engine) now exposes the structured fields directly. The constructor is source-compatible — EngineException("msg") still works — so existing callers that read e.message keep working unchanged.
import ly.img.engine.EngineErrorCodeimport ly.img.engine.EngineException
try { engine.scene.create()} catch (e: EngineException) { when (e.code) { EngineErrorCode.SCENE_NOT_VALID -> toast("No scene is loaded.") // Any code you don't handle explicitly — fall back to message. else -> toast(e.message ?: "Unknown error") }
e.docsUrl?.let { openInBrowser(it) }}EngineErrorCode is an object of String constants — branch on EngineErrorCode.SCENE_NOT_VALID instead of a hand-typed string. The code itself stays a plain string, so an unrecognized or future code never breaks your build — always keep an else branch.
Typed args#
EngineException.args is a Map<String, Any> whose values are Boolean, Long, Double, or String — the original primitive type is preserved across the JNI boundary. Cast in Kotlin:
val fontSize = e.args["fontSize"] as? Longval blockId = e.args["blockId"] as? LongWhat is not a breaking change#
EngineException.messagestill returns the engine’s rendered English string, and theEngineException(message)constructor signature is unchanged (new fields have default values), so existing code keeps working.
What might surprise you#
codeis an append-only contract. Existing codes are never renamed or repurposed, and new codes are added over time — which is why you should always keep anelsebranch. Generic failures wrapped from third-party libraries surface under a catch-all code such asUTILS.STD_EXPECTED_STRING_ERROR.silent: trueflags errors the catalog marks as expected limitations — programmatically observable but suppressed from logs.