บริการป้อนข้อความอัตโนมัติคือแอปที่ช่วยให้ผู้ใช้กรอกแบบฟอร์มได้ง่ายขึ้น โดยการแทรกข้อมูลลงในมุมมองของแอปอื่นๆ บริการป้อนข้อความอัตโนมัติยังสามารถ ดึงข้อมูลผู้ใช้จากมุมมองในแอปและจัดเก็บไว้เพื่อใช้ในภายหลังได้ด้วย โดยปกติแล้ว บริการป้อนข้อความอัตโนมัติจะให้บริการโดยแอปที่จัดการข้อมูลผู้ใช้ เช่น เครื่องมือจัดการรหัสผ่าน
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
สิทธิ์
BIND_AUTOFILL_SERVICE
และผู้ใช้เปิดใช้ใน
การตั้งค่าอุปกรณ์ แอปสามารถยืนยันได้ว่าแอปเป็นบริการที่เปิดใช้อยู่ในปัจจุบันหรือไม่โดย
เรียกใช้เมธอด
hasEnabledAutofillServices()
ของคลาส AutofillManager
หากแอปไม่ใช่บริการป้อนข้อความอัตโนมัติปัจจุบัน แอปจะขอให้ผู้ใช้
เปลี่ยนการตั้งค่าการป้อนข้อความอัตโนมัติได้โดยใช้
ACTION_REQUEST_SET_AUTOFILL_SERVICE
Intent Intent จะแสดงค่า RESULT_OK
หากผู้ใช้เลือกบริการป้อนข้อความอัตโนมัติที่ตรงกับแพ็กเกจของผู้โทร
กรอกข้อมูลมุมมองของลูกค้า
บริการป้อนข้อความอัตโนมัติจะได้รับคำขอให้กรอกข้อมูลในมุมมองของไคลเอ็นต์เมื่อผู้ใช้ โต้ตอบกับแอปอื่นๆ หากบริการป้อนข้อความอัตโนมัติมีข้อมูลผู้ใช้ที่ตรงตามคำขอ ระบบจะส่งข้อมูลดังกล่าวในการตอบกลับ ระบบ Android จะแสดง UI การป้อนข้อความอัตโนมัติพร้อมข้อมูลที่มีอยู่ ดังที่แสดงในรูปที่ 1
รูปที่ 1 UI การป้อนข้อความอัตโนมัติที่แสดงชุดข้อมูล
เฟรมเวิร์กการป้อนข้อความอัตโนมัติกำหนดเวิร์กโฟลว์เพื่อกรอกข้อมูลในมุมมองที่ออกแบบมาเพื่อ
ลดเวลาที่ระบบ 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 จะแสดงตัวเลือกหลายรายการ ซึ่งมีตัวเลือกหนึ่งสำหรับชุดข้อมูลแต่ละชุดใน 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
รูปที่ 2 UI การบันทึกการป้อนข้อความอัตโนมัติ
หากต้องการบันทึกข้อมูล บริการต้องระบุว่าต้องการจัดเก็บข้อมูล
เพื่อใช้ในอนาคต ก่อนที่ระบบ 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
แฟล็ก
หากตั้งค่าสถานะนี้ ระบบจะไม่ทริกเกอร์ 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 คุณซ่อนข้อมูลได้จนกว่าจะปลดล็อกชุดข้อมูลโดยการแสดงข้อมูลมาตรฐาน เช่น ชื่อธนาคารและตัวเลข 4 หลักสุดท้ายของหมายเลขบัตรเครดิต ตัวอย่างต่อไปนี้แสดงวิธี กำหนดให้มีการตรวจสอบสิทธิ์สำหรับชุดข้อมูลและซ่อนข้อมูลจนกว่าผู้ใช้จะระบุ 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
เพิ่มเติมเป็น
ออบเจ็กต์ 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_username | work_password |
personal_username | personal_password | |
ที่อยู่ | 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
- ในกิจกรรมการตรวจสอบสิทธิ์การป้อนข้อความอัตโนมัติ ให้ใช้สิทธิ์
SmsRetriever.SEND_PERMISSION
เพื่อลงทะเบียนBroadcastReceiver
เพื่อรอรับผลลัพธ์รหัส SMS เมื่อพร้อมใช้งานSmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION
โทรหา
startSmsCodeRetriever
ในSmsCodeAutofillClient
เพื่อเริ่มฟังรหัสแบบใช้ครั้งเดียวที่ส่งผ่าน SMS หากผู้ใช้ให้สิทธิ์แก่บริการป้อนข้อความอัตโนมัติเพื่อดึงรหัสแบบครั้งเดียวจาก SMS ระบบจะค้นหาข้อความ SMS ที่ได้รับในช่วง 1-5 นาทีที่ผ่านมาหากบริการป้อนข้อความอัตโนมัติต้องขอสิทธิ์จากผู้ใช้เพื่ออ่านรหัสแบบใช้ครั้งเดียว
Task
ที่startSmsCodeRetriever
แสดงผลอาจล้มเหลวและแสดงผลResolvableApiException
หากเกิดกรณีนี้ขึ้น คุณต้องเรียกใช้เมธอดResolvableApiException.startResolutionForResult()
เพื่อแสดง กล่องโต้ตอบความยินยอมสำหรับคำขอสิทธิ์รับผลลัพธ์รหัส SMS จาก Intent แล้วส่งคืนรหัส SMS เป็นการตอบกลับการป้อนข้อความอัตโนมัติ
เปิดใช้การป้อนข้อความอัตโนมัติใน Chrome
Chrome อนุญาตให้บริการป้อนข้อความอัตโนมัติของบุคคลที่สามป้อนข้อความอัตโนมัติในแบบฟอร์มโดยกำเนิด ซึ่งจะช่วยให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ราบรื่นและง่ายดายยิ่งขึ้น หากต้องการใช้บริการป้อนข้อความอัตโนมัติของบุคคลที่สามเพื่อป้อนรหัสผ่าน พาสคีย์ และข้อมูลอื่นๆ เช่น ที่อยู่และข้อมูลการชำระเงินโดยอัตโนมัติ ผู้ใช้ต้องเลือกป้อนข้อความอัตโนมัติโดยใช้บริการอื่นในการตั้งค่า Chrome
เพื่อให้ผู้ใช้ได้รับประสบการณ์การป้อนข้อความอัตโนมัติที่ดีที่สุดเท่าที่จะเป็นไปได้ด้วยบริการของคุณและ Chrome ใน Android ผู้ให้บริการป้อนข้อความอัตโนมัติควรสนับสนุนให้ผู้ใช้ระบุผู้ให้บริการที่ต้องการในการตั้งค่า Chrome
นักพัฒนาแอปสามารถทำสิ่งต่อไปนี้เพื่อช่วยให้ผู้ใช้เปิดปุ่มสลับได้
- ค้นหาการตั้งค่า Chrome และดูว่าผู้ใช้ต้องการใช้บริการป้อนข้อความอัตโนมัติของบุคคลที่สามหรือไม่
- Deep Link ไปยังหน้าการตั้งค่า Chrome ที่ผู้ใช้สามารถเปิดใช้บริการป้อนข้อความอัตโนมัติของบุคคลที่สามได้
อ่านการตั้งค่า Chrome
แอปใดก็ได้อ่านได้ว่า Chrome ใช้โหมดป้อนข้อความอัตโนมัติของบุคคลที่สามที่อนุญาตให้ใช้
การป้อนข้อความอัตโนมัติของ Android หรือไม่ Chrome ใช้ ContentProvider
ของ Android เพื่อสื่อสารข้อมูลดังกล่าว
ประกาศในไฟล์ Manifest ของ Android ว่าคุณต้องการอ่านการตั้งค่าจากช่องใด
<uses-permission android:name="android.permission.READ_USER_DICTIONARY"/>
<queries>
<!-- To Query Chrome Beta: -->
<package android:name="com.chrome.beta" />
<!-- To Query Chrome Stable: -->
<package android:name="com.android.chrome" />
</queries>
จากนั้นใช้ ContentResolver
ของ Android เพื่อขอข้อมูลดังกล่าวโดยสร้าง URI เนื้อหา ดังนี้
Kotlin
val CHROME_CHANNEL_PACKAGE = "com.android.chrome" // Chrome Stable. val CONTENT_PROVIDER_NAME = ".AutofillThirdPartyModeContentProvider" val THIRD_PARTY_MODE_COLUMN = "autofill_third_party_state" val THIRD_PARTY_MODE_ACTIONS_URI_PATH = "autofill_third_party_mode" val uri = Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(CHROME_CHANNEL_PACKAGE + CONTENT_PROVIDER_NAME) .path(THIRD_PARTY_MODE_ACTIONS_URI_PATH) .build() val cursor = contentResolver.query( uri, arrayOf(THIRD_PARTY_MODE_COLUMN), // projection null, // selection null, // selectionArgs null // sortOrder ) if (cursor == null) { // Terminate now! Older versions of Chromium don't provide this information. } cursor?.use { // Use the safe call operator and the use function for auto-closing if (it.moveToFirst()) { // Check if the cursor has any rows val index = it.getColumnIndex(THIRD_PARTY_MODE_COLUMN) if (index != -1) { // Check if the column exists val value = it.getInt(index) if (0 == value) { // 0 means that the third party mode is turned off. Chrome uses its built-in // password manager. This is the default for new users. } else { // 1 means that the third party mode is turned on. Chrome uses forwards all // autofill requests to Android Autofill. Users have to opt-in for this. } } else { // Handle the case where the column doesn't exist. Log a warning, perhaps. Log.w("Autofill", "Column $THIRD_PARTY_MODE_COLUMN not found in cursor") } } } // The cursor is automatically closed here
Java
final String CHROME_CHANNEL_PACKAGE = "com.android.chrome"; // Chrome Stable. final String CONTENT_PROVIDER_NAME = ".AutofillThirdPartyModeContentProvider"; final String THIRD_PARTY_MODE_COLUMN = "autofill_third_party_state"; final String THIRD_PARTY_MODE_ACTIONS_URI_PATH = "autofill_third_party_mode"; final Uri uri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(CHROME_CHANNEL_PACKAGE + CONTENT_PROVIDER_NAME) .path(THIRD_PARTY_MODE_ACTIONS_URI_PATH) .build(); final Cursor cursor = getContentResolver().query( uri, /*projection=*/new String[] {THIRD_PARTY_MODE_COLUMN}, /*selection=*/ null, /*selectionArgs=*/ null, /*sortOrder=*/ null); if (cursor == null) { // Terminate now! Older versions of Chromium don't provide this information. } cursor.moveToFirst(); // Retrieve the result; int index = cursor.getColumnIndex(THIRD_PARTY_MODE_COLUMN); if (0 == cursor.getInt(index)) { // 0 means that the third party mode is turned off. Chrome uses its built-in // password manager. This is the default for new users. } else { // 1 means that the third party mode is turned on. Chrome uses forwards all // autofill requests to Android Autofill. Users have to opt-in for this. }
Deep Link ไปยังการตั้งค่า Chrome
หากต้องการ Deep Link ไปยังหน้าการตั้งค่า Chrome ที่ผู้ใช้สามารถเปิดใช้บริการป้อนข้อความอัตโนมัติของบุคคลที่สามได้ ให้ใช้ Intent
ของ Android
อย่าลืมกำหนดค่าการดำเนินการและหมวดหมู่ตามที่แสดงในตัวอย่างนี้
Kotlin
val autofillSettingsIntent = Intent(Intent.ACTION_APPLICATION_PREFERENCES) autofillSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT) autofillSettingsIntent.addCategory(Intent.CATEGORY_APP_BROWSER) autofillSettingsIntent.addCategory(Intent.CATEGORY_PREFERENCE) // Invoking the intent with a chooser allows users to select the channel they // want to configure. If only one browser reacts to the intent, the chooser is // skipped. val chooser = Intent.createChooser(autofillSettingsIntent, "Pick Chrome Channel") startActivity(chooser) // If the caller knows which Chrome channel they want to configure, // they can instead add a package hint to the intent, e.g. val specificChromeIntent = Intent(Intent.ACTION_APPLICATION_PREFERENCES) // Create a *new* intent specificChromeIntent.addCategory(Intent.CATEGORY_DEFAULT) specificChromeIntent.addCategory(Intent.CATEGORY_APP_BROWSER) specificChromeIntent.addCategory(Intent.CATEGORY_PREFERENCE) specificChromeIntent.setPackage("com.android.chrome") // Set the package on the *new* intent startActivity(specificChromeIntent) // Start the *new* intent
Java
Intent autofillSettingsIntent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES); autofillSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT); autofillSettingsIntent.addCategory(Intent.CATEGORY_APP_BROWSER); autofillSettingsIntent.addCategory(Intent.CATEGORY_PREFERENCE); // Invoking the intent with a chooser allows users to select the channel they // want to configure. If only one browser reacts to the intent, the chooser is // skipped. Intent chooser = Intent.createChooser(autofillSettingsIntent, "Pick Chrome Channel"); startActivity(chooser); // If the caller knows which Chrome channel they want to configure, // they can instead add a package hint to the intent, e.g. autofillSettingsIntent.setPackage("com.android.chrome"); startActivity(autofillSettingsInstent);
สถานการณ์การป้อนข้อความอัตโนมัติขั้นสูง
- ผสานรวมกับแป้นพิมพ์
- ตั้งแต่ 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_SAVE_ON_ALL_VIEWS_INVISIBLE
หรือเรียกใช้เมธอดsetTrigger()
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
จะตั้งค่าโดยค่าเริ่มต้นเมื่อ ใช้โหมดความเข้ากันได้ - ค่าข้อความของโหนดอาจไม่พร้อมใช้งานในเมธอด
onSaveRequest(SaveRequest, SaveCallback)
ดูข้อมูลเพิ่มเติมเกี่ยวกับโหมดความเข้ากันได้ รวมถึงข้อจำกัด
ที่เกี่ยวข้องได้ที่
AutofillService
การอ้างอิงคลาส