AppFunctions (AI Assistant Integration)
Android 16 introduces androidx.appfunctions, enabling system-level AI assistants (such as Google Gemini) to discover and execute actions within applications without opening the user interface. Geotify exposes six specialized AppFunctions for managing saved locations and geofenced reminders.
Overview & System Flow
When a user prompts an AI assistant (e.g., "Remind me to buy milk when I arrive at the supermarket"), the assistant inspects registered androidx.appfunctions schemas, builds the parameter payloads, and invokes the function directly in the background.
sequenceDiagram
autonumber
actor User
participant AI as AI Assistant (Gemini)
participant AF as GeotifyAppFunctions
participant Repo as Repositories
participant DB as Room Database (geotify.db)
User->>AI: "Remind me to buy milk when I get home"
AI->>AF: listLocations(context)
AF->>Repo: getAllLocations()
Repo->>DB: Query saved locations
DB-->>AF: [SavedLocation(alias="home", ...)]
AF-->>AI: List of SavedLocation DTOs
AI->>AF: createGeofenceReminder(context, targetAlias="home", payloadMessage="buy milk", triggerOnArrival=true)
AF->>Repo: createReminder(...)
Repo->>DB: Insert ReminderEntity
AF-->>AI: CreateReminderResult DTO
AI-->>User: "I've set a reminder for when you arrive at home."
Context Injection & Metadata Extraction
Every function in GeotifyAppFunctions accepts an AppFunctionContext as its first parameter. The system automatically injects this context upon invocation.
@AppFunction(isDescribedByKDoc = true): Instructs the KSP compiler plugin (androidx.appfunctions.compiler) to parse KDoc comments into app metadata XML (app_metadata.xml), exposing function capabilities to system AI callers.@AppFunctionSerializable: Annotates all return DTO classes (SaveLocationResult,CreateReminderResult,SavedLocation,DeleteResult,SavedReminder) to allow automated cross-process serialization.
API Summary & Method Specs
GeotifyAppFunctions is defined in GeotifyAppFunctions.kt.
| Function | Purpose | Key Parameters | Return Type |
|---|---|---|---|
saveCurrentLocation |
Captures current device GPS coordinates and saves them under an alias. | alias: String |
SaveLocationResult |
createGeofenceReminder |
Attaches a geofence trigger (arrival or departure) to a saved location. | targetAlias: String, payloadMessage: String, triggerOnArrival: Boolean |
CreateReminderResult |
listLocations |
Retrieves all saved points of interest with coordinates. | None | List<SavedLocation> |
deleteLocation |
Removes a location and all cascaded reminders. | alias: String |
DeleteResult |
deleteReminder |
Cancels an active reminder for a location. | targetAlias: String, message: String? = null |
DeleteResult |
listActiveReminders |
Returns all active reminders across all locations. | None | List<SavedReminder> |
Function Details
saveCurrentLocation
Fetches the device's current location via high-accuracy GPS and stores it under a human-readable alias (e.g., "home", "office").
- Exceptions: Throws AppFunctionInvalidArgumentException if alias already exists.
createGeofenceReminder
Registers a geofence notification for a saved location alias.
- triggerOnArrival: true for entry transition (GEOFENCE_TRANSITION_ENTER), false for exit transition (GEOFENCE_TRANSITION_EXIT).
- Exceptions: Throws AppFunctionInvalidArgumentException if targetAlias is not found.
listLocations
Returns a list of all saved POIs (SavedLocation(alias, latitude, longitude)).
deleteLocation
Deletes a saved location by alias. Removes associated reminders automatically via SQLite foreign key cascading rules.
deleteReminder
Deletes active reminders matching targetAlias.
- If message is specified, performs a case-insensitive substring match. If message is omitted and multiple reminders exist for that location, throws an exception requiring clarification.
listActiveReminders
Returns all active reminders (SavedReminder(reminderId, targetAlias, payloadMessage, triggerType)).
AI Self-Correction Strategy (throwAliasNotFound)
When an invalid location alias is passed by an AI caller, Geotify returns actionable feedback rather than a generic error. The application queries all existing aliases and embeds them directly into the exception message:
private suspend fun throwAliasNotFound(alias: String): Nothing {
val aliases = locationRepository.getAllAliases()
val message = if (aliases.isEmpty()) {
"Alias '$alias' not found. No locations are saved yet. Please save a location first using saveCurrentLocation."
} else {
"Alias '$alias' not found. Valid locations are: ${aliases.joinToString()}. Map the user's intent to an exact value from this list and invoke again."
}
throw AppFunctionInvalidArgumentException(message)
}
Self-Correction Lifecycle Example
- User request: "Remind me to buy bread at the store."
- AI Action: Invoques
createGeofenceReminder(targetAlias="store", ...) - App Exception:
AppFunctionInvalidArgumentException: "Alias 'store' not found. Valid locations are: home, supermarket, gym. Map the user's intent to an exact value from this list and invoke again." - AI Recovery: The AI assistant reads the available alias list, maps
"store"to"supermarket", and re-invokescreateGeofenceReminder(targetAlias="supermarket", ...).