Um serviço de preenchimento automático é um aplicativo que facilita o preenchimento de formulários pelos usuários, inserindo dados nas visualizações de outros aplicativos. Os serviços de preenchimento automático também podem recuperar dados do usuário das visualizações em um aplicativo e armazená-los para uso posterior. Em geral, os serviços de preenchimento automático são fornecidos por aplicativos que gerenciam dados do usuário, como gerenciadores de senhas.
O Android facilita o preenchimento de formulários com a biblioteca de preenchimento automático disponível no Android 8.0 (API de nível 26) e versões superiores. Os usuários só poderão aproveitar os recursos de preenchimento automático se houver um aplicativo que forneça esse tipo de serviço no dispositivo.
Esta página mostra como implementar um serviço de preenchimento automático no seu aplicativo. Se você estiver procurando um exemplo de código que mostre como implementar um serviço, consulte a amostra de AutofillFramework do Android. Para saber mais detalhes sobre como os serviços de preenchimento automático funcionam, consulte a documentação de referência das classes AutofillService
e AutofillManager
.
Declarações e permissões do manifesto
Os aplicativos que oferecem o recurso de preenchimento automático precisam incluir uma declaração que descreva a implementação do serviço. Para especificar a declaração, inclua um elemento <service>
no manifesto do aplicativo. O elemento <service>
deve incluir os seguintes atributos e elementos:
- O atributo
android:name
, que aponta para a subclasse deAutofillService
no aplicativo responsável pela implementação do serviço. - O atributo
android:permission
, que declara a permissãoBIND_AUTOFILL_SERVICE
. - O elemento
<intent-filter>
, que tem o filho obrigatório<action>
especificando a açãoandroid.service.autofill.AutofillService
. - O elemento opcional
<meta-data>
, que pode ser usado para fornecer parâmetros de configuração adicionais ao serviço.
O exemplo a seguir mostra uma declaração de serviço de preenchimento automático:
<service
android:name=".MyAutofillService"
android:label="My Autofill Service"
android:permission="android.permission.BIND_AUTOFILL_SERVICE">
<intent-filter>
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
<meta-data
android:name="android.autofill"
android:resource="@xml/service_configuration" />
</service>
O elemento <meta-data>
inclui um atributo android:resource
que aponta para um recurso XML com mais detalhes sobre o serviço. O recurso service_configuration
no exemplo anterior especifica uma atividade que permite que os usuários configurem o serviço. O exemplo a seguir mostra o recurso XML service_configuration
:
<autofill-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.android.SettingsActivity" />
Para saber mais informações sobre recursos XML, consulte Como fornecer recursos.
Enviar avisos para ativar o serviço
Um aplicativo é usado como serviço de preenchimento automático após declarar a permissão BIND_AUTOFILL_SERVICE
e o usuário o ativar o recurso nas configurações do dispositivo. Um aplicativo pode verificar se está definido como o serviço atualmente ativado chamando o método hasEnabledAutofillServices()
da classe AutofillManager
.
Se o aplicativo não estiver definido como o serviço de preenchimento automático atual, ele poderá solicitar que o usuário altere as configurações de preenchimento automático usando o intent ACTION_REQUEST_SET_AUTOFILL_SERVICE
. O intent retornará um valor RESULT_OK
se o usuário tiver selecionado um serviço de preenchimento automático que corresponda ao pacote do autor da chamada.
Preencher visualizações de clientes
O serviço de preenchimento automático recebe solicitações para preencher as visualizações de clientes de acordo com a interação do usuário com outros aplicativos. Se o serviço de preenchimento automático tiver dados do usuário que atendem à solicitação, ele enviará as informações na resposta. O sistema Android mostra uma IU de preenchimento automático com os dados disponíveis, conforme exibido na imagem 1:
Imagem 1. IU de preenchimento automático exibindo um conjunto de dados.
A biblioteca de preenchimento automático define um fluxo de trabalho para preencher visualizações criadas com o objetivo de minimizar o tempo de vinculação do sistema Android a esse serviço. Em cada solicitação, o sistema Android envia um objeto AssistStructure
ao serviço chamando o método onFillRequest()
. O serviço de preenchimento automático verifica se pode atender à solicitação com dados do usuário que foram armazenados anteriormente. Se puder atender à solicitação, o serviço empacotará os dados em objetos Dataset
. O serviço chama o método onSuccess()
passando um objeto FillResponse
, que contém objetos Dataset
. Se o serviço não tiver dados para atender à solicitação, ele enviará null
ao método onSuccess()
. Caso haja um erro ao processar a solicitação, o serviço chamará o método onFailure()
. Para ver uma explicação detalhada do fluxo de trabalho, consulte o artigo Uso básico.
O código a seguir mostra um exemplo do método onFillRequest()
:
Kotlin
override fun onFillRequest( request: FillRequest, cancellationSignal: CancellationSignal, callback: FillCallback ) { // Get the structure from the request val context: List<FillContext> = request.fillContexts val structure: AssistStructure = context[context.size - 1].structure // Traverse the structure looking for nodes to fill out. val parsedStructure: ParsedStructure = parseStructure(structure) // Fetch user data that matches the fields. val (username: String, password: String) = fetchUserData(parsedStructure) // Build the presentation of the datasets val usernamePresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1) usernamePresentation.setTextViewText(android.R.id.text1, "my_username") val passwordPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1) passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username") // Add a dataset to the response val fillResponse: FillResponse = FillResponse.Builder() .addDataset(Dataset.Builder() .setValue( parsedStructure.usernameId, AutofillValue.forText(username), usernamePresentation ) .setValue( parsedStructure.passwordId, AutofillValue.forText(password), passwordPresentation ) .build()) .build() // If there are no errors, call onSuccess() and pass the response callback.onSuccess(fillResponse) } data class ParsedStructure(var usernameId: AutofillId, var passwordId: AutofillId) data class UserData(var username: String, var password: String)
Java
@Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) { // Get the structure from the request List<FillContext> context = request.getFillContexts(); AssistStructure structure = context.get(context.size() - 1).getStructure(); // Traverse the structure looking for nodes to fill out. ParsedStructure parsedStructure = parseStructure(structure); // Fetch user data that matches the fields. UserData userData = fetchUserData(parsedStructure); // Build the presentation of the datasets RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); usernamePresentation.setTextViewText(android.R.id.text1, "my_username"); RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username"); // Add a dataset to the response FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(userData.username), usernamePresentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(userData.password), passwordPresentation) .build()) .build(); // If there are no errors, call onSuccess() and pass the response callback.onSuccess(fillResponse); } class ParsedStructure { AutofillId usernameId; AutofillId passwordId; } class UserData { String username; String password; }
Um serviço pode ter mais de um conjunto de dados que satisfaça a solicitação. Nesse caso, o sistema Android mostrará várias opções, uma para cada conjunto de dados, na IU de preenchimento automático. O exemplo de código a seguir mostra como fornecer vários conjuntos de dados em uma resposta:
Kotlin
// Add multiple datasets to the response val fillResponse: FillResponse = FillResponse.Builder() .addDataset(Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(user1Data.username), username1Presentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(user1Data.password), password1Presentation) .build()) .addDataset(Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(user2Data.username), username2Presentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(user2Data.password), password2Presentation) .build()) .build()
Java
// Add multiple datasets to the response FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(user1Data.username), username1Presentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(user1Data.password), password1Presentation) .build()) .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(user2Data.username), username2Presentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(user2Data.password), password2Presentation) .build()) .build();
Os serviços de preenchimento automático podem navegar pelos objetos ViewNode
em AssistStructure
para recuperar os dados necessários e atender à solicitação. Um serviço pode recuperar dados de preenchimento automático usando métodos da classe ViewNode
, como getAutofillId()
. Um serviço deve ser capaz de descrever o conteúdo de uma visualização para verificar se pode atender à solicitação. O atributo autofillHints
deve ser a primeira abordagem de um serviço para descrever o conteúdo de uma visualização. No entanto, os aplicativos clientes precisam fornecer explicitamente o atributo nas próprias visualizações antes de ficarem disponíveis para o serviço. Se um aplicativo cliente não fornecer o atributo autofillHints
, um serviço deverá usar as próprias heurísticas para descrever o conteúdo. O serviço pode usar métodos de outras classes para ter informações sobre o conteúdo da visualização, como getText()
ou getHint()
. Para mais informações, veja Como fornecer dicas sobre o preenchimento automático. O exemplo a seguir mostra como percorrer AssistStructure
e recuperar dados de preenchimento automático de um objeto ViewNode
:
Kotlin
fun traverseStructure(structure: AssistStructure) { val windowNodes: List<AssistStructure.WindowNode> = structure.run { (0 until windowNodeCount).map { getWindowNodeAt(it) } } windowNodes.forEach { windowNode: AssistStructure.WindowNode -> val viewNode: ViewNode? = windowNode.rootViewNode traverseNode(viewNode) } } fun traverseNode(viewNode: ViewNode?) { if (viewNode?.autofillHints?.isNotEmpty() == true) { // If the client app provides autofill hints, you can obtain them using: // viewNode.getAutofillHints(); } else { // Or use your own heuristics to describe the contents of a view // using methods such as getText() or getHint(). } val children: List<ViewNode>? = viewNode?.run { (0 until childCount).map { getChildAt(it) } } children?.forEach { childNode: ViewNode -> traverseNode(childNode) } }
Java
public void traverseStructure(AssistStructure structure) { int nodes = structure.getWindowNodeCount(); for (int i = 0; i < nodes; i++) { WindowNode windowNode = structure.getWindowNodeAt(i); ViewNode viewNode = windowNode.getRootViewNode(); traverseNode(viewNode); } } public void traverseNode(ViewNode viewNode) { if(viewNode.getAutofillHints() != null && viewNode.getAutofillHints().length > 0) { // If the client app provides autofill hints, you can obtain them using: // viewNode.getAutofillHints(); } else { // Or use your own heuristics to describe the contents of a view // using methods such as getText() or getHint(). } for(int i = 0; i < viewNode.getChildCount(); i++) { ViewNode childNode = viewNode.getChildAt(i); traverseNode(childNode); } }
Salvar dados do usuário
Um serviço de preenchimento automático precisa de dados do usuário para preencher visualizações em aplicativos. Quando os usuários preenchem manualmente uma visualização, eles são solicitados a salvar os dados no serviço de preenchimento automático atual, como mostra a imagem 2.
Imagem 2. IU de salvamento do preenchimento automático
Para salvar os dados, o serviço precisa indicar que está interessado em armazenar as informações para uso futuro. Antes de o sistema Android enviar uma solicitação para salvar os dados, há uma solicitação de preenchimento para que o serviço possa preencher as visualizações. Para indicar que está interessado em salvar os dados, o serviço incluirá um objeto SaveInfo
na resposta da solicitação de preenchimento precedente. O objeto SaveInfo
contém pelo menos os seguintes dados:
- O tipo de dados do usuário que seriam salvos. Para ver uma lista dos valores
SAVE_DATA
disponíveis, consulteSaveInfo
. - O conjunto mínimo de visualizações que precisam ser alteradas para acionar uma solicitação de salvamento. Por exemplo, um formulário de login normalmente exige que o usuário atualize as visualizações
username
epassword
para acionar uma solicitação de salvamento.
Um objeto SaveInfo
é associado a um objeto FillResponse
, como mostrado no seguinte exemplo de código:
Kotlin
override fun onFillRequest( request: FillRequest, cancellationSignal: CancellationSignal, callback: FillCallback ) { ... // Builder object requires a non-null presentation. val notUsed = RemoteViews(packageName, android.R.layout.simple_list_item_1) val fillResponse: FillResponse = FillResponse.Builder() .addDataset( Dataset.Builder() .setValue(parsedStructure.usernameId, null, notUsed) .setValue(parsedStructure.passwordId, null, notUsed) .build() ) .setSaveInfo( SaveInfo.Builder( SaveInfo.SAVE_DATA_TYPE_USERNAME or SaveInfo.SAVE_DATA_TYPE_PASSWORD, arrayOf(parsedStructure.usernameId, parsedStructure.passwordId) ).build() ) .build() ... }
Java
@Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) { ... // Builder object requires a non-null presentation. RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, null, notUsed) .setValue(parsedStructure.passwordId, null, notUsed) .build()) .setSaveInfo(new SaveInfo.Builder( SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD, new AutofillId[] {parsedStructure.usernameId, parsedStructure.passwordId}) .build()) .build(); ... }
O serviço de preenchimento automático pode implementar lógica para persistir os dados do usuário no método onSaveRequest()
, que geralmente é chamado após o término da atividade do cliente ou quando o aplicativo cliente chama commit()
. O código a seguir mostra um exemplo do método onSaveRequest()
:
Kotlin
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) { // Get the structure from the request val context: List<FillContext> = request.fillContexts val structure: AssistStructure = context[context.size - 1].structure // Traverse the structure looking for data to save traverseStructure(structure) // Persist the data, if there are no errors, call onSuccess() callback.onSuccess() }
Java
@Override public void onSaveRequest(SaveRequest request, SaveCallback callback) { // Get the structure from the request List<FillContext> context = request.getFillContexts(); AssistStructure structure = context.get(context.size() - 1).getStructure(); // Traverse the structure looking for data to save traverseStructure(structure); // Persist the data, if there are no errors, call onSuccess() callback.onSuccess(); }
Os serviços de preenchimento automático devem criptografar dados confidenciais antes de persistir. No entanto, os dados do usuário incluem rótulos ou dados que não são confidenciais. Por exemplo, uma conta de usuário pode incluir um rótulo que marque os dados como uma conta profissional ou pessoal. Os serviços não devem criptografar rótulos. Isso permite usar os rótulos nas visualizações de apresentação, caso o usuário não tenha realizado o processo de autenticação, e substituir os rótulos pelos dados reais após a autenticação.
Adiar a IU de salvamento do preenchimento automático
A partir do Android 10, se você usar várias telas para implementar um fluxo de trabalho de preenchimento automático (por exemplo, uma tela para o campo de nome de usuário e outra para a senha), poderá adiar a IU de salvamento do preenchimento automático usando a sinalização SaveInfo.FLAG_DELAY_SAVE
.
Se essa sinalização for definida, a IU de salvamento do preenchimento automático não será acionada quando o contexto de preenchimento automático associado à resposta SaveInfo
for confirmado. Em vez disso, você poderá usar uma atividade separada na mesma tarefa para entregar solicitações de preenchimento futuras e, em seguida, mostrar a IU por meio de uma solicitação de salvamento. Para saber mais, consulte SaveInfo.FLAG_DELAY_SAVE
.
Exigir autenticação do usuário
Os serviços de preenchimento automático podem fornecer um nível adicional de segurança, exigindo que o usuário faça a autenticação antes de preencher as visualizações. Os cenários a seguir são adequados para a implementação da autenticação do usuário:
- Os dados do usuário no aplicativo precisam ser desbloqueados usando uma senha mestra ou uma verificação por impressão digital.
- Um conjunto de dados específico precisa ser desbloqueado, por exemplo, detalhes do cartão de crédito usando um código de verificação de cartão (CVC).
Caso o serviço exija a autenticação do usuário antes de desbloquear os dados, ele poderá apresentar dados padrão ou um rótulo e especificar o Intent
responsável pela autenticação. Se você precisar de dados adicionais para processar a solicitação após a conclusão do fluxo de autenticação, será possível adicionar essas informações ao intent. Sua atividade de autenticação pode retornar os dados para a classe AutofillService
no seu aplicativo. O exemplo de código a seguir mostra como especificar que a solicitação exige autenticação:
Kotlin
val authPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply { setTextViewText(android.R.id.text1, "requires authentication") } val authIntent = Intent(this, AuthActivity::class.java).apply { // Send any additional data required to complete the request. putExtra(MY_EXTRA_DATASET_NAME, "my_dataset") } val intentSender: IntentSender = PendingIntent.getActivity( this, 1001, authIntent, PendingIntent.FLAG_CANCEL_CURRENT ).intentSender // Build a FillResponse object that requires authentication. val fillResponse: FillResponse = FillResponse.Builder() .setAuthentication(autofillIds, intentSender, authPresentation) .build()
Java
RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); authPresentation.setTextViewText(android.R.id.text1, "requires authentication"); Intent authIntent = new Intent(this, AuthActivity.class); // Send any additional data required to complete the request. authIntent.putExtra(MY_EXTRA_DATASET_NAME, "my_dataset"); IntentSender intentSender = PendingIntent.getActivity( this, 1001, authIntent, PendingIntent.FLAG_CANCEL_CURRENT ).getIntentSender(); // Build a FillResponse object that requires authentication. FillResponse fillResponse = new FillResponse.Builder() .setAuthentication(autofillIds, intentSender, authPresentation) .build();
Depois que a atividade concluir o fluxo de autenticação, ela deverá chamar o método setResult()
passando um valor RESULT_OK
e definir o EXTRA_AUTHENTICATION_RESULT
adicional para o objeto FillResponse
que inclui o conjunto de dados preenchido. O código a seguir mostra um exemplo de como retornar o resultado após a conclusão dos fluxos de autenticação:
Kotlin
// The data sent by the service and the structure are included in the intent. val datasetName: String? = intent.getStringExtra(MY_EXTRA_DATASET_NAME) val structure: AssistStructure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE) val parsedStructure: ParsedStructure = parseStructure(structure) val (username, password) = fetchUserData(parsedStructure) // Build the presentation of the datasets. val usernamePresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply { setTextViewText(android.R.id.text1, "my_username") } val passwordPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply { setTextViewText(android.R.id.text1, "Password for my_username") } // Add the dataset to the response. val fillResponse: FillResponse = FillResponse.Builder() .addDataset(Dataset.Builder() .setValue( parsedStructure.usernameId, AutofillValue.forText(username), usernamePresentation ) .setValue( parsedStructure.passwordId, AutofillValue.forText(password), passwordPresentation ) .build() ).build() val replyIntent = Intent().apply { // Send the data back to the service. putExtra(MY_EXTRA_DATASET_NAME, datasetName) putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse) } setResult(Activity.RESULT_OK, replyIntent)
Java
Intent intent = getIntent(); // The data sent by the service and the structure are included in the intent. String datasetName = intent.getStringExtra(MY_EXTRA_DATASET_NAME); AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE); ParsedStructure parsedStructure = parseStructure(structure); UserData userData = fetchUserData(parsedStructure); // Build the presentation of the datasets. RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); usernamePresentation.setTextViewText(android.R.id.text1, "my_username"); RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username"); // Add the dataset to the response. FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(userData.username), usernamePresentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(userData.password), passwordPresentation) .build()) .build(); Intent replyIntent = new Intent(); // Send the data back to the service. replyIntent.putExtra(MY_EXTRA_DATASET_NAME, datasetName); replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse); setResult(RESULT_OK, replyIntent);
No cenário em que um conjunto de dados de cartão de crédito precisa ser desbloqueado, o serviço poderá exibir a IU solicitando o CVC. Você pode ocultar os dados até que o conjunto de dados seja desbloqueado, apresentando dados padrão, como o nome do banco e os quatro últimos dígitos do número do cartão de crédito. O exemplo a seguir mostra como exigir autenticação para um conjunto de dados e ocultar os dados até que o usuário forneça o CVC:
Kotlin
// Parse the structure and fetch payment data. val parsedStructure: ParsedStructure = parseStructure(structure) val paymentData: Payment = fetchPaymentData(parsedStructure) // Build the presentation that shows the bank and the last four digits of the credit card number. // For example 'Bank-1234'. val maskedPresentation: String = "${paymentData.bank}-" + paymentData.creditCardNumber.substring(paymentData.creditCardNumber.length - 4) val authPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply { setTextViewText(android.R.id.text1, maskedPresentation) } // Prepare an intent that displays the UI that asks for the CVC. val cvcIntent = Intent(this, CvcActivity::class.java) val cvcIntentSender: IntentSender = PendingIntent.getActivity( this, 1001, cvcIntent, PendingIntent.FLAG_CANCEL_CURRENT ).intentSender // Build a FillResponse object that includes a Dataset that requires authentication. val fillResponse: FillResponse = FillResponse.Builder() .addDataset( Dataset.Builder() // The values in the dataset are replaced by the actual // data once the user provides the CVC. .setValue(parsedStructure.creditCardId, null, authPresentation) .setValue(parsedStructure.expDateId, null, authPresentation) .setAuthentication(cvcIntentSender) .build() ).build()
Java
// Parse the structure and fetch payment data. ParsedStructure parsedStructure = parseStructure(structure); Payment paymentData = fetchPaymentData(parsedStructure); // Build the presentation that shows the bank and the last four digits of the credit card number. // For example 'Bank-1234'. String maskedPresentation = paymentData.bank + "-" + paymentData.creditCardNumber.subString(paymentData.creditCardNumber.length - 4); RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); authPresentation.setTextViewText(android.R.id.text1, maskedPresentation); // Prepare an intent that displays the UI that asks for the CVC. Intent cvcIntent = new Intent(this, CvcActivity.class); IntentSender cvcIntentSender = PendingIntent.getActivity( this, 1001, cvcIntent, PendingIntent.FLAG_CANCEL_CURRENT ).getIntentSender(); // Build a FillResponse object that includes a Dataset that requires authentication. FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() // The values in the dataset are replaced by the actual // data once the user provides the CVC. .setValue(parsedStructure.creditCardId, null, authPresentation) .setValue(parsedStructure.expDateId, null, authPresentation) .setAuthentication(cvcIntentSender) .build()) .build();
Depois que a atividade validar o CVC, ela deverá chamar o método setResult()
passando um valor RESULT_OK
e definir o EXTRA_AUTHENTICATION_RESULT
adicional para o objeto Dataset
que contém o número do cartão de crédito e a data de validade. O novo conjunto de dados substituirá o conjunto de dados que requer autenticação e as visualizações serão preenchidas imediatamente. O código a seguir mostra um exemplo de como retornar o conjunto de dados depois que o usuário fornece o CVC:
Kotlin
// Parse the structure and fetch payment data. val parsedStructure: ParsedStructure = parseStructure(structure) val paymentData: Payment = fetchPaymentData(parsedStructure) // Build a non-null RemoteViews object that we can use as the presentation when // creating the Dataset object. This presentation isn't actually used, but the // Builder object requires a non-null presentation. val notUsed = RemoteViews(packageName, android.R.layout.simple_list_item_1) // Create a dataset with the credit card number and expiration date. val responseDataset: Dataset = Dataset.Builder() .setValue( parsedStructure.creditCardId, AutofillValue.forText(paymentData.creditCardNumber), notUsed ) .setValue( parsedStructure.expDateId, AutofillValue.forText(paymentData.expirationDate), notUsed ) .build() val replyIntent = Intent().apply { putExtra(EXTRA_AUTHENTICATION_RESULT, responseDataset) }
Java
// Parse the structure and fetch payment data. ParsedStructure parsedStructure = parseStructure(structure); Payment paymentData = fetchPaymentData(parsedStructure); // Build a non-null RemoteViews object that we can use as the presentation when // creating the Dataset object. This presentation isn't actually used, but the // Builder object requires a non-null presentation. RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); // Create a dataset with the credit card number and expiration date. Dataset responseDataset = new Dataset.Builder() .setValue(parsedStructure.creditCardId, AutofillValue.forText(paymentData.creditCardNumber), notUsed) .setValue(parsedStructure.expDateId, AutofillValue.forText(paymentData.expirationDate), notUsed) .build(); Intent replyIntent = new Intent(); replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, responseDataset);
Organizar os dados em grupos lógicos
Os serviços de preenchimento automático devem organizar os dados em grupos lógicos que isolam conceitos de diferentes domínios. Esses grupos lógicos são referidos como partições nesta página. A lista a seguir mostra exemplos típicos de partições e campos:
- Credenciais, que incluem campos de nome de usuário e senha.
- Endereço, que inclui campos de rua, cidade, estado e CEP.
- Informações de pagamento, que incluem campos de número do cartão de crédito, data de validade e código de verificação.
Um serviço de preenchimento automático que faz partições da maneira correta pode proteger melhor os dados dos usuários, evitando a exposição de informações de mais de uma partição em um conjunto de dados. Por exemplo, um conjunto de dados que inclui credenciais não necessariamente deve incluir informações de pagamento. Organizar seus dados em partições permite que seu serviço exponha a quantidade mínima de informações necessárias para atender a uma solicitação.
Com essa organização, os serviços podem preencher atividades com visualizações de várias partições enquanto enviam a quantidade mínima de dados para o aplicativo cliente. Por exemplo, considere uma atividade que inclua visualizações de nome de usuário, senha, rua e cidade e um serviço de preenchimento automático com os seguintes dados:
Partição | Campo 1 | Campo 2 |
---|---|---|
Credenciais | work_username | work_password |
personal_username | personal_password | |
Endereço | work_street | work_city |
personal_street | personal_city |
O serviço pode preparar um conjunto de dados que inclua a partição de credenciais para as contas profissional e pessoal. Quando o usuário escolher uma dessas alternativas, uma resposta subsequente ao preenchimento automático poderá fornecer o endereço comercial ou pessoal, dependendo da primeira opção do usuário.
Um serviço pode identificar o campo que originou a solicitação chamando o método isFocused()
ao percorrer o objeto AssistStructure
. Isso permite que os serviços preparem uma FillResponse
com os dados de partição adequados.
Cenários avançados de preenchimento automático
- Paginar conjuntos de dados
- Uma resposta grande de preenchimento automático pode exceder o tamanho de transação permitido pelo objeto
Binder
que representa o objeto remoto necessário para processar a solicitação. Para impedir que o sistema Android acione uma exceção nesses cenários, mantenha umaFillResponse
pequena adicionando no máximo 20 objetosDataset
por vez. Caso seja necessário, você poderá adicionar à resposta um conjunto de dados que informe aos usuários que há mais informações e recuperar o próximo grupo de conjuntos de dados quando selecionado. Para saber mais, consulteaddDataset(Dataset)
. - Como salvar dados divididos em várias telas
Em geral, os aplicativos dividem os dados do usuário em várias telas na mesma atividade, especialmente nas usadas para criar uma nova conta de usuário. Por exemplo, a primeira tela solicita um nome de usuário e, se essa informação estiver disponível, o processo seguirá para uma segunda tela, que solicita uma senha. Nessas situações, o serviço de preenchimento automático precisa esperar até que o usuário insira os dois campos antes que a IU de salvamento do preenchimento automático seja exibida. Um serviço pode seguir estas etapas para gerenciar esses cenários:
- Na primeira solicitação de preenchimento, o serviço adiciona um pacote de estados do cliente à resposta, contendo os códigos de preenchimento automático dos campos parciais presentes na tela.
- Na segunda solicitação de preenchimento, o serviço recupera o pacote de estados do cliente, recebe os códigos de preenchimento automático definidos na solicitação anterior e adiciona esses códigos e a sinalização
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
ao objetoSaveInfo
usado na segunda resposta. - Na solicitação de salvamento, o serviço usa os objetos apropriados
FillContext
para acessar o valor de cada campo. Há um contexto por solicitação de preenchimento.
Para saber mais, consulte Salvar dados divididos em várias telas.
- Inicializar e descartar a lógica de cada solicitação
Sempre que houver uma solicitação de preenchimento automático, o sistema Android se vinculará ao serviço e chamará o método
onConnected()
correspondente. Depois que o serviço processar a solicitação, o sistema Android chamará o métodoonDisconnected()
e se desvinculará do serviço. Você pode implementaronConnected()
para fornecer o código executado antes de processar uma solicitação eonDisconnected()
para fornecer o código que é executado após o processamento de uma solicitação.- Personalizar a IU de salvamento do preenchimento automático
Os serviços de preenchimento automático podem personalizar a IU de salvamento para ajudar os usuários a decidir se querem permitir que o serviço salve os dados fornecidos por eles. Os serviços podem fornecer informações adicionais sobre o que seria salvo por meio de um texto simples ou por uma visualização personalizada. Além disso, eles também podem alterar a aparência do botão que cancela a solicitação de salvamento e exibir uma notificação quando o usuário tocar nesse comando. Para saber mais, consulte a documentação de referência
SaveInfo
.- Modo de compatibilidade
O modo de compatibilidade permite que os serviços de preenchimento automático usem a biblioteca virtual de acessibilidade para fins de preenchimento automático. Esse recurso serve especialmente para fornecer funcionalidade de preenchimento automático a navegadores que ainda não implementaram explicitamente as APIs do serviço.
Para testar seu serviço de preenchimento automático usando o modo de compatibilidade, inclua explicitamente o navegador ou aplicativo que requer o modo de compatibilidade à lista de permissões. Você pode verificar quais pacotes já estão na lista de permissões executando o seguinte comando:
$ adb shell settings get global autofill_compat_mode_allowed_packages
Se o pacote que você está testando não estiver listado, será possível incluí-lo na lista de permissões executando este comando:
$ adb shell settings put global autofill_compat_mode_allowed_packages pkg1[resId1]:pkg2[resId1,resId2]
... em que
pkgX
é o pacote do aplicativo. Se o aplicativo for um navegador, useresIdx
para especificar o código do recurso do campo de entrada que contém o URL da página renderizada.
O modo de compatibilidade tem as seguintes limitações:
- Uma solicitação de salvamento é acionada quando o serviço usa a sinalização FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE ou quando o método
setTrigger()
é chamado.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
é definido por padrão ao usar o modo de compatibilidade. - O valor de texto dos nós pode não estar disponível no método
onSaveRequest(SaveRequest, SaveCallback)
.
Para mais informações sobre o modo de compatibilidade, incluindo as limitações associadas, consulte a classe de referência AutofillService
.