บริการป้อนข้อความอัตโนมัติเป็นแอปที่ช่วยให้ผู้ใช้กรอกแบบฟอร์มได้ง่ายขึ้นด้วยการแทรกข้อมูลลงในมุมมองของแอปอื่นๆ บริการป้อนข้อความอัตโนมัติยังดึงข้อมูลผู้ใช้จากมุมมองในแอปและจัดเก็บไว้เพื่อใช้ในภายหลังได้ด้วย บริการป้อนข้อความอัตโนมัติมักให้บริการโดยแอปที่จัดการข้อมูลผู้ใช้ เช่น ในฐานะเครื่องมือจัดการรหัสผ่าน
Android ทำให้การกรอกแบบฟอร์มง่ายขึ้นด้วยเฟรมเวิร์กการป้อนข้อความอัตโนมัติที่มีอยู่ใน Android 8.0 (API ระดับ 26) ขึ้นไป ผู้ใช้สามารถใช้ประโยชน์จากการป้อนข้อความอัตโนมัติ เมื่อมีแอปที่ให้บริการป้อนข้อความอัตโนมัติบน อุปกรณ์
หน้านี้จะแสดงวิธีใช้บริการป้อนข้อความอัตโนมัติในแอปของคุณ หากคุณ
หากต้องการดูตัวอย่างโค้ดที่แสดงวิธีนำบริการไปใช้ โปรดดู
ตัวอย่าง AutofillFramework ใน Java
หรือ
Kotlin
ดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการทำงานของบริการป้อนข้อความอัตโนมัติได้ที่หน้าข้อมูลอ้างอิงสำหรับคลาส AutofillService
และ AutofillManager
การประกาศและสิทธิ์ของไฟล์ Manifest
แอปที่ให้บริการป้อนข้อความอัตโนมัติต้องมีการประกาศที่อธิบายถึง
การใช้บริการ หากต้องการระบุประกาศ ให้ใส่องค์ประกอบ <service>
ในไฟล์ Manifest ของแอป องค์ประกอบ <service>
ต้องมีแอตทริบิวต์และองค์ประกอบต่อไปนี้
- แอตทริบิวต์ของ
android:name
ที่ชี้ไปยังคลาสย่อยของAutofillService
ในแอปที่ใช้งาน บริการ android:permission
แอตทริบิวต์ที่ประกาศสิทธิ์BIND_AUTOFILL_SERVICE
<intent-filter>
องค์ประกอบที่จำเป็น องค์ประกอบย่อย<action>
ระบุ การดำเนินการandroid.service.autofill.AutofillService
- ไม่บังคับ
<meta-data>
ที่คุณสามารถใช้เพื่อระบุพารามิเตอร์การกำหนดค่าเพิ่มเติมสำหรับ บริการ
ตัวอย่างต่อไปนี้แสดงการประกาศบริการป้อนข้อความอัตโนมัติ
<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>
องค์ประกอบ <meta-data>
ประกอบด้วย
android:resource
ที่ชี้ไปยังทรัพยากร XML พร้อมรายละเอียดเพิ่มเติมเกี่ยวกับบริการ
ทรัพยากร service_configuration
ในตัวอย่างก่อนหน้านี้ระบุกิจกรรมที่อนุญาตให้ผู้ใช้กําหนดค่าบริการ ตัวอย่างต่อไปนี้แสดงทรัพยากร XML service_configuration
<autofill-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.android.SettingsActivity" />
โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับทรัพยากร XML ได้ที่ภาพรวมทรัพยากรของแอป
ข้อความแจ้งให้เปิดใช้บริการ
มีการใช้แอปเป็นบริการป้อนข้อความอัตโนมัติหลังจากที่ประกาศ
สิทธิ์ BIND_AUTOFILL_SERVICE
และผู้ใช้เปิดใช้ในอุปกรณ์
การตั้งค่า แอปสามารถตรวจสอบได้ว่าเป็นบริการที่เปิดใช้อยู่ในปัจจุบันหรือไม่โดยเรียกใช้เมธอด hasEnabledAutofillServices()
ของคลาส AutofillManager
หากแอปไม่ใช่บริการป้อนข้อความอัตโนมัติปัจจุบัน แอปจะขอให้ผู้ใช้เปลี่ยนการตั้งค่าการป้อนข้อความอัตโนมัติได้โดยใช้ Intent ACTION_REQUEST_SET_AUTOFILL_SERVICE
Intent จะแสดงผลค่า RESULT_OK
หากผู้ใช้เลือกบริการป้อนข้อความอัตโนมัติที่ตรงกับแพ็กเกจของผู้เรียก
กรอกข้อมูลมุมมองของลูกค้า
บริการป้อนข้อความอัตโนมัติได้รับคำขอให้กรอกข้อมูลมุมมองของลูกค้าเมื่อผู้ใช้ โต้ตอบกับแอปอื่นๆ หากบริการป้อนข้อความอัตโนมัติมีข้อมูลผู้ใช้ที่ตรงกับคำขอ ระบบจะส่งข้อมูลในการตอบกลับ ระบบ Android แสดง UI ป้อนข้อความอัตโนมัติด้วยข้อมูลที่พร้อมใช้งาน ดังแสดงในรูปที่ 1:
เฟรมเวิร์กการป้อนข้อความอัตโนมัติจะกำหนดเวิร์กโฟลว์ในการกรอกข้อมูลมุมมองที่ออกแบบมาเพื่อ
ลดเวลาที่ระบบ Android ผูกกับบริการป้อนข้อความอัตโนมัติ ใน
แต่ละคำขอ ระบบ Android จะส่งออบเจ็กต์ AssistStructure
ไปยังบริการโดย
กำลังโทรหา onFillRequest()
บริการป้อนข้อความอัตโนมัติจะตรวจสอบว่าสามารถตอบสนองคำขอด้วยข้อมูลผู้ใช้ที่
ที่จัดเก็บไว้ก่อนหน้านี้ ถ้าตอบสนองคำขอได้ แพ็กเกจบริการ
ข้อมูลใน Dataset
ออบเจ็กต์ บริการที่เรียกใช้
onSuccess()
ในการส่งผ่าน FillResponse
ที่มีออบเจ็กต์ Dataset
รายการ หากบริการไม่มีข้อมูลที่จะตอบสนองคําขอ ระบบจะส่ง null
ไปยังเมธอด onSuccess()
บริการ
โทรหา onFailure()
แทน หากมีข้อผิดพลาดในการประมวลผลคำขอ หากต้องการทราบรายละเอียด
คำอธิบายเกี่ยวกับเวิร์กโฟลว์ โปรดดูคำอธิบายใน AutofillService
หน้าอ้างอิง
โค้ดต่อไปนี้แสดงตัวอย่างของเมธอด 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; }
บริการหนึ่งอาจมีชุดข้อมูลมากกว่า 1 ชุดที่ตรงกับคำขอ ในกรณีนี้ ระบบ Android จะแสดงตัวเลือกหลายรายการ (1 รายการสำหรับชุดข้อมูลแต่ละชุด) ใน UI การป้อนข้อความอัตโนมัติ ตัวอย่างโค้ดต่อไปนี้จะแสดงวิธีการ ให้ชุดข้อมูลหลายชุดในการตอบกลับ:
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();
บริการป้อนข้อความอัตโนมัติจะไปยังส่วนต่างๆ ของออบเจ็กต์ ViewNode
ได้ใน
AssistStructure
เพื่อเรียกข้อมูลการป้อนข้อความอัตโนมัติ
ที่จำเป็นต่อการดำเนินการตามคำขอ บริการสามารถเรียกข้อมูลการป้อนข้อความอัตโนมัติโดยใช้
วิธีการของ ViewNode
เช่น getAutofillId()
บริการต้องสามารถอธิบายเนื้อหาของข้อมูลพร็อพเพอร์ตี้เพื่อตรวจสอบว่า
ตอบสนองคำขอได้ การใช้autofillHints
จะใช้แอตทริบิวต์แรก
วิธีที่บริการต้องใช้เพื่ออธิบายเนื้อหาของข้อมูลพร็อพเพอร์ตี้ อย่างไรก็ตาม แอปไคลเอ็นต์ต้องระบุแอตทริบิวต์ในมุมมองอย่างชัดเจนก่อนจึงจะพร้อมให้บริการ
หากแอปไคลเอ็นต์ไม่ได้ระบุแอตทริบิวต์ autofillHints
บริการต้องใช้วิธีการหาค่าประมาณของตนเองเพื่ออธิบายเนื้อหา
บริการสามารถใช้เมธอดจากคลาสอื่นๆ เช่น getText()
หรือ getHint()
เพื่อรับข้อมูลเกี่ยวกับเนื้อหาของมุมมอง
ดูข้อมูลเพิ่มเติมได้ที่ระบุคำแนะนำสำหรับการป้อนข้อความอัตโนมัติ
ตัวอย่างต่อไปนี้แสดงวิธีข้ามผ่าน AssistStructure
และเรียกข้อมูล
ข้อมูลที่ป้อนอัตโนมัติจากออบเจ็กต์ 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); } }
บันทึกข้อมูลผู้ใช้
บริการป้อนข้อความอัตโนมัติต้องใช้ข้อมูลผู้ใช้เพื่อกรอกข้อมูลมุมมองในแอป เมื่อผู้ใช้ กรอกข้อมูลมุมมองด้วยตนเอง ผู้ใช้จะได้รับแจ้งให้บันทึกข้อมูลลงในมุมมองปัจจุบัน บริการป้อนข้อความอัตโนมัติดังที่แสดงในรูปที่ 2
ในการบันทึกข้อมูล บริการต้องระบุว่าตนสนใจที่จะจัดเก็บข้อมูล
ไว้ใช้ในอนาคต ก่อนที่ระบบ Android จะส่งคำขอบันทึกข้อมูล
มีคำขอให้กรอกข้อมูลที่บริการมีโอกาสได้กรอกข้อมูล
ครั้ง บริการระบุว่าลูกค้าสนใจที่จะบันทึกข้อมูล
มี SaveInfo
ในการตอบกลับคำขอกรอกข้อมูล ออบเจ็กต์ SaveInfo
มีข้อมูลต่อไปนี้เป็นอย่างน้อย
- ประเภทข้อมูลผู้ใช้ที่บันทึก ดูรายการค่า
SAVE_DATA
ที่พร้อมใช้งานได้ที่SaveInfo
- จำนวนพร็อพเพอร์ตี้ขั้นต่ำที่ต้องเปลี่ยนแปลงเพื่อเรียกใช้คำขอบันทึก
เช่น แบบฟอร์มการเข้าสู่ระบบมักจะกำหนดให้ผู้ใช้อัปเดต
username
และpassword
การดูเพื่อเรียกคำขอบันทึก
ออบเจ็กต์ SaveInfo
จะเชื่อมโยงกับออบเจ็กต์ FillResponse
ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้
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(); ... }
บริการป้อนข้อความอัตโนมัติสามารถใช้ตรรกะเพื่อคงข้อมูลผู้ใช้ไว้ใน
onSaveRequest()
ซึ่งโดยปกติจะเรียกใช้หลังจากกิจกรรมของลูกค้าเสร็จสิ้นหรือเมื่อ
การเรียกในแอปไคลเอ็นต์ commit()
โค้ดต่อไปนี้แสดงตัวอย่างเมธอด 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(); }
บริการป้อนข้อความอัตโนมัติต้องเข้ารหัสข้อมูลที่ละเอียดอ่อนก่อนที่จะเก็บไว้ อย่างไรก็ตาม ข้อมูลผู้ใช้อาจรวมถึงป้ายกำกับหรือข้อมูลที่ไม่ใช่ข้อมูลที่ละเอียดอ่อน ตัวอย่างเช่น บัญชีผู้ใช้อาจมีป้ายกำกับที่ทําเครื่องหมายข้อมูลว่าเป็นบัญชีงานหรือส่วนตัว บริการต้องไม่เข้ารหัสป้ายกำกับ การไม่เข้ารหัสป้ายกำกับจะทำให้บริการต่างๆ ใช้ป้ายกำกับในมุมมองการนำเสนอได้หากผู้ใช้ไม่ได้ตรวจสอบสิทธิ์ จากนั้นบริการจะแทนที่ป้ายกำกับด้วยข้อมูลจริงได้หลังจากที่ผู้ใช้ตรวจสอบสิทธิ์แล้ว
เลื่อน UI การบันทึกการป้อนข้อความอัตโนมัติ
ใน Android 10 เป็นต้นไป หากคุณใช้หลายหน้าจอเพื่อใช้การป้อนข้อความอัตโนมัติ
เช่น หน้าจอหนึ่งสำหรับช่องชื่อผู้ใช้ และอีกหน้าจอหนึ่งสำหรับ
รหัสผ่าน คุณสามารถเลื่อน UI การบันทึกการป้อนข้อความอัตโนมัติได้โดยใช้
SaveInfo.FLAG_DELAY_SAVE
แจ้ง
หากตั้งค่า Flag นี้ ระบบจะไม่ทริกเกอร์ UI บันทึกการป้อนข้อความอัตโนมัติเมื่อมีการบันทึกบริบทการป้อนข้อความอัตโนมัติที่เชื่อมโยงกับการตอบกลับ SaveInfo
อย่างไรก็ตาม คุณสามารถ
ใช้กิจกรรมแยกต่างหากภายในงานเดียวกันเพื่อส่งคำขอเติมเต็มในอนาคตและ
จากนั้นแสดง UI ผ่านคำขอบันทึก สำหรับข้อมูลเพิ่มเติม โปรดดู
SaveInfo.FLAG_DELAY_SAVE
ต้องมีการตรวจสอบสิทธิ์ผู้ใช้
บริการป้อนข้อความอัตโนมัติสามารถช่วยเพิ่มความปลอดภัยอีกขั้นโดย ผู้ใช้เพื่อตรวจสอบสิทธิ์ก่อนที่จะกรอกข้อมูลมุมมอง สถานการณ์ต่อไปนี้เหมาะที่จะใช้การตรวจสอบสิทธิ์ผู้ใช้
- ข้อมูลผู้ใช้ในแอปต้องปลดล็อกโดยใช้รหัสผ่านหลักหรือการสแกนลายนิ้วมือ
- คุณต้องปลดล็อกชุดข้อมูลที่ต้องการ เช่น รายละเอียดบัตรเครดิต โดยใช้รหัสยืนยันบัตร (CVC)
ในกรณีที่บริการกำหนดให้มีการตรวจสอบสิทธิ์ผู้ใช้ก่อนปลดล็อก
ข้อมูล บริการก็สามารถนำเสนอข้อมูลสำเร็จรูปหรือป้ายกำกับและระบุ
Intent
ซึ่งดูแล
การตรวจสอบสิทธิ์ หากต้องการข้อมูลเพิ่มเติมเพื่อประมวลผลคำขอหลังจากทำตามขั้นตอนการตรวจสอบสิทธิ์เสร็จแล้ว คุณสามารถเพิ่มข้อมูลดังกล่าวลงใน Intent ได้ จากนั้นกิจกรรมการตรวจสอบสิทธิ์จะส่งข้อมูลกลับไปยังAutofillService
คลาส
ในแอปของคุณได้
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีระบุว่าคําขอต้องได้รับการตรวจสอบสิทธิ์
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();
เมื่อกิจกรรมทำตามขั้นตอนการตรวจสอบสิทธิ์เสร็จแล้ว กิจกรรมจะต้องเรียกใช้เมธอด setResult()
โดยส่งค่า RESULT_OK
และตั้งค่า EXTRA_AUTHENTICATION_RESULT
เพิ่มเติมให้กับออบเจ็กต์ FillResponse
ที่มีชุดข้อมูลที่สร้างขึ้น โค้ดต่อไปนี้แสดงตัวอย่างวิธีแสดงผลลัพธ์เมื่อขั้นตอนการตรวจสอบสิทธิ์เสร็จสมบูรณ์
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);
ในกรณีที่ต้องปลดล็อกชุดข้อมูลบัตรเครดิต บริการจะแสดง UI ที่ขอ CVC คุณจะซ่อนข้อมูลได้จนกว่าชุดข้อมูลจะ ปลดล็อกด้วยการนำเสนอข้อมูลสำเร็จรูป เช่น ชื่อธนาคารและ ตัวเลขสี่หลักสุดท้ายของหมายเลขบัตรเครดิต ตัวอย่างต่อไปนี้จะแสดงวิธีการ ต้องมีการตรวจสอบสิทธิ์สำหรับชุดข้อมูลและซ่อนข้อมูลจนกว่าผู้ใช้จะระบุ 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();
เมื่อกิจกรรมตรวจสอบ CVC แล้ว ก็ควรเรียกใช้เมธอด setResult()
โดยส่งค่า RESULT_OK
และตั้งค่า EXTRA_AUTHENTICATION_RESULT
extra เป็นออบเจ็กต์ Dataset
ที่มีหมายเลขบัตรเครดิตและวันที่หมดอายุ ชุดข้อมูลใหม่จะแทนที่ชุดข้อมูลที่ต้องตรวจสอบสิทธิ์ และระบบจะป้อนข้อมูลพร็อพเพอร์ตี้ทันที โค้ดต่อไปนี้แสดงตัวอย่างวิธีแสดงผลชุดข้อมูลเมื่อผู้ใช้ระบุ 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);
จัดระเบียบข้อมูลเป็นกลุ่มที่สมเหตุสมผล
บริการป้อนข้อความอัตโนมัติต้องจัดระเบียบข้อมูลเป็นกลุ่มเชิงตรรกะที่แยกข้อมูลออกมา แนวคิดจากโดเมนที่แตกต่างกัน ในหน้านี้ กลุ่มเชิงตรรกะเหล่านี้เรียกว่า เป็นพาร์ติชัน รายการต่อไปนี้แสดงตัวอย่างทั่วไปของพาร์ติชันและช่อง
- ข้อมูลเข้าสู่ระบบ ซึ่งมีช่องชื่อผู้ใช้และรหัสผ่าน
- ที่อยู่ ซึ่งประกอบด้วยช่องถนน เมือง รัฐ และรหัสไปรษณีย์
- ข้อมูลการชำระเงิน ซึ่งรวมถึงช่องหมายเลขบัตรเครดิต วันที่หมดอายุ และรหัสยืนยัน
บริการป้อนข้อความอัตโนมัติที่แบ่งพาร์ติชันข้อมูลอย่างถูกต้องจะสามารถป้องกัน ของผู้ใช้ด้วยการไม่เปิดเผยข้อมูลจากพาร์ติชันมากกว่า 1 พาร์ติชันใน ชุดข้อมูล เช่น ชุดข้อมูลที่มีข้อมูลเข้าสู่ระบบไม่จำเป็นต้องมีข้อมูลการชำระเงิน การจัดระเบียบข้อมูลในพาร์ติชันช่วยให้ เพื่อแสดงข้อมูลที่เกี่ยวข้องในปริมาณขั้นต่ำที่จำเป็นต่อการ ทำตามคำขอ
การจัดระเบียบข้อมูลในพาร์ติชันช่วยให้บริการสามารถป้อนข้อมูลในกิจกรรมที่มีมุมมองจากหลายพาร์ติชันขณะส่งข้อมูลที่เกี่ยวข้องไปยังแอปไคลเอ็นต์ในปริมาณที่น้อยที่สุดได้ ตัวอย่างเช่น พิจารณากิจกรรมที่มีมุมมองสำหรับชื่อผู้ใช้ รหัสผ่าน ถนน และเมือง รวมถึงบริการป้อนข้อความอัตโนมัติที่มีข้อมูลต่อไปนี้
พาร์ติชัน | ฟิลด์ 1 | ฟิลด์ 2 |
---|---|---|
ข้อมูลรับรอง | ชื่อผู้ใช้งาน | รหัสผ่านที่ทำงาน |
ชื่อผู้ใช้ส่วนตัว | รหัสผ่านส่วนตัว | |
ที่อยู่ | work_street | work_city |
personal_street | personal_city |
บริการนี้สามารถเตรียมชุดข้อมูลที่มีพาร์ติชันข้อมูลเข้าสู่ระบบสำหรับทั้งบัญชีงานและบัญชีส่วนตัว เมื่อผู้ใช้เลือกชุดข้อมูลแล้ว การตอบกลับที่ป้อนข้อความอัตโนมัติครั้งถัดไปอาจแสดงที่อยู่งานหรือที่อยู่ส่วนตัว ทั้งนี้ขึ้นอยู่กับตัวเลือกแรกของผู้ใช้
บริการจะระบุช่องที่เป็นต้นทางของคําขอได้โดยเรียกใช้เมธอด isFocused()
ขณะที่ไปยังออบเจ็กต์ AssistStructure
ซึ่งช่วยให้
เพื่อเตรียม FillResponse
ด้วยข้อมูลพาร์ติชันที่เหมาะสม
ป้อนรหัสแบบใช้ครั้งเดียวทาง SMS โดยอัตโนมัติ
บริการป้อนข้อความอัตโนมัติช่วยผู้ใช้ในการป้อนรหัสแบบครั้งเดียวที่ส่งผ่าน SMS โดยใช้ SMS Retriever API
คุณต้องมีคุณสมบัติตรงตามข้อกำหนดต่อไปนี้จึงจะใช้ฟีเจอร์นี้ได้
- บริการป้อนข้อความอัตโนมัติกำลังทำงานใน Android 9 (API ระดับ 28) ขึ้นไป
- ผู้ใช้ให้ความยินยอมให้บริการป้อนข้อความอัตโนมัติอ่านรหัสแบบครั้งเดียวจาก SMS
- แอปพลิเคชันที่คุณให้การป้อนข้อความอัตโนมัติยังไม่ได้ใช้ SMS Retriever API เพื่ออ่านรหัสแบบใช้ครั้งเดียว
บริการป้อนข้อความอัตโนมัติสามารถใช้ SmsCodeAutofillClient
พร้อมให้บริการเมื่อโทรไปที่ SmsCodeRetriever.getAutofillClient()
จาก Google Play
บริการ 19.0.56 ขึ้นไป
ขั้นตอนหลักในการใช้ API นี้ในบริการป้อนข้อความอัตโนมัติมีดังนี้
- ในบริการป้อนข้อความอัตโนมัติ ให้ใช้
hasOngoingSmsRequest
จากSmsCodeAutofillClient
เพื่อดูว่ามีคำขอที่ใช้งานอยู่สำหรับชื่อแพ็กเกจของแอปพลิเคชันที่คุณกรอกข้อความอัตโนมัติอยู่หรือไม่ การป้อนข้อความอัตโนมัติของคุณ บริการต้องแสดงพรอมต์คำแนะนำก็ต่อเมื่อแสดงfalse
เท่านั้น - ในบริการป้อนข้อความอัตโนมัติ ให้ใช้
checkPermissionState
จากSmsCodeAutofillClient
เพื่อตรวจสอบว่าบริการป้อนข้อความอัตโนมัติมีสิทธิ์ป้อนรหัสแบบครั้งเดียวโดยอัตโนมัติหรือไม่ สถานะสิทธิ์นี้อาจเป็นNONE
,GRANTED
หรือDENIED
บริการป้อนข้อความอัตโนมัติต้องแสดงข้อความแจ้งคำแนะนำ สำหรับNONE
และGRANTED
รัฐ - ในกิจกรรมการตรวจสอบสิทธิ์การป้อนข้อความอัตโนมัติ ให้ใช้
สิทธิ์ในการจดทะเบียน
BroadcastReceiver
ของSmsRetriever.SEND_PERMISSION
กำลังฟังSmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION
เพื่อรับ SMS โค้ดเมื่อพร้อมใช้งาน โทร
startSmsCodeRetriever
ในวันที่SmsCodeAutofillClient
เพื่อเริ่มฟังรหัสแบบใช้ครั้งเดียวที่ส่งทาง SMS หากผู้ใช้ให้สิทธิ์บริการป้อนข้อความอัตโนมัติในการดึงข้อมูลแบบครั้งเดียว รหัสจาก SMS ซึ่งจะมองหาข้อความ SMS ที่ได้รับในช่วงหนึ่งถึงห้าครั้งล่าสุด นาทีนับจากนี้หากบริการป้อนข้อความอัตโนมัติต้องขอสิทธิ์จากผู้ใช้ในการอ่านแบบครั้งเดียว
Task
ที่แสดงผลโดยstartSmsCodeRetriever
อาจล้มเหลวโดยมีResolvableApiException
ส่งคืนแล้ว ในกรณีนี้ คุณต้องเรียกใช้เมธอดResolvableApiException.startResolutionForResult()
เพื่อแสดงกล่องโต้ตอบความยินยอมสําหรับคําขอสิทธิ์รับผลลัพธ์รหัส SMS จาก Intent แล้วแสดงรหัส SMS เป็นการตอบกลับแบบป้อนข้อความอัตโนมัติ
สถานการณ์การป้อนข้อความอัตโนมัติขั้นสูง
- ผสานรวมกับแป้นพิมพ์
- สำหรับ Android 11 เป็นต้นไป แพลตฟอร์มนี้จะอนุญาตให้แป้นพิมพ์ และตัวแก้ไขวิธีการป้อนข้อมูลอื่นๆ (IME) เพื่อแสดงคำแนะนำการป้อนข้อความอัตโนมัติในบรรทัด แทนการใช้เมนูแบบเลื่อนลง ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีที่บริการป้อนข้อความอัตโนมัติรองรับฟังก์ชันการทำงานนี้ได้ที่หัวข้อผสานรวมการป้อนข้อความอัตโนมัติกับแป้นพิมพ์
- จัดชุดข้อมูลเป็นหน้า
- คำตอบการเติมข้อความอัตโนมัติขนาดใหญ่อาจเกินขนาดธุรกรรมที่อนุญาตของออบเจ็กต์
Binder
ที่แสดงถึงออบเจ็กต์ระยะไกลที่จําเป็นสําหรับประมวลผลคําขอ หากต้องการป้องกันไม่ให้ระบบ Android แสดงข้อยกเว้นในสถานการณ์เหล่านี้ คุณสามารถทำให้FillResponse
มีขนาดไม่ใหญ่โดยการเพิ่มออบเจ็กต์Dataset
ไม่เกิน 20 รายการในแต่ละครั้ง หากคำตอบต้องใช้ชุดข้อมูลเพิ่ม คุณสามารถเพิ่มชุดข้อมูลที่จะแจ้งให้ผู้ใช้ทราบว่ามีข้อมูลเพิ่มเติมและดึงชุดข้อมูลกลุ่มถัดไปเมื่อเลือก ดูข้อมูลเพิ่มเติมได้ที่addDataset(Dataset)
- บันทึกข้อมูลที่แยกออกเป็นหลายหน้าจอ
แอปมักแยกข้อมูลผู้ใช้ในหลายหน้าจอในกิจกรรมเดียวกัน โดยเฉพาะในกิจกรรมที่ใช้สร้างบัญชีผู้ใช้ใหม่ เช่น หน้าจอแรกจะขอชื่อผู้ใช้ และหากชื่อผู้ใช้นั้นใช้ได้ หน้าจอที่ 2 จะขอรหัสผ่าน ในกรณีเหล่านี้ บริการป้อนข้อความอัตโนมัติต้องรอจนกว่าผู้ใช้จะป้อนข้อมูลในทั้ง 2 ช่องก่อนจึงจะแสดง UI บันทึกการป้อนข้อความอัตโนมัติได้ ทำตาม ในการรับมือกับสถานการณ์ดังกล่าว
- ในคำขอกรอกข้อมูลแรก ให้เพิ่มกลุ่มสถานะไคลเอ็นต์ในการตอบกลับซึ่งมีรหัสป้อนข้อความอัตโนมัติของฟิลด์บางส่วนที่แสดงในหน้าจอ
- ในคำขอที่ 2
ดึงข้อมูลกลุ่มสถานะไคลเอ็นต์ รับชุดรหัสป้อนข้อความอัตโนมัติ
ในคำขอก่อนหน้าจากสถานะไคลเอ็นต์ แล้วเพิ่มรหัสเหล่านี้รวมถึง
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
ไปยังออบเจ็กต์SaveInfo
ที่ใช้ในคำตอบที่ 2 - ในคำขอบันทึก
ใช้
FillContext
ที่เหมาะสม เพื่อหาค่าของแต่ละช่อง บริบทการโฆษณาจะอยู่ที่ 1 รายการต่อคำขอ
ดูข้อมูลเพิ่มเติมได้ที่บันทึกเมื่อข้อมูลมีการแยกออกเป็นหลายหน้าจอ
- ระบุตรรกะการเริ่มต้นและการเลิกใช้งานสำหรับคำขอแต่ละรายการ
ทุกครั้งที่มีคำขอป้อนข้อความอัตโนมัติ ระบบ Android จะเชื่อมโยงกับบริการและเรียกใช้เมธอด
onConnected()
เมื่อบริการประมวลผลคําขอแล้ว ระบบ Android จะเรียกใช้เมธอดonDisconnected()
และยกเลิกการเชื่อมโยงกับบริการ คุณสามารถใช้onConnected()
เพื่อระบุโค้ดที่จะทำงานก่อนประมวลผลคำขอ และonDisconnected()
เพื่อระบุโค้ดที่จะทำงานหลังจากประมวลผลคำขอ- ปรับแต่ง UI การบันทึกการป้อนข้อความอัตโนมัติ
บริการป้อนข้อความอัตโนมัติสามารถปรับแต่ง UI บันทึกการป้อนข้อความอัตโนมัติเพื่อช่วยผู้ใช้ตัดสินใจว่าต้องการอนุญาตให้บริการบันทึกข้อมูลหรือไม่ บริการต่างๆ ข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่บันทึกผ่านข้อความธรรมดาหรือ ผ่านมุมมองที่กำหนดเอง บริการต่างๆ ยังเปลี่ยนรูปลักษณ์ของปุ่มด้วย ที่ยกเลิกคำขอบันทึกและรับการแจ้งเตือนเมื่อผู้ใช้แตะ โปรดดูข้อมูลเพิ่มเติมที่หน้าข้อมูลอ้างอิง
SaveInfo
- โหมดความเข้ากันได้
โหมดความเข้ากันได้จะช่วยให้บริการป้อนข้อความอัตโนมัติใช้การช่วยเหลือพิเศษได้ โครงสร้างเสมือน เพื่อการป้อนข้อความอัตโนมัติ มีประโยชน์อย่างยิ่งสำหรับการให้บริการฟังก์ชันป้อนข้อความอัตโนมัติในเบราว์เซอร์ที่ ไม่ใช้ API สำหรับการป้อนข้อความอัตโนมัติอย่างชัดเจน
หากต้องการทดสอบบริการป้อนข้อความอัตโนมัติโดยใช้โหมดความเข้ากันได้ อนุญาตเบราว์เซอร์หรือแอปที่ต้องใช้โหมดความเข้ากันได้ คุณสามารถดู แพ็กเกจที่อยู่ในรายการที่อนุญาตอยู่แล้วโดยเรียกใช้คำสั่งต่อไปนี้
$ adb shell settings get global autofill_compat_mode_allowed_packages
หากแพ็กเกจที่คุณจะทดสอบไม่อยู่ในรายการ ให้เพิ่มแพ็กเกจดังกล่าวโดยการเรียกใช้ คำสั่งต่อไปนี้ โดยที่
pkgX
คือแพ็กเกจของแอป$ adb shell settings put global autofill_compat_mode_allowed_packages pkg1[resId1]:pkg2[resId1,resId2]
หากแอปเป็นเบราว์เซอร์ ใช้
resIdx
เพื่อระบุรหัสทรัพยากรของช่องป้อนข้อมูลที่มี URL ของหน้าที่แสดงผล
โหมดความเข้ากันได้มีข้อจำกัดต่อไปนี้
- คำขอบันทึกจะทริกเกอร์เมื่อบริการใช้ Flag
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
หรือเรียกใช้เมธอดsetTrigger()
ระบบจะตั้งค่าFLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
เป็นค่าเริ่มต้นเมื่อ โดยใช้โหมดความเข้ากันได้ - ค่าข้อความของโหนดอาจไม่พร้อมใช้งานในวิธี
onSaveRequest(SaveRequest, SaveCallback)
โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับโหมดความเข้ากันได้ รวมถึงข้อจำกัด
ที่เกี่ยวข้อง โปรดดูที่
AutofillService
ข้อมูลอ้างอิงของชั้นเรียน