Skip to content

Expose task capabilities as Android App Functions #222

Description

@MessiasLima

Description

Expose Foliary's task capabilities as Android App Functions (androidx.appfunctions) so system surfaces (Google Assistant, Google Home) can invoke them. Implement an AppFunctionService in the Android module that exposes four functions backed by TaskRepository:

  • getTodayTasks — tasks due today and overdue tasks; maps 1:1 to TaskRepository.findTodayTasks()
  • findById — a task by its UUID; maps to TaskRepository.findById()
  • search — case-insensitive free-text search over task titles and descriptions; no backing implementation exists, so this requires a new TaskDao query and TaskRepository method (with tests)
  • createTask — persists a new task via TaskRepository.save(); the full Task must be constructed with defaults, mirroring the existing logic in CreateTaskViewModel.onSaveClick (UUID via UuidProvider, Priority.MEDIUM, creation date via TimeProvider)

Technical and design context:

  • App Functions requires adding the first-party androidx.appfunctions:appfunctions runtime + compiler dependency (used version previously: 1.0.0-alpha10) to gradle/libs.versions.toml — a new dependency, needs approval.
  • The service and its generated subclass must be registered in the Android manifest with the android.permission.BIND_APP_FUNCTION_SERVICE permission, the android.app.appfunctions.AppFunctionService intent filter, the android.app.appfunctions.schema (XSD) and android.app.appfunctions.v2 inventory XML properties, plus uses-library com.android.extensions.appfunctions with required="false".
  • Koin currently starts inside Compose composition (App.kt), so the system-instantiated service cannot rely on that graph; it needs its own Koin module/wiring in the android module.
  • Expose tasks through stable @AppFunctionSerializable function models (id, title, description, dates, priority, url, location) mapped from the Room Task entity — do not expose Room entities directly.
  • minSdk is 31 while App Functions services are @RequiresApi(36); behavior on older devices must be gated at runtime and must not affect normal app behavior.
  • Edge cases: malformed/unknown UUID input to findById must yield an error result, not a crash; createTask with a blank title must fail with an error and persist nothing; empty search results must return an empty list; nulls (description, due date, location) must map cleanly to the serializable models.
  • Reference material: a previous uncommitted implementation (package dev.appoutlet.foliary.android.appfunction with TaskFunctionService, TaskFunctionModel, LocationFunctionModel, TaskFunctionModelMapper, AppFunctionModule) was deleted from source but its full schema and inventory survive in android/build/ KSP and dex backups — use them as reference.

Scope

In scope:

  • App Functions runtime + compiler dependency in gradle/libs.versions.toml
  • Serializable function models and entity-to-model mapper
  • AppFunctionService exposing the four functions with correct schema/inventory metadata
  • Manifest service registration and schema resources
  • Backing search capability: TaskDao query + TaskRepository method
  • Unit tests: mapper/service logic, DAO and repository search, create-task validation
  • Koin wiring for the system-instantiated service

Out of scope:

  • Device-level verification with an actual system surface (Assistant/Home) — manual QA item
  • Any UI changes or user-facing strings
  • Functions beyond the four listed

Acceptance criteria

  • GIVEN the app is running on a device at API 36 or higher
    WHEN the getTodayTasks app function is invoked
    THEN it returns all tasks due today and overdue tasks, mapped to the public function models
  • GIVEN an existing task with a valid UUID
    WHEN the findById app function is invoked with that UUID
    THEN it returns the task
  • GIVEN a UUID that is unknown or malformed
    WHEN the findById app function is invoked
    THEN it returns an error result and the app does not crash
  • GIVEN tasks whose titles or descriptions contain a query string
    WHEN the search app function is invoked with that string
    THEN it returns the matching tasks regardless of letter case, and an empty list when there are no matches
  • GIVEN a createTask invocation with a valid title and optional fields
    WHEN the function executes
    THEN a new task is persisted through TaskRepository.save() and its UUID is returned
  • GIVEN a createTask invocation with a blank title
    WHEN the function executes
    THEN it returns an error result and no task is persisted
  • GIVEN the service and schema are registered in the manifest and resources
    WHEN the build succeeds
    THEN the merged manifest contains the service with BIND_APP_FUNCTION_SERVICE, the schema/inventory properties, and the uses-library declaration
  • GIVEN the app runs on a device below API 36
    WHEN the app is used normally
    THEN behavior is unchanged and no crash or service registration occurs

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions