Layanan isi otomatis adalah aplikasi yang memudahkan pengguna mengisi formulir dengan memasukkan data ke tampilan aplikasi lain. Layanan isi otomatis juga dapat mengambil data pengguna dari tampilan dalam aplikasi dan menyimpannya untuk digunakan di lain waktu. Layanan isi otomatis biasanya disediakan oleh aplikasi yang mengelola data pengguna, seperti pengelola sandi.
Android memudahkan pengisian formulir dengan framework isi otomatis yang tersedia di Android 8.0 (API level 26) dan yang lebih baru. Pengguna dapat memanfaatkan fitur isi otomatis hanya jika ada aplikasi yang menyediakan layanan isi otomatis di perangkat mereka.
Halaman ini menunjukkan cara mengimplementasikan layanan isi otomatis di aplikasi. Jika Anda
mencari contoh kode yang menunjukkan cara mengimplementasikan layanan, lihat
contoh AutofillFramework di Java
atau
Kotlin.
Untuk detail selengkapnya tentang cara kerja layanan isi otomatis, lihat halaman
referensi untuk class
AutofillService
dan AutofillManager
.
Izin dan deklarasi manifes
Aplikasi yang menyediakan layanan isi otomatis harus menyertakan deklarasi yang menjelaskan
implementasi layanan. Untuk menentukan deklarasi, sertakan elemen
<service>
dalam
manifes aplikasi. Elemen
<service>
harus
menyertakan atribut dan elemen berikut:
- Atribut
android:name
yang menunjuk ke subclassAutofillService
di aplikasi yang mengimplementasikan layanan. - Atribut
android:permission
yang mendeklarasikan izinBIND_AUTOFILL_SERVICE
. - Elemen
<intent-filter>
, yang turunan<action>
wajibnya menentukan tindakanandroid.service.autofill.AutofillService
. - Elemen
<meta-data>
opsional yang dapat digunakan untuk memberikan parameter konfigurasi tambahan untuk layanan.
Contoh berikut menunjukkan deklarasi layanan isi otomatis:
<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>
Elemen <meta-data>
menyertakan atribut
android:resource
yang menunjuk ke resource XML dengan detail selengkapnya tentang layanan.
Resource service_configuration
dalam contoh sebelumnya menetapkan aktivitas
yang memungkinkan pengguna mengonfigurasi layanan. Contoh berikut menunjukkan
resource XML service_configuration
:
<autofill-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.android.SettingsActivity" />
Untuk informasi selengkapnya tentang resource XML, lihat Ringkasan resource aplikasi.
Perintah untuk mengaktifkan layanan
Aplikasi digunakan sebagai layanan isi otomatis setelah mendeklarasikan izin
BIND_AUTOFILL_SERVICE
dan pengguna mengaktifkannya di setelan
perangkat. Aplikasi dapat memverifikasi apakah layanan saat ini diaktifkan dengan
memanggil metode
hasEnabledAutofillServices()
class AutofillManager
.
Jika bukan merupakan layanan isi otomatis saat ini, aplikasi dapat meminta pengguna untuk
mengubah setelan isi otomatis menggunakan
intent
ACTION_REQUEST_SET_AUTOFILL_SERVICE
. Intent menampilkan nilai RESULT_OK
jika pengguna
memilih layanan isi otomatis yang cocok dengan paket pemanggil.
Mengisi tampilan klien
Layanan isi otomatis menerima permintaan untuk mengisi tampilan klien saat pengguna berinteraksi dengan aplikasi lain. Jika memiliki data pengguna yang memenuhi permintaan, layanan isi otomatis akan mengirimkan data tersebut dalam respons. Sistem Android menampilkan UI isi otomatis dengan data yang tersedia, sebagaimana ditampilkan dalam gambar 1:
Framework isi otomatis menentukan alur kerja untuk mengisi tampilan yang dirancang untuk
meminimalkan waktu sistem Android terikat pada layanan isi otomatis. Dalam
setiap permintaan, sistem Android mengirimkan objek AssistStructure
ke layanan dengan
memanggil metode
onFillRequest()
.
Layanan isi otomatis memeriksa apakah dapat memenuhi permintaan dengan data pengguna yang
telah disimpan sebelumnya. Jika dapat memenuhi permintaan, layanan akan memaketkan
data tersebut dalam objek
Dataset
. Layanan memanggil
metode onSuccess()
,
yang meneruskan objek FillResponse
yang berisi objek Dataset
. Jika layanan tidak
memiliki data untuk memenuhi permintaan, metode tersebut akan meneruskan null
ke metode onSuccess()
.
Layanan
memanggil metode onFailure()
jika terjadi error saat memproses permintaan. Untuk penjelasan mendetail
tentang alur kerja, lihat deskripsi di halaman
referensi AutofillService
.
Kode berikut menunjukkan contoh metode 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; }
Layanan dapat memiliki lebih dari satu set data yang memenuhi permintaan. Dalam hal ini, sistem Android menampilkan beberapa opsi—satu untuk setiap set data—di UI isi otomatis. Contoh kode berikut menunjukkan cara menyediakan beberapa set data dalam respons:
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();
Layanan isi otomatis dapat membuka objek ViewNode
di
AssistStructure
untuk mengambil data isi otomatis
yang diperlukan untuk memenuhi permintaan. Layanan dapat mengambil data isi otomatis menggunakan
metode class ViewNode
,
seperti getAutofillId()
.
Layanan harus dapat menjelaskan isi tampilan untuk memeriksa apakah
dapat memenuhi permintaan. Menggunakan atribut autofillHints
adalah pendekatan
pertama yang harus digunakan layanan untuk menjelaskan isi tampilan. Namun,
aplikasi klien harus secara eksplisit menyediakan atribut dalam tampilan agar
tersedia untuk layanan.
Jika aplikasi klien tidak menyediakan atribut autofillHints
, layanan
harus menggunakan heuristiknya sendiri untuk menjelaskan isinya.
Layanan dapat menggunakan metode dari class lain, seperti getText()
atau getHint()
,
untuk mendapatkan informasi tentang isi tampilan.
Untuk informasi selengkapnya, lihat Memberikan petunjuk untuk
isi otomatis.
Contoh berikut menunjukkan cara melewati AssistStructure
dan mengambil
data isi otomatis dari objek 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); } }
Menyimpan data pengguna
Layanan isi otomatis memerlukan data pengguna untuk mengisi tampilan dalam aplikasi. Bila secara manual mengisi tampilan, pengguna akan diminta untuk menyimpan data ke layanan isi otomatis saat ini, sebagaimana ditunjukkan dalam gambar 2.
Untuk menyimpan data, layanan harus menunjukkan ketertarikan terhadap
penyimpanan data untuk digunakan di masa mendatang. Sebelum sistem Android mengirim permintaan untuk menyimpan data,
ada permintaan pengisian yang memberi layanan kesempatan untuk mengisi
tampilan. Untuk menunjukkan minat terhadap penyimpanan data, layanan
menyertakan objek SaveInfo
dalam respons terhadap permintaan pengisian. Objek SaveInfo
berisi setidaknya data berikut:
- Jenis data pengguna yang disimpan. Untuk mengetahui daftar nilai
SAVE_DATA
yang tersedia, lihatSaveInfo
. - Rangkaian tampilan minimum yang harus diubah untuk memicu permintaan simpan.
Misalnya, formulir login biasanya mengharuskan pengguna memperbarui tampilan
username
danpassword
untuk memicu permintaan simpan.
Objek SaveInfo
terkait dengan objek FillResponse
, sebagaimana ditunjukkan dalam contoh kode
berikut:
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(); ... }
Layanan isi otomatis dapat mengimplementasikan logika untuk mempertahankan data pengguna dalam metode onSaveRequest()
,
yang biasanya dipanggil setelah aktivitas klien selesai atau saat aplikasi
klien memanggil commit()
.
Kode berikut menunjukkan contoh metode 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(); }
Layanan isi otomatis harus mengenkripsi data sensitif sebelum mempertahankannya. Namun, data pengguna dapat menyertakan label atau data yang tidak sensitif. Misalnya, akun pengguna dapat menyertakan label yang menandai data sebagai akun kerja atau pribadi. Layanan tidak boleh mengenkripsi label. Dengan tidak mengenkripsi label, layanan dapat menggunakan label dalam tampilan presentasi jika pengguna belum melakukan autentikasi. Kemudian, layanan dapat mengganti label dengan data sebenarnya setelah pengguna melakukan autentikasi.
Menunda UI simpan isi otomatis
Mulai Android 10, jika Anda menggunakan beberapa layar untuk mengimplementasikan alur kerja
isi otomatis—misalnya, satu layar untuk kolom nama pengguna dan lainnya untuk
sandi—Anda dapat menunda UI simpan isi otomatis
menggunakan tanda
SaveInfo.FLAG_DELAY_SAVE
.
Jika tanda ini disetel, UI simpan isi otomatis tidak terpicu saat konteks
isi otomatis terkait respons SaveInfo
di-commit. Sebagai gantinya, Anda dapat
menggunakan aktivitas terpisah dalam tugas yang sama untuk mengirimkan permintaan pengisian di masa mendatang, lalu
menampilkan UI melalui permintaan simpan. Untuk informasi selengkapnya, lihat
SaveInfo.FLAG_DELAY_SAVE
.
Mengharuskan autentikasi pengguna
Layanan isi otomatis dapat menyediakan level keamanan tambahan dengan mengharuskan pengguna mengautentikasi sebelum dapat mengisi tampilan. Skenario berikut adalah kandidat yang baik untuk mengimplementasikan autentikasi pengguna:
- Data pengguna di aplikasi harus dibuka menggunakan sandi utama atau pemindaian sidik jari.
- Set data tertentu harus dibuka kuncinya, seperti detail kartu kredit dengan menggunakan kode verifikasi kartu (CVC).
Dalam skenario ketika layanan memerlukan autentikasi pengguna sebelum membuka
data, layanan dapat menampilkan data boilerplate atau label dan menentukan
Intent
yang menangani
autentikasi. Jika memerlukan data tambahan untuk memproses permintaan setelah alur autentikasi selesai,
Anda dapat menambahkan data tersebut ke intent. Aktivitas
autentikasi Anda kemudian dapat menampilkan data ke class AutofillService
di aplikasi Anda.
Contoh kode berikut menunjukkan cara menentukan apakah permintaan memerlukan autentikasi:
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();
Setelah menyelesaikan alur autentikasi, aktivitas harus memanggil metode
setResult()
,
yang meneruskan nilai RESULT_OK
dan menyetel tambahan EXTRA_AUTHENTICATION_RESULT
ke objek FillResponse
yang menyertakan set data terisi. Kode
berikut menunjukkan contoh cara menampilkan hasil setelah alur autentikasi selesai:
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);
Dalam skenario ketika set data kartu kredit perlu dibuka, layanan dapat menampilkan UI yang meminta CVC. Anda dapat menyembunyikan data hingga set data dibuka dengan menampilkan data boilerplate, seperti nama bank dan empat digit terakhir nomor kartu kredit. Contoh berikut menunjukkan cara mengharuskan autentikasi untuk set data dan menyembunyikan data hingga pengguna memberikan 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, such as '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, such as '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();
Setelah memvalidasi CVC, aktivitas harus memanggil metode setResult()
,
yang meneruskan nilai RESULT_OK
dan menyetel tambahan EXTRA_AUTHENTICATION_RESULT
ke
objek Dataset
yang berisi nomor kartu kredit dan tanggal habis masa berlaku. Set
data baru menggantikan set data yang memerlukan autentikasi dan tampilan
segera diisi. Kode berikut menunjukkan contoh cara menampilkan
set data setelah pengguna memberikan 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 to 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 to 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);
Mengatur data dalam grup logis
Layanan isi otomatis harus mengatur data dalam grup logis yang mengisolasi konsep dari domain berbeda. Di halaman ini, grup logis ini disebut sebagai partisi. Daftar berikut menunjukkan contoh khas partisi dan kolom:
- Kredensial, yang mencakup kolom nama pengguna dan sandi.
- Alamat, yang mencakup kolom jalan, kota, negara bagian, dan kode pos.
- Informasi pembayaran, yang mencakup kolom nomor kartu kredit, tanggal habis masa berlaku, dan kode verifikasi.
Layanan isi otomatis yang dengan benar melakukan partisi data dapat lebih baik melindungi data penggunanya dengan tidak mengekspos data dari lebih dari satu partisi dalam set data. Misalnya, set data yang menyertakan kredensial tidak perlu menyertakan informasi pembayaran. Pengaturan data dalam partisi memungkinkan layanan mengekspos informasi relevan yang diperlukan untuk memenuhi permintaan dalam jumlah minimum.
Pengaturan data dalam partisi memungkinkan layanan mengisi aktivitas yang memiliki tampilan dari beberapa partisi sekaligus mengirim data yang relevan dalam jumlah minimum ke aplikasi klien. Misalnya, pertimbangkan aktivitas yang menyertakan tampilan untuk nama pengguna, sandi, jalan, dan kota, dan layanan isi otomatis yang memiliki data berikut:
Partisi | Kolom 1 | Kolom 2 |
---|---|---|
Kredensial | work_username | work_password |
personal_username | personal_password | |
Alamat | work_street | work_city |
personal_street | personal_city |
Layanan dapat menyiapkan set data yang menyertakan partisi kredensial untuk akun kerja dan pribadi. Saat pengguna memilih set data, respons isi otomatis berikutnya dapat memberikan alamat kantor atau pribadi, bergantung pada pilihan pertama pengguna.
Layanan dapat mengidentifikasi kolom yang menghasilkan permintaan dengan memanggil
metode isFocused()
saat
melewati objek AssistStructure
. Hal ini memungkinkan
layanan menyiapkan FillResponse
dengan data partisi yang sesuai.
Isi otomatis kode sekali pakai SMS
Layanan isi otomatis dapat membantu pengguna dalam mengisi kode sekali pakai yang dikirim melalui SMS menggunakan SMS Retriever API.
Untuk menggunakan fitur ini, persyaratan berikut harus dipenuhi:
- Layanan isi otomatis berjalan di Android 9 (API level 28) atau yang lebih baru.
- Pengguna memberikan izin agar layanan isi otomatis membaca kode sekali pakai dari SMS.
- Aplikasi yang disediakan untuk isi otomatis belum menggunakan SMS Retriever API untuk membaca kode sekali pakai.
Layanan isi otomatis dapat menggunakan SmsCodeAutofillClient
, yang tersedia dengan memanggil
SmsCodeRetriever.getAutofillClient()
dari Layanan Google
Play 19.0.56 atau yang lebih baru.
Langkah utama untuk menggunakan API ini dalam layanan isi otomatis adalah:
- Di layanan isi otomatis, gunakan
hasOngoingSmsRequest
dariSmsCodeAutofillClient
untuk menentukan apakah sudah ada permintaan aktif untuk nama paket aplikasi yang Anda isi otomatis. Layanan isi otomatis hanya akan menampilkan perintah saran jika nilai yang ditampilkanfalse
. - Di layanan isi otomatis, gunakan
checkPermissionState
dariSmsCodeAutofillClient
untuk memeriksa apakah layanan isi otomatis memiliki izin untuk mengisi otomatis kode sekali pakai. Status izin ini dapat berupaNONE
,GRANTED
, atauDENIED
. Layanan isi otomatis akan menampilkan perintah saran untuk statusNONE
danGRANTED
. - Dalam aktivitas autentikasi isi otomatis, gunakan izin
SmsRetriever.SEND_PERMISSION
untuk mendaftarkan pemrosesanBroadcastReceiver
agarSmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION
menerima hasil kode SMS saat tersedia. Panggil
startSmsCodeRetriever
diSmsCodeAutofillClient
untuk mulai memproses kode sekali pakai yang dikirim melalui SMS. Jika pengguna memberikan izin pada layanan isi otomatis untuk mengambil kode sekali pakai dari SMS, layanan ini akan mencari pesan SMS yang diterima dalam satu hingga lima menit terakhir dari sekarang.Jika layanan isi otomatis perlu meminta izin pengguna untuk membaca kode sekali pakai, maka
Task
yang ditampilkan olehstartSmsCodeRetriever
mungkin gagal dengan menampilkanResolvableApiException
. Jika hal ini terjadi, Anda harus memanggil metodeResolvableApiException.startResolutionForResult()
guna menampilkan dialog izin untuk permintaan izin.Terima hasil kode SMS dari intent, lalu tampilkan kode SMS sebagai respons isi otomatis.
Skenario isi otomatis lanjutan
- Mengintegrasi dengan keyboard
- Mulai dari Android 11, platform ini memungkinkan keyboard dan editor metode input (IME) lainnya menampilkan saran isi otomatis inline, bukan menggunakan menu pull-down. Untuk informasi selengkapnya tentang cara layanan isi otomatis mendukung fungsi ini, lihat Mengintegrasikan isi otomatis dengan keyboard.
- Memberi nomor set data
- Respons isi otomatis besar dapat melebihi batas ukuran transaksi
objek
Binder
yang merepresentasikan objek jarak jauh yang diperlukan untuk memproses permintaan. Agar sistem Android tidak menampilkan pengecualian dalam skenario ini, Anda dapat membuatFillResponse
tetap kecil dengan menambahkan tidak lebih dari 20 objekDataset
sekaligus. Jika respons membutuhkan lebih banyak set data, Anda dapat menambahkan set data yang memungkinkan pengguna mengetahui bahwa ada lebih banyak informasi dan mengambil grup set data berikutnya saat dipilih. Untuk informasi selengkapnya, lihataddDataset(Dataset)
. - Menyimpan pemisahan data di beberapa layar
Aplikasi sering kali memisahkan data pengguna menjadi beberapa layar dalam aktivitas yang sama, terutama dalam aktivitas yang digunakan untuk membuat akun pengguna baru. Misalnya, layar pertama meminta nama pengguna, dan jika nama pengguna tersedia, layar kedua akan meminta sandi. Dalam situasi ini, layanan isi otomatis harus menunggu hingga pengguna memasukkan kedua kolom agar UI simpan isi otomatis dapat ditampilkan. Ikuti langkah-langkah berikut untuk menangani skenario tersebut:
- Dalam permintaan pengisian pertama, tambahkan paket status klien dalam respons yang berisi ID isi otomatis kolom parsial yang ada di layar.
- Pada permintaan pengisian kedua,
ambil paket status klien, setel ID isi otomatis
dalam permintaan sebelumnya dari status klien, lalu tambahkan ID ini dan
tanda
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
keSaveInfo
objek yang digunakan dalam respons kedua. - Dalam permintaan simpan,
gunakan objek
FillContext
yang tepat untuk mendapatkan nilai dari setiap kolom. Ada satu konteks pengisian per permintaan pengisian.
Untuk informasi selengkapnya, lihat Menyimpan saat data dipisah menjadi beberapa layar.
- Memberikan logika teardown dan inisialisasi untuk setiap permintaan
Setiap kali ada permintaan pengisian otomatis, sistem Android mengikat ke layanan dan memanggil metode
onConnected()
. Setelah layanan memproses permintaan, sistem Android memanggil metodeonDisconnected()
dan melepaskan ikatan dari layanan. Anda dapat mengimplementasikanonConnected()
untuk memberikan kode yang berjalan sebelum memproses permintaan danonDisconnected()
untuk memberikan kode yang berjalan setelah memproses permintaan.- Menyesuaikan UI simpan isi otomatis
Layanan isi otomatis dapat menyesuaikan UI simpan isi otomatis untuk membantu pengguna memutuskan apakah mereka ingin mengizinkan layanan untuk menyimpan data. Layanan dapat memberikan informasi tambahan tentang data yang akan disimpan baik melalui teks sederhana maupun tampilan yang disesuaikan. Layanan juga dapat mengubah tampilan tombol yang membatalkan permintaan simpan dan mendapatkan notifikasi saat pengguna mengetuk tombol tersebut. Untuk informasi selengkapnya, lihat halaman referensi
SaveInfo
.- Mode kompatibilitas
Mode kompatibilitas memungkinkan layanan isi otomatis menggunakan struktur virtual aksesibilitas untuk tujuan isi otomatis. Hal ini sangat berguna untuk menyediakan fungsi isi otomatis di browser yang tidak secara eksplisit mengimplementasikan API isi otomatis.
Untuk menguji layanan isi otomatis menggunakan mode kompatibilitas, tambahkan secara eksplisit browser atau aplikasi yang memerlukan mode kompatibilitas ke daftar yang diizinkan. Anda dapat memeriksa paket mana yang sudah ditambahkan ke daftar yang diizinkan dengan menjalankan perintah berikut:
$ adb shell settings get global autofill_compat_mode_allowed_packages
Jika paket yang diuji tidak tercantum, tambahkan dengan menjalankan perintah berikut, dengan
pkgX
adalah paket aplikasi:$ adb shell settings put global autofill_compat_mode_allowed_packages pkg1[resId1]:pkg2[resId1,resId2]
Jika aplikasi adalah browser, gunakan
resIdx
untuk menentukan ID resource dari kolom input yang berisi URL halaman yang dirender.
Mode kompatibilitas memiliki batasan berikut:
- Permintaan simpan dipicu saat layanan menggunakan
tanda
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
atau metodesetTrigger()
dipanggil.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
disetel secara default saat menggunakan mode kompatibilitas. - Nilai teks node mungkin tidak tersedia dalam
metode
onSaveRequest(SaveRequest, SaveCallback)
.
Untuk informasi selengkapnya tentang mode kompatibilitas, termasuk batasan
yang terkait dengannya, lihat
referensi class
AutofillService
.