Este guia explica como integrar a API AppFunctions ao seu app Android, implementar a lógica de uma função e verificar se a integração funciona corretamente.
Compatibilidade de versões
Essa implementação exige que o compileSdk do projeto seja definido como o nível 36 da API ou mais recente.
Não é necessário que o app verifique se as AppFunctions são compatíveis. Isso é processado automaticamente na biblioteca AppFunctions Jetpack.
AppFunctionManager retorna uma instância se o recurso for compatível e retorna nulo caso contrário.
Dependências
Adicione as dependências de biblioteca necessárias ao arquivo build.gradle.kts (ou build.gradle) do módulo e configure o plug-in KSP no módulo do app de nível superior, conforme mostrado abaixo:
dependencies {
implementation("androidx.appfunctions:appfunctions:1.0.0-alpha10")
// If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
// See Add the KSP plugin to your project
ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha10")
}
Implementar a lógica das AppFunctions
Para implementar uma AppFunction no seu app Android, crie uma classe que implemente a lógica específica das AppFunctions. Isso envolve a criação de classes de dados serializáveis para parâmetros e respostas e, em seguida, o fornecimento da lógica principal no método da função.
O código a seguir mostra um exemplo de implementação para criar uma tarefa no app TODO, incluindo a definição de parâmetros e tipos de resposta personalizados e a lógica da função principal usando um repositório.
@RequiresApi(36) @AndroidEntryPoint @AppFunctionServiceEntryPoint( serviceName = "TaskAppFunctionService", appFunctionXmlFileName = "task_app_function_service", ) abstract class BaseTaskAppFunctionService : AppFunctionService() { @Inject internal lateinit var taskRepository: TaskRepository /** * Creates a task based on [createTaskParams]. * * @param createTaskParams The parameter to describe how to create the task. */ @AppFunction(isDescribedByKDoc = true) suspend fun createTask( createTaskParams: CreateTaskParams, ): Task = withContext(Dispatchers.IO) { // Developers can use predefined exceptions to let the agent know // why it failed. if (createTaskParams.title == null && createTaskParams.content == null) { throw AppFunctionInvalidArgumentException("Title or content should be non-null") } val id = taskRepository.createTask( createTaskParams.title, createTaskParams.content ) return@withContext taskRepository .getTask(id) ?.toTask() ?: throw AppFunctionElementNotFoundException("Task not found for ID = $id") } // Maps internal TaskEntity private fun TaskEntity.toTask() = Task(id = id, title = title, content = description) }
Pontos principais sobre o código
- Por padrão, uma implementação de AppFunction é executada na linha de execução da interface do Android.
Portanto, uma operação de longa duração precisa fazer o seguinte:
- Declarar a AppFunction como uma função de suspensão.
- Mudar para um dispatcher de corrotina adequado quando a operação puder bloquear a linha de execução.
- Quando
isDescribedByKDocé definido comotrue, a descrição da função ou a descrição serializável é codificada como parte doAppFunctionMetadatapara ajudar o agente a entender como usar a AppFunction do app.
Declarar o serviço AppFunction no manifesto
Registre a declaração de serviço gerada pelo KSP e a propriedade app_metadata no manifesto do módulo, por exemplo, em src/main/AndroidManifest.xml.
O compilador KSP gera a classe de serviço concreta (TaskAppFunctionService) que estende a classe de ponto de entrada abstrata, juntamente com o esquema XML correspondente no diretório assets/.
<service android:name="com.example.snippets.ai.TaskAppFunctionService" android:permission="android.permission.BIND_APP_FUNCTION_SERVICE" android:exported="true" tools:targetApi="36"> <property android:name="android.app.appfunctions.schema" android:value="app_functions_schema.xsd" /> <property android:name="android.app.appfunctions.v2" android:value="task_app_function_service.xml" /> <intent-filter> <action android:name="android.app.appfunctions.AppFunctionService" /> </intent-filter> </service> <property android:name="android.app.appfunctions.app_metadata" android:resource="@xml/app_metadata" />
Opcional: ativar/desativar a disponibilidade da AppFunction no ambiente de execução
Use a API AppFunctionManager para ativar ou desativar funções explicitamente ao controlar as AppFunctions. O controle pode ser útil quando determinados recursos do app não estão disponíveis para todos os usuários. Ao ativar ou desativar as AppFunctions de forma dinâmica, o sistema de inteligência sabe exatamente quais recursos estão disponíveis para o usuário a qualquer momento.
Para controlar com segurança as AppFunctions que exigem um estado de conta específico, siga um processo de duas etapas:
Etapa 1. Desativar a função por padrão
Para impedir que a função seja acessível antes que o flag de recurso seja verificado, defina o parâmetro isEnabled da anotação @AppFunction como false.
@AppFunction(isEnabled = false, isDescribedByKDoc = true) suspend fun createTask( createTaskParams: CreateTaskParams, ): Task = TODO()
Etapa 2. Ativar a função de forma dinâmica no ambiente de execução
Para cada classe AppFunction, o compilador gera uma classe correspondente que contém constantes de ID de função (usando um sufixo Ids). É possível usar essas constantes de ID geradas com o método setAppFunctionEnabled de AppFunctionManagerCompat para mudar o estado ativado de uma função no ambiente de execução.
suspend fun onFeatureEnabled(context: Context) { try { AppFunctionManager.getInstance(context) ?.setAppFunctionEnabled( BaseTaskAppFunctionServiceIds.CREATE_TASK_ID, AppFunctionManager.APP_FUNCTION_STATE_ENABLED, ) } catch (e: Exception) { // Handle exception: AppFunctions indexation may not be fully completed // upon initial app startup. } } suspend fun onFeatureDisabled(context: Context) { try { AppFunctionManager.getInstance(context) ?.setAppFunctionEnabled( BaseTaskAppFunctionServiceIds.CREATE_TASK_ID, AppFunctionManager.APP_FUNCTION_STATE_DISABLED, ) } catch (e: Exception) { // Handle exception } }
Considerações sobre os tipos de funcionalidades a serem disponibilizadas
A segurança é sempre fundamental. Ao escolher quais recursos do app serão disponibilizados como AppFunctions, é importante lembrar que os agentes do sistema podem processar consultas do usuário no servidor para aproveitar os recursos avançados de LLM.
Para oferecer uma ótima experiência do usuário que também evite a exposição de informações sensíveis, recomendamos seguir estas diretrizes:
- Funcionalidade que se beneficia da linguagem natural: disponibilize tarefas que sejam mais fáceis para um usuário expressar em uma conversa do que pela navegação manual na interface.
- Acesso restrito: crie AppFunctions que só dão ao agente acesso aos dados e ações necessários para atender ao pedido específico do usuário.
- Informações não sensíveis: compartilhe apenas dados que não sejam altamente pessoais ou confidenciais ou dados que o usuário consinta explicitamente em compartilhar no contexto da ação.
- Confirmação inequívoca para qualquer ação destrutiva: seja extremamente cauteloso com funções que executam ações destrutivas (como excluir dados). Embora o agente possa invocá-las, o app precisa incluir a própria etapa de confirmação e usar uma linguagem clara e inequívoca sobre as intenções. Também é útil adicionar mais de uma etapa de confirmação para garantir que o usuário esteja ciente do que está sendo solicitado.
Verificar a integração da AppFunction
Para verificar se você integrou as AppFunctions corretamente, use adb
shell cmd app_function.
Use adb shell cmd app_function list-app-functions | grep --after-context 10
$myPackageName para conferir detalhes das AppFunctions fornecidas pelo app.
Também é possível executar uma AppFunction diretamente na linha de comando usando seu
identificador explícito ("$enclosingClassName#$methodName"):
adb shell "cmd app_function execute-app-function \
--package com.example.android.appfunctions \
--function 'com.example.android.appfunctions.BaseTaskAppFunctionService#createTask' \
--parameters '{\"createTaskParams\": {\"title\": \"Buy milk\", \"content\": \"From grocery store\"}}'"
Para testar o MCP do Android e verificar fluxos de trabalho completos sem precisar de comandos, instale e execute o app Android do agente de teste AppFunctions no seu dispositivo.
Se você estiver verificando a integração usando assistentes baseados em chat, como o Gemini no Android Studio, use a habilidade de desenvolvimento de AppFunctions ou forneça um comando como o seguinte:
Execute `adb shell cmd app_function` to learn how the tool works, then act as a
chat agent aiming to invoke AppFunctions to fulfil user prompts for this app.
Rely on the AppFunction description as instructions.
Migrar de versões anteriores da API
Na versão 1.0.0-alpha10, as AppFunctions introduziram uma arquitetura @AppFunctionServiceEntryPoint de tempo de compilação que consolida as dependências da biblioteca e substitui os provedores de configuração legados (AppFunctionConfiguration.Provider).
Se o app usa uma versão anterior das AppFunctions (como a
1.0.0-alpha09), é possível automatizar a migração usando a habilidade do agente AppFunctions em um ambiente de desenvolvimento integrado de IA, como o Gemini no Android Studio. A habilidade contém regras de migração dedicadas que orientam um agente a consolidar as dependências de build, criar o wrapper de serviço @AppFunctionServiceEntryPoint necessário, desacoplar parâmetros de contexto e atualizar as declarações de manifesto.
Para iniciar uma migração automatizada com o agente de IA, use um comando como o seguinte:
Use the AppFunctions migration skill to upgrade my app's AppFunctions implementation from 1.0.0-alpha09 to the 1.0.0-alpha10 @AppFunctionServiceEntryPoint architecture.