public final class Intents

  1. Object
  2. 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.

Methods

public static boolean areIntentsSupported()True when this platform can expose intents to the system.
public static boolean isHeadlessExecutionSupported()True when this platform can run an intent without bringing the app to the foreground.
public static boolean isVoiceInvocationSupported()True when a voice assistant can invoke intents here.
public static boolean isIndexingSupported()True when index can publish content to a system-wide search index.
public static List<IntentDeclaration> getDeclarations()Every intent this application declares, from the build-time table and from registerDynamicIntent.
public static IntentDeclaration getDeclaration(String intentId)The declaration with this id, or null.
public static void registerDynamicIntent(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.
public static DynamicIntent getDynamicIntent(String intentId)The parameterization registered under this id, or null when the id is a build-time declaration or nothing at all.
public static IntentResult invoke(String intentId, Map<String, Object> params)Runs an intent on the calling thread and returns its result.
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.
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.
public static int getDefaultTimeout()The default handler time budget in seconds.
public static void donate(String intentId, Map<String, Object> params)Tells the system the user just performed this capability, so it can suggest or predict it later.
public static void index(List<AppEntity> entities)Publishes app content to the device’s search index, replacing any entry carrying the same type and id.
public static void index(AppEntity entity)Publishes a single entity.
public static void removeFromIndex(String entityType, String id)Removes one entry from the search index.
public static void clearIndex(String entityType)Removes every indexed entry of one type, or everything this app indexed.
public static List<AppEntity> queryEntities(String entityType, String kind, String argument)Runs one of an entity type’s declared queries.
public static List<Tool> asTools()The intents that opted into Exposure.MODEL, projected down to com.codename1.ai.Tool so they can be handed to a language model or an MCP host.
public static void setSelectionHandler(EntitySelectionHandler handler)Registers the single handler that receives taps on content published with index.
public static void dispatchSpotlightSelection(String uniqueId)Framework/port entry point: the user opened an indexed item.
public static boolean dispatchUserActivity(String activityType, Map<String, Object> params)Framework/port entry point: a platform activity arrived that is not a web link.
public static void setDispatcher(IntentDispatcher d)Internal: installs the build-time-generated dispatcher and drains any invocation that arrived before it.
public static void publishPendingDeclarations()Framework/port entry point: resolves the platform bridge, which publishes any declarations that were installed before one existed.
public static void setBridge(IntentBridge b)Framework/port/test entry point: overrides the bridge resolved from the platform port.

Inherited methods

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 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 when index can publish content to a system-wide search index.

getDeclarations

public static List<IntentDeclaration> getDeclarations()

Every intent this application declares, from the build-time table and from registerDynamicIntent.

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

public static IntentDeclaration getDeclaration(String intentId)
The declaration with this id, or null.

Parameters

intentId String
the intent id

registerDynamicIntent

public static void registerDynamicIntent(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.

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 DynamicIntent
the parameterization

getDynamicIntent

public static DynamicIntent getDynamicIntent(String intentId)

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 String
the id to look up

invoke

public static IntentResult invoke(String intentId, Map<String, Object> params)

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, 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, where the framework has already told the platform the invocation failed and a second answer would be a protocol violation.

Parameters

intentId String
the declared intent id
params Map<String, Object>
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 String
the intent to run
params Map<String, Object>
parameter values keyed by name; may be null
source IntentSource
where the invocation came from
headless boolean
true when the app has no UI on screen
completion IntentCompletion
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 and do the work in the app.

Parameters

seconds int
the default budget; values below 1 are ignored

getDefaultTimeout

public static int getDefaultTimeout()
The default handler time budget in seconds.

donate

public static void donate(String intentId, Map<String, Object> params)

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 String
the capability that was performed
params Map<String, Object>
the values it was performed with; may be null

index

public static void index(List<AppEntity> entities)

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 List<AppEntity>
the content to publish; null and empty are no-ops

index

public static void index(AppEntity entity)
Publishes a single entity. Shorthand for the list form.

Parameters

entity AppEntity
the content to publish

removeFromIndex

public static void removeFromIndex(String entityType, String id)

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 String
the entity type id
id String
the entity id

clearIndex

public static void clearIndex(String entityType)
Removes every indexed entry of one type, or everything this app indexed.

Parameters

entityType String
the type to clear, or null for all of this app’s entries

queryEntities

public static List<AppEntity> queryEntities(String entityType, String kind, String argument)

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 String
the entity type id
kind String
byId, suggested or search
argument String
the id, the search text, or null

Returns

the matching entities, never null

asTools

public static List<Tool> asTools()

The intents that opted into Exposure.MODEL, projected down to com.codename1.ai.Tool so 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 Tool is 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

public static void setSelectionHandler(EntitySelectionHandler handler)

Registers the single handler that receives taps on content published with index.

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 EntitySelectionHandler
the handler, or null to clear

dispatchSpotlightSelection

public static void dispatchSpotlightSelection(String uniqueId)
Framework/port entry point: the user opened an indexed item. The id is the composite the framework indexed under, type:id.

Parameters

uniqueId String
the identifier the platform handed back

dispatchUserActivity

public static boolean dispatchUserActivity(String activityType, Map<String, Object> params)

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 String
the platform activity type
params Map<String, Object>
the activity payload, may be null

setDispatcher

public static void setDispatcher(IntentDispatcher d)
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 IntentDispatcher
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 before startContext on 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

public static void setBridge(IntentBridge b)
Framework/port/test entry point: overrides the bridge resolved from the platform port. Passing null restores platform resolution.

Parameters

b IntentBridge
the bridge, or null