Class Intents
The entry point for app intents: the capabilities your application offers to the outside world.
You do not register intents here. You declare them with
com.codename1.annotations.AppIntent on a public static method and the
build generates the table, because the platforms compile their intent
catalogues into the native binary and a runtime-only registration could never
reach them. What this class gives you is everything around that declaration:
running an intent yourself, telling the system one just happened, publishing
content to device search, and asking what the current platform can actually
do.
// In your application code, once:
@AppIntent(value = "log_workout", title = "Log a workout",
phrases = {"Log a workout in ${applicationName}"}, headless = true)
public static IntentResult logWorkout(
@IntentParam("minutes") int minutes) {
WorkoutStore.append(minutes);
return IntentResult.spoken("Logged " + minutes + " minutes.");
}
// Later, after the user does it by hand, so the system learns to suggest it:
Intents.donate("log_workout", params);
What is honestly supported where
Ask, do not assume. areIntentsSupported() is true wherever intents can be
exposed to the platform at all, but the interesting question is usually
isVoiceInvocationSupported(), which is true on iOS and false on Android --
Android has no assistant contract that hands a typed result back to an app.
Android gets launcher shortcuts and headless execution; it does not get Siri.
The package documentation has the full table.
Zero cost when unused
Referencing this package is what makes the build inject the native plumbing.
An application that never touches com.codename1.intents gets none of it and
builds exactly as it did before.
-
Method Summary
Modifier and TypeMethodDescriptionstatic booleanTrue when this platform can expose intents to the system.asTools()The intents that opted intoExposure.MODEL, projected down tocom.codename1.ai.Toolso they can be handed to a language model or an MCP host.static voidclearIndex(String entityType) Removes every indexed entry of one type, or everything this app indexed.static voiddispatchInvocation(String intentId, Map<String, Object> params, IntentSource source, boolean headless, IntentCompletion completion) Framework/port entry point: runs an intent the platform asked for and reports the outcome exactly once.static voiddispatchSpotlightSelection(String uniqueId) Framework/port entry point: the user opened an indexed item.static booleandispatchUserActivity(String activityType, Map<String, Object> params) Framework/port entry point: a platform activity arrived that is not a web link.static voidTells the system the user just performed this capability, so it can suggest or predict it later.static IntentDeclarationgetDeclaration(String intentId) The declaration with this id, or null.static List<IntentDeclaration> Every intent this application declares, from the build-time table and fromregisterDynamicIntent(DynamicIntent).static intThe default handler time budget in seconds.static DynamicIntentgetDynamicIntent(String intentId) The parameterization registered under this id, or null when the id is a build-time declaration or nothing at all.static voidPublishes a single entity.static voidPublishes app content to the device's search index, replacing any entry carrying the same type and id.static IntentResultRuns an intent on the calling thread and returns its result.static booleanTrue when this platform can run an intent without bringing the app to the foreground.static booleanTrue whenindex(List)can publish content to a system-wide search index.static booleanTrue when a voice assistant can invoke intents here.static voidFramework/port entry point: resolves the platform bridge, which publishes any declarations that were installed before one existed.queryEntities(String entityType, String kind, String argument) Runs one of an entity type's declared queries.static voidregisterDynamicIntent(DynamicIntent intent) Declares a parameterization of an intent the application already declares -- a specific shortcut such as "reorder my usual", built from data only known once the app is running.static voidremoveFromIndex(String entityType, String id) Removes one entry from the search index.static voidFramework/port/test entry point: overrides the bridge resolved from the platform port.static voidsetDefaultTimeout(int seconds) Overrides how long a handler may run before the framework gives up, for intents that did not state their own budget.static voidInternal: installs the build-time-generated dispatcher and drains any invocation that arrived before it.static voidRegisters the single handler that receives taps on content published withindex(List).
-
Method Details
-
areIntentsSupported
public static boolean areIntentsSupported()True when this platform can expose intents to the system.
False does not make the API useless:
invoke(String, Map)still runs your handlers in-process on every platform, because the dispatch table is generated code rather than a platform service. Only the projections outward -- voice, search indexing, shortcuts -- go quiet. -
isHeadlessExecutionSupported
public static boolean isHeadlessExecutionSupported()True when this platform can run an intent without bringing the app to the foreground. -
isVoiceInvocationSupported
public static boolean isVoiceInvocationSupported()True when a voice assistant can invoke intents here.
This is the honest discriminator between the platforms. Branch on it rather than on
areIntentsSupported()when deciding whether to tell a user they can talk to your app. -
isIndexingSupported
public static boolean isIndexingSupported()True whenindex(List)can publish content to a system-wide search index. -
getDeclarations
Every intent this application declares, from the build-time table and from
registerDynamicIntent(DynamicIntent).The simulator's Intents window is built from exactly this list, which is what makes it trustworthy: it can only show what actually shipped.
-
getDeclaration
The declaration with this id, or null.
Parameters
intentId: the intent id
-
registerDynamicIntent
Declares a parameterization of an intent the application already declares -- a specific shortcut such as "reorder my usual", built from data only known once the app is running.
It runs by running the intent it names, with the bound values filled in, which is what makes it invokable at all. It cannot introduce a new capability: the native catalogue is compiled into the app, so a genuinely new verb could never reach the platform.
Ignored when the base intent is not declared, or when the id would shadow a declared one.
Parameters
intent: the parameterization
-
getDynamicIntent
The parameterization registered under this id, or null when the id is a build-time declaration or nothing at all.
Ports need this to resolve a donation: a shortcut outlives the process while a parameterization does not, so the shortcut has to record the base intent and the bound values rather than a runtime id nothing will recognise later.
Parameters
intentId: the id to look up
-
invoke
Runs an intent on the calling thread and returns its result.
This is the in-app path -- your own code deciding to perform one of its declared capabilities -- and it works on every platform, including those with no intent support at all, because the dispatch table is generated code rather than a platform service.
Platform-initiated invocations do not come through here; they arrive at
dispatchInvocation(String, Map, IntentSource, boolean, IntentCompletion), which adds thread marshalling and an enforced deadline.The deadline on this path is a budget the handler may consult, not a cutoff: your own thread is blocked in this call, nothing else is waiting to report an outcome, and a handler that overruns has still done the work and produced the answer you asked for. So a late result is returned rather than discarded. Discarding is the right behaviour under
dispatchInvocation(String, Map, IntentSource, boolean, IntentCompletion), where the framework has already told the platform the invocation failed and a second answer would be a protocol violation.Parameters
intentId: the declared intent idparams: parameter values keyed by name; may be null
Returns
the handler's result, or a failed result when no such intent exists
-
dispatchInvocation
public static void dispatchInvocation(String intentId, Map<String, Object> params, IntentSource source, boolean headless, IntentCompletion completion) Framework/port entry point: runs an intent the platform asked for and reports the outcome exactly once.
Ports call this after decoding their platform payload. It owns everything the ports should not each reinvent: queuing across a cold start, running the handler off the event dispatch thread, enforcing the deadline, and guaranteeing the completion fires once and only once.
Parameters
intentId: the intent to runparams: parameter values keyed by name; may be nullsource: where the invocation came fromheadless: true when the app has no UI on screencompletion: notified with the outcome; may be null
-
setDefaultTimeout
public static void setDefaultTimeout(int seconds) Overrides how long a handler may run before the framework gives up, for intents that did not state their own budget.
Raising this is rarely the right fix. The platform's patience is not the constraint that matters -- a spoken interaction that takes ten seconds has already failed as an interaction. An intent that genuinely needs longer should return
IntentResult.opens(String)and do the work in the app.Parameters
seconds: the default budget; values below 1 are ignored
-
getDefaultTimeout
public static int getDefaultTimeout()The default handler time budget in seconds. -
donate
Tells the system the user just performed this capability, so it can suggest or predict it later.
Donate when the user does the thing in your app by hand. That is the signal the system learns from; donating on every intent invocation teaches it only that the user uses shortcuts.
Callable from any thread. A no-op where unsupported.
Parameters
intentId: the capability that was performedparams: the values it was performed with; may be null
-
index
Publishes app content to the device's search index, replacing any entry carrying the same type and id.
Threading
A background thread is the right thread, not merely a permitted one. This writes through to the platform index and encodes any thumbnails on the way, so calling it on the event dispatch thread looks instantaneous in the simulator and stalls the UI on a device.
Parameters
entities: the content to publish; null and empty are no-ops
-
index
Publishes a single entity. Shorthand for the list form.
Parameters
entity: the content to publish
-
removeFromIndex
Removes one entry from the search index.
Removal matters more than it looks. An index entry outlives the data behind it, so content the user deleted keeps appearing in device search and taps resolve to nothing until the app removes it.
Parameters
entityType: the entity type idid: the entity id
-
clearIndex
Removes every indexed entry of one type, or everything this app indexed.
Parameters
entityType: the type to clear, or null for all of this app's entries
-
queryEntities
Runs one of an entity type's declared queries.
The platform calls this on its own when it has to disambiguate a parameter -- "which playlist?" -- and the simulator calls it to populate its picker, which is why the simulator exercises the real query rather than a stand-in.
Parameters
entityType: the entity type idkind:byId,suggestedorsearchargument: the id, the search text, or null
Returns
the matching entities, never null
-
asTools
The intents that opted into
Exposure.MODEL, projected down tocom.codename1.ai.Toolso they can be handed to a language model or an MCP host.Nothing is exposed by calling this. It returns descriptions; the application decides whether to give them to a model, which is deliberately a separate act from declaring the intent, because a model calls a capability because it inferred it should rather than because a person asked by name.
The projection is one-way and lossy on purpose. A
Toolis stringly typed -- a JSON schema in, a JSON string out -- which is right for a model and wrong for the platform, where entity types let the system run its own picker before the handler is reached. So the richer declaration projects down to the weaker one, never the reverse.Returns
one tool per model-exposed intent, never null
-
setSelectionHandler
Registers the single handler that receives taps on content published with
index(List).Registration drains anything that arrived before it, which is the normal case: a tap in device search is frequently what started the process, so the selection is already waiting by the time your
init()runs.Parameters
handler: the handler, or null to clear
-
dispatchSpotlightSelection
Framework/port entry point: the user opened an indexed item. The id is the composite the framework indexed under,
type:id.Parameters
uniqueId: the identifier the platform handed back
-
dispatchUserActivity
Framework/port entry point: a platform activity arrived that is not a web link. Returns true when this application claimed it.
Answering honestly matters. Claiming everything would swallow handoff and third-party activities the app never declared, so an activity is claimed only when its type names an intent this application actually declares -- which is the shape the platform uses to continue a donated action.
Parameters
activityType: the platform activity typeparams: the activity payload, may be null
-
setDispatcher
Internal: installs the build-time-generated dispatcher and drains any invocation that arrived before it. Invoked once during startup by the generated bootstrap; application code should not call this.
Parameters
d: the generated dispatcher
-
publishPendingDeclarations
public static void publishPendingDeclarations()Framework/port entry point: resolves the platform bridge, which publishes any declarations that were installed before one existed.
The generated bootstrap installs the dispatcher before the port has booted -- from
main()on iOS, and beforestartContexton Android -- so the first publication finds no bridge and is deferred. Something has to ask for the bridge afterwards or the platform never learns the catalogue, and on Android a request parked at a cold start is never judged, so the shortcut opens the app and runs nothing.Ports call this once the runtime is up. It is safe to call at any time and does nothing when there is nothing owed.
-
setBridge
Framework/port/test entry point: overrides the bridge resolved from the platform port. Passing null restores platform resolution.
Parameters
b: the bridge, or null
-