public final class Intents
- Object
- 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
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()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()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)Parameters
intentIdString- 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
intentDynamicIntent- 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
intentIdString- 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
intentIdString- the declared intent id
paramsMap<String, Object>- parameter values keyed by name; may be null
Returns
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
intentIdString- the intent to run
paramsMap<String, Object>- parameter values keyed by name; may be null
sourceIntentSource- where the invocation came from
headlessboolean- true when the app has no UI on screen
completionIntentCompletion- 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
secondsint- the default budget; values below 1 are ignored
getDefaultTimeout
public static int getDefaultTimeout()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
intentIdString- the capability that was performed
paramsMap<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
entitiesList<AppEntity>- the content to publish; null and empty are no-ops
index
public static void index(AppEntity entity)Parameters
entityAppEntity- 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
entityTypeString- the entity type id
idString- the entity id
clearIndex
public static void clearIndex(String entityType)Parameters
entityTypeString- 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
entityTypeString- the entity type id
kindStringbyId,suggestedorsearchargumentString- the id, the search text, or null
Returns
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
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
handlerEntitySelectionHandler- the handler, or null to clear
dispatchSpotlightSelection
public static void dispatchSpotlightSelection(String uniqueId)type:id.Parameters
uniqueIdString- 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
activityTypeString- the platform activity type
paramsMap<String, Object>- the activity payload, may be null
setDispatcher
public static void setDispatcher(IntentDispatcher d)Parameters
dIntentDispatcher- 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)Parameters
bIntentBridge- the bridge, or null