Layanan IsiOtomatis adalah aplikasi yang memudahkan pengguna untuk mengisi formulir dengan menyuntikkan data ke tampilan aplikasi lain. Layanan IsiOtomatis juga dapat mengambil data pengguna dari tampilan dalam aplikasi dan menyimpannya untuk digunakan di lain waktu. Layanan IsiOtomatis biasanya disediakan oleh aplikasi yang mengelola data pengguna, seperti pengelola sandi.
Android mempermudah pengisian formulir dengan kerangka kerja IsiOtomatis yang tersedia di Android 8.0 (API level 26) dan lebih tinggi. Pengguna dapat memanfaatkan fitur IsiOtomatis hanya jika ada aplikasi yang menyediakan layanan IsiOtomatis pada perangkat mereka.
Halaman ini menunjukkan cara menerapkan layanan IsiOtomatis di aplikasi Anda. Jika Anda mencari sampel kode yang menunjukkan cara menerapkan layanan, lihat Android AutofillFramework Sample. Untuk detail selengkapnya tentang cara kerja layanan IsiOtomatis, lihat dokumentasi referensi dari class AutofillService
dan AutofillManager
.
Izin dan pernyataan manifes
Aplikasi yang menyediakan layanan IsiOtomatis 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 subkelas dariAutofillService
di aplikasi yang mengimplementasikan layanan. - Atribut
android:permission
yang menyatakan izinBIND_AUTOFILL_SERVICE
. - Elemen
<intent-filter>
elemen, yang turunan<action>
wajibnya menentukan tindakanandroid.service.autofill.AutofillService
. - Elemen
<meta-data>
opsional yang dapat Anda gunakan untuk memberikan parameter konfigurasi tambahan untuk layanan.
Contoh berikut menunjukkan deklarasi layanan IsiOtomatis:
<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 sumber daya XML dengan detail selengkapnya tentang layanan. Sumber daya service_configuration
dalam contoh sebelumnya menetapkan aktivitas yang memungkinkan pengguna untuk mengonfigurasi layanan. Contoh berikut menunjukkan sumber daya XML service_configuration
:
<autofill-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.android.SettingsActivity" />
Untuk informasi selengkpanya tentang sumber daya XML, lihat Menyediakan Sumber Daya.
Perintah untuk mengaktifkan layanan
Aplikasi digunakan sebagai layanan IsiOtomatis setelah menyatakan izin BIND_AUTOFILL_SERVICE
dan pengguna mengaktifkannya di setelan perangkat. Aplikasi dapat memverifikasi apakah itu layanan yang saat ini diaktifkan dengan memanggil metode hasEnabledAutofillServices()
dari class AutofillManager
.
Jika aplikasi ini bukan layanan IsiOtomatis saat ini, maka aplikasi tersebut dapat meminta pengguna untuk mengubah setelan IsiOtomatis dengan menggunakan intent ACTION_REQUEST_SET_AUTOFILL_SERVICE
. Intent menampilkan nilai RESULT_OK
jika pengguna memilih layanan IsiOtomatis yang cocok dengan paket pemanggil.
Isi tampilan klien
Layanan IsiOtomatis menerima permintaan untuk mengisi tampilan klien saat pengguna berinteraksi dengan aplikasi lain. Jika layanan isiOtomatis memiliki data pengguna yang memenuhi permintaan, maka layanan tersebut akan mengirim data sebagai tanggapannya. Sistem Android menampilkan UI IsiOtomatis dengan data yang tersedia, seperti yang ditampilkan pada gambar 1:
Gambar 1. UI IsiOtomatis yang menampilkan dataset.
Kerangka kerja IsiOtomatis menentukan alur kerja untuk mengisi tampilan yang dirancang untuk meminimalkan waktu sistem Android terikat pada layanan IsiOtomatis. Dalam setiap permintaan, sistem Android mengirimkan objek AssistStructure
ke layanan dengan memanggil metode onFillRequest()
. Layanan IsiOtomatis memeriksa apakah dapat memenuhi permintaan dengan data pengguna yang telah disimpan sebelumnya. Jika dapat memenuhi permintaan, maka layanan mengemas data 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 meneruskan null
ke metode onSuccess()
. Layanan memanggil metode onFailure()
jika terjadi kesalahan saat memproses permintaan. Untuk penjelasan mendetail tentang alur kerja, lihat Penggunaan dasar.
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; }
Suatu 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 IsiOtomatis. Contoh kode berikut menunjukkan cara menyediakan beberapa set data sebagai tanggapan:
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 IsiOtomatis dapat membuka objek ViewNode
di AssistStructure
untuk mengambil data IsiOtomatis yang diperlukan untuk memenuhi permintaan. Layanan dapat mengambil data IsiOtomatis dengan menggunakan metode class ViewNode
, misalnya getAutofillId()
. Suatu 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 mereka sebelum tersedia untuk layanan. Jika aplikasi klien tidak menyediakan atribut autofillHints
, layanan harus menggunakan heuristiknya sendiri untuk menjelaskan isinya. Layanan dapat menggunakan metode class lain untuk mendapatkan informasi tentang isi tampilan, seperti getText()
atau getHint()
. Untuk informasi selengkapnya, lihat Memberikan petunjuk untuk IsiOtomatis. Contoh berikut menunjukkan cara melewati AssistStructure
dan mengambil data IsiOtomatis 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); } }
Simpan data pengguna
Layanan IsiOtomatis memerlukan data pengguna untuk mengisi tampilan dalam aplikasi. Ketika pengguna secara manual mengisi tampilan, mereka diminta untuk menyimpan data ke layanan IsiOtomatis saat ini, seperti yang ditampilkan pada gambar 2.
Gambar 2. IsiOtomatis menyimpan UI
Untuk menyimpan data, layanan harus menunjukkan bahwa ia tertarik menyimpan data untuk digunakan di masa mendatang. Sebelum sistem Android mengirim permintaan untuk menyimpan data, ada permintaan pengisian saat layanan memiliki kesempatan untuk mengisi tampilan. Untuk menunjukkan bahwa layanan tersebut berminat untuk menyimpan data, layanan ini menyertakan objek SaveInfo
untuk menanggapi permintaan pengisian preseden. Objek SaveInfo
berisi setidaknya data berikut:
- Jenis data pengguna yang akan disimpan. Untuk mengetahui daftar nilai
SAVE_DATA
yang tersedia, lihatSaveInfo
. - Rangkaian tampilan minimum yang perlu diubah untuk memicu permintaan simpan. Misalnya, formulir login biasanya mengharuskan pengguna untuk memperbarui tampilan
username
danpassword
untuk memicu permintaan simpan.
Objek SaveInfo
dikaitkan dengan objek FillResponse
, seperti yang ditampilkan pada 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 IsiOtomatis dapat menerapkan logika untuk mempertahankan data pengguna dalam metode onSaveRequest()
, yang biasanya dipanggil setelah aktivitas klien selesai atau ketika 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 IsiOtomatis harus mengenkripsi data sensitif sebelum menyimpannya. Akan tetapi, data pengguna 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, yang memungkinkan mereka menggunakan label dalam tampilan presentasi jika pengguna belum mengautentikasi, dan mengganti label dengan data aktual setelah pengguna mengautentikasi.
Tunda UI penyimpanan IsiOtomatis
Mulai dari Android 10, jika Anda menggunakan beberapa layar untuk menerapkan alur kerja IsiOtomatis (misalnya, satu layar untuk kolom nama pengguna dan lainnya untuk sandi), Anda dapat menunda UI penyimpanan IsiOtomatis dengan menggunakan flag SaveInfo.FLAG_DELAY_SAVE
.
Jika flag ini disetel, UI penyimpanan IsiOtomatis tidak terpicu saat konteks IsiOtomatis yang terkait dengan tanggapan SaveInfo
dilakukan. Sebagai gantinya, Anda dapat menggunakan aktivitas terpisah dalam tugas yang sama untuk mengirimkan permintaan pengisian di masa mendatang kemudian menampilkan UI melalui permintaan simpan. Untuk informasi selengkapnya, lihat SaveInfo.FLAG_DELAY_SAVE
.
Memerlukan autentikasi pengguna
Layanan IsiOtomatis dapat menyediakan tingkat keamanan tambahan dengan mengharuskan pengguna mengautentikasi sebelum dapat mengisi tampilan. Skenario berikut adalah kandidat yang baik untuk mengimplementasikan autentikasi pengguna:
- Data pengguna di aplikasi perlu dibuka menggunakan sandi master atau pemindaian sidik jari.
- Set data tertentu perlu dibuka kuncinya, misalnya, detail kartu kredit dengan menggunakan kode verifikasi kartu (CVC).
Dalam skenario dengan layanan memerlukan autentikasi pengguna sebelum membuka kunci data, layanan dapat menyajikan data boilerplate atau label dan menentukan Intent
yang menangani autentikasi. Jika Anda 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 otentikasi:
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 aktivitas menyelesaikan alur autentikasi, aktivitas tersebut harus memanggil metode setResult()
yang meneruskan nilai RESULT_OK
dan menyetel ekstra EXTRA_AUTHENTICATION_RESULT
ke objek FillResponse
yang menyertakan set data yang terisi. Kode berikut menunjukkan contoh cara mengembalikan 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 dengan set data kartu kredit perlu dibuka kuncinya, layanan dapat menampilkan UI yang meminta CVC. Anda dapat menyembunyikan data hingga set data dibuka kuncinya dengan menampilkan data boilerplate, seperti nama bank dan empat digit terakhir nomor kartu kredit. Contoh berikut menunjukkan bagaimana memerlukan 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. // 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();
Setelah aktivitas memvalidasi CVC, aktivitas tersebut harus memanggil metode setResult()
yang meneruskan nilai RESULT_OK
dan menyetel ekstra EXTRA_AUTHENTICATION_RESULT
ke objek Dataset
yang berisi nomor kartu kredit dan tanggal habis masa berlaku. Set data baru menggantikan set data yang membutuhkan 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 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);
Mengatur data dalam grup logis
Layanan IsiOtomatis harus mengatur data dalam grup logis yang mengisolasi konsep dari domain yang berbeda. Grup logis ini disebut sebagai partisi di halaman ini. Daftar berikut menunjukkan contoh khas partisi dan kolom:
- Kredensial, yang meliputi kolom nama pengguna dan sandi.
- Alamat, yang meliputi kolom jalan, kota, negara bagian, dan kode pos.
- Informasi pembayaran, yang meliputi kolom nomor kartu kredit, tanggal habis masa berlaku, dan kode verifikasi.
Layanan IsiOtomatis yang dengan benar mempartisi data dapat lebih melindungi data penggunanya dengan tidak mengekspos data dari lebih dari satu partisi dalam set data. Misalnya, set data yang menyertakan kredensial tidak harus menyertakan informasi pembayaran. Pengaturan data dalam partisi memungkinkan layanan Anda mengekspos jumlah minimum informasi yang diperlukan untuk memenuhi permintaan.
Pengaturan data dalam partisi memungkinkan layanan mengisi aktivitas yang memiliki tampilan dari beberapa partisi sekaligus mengirim jumlah data minimum ke aplikasi klien. Misalnya, pertimbangkan aktivitas yang menyertakan tampilan untuk nama pengguna, sandi, jalan, dan kota, dan layanan IsiOtomatis 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 pekerjaan dan pribadi. Ketika pengguna memilih satu, respons isi IsiOtomatis berikutnya dapat menyediakan pekerjaan atau alamat pribadi, tergantung pilihan pertama pengguna.
Suatu layanan dapat mengidentifikasi kolom yang berasal dari permintaan dengan memanggil metode isFocused()
saat melintasi objek AssistStructure
. Ini memungkinkan layanan menyiapkan FillResponse
dengan data partisi yang sesuai.
Skenario IsiOtomatis lanjutan
- Memberi nomor set data
- Respons IsiOtomatis yang besar dapat melebihi ukuran transaksi yang diizinkan dari objek
Binder
yang mewakili objek yang dapat di-remot yang diperlukan untuk memproses permintaan. Untuk mencegah agar sistem Android tidak mengembalikan pengecualian dalam skenario ini, Anda dapat menjagaFillResponse
tetap kecil dengan menambahkan tidak lebih dari 20Dataset
objek sekaligus. Jika respons Anda membutuhkan lebih banyak set data, Anda dapat menambahkan set data yang memungkinkan pengguna tahu bahwa ada lebih banyak informasi dan mengambil kelompok set data berikutnya saat dipilih. Untuk informasi selengkapnya, lihataddDataset(Dataset)
. - Menyimpan data yang terpecah dalam beberapa layar
Aplikasi sering membagi data pengguna dalam 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 akan pindah ke layar kedua, yang meminta sandi. Dalam situasi ini, layanan IsiOtomatis harus menunggu hingga pengguna memasukkan kedua kolom sebelum UI penyimpanan IsiOtomatis dapat ditampilkan. Layanan dapat mengikuti langkah-langkah ini untuk menangani skenario seperti itu:
- Dalam permintaan pengisian pertama, layanan menambahkan paket status klien dalam respons, yang berisi ID IsiOtomatis dari kolom parsial yang ada di layar.
- Dalam permintaan pengisian kedua, layanan mengambil paket status klien, menyetel ID IsiOtomatis dalam permintaan sebelumnya dari status klien, dan menambahkan ID ini dan flag
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
ke objekSaveInfo
yang digunakan dalam respons kedua. - Dalam permintaan simpan, layanan menggunakan objek
FillContext
yang tepat untuk mendapatkan nilai setiap kolom. Ada satu konteks pengisian setiap permintaan pengisian.
Untuk informasi selengkapnya, lihat Menyimpan saat data dipecah 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()
-nya. Setelah layanan memproses permintaan, sistem Android memanggil metodeonDisconnected()
dan melepaskan ikatan dari layanan. Anda dapat menerapkanonConnected()
untuk memberikan kode yang berjalan sebelum memproses permintaan danonDisconnected()
untuk memberikan kode yang berjalan setelah memproses permintaan.- Menyesuaikan UI penyimpanan IsiOtomatis
Layanan IsiOtomatis dapat menyesuaikan UI penyimpanan IsiOtomatis untuk membantu pengguna memutuskan apakah mereka ingin mengizinkan layanan untuk menyimpan data mereka. Layanan dapat memberikan informasi tambahan tentang apa yang akan disimpan baik melalui teks sederhana atau melalui 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 dokumentasi referensi
SaveInfo
.- Mode kompatibilitas
Mode kompatibilitas memungkinkan layanan IsiOtomatis menggunakan struktur aksesibilitas virtual untuk tujuan IsiOtomatis. Ini sangat berguna untuk menyediakan fungsionalitas IsiOtomatis di browser yang belum secara eksplisit mengimplementasikan API IsiOtomatis.
Untuk menguji layanan IsiOtomatis Anda menggunakan mode kompatibilitas, Anda harus secara eksplisit mengizinkan browser atau aplikasi yang memerlukan mode kompatibilitas. Anda dapat memeriksa paket mana yang sudah diizinkan dengan menjalankan perintah berikut:
$ adb shell settings get global autofill_compat_mode_allowed_packages
Jika paket yang diuji tidak terdaftar, Anda dapat mengizinkannya dengan menjalankan perintah ini:
$ adb shell settings put global autofill_compat_mode_allowed_packages pkg1[resId1]:pkg2[resId1,resId2]
...dengan
pkgX
adalah paket aplikasi. Jika aplikasi adalah browser, maka Anda menggunakanresIdx
untuk menentukan ID sumber daya dari kolom input yang berisi URL halaman yang dirender.
Mode kompatibilitas memiliki batasan berikut:
- Permintaan simpan dipicu saat layanan menggunakan flag FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE atau metode
setTrigger()
dipanggil.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
disetel secara default saat menggunakan mode kompatibilitas. - Nilai teks dari node mungkin tidak tersedia dalam metode
onSaveRequest(SaveRequest, SaveCallback)
.
Untuk informasi selengkapnya tentang mode kompatibilitas, termasuk batasan yang terkait dengannya, lihat referensi class AutofillService
.