Hướng dẫn làm từ điển android đơn giản năm 2024

Dịch vụ tự động điền (autofill service) là một ứng dụng giúp người dùng dễ dàng điền vào các biểu mẫu bằng cách chèn dữ liệu vào thành phần hiển thị (view) của ứng dụng khác. Dịch vụ tự động điền cũng có thể truy xuất dữ liệu người dùng trên thành phần hiển thị trong ứng dụng và lưu trữ để sử dụng sau này. Dịch vụ tự động điền thường do các ứng dụng quản lý dữ liệu người dùng (chẳng hạn như trình quản lý mật khẩu) cung cấp.

Android giúp việc điền biểu mẫu trở nên dễ dàng hơn nhờ khung tự động điền có trong Android 8.0 (API cấp 26) trở lên. Người dùng chỉ có thể tận dụng các tính năng tự động điền nếu có ứng dụng cung cấp dịch vụ tự động điền trên thiết bị.

Trang này cho biết cách triển khai dịch vụ tự động điền trong ứng dụng. Nếu bạn đang tìm mã mẫu cho thấy cách triển khai một dịch vụ, hãy xem mẫu AutofillFramework trong Java hoặc Kotlin. Để biết thêm thông tin về cách thức hoạt động của dịch vụ tự động điền, hãy xem các trang tham khảo dành cho các lớp

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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)

2 và

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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)

3.

Quyền và nội dung khai báo trong tệp kê khai

Ứng dụng cung cấp dịch vụ tự động điền phải đưa ra nội dung khai báo mô tả cách triển khai dịch vụ. Để chỉ định nội dung khai báo, hãy đưa phần tử

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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)

4 vào tệp kê khai ứng dụng. Phần tử

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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)

4 phải bao gồm các thuộc tính và phần tử sau đây:

  • Thuộc tính trỏ đến lớp con của override fun onFillRequest(
    request: FillRequest,  
    cancellationSignal: CancellationSignal,  
    callback: FillCallback  
    
    ) {
    // Get the structure from the request  
    val context: List = 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) 2 trong ứng dụng triển khai dịch vụ.
  • Thuộc tính khai báo quyền .
  • Phần tử @Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    // Get the structure from the request  
    List 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;  
    
    } 0 có phần tử con @Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    // Get the structure from the request  
    List 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 bắt buộc chỉ định thao tác @Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    // Get the structure from the request  
    List 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;  
    
    } 2.
  • Phần tử @Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    // Get the structure from the request  
    List 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;  
    
    } 3 (không bắt buộc) mà bạn có thể sử dụng để cung cấp thêm tham số cấu hình cho dịch vụ.

Ví dụ sau đây cho thấy một đoạn mã khai báo dịch vụ tự động điền:


    
        
    
    

Phần tử

@Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {

// Get the structure from the request
List 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;
}

3 bao gồm cả một thuộc tính trỏ đến một tài nguyên XML có thông tin chi tiết khác về dịch vụ. Tài nguyên

@Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {

// Get the structure from the request
List 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;
}

6 trong ví dụ trước chỉ định một hoạt động cho phép người dùng định cấu hình dịch vụ. Ví dụ sau đây cho thấy tài nguyên XML

@Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {

// Get the structure from the request
List 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;
}

6:


Để biết thêm thông tin về tài nguyên XML, hãy xem phần Tổng quan về tài nguyên ứng dụng.

Lời nhắc bật dịch vụ

Một ứng dụng được dùng làm dịch vụ tự động điền sau khi khai báo quyền

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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)

9 và người dùng bật ứng dụng đó trong phần cài đặt thiết bị. Một ứng dụng có thể xác minh xem đó có phải là dịch vụ đang bật hay không bằng cách gọi phương thức của lớp

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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)

3.

Nếu không phải là dịch vụ tự động điền hiện tại thì ứng dụng có thể yêu cầu người dùng thay đổi chế độ cài đặt tự động điền bằng cách sử dụng ý định . Ý định sẽ trả về giá trị nếu người dùng chọn dịch vụ tự động điền khớp với gói của phương thức gọi.

Điền vào thành phần hiển thị ứng dụng khách

Dịch vụ tự động điền nhận các yêu cầu điền vào thành phần hiển thị ứng dụng khi người dùng tương tác với ứng dụng khác. Nếu có dữ liệu người dùng đáp ứng yêu cầu thì dịch vụ tự động điền sẽ gửi dữ liệu đó trong phản hồi. Hệ thống Android cho thấy một giao diện người dùng tự động điền kèm theo dữ liệu có sẵn, như trong hình 1:

Hướng dẫn làm từ điển android đơn giản năm 2024

Hình 1. Giao diện người dùng tự động điền cho thấy một tập dữ liệu.

Khung tự động điền xác định một quy trình để điền thông tin vào thành phần hiển thị nhằm giảm thiểu thời gian hệ thống Android liên kết với dịch vụ tự động điền. Trong mỗi yêu cầu, hệ thống Android gửi một đối tượng

// 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()
3 đến dịch vụ bằng cách gọi phương thức .

Dịch vụ tự động điền sẽ kiểm tra xem có thể đáp ứng yêu cầu bằng dữ liệu người dùng mà dịch vụ này từng lưu trữ hay không. Nếu có thể đáp ứng yêu cầu thì dịch vụ sẽ gói dữ liệu trong các đối tượng

// 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()
5. Dịch vụ gọi phương thức , truyền đối tượng

// 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()
7 có chứa đối tượng

// 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()
5. Nếu không có dữ liệu để đáp ứng yêu cầu thì dịch vụ sẽ truyền

// 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()
9 đến phương thức

// 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()
6.

Nếu có lỗi khi xử lý yêu cầu thì dịch vụ sẽ gọi phương thức . Để nắm được nội dung giải thích chi tiết về quy trình này, hãy xem phần .

Đoạn mã sau đây cho thấy ví dụ về phương thức

// 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()
4:

Kotlin

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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 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;
}

Một dịch vụ có thể có nhiều tập dữ liệu đáp ứng yêu cầu. Trong trường hợp này, hệ thống Android sẽ hiển thị nhiều lựa chọn (mỗi tập dữ liệu là một lựa chọn) trong giao diện người dùng tự động điền. Đoạn mã ví dụ sau đây cho thấy cách cung cấp nhiều tập dữ liệu trong một phản hồi:

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();
Dịch vụ tự động điền có thể di chuyển các đối tượng

// 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();
4 trong

// 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()
3 nhằm truy xuất dữ liệu tự động điền cần thiết để thực hiện yêu cầu. Dịch vụ có thể truy xuất dữ liệu tự động điền bằng các phương thức của lớp

// 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();
4, chẳng hạn như .

Dịch vụ phải mô tả được nội dung của thành phần hiển thị (view) để kiểm tra xem nội dung đó có đáp ứng được yêu cầu hay không. Thuộc tính là phương pháp đầu tiên mà dịch vụ nên sử dụng để mô tả nội dung của thành phần hiển thị. Tuy nhiên, ứng dụng khách phải cung cấp rõ ràng thuộc tính đó trong thành phần hiển thị trước khi thuộc tính đó được sử dụng trong dịch vụ.

Nếu ứng dụng khách không cung cấp thuộc tính

// 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();
8 thì dịch vụ phải sử dụng thông tin tự phỏng đoán để mô tả nội dung. Dịch vụ có thể sử dụng phương thức của các lớp khác, chẳng hạn như hoặc , để nhận thông tin về nội dung của thành phần hiển thị. Để biết thêm thông tin, hãy xem phần .

Ví dụ sau đây cho thấy cách truyền tải

// 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()
3 và truy xuất dữ liệu tự động điền qua đối tượng

// 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();
4:

Kotlin

fun traverseStructure(structure: AssistStructure) {

val windowNodes: List =
        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?.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);
}
}

Lưu dữ liệu người dùng

Dịch vụ tự động điền cần dữ liệu người dùng để điền vào thành phần hiển thị trong ứng dụng. Khi người dùng điền vào thành phần hiển thị theo cách thủ công, họ sẽ được nhắc lưu dữ liệu vào dịch vụ tự động điền hiện tại, như minh hoạ trong hình 2.

Hướng dẫn làm từ điển android đơn giản năm 2024

Hình 2. Giao diện người dùng lưu thông tin tự động điền.

Để lưu dữ liệu, dịch vụ phải cho biết ý định lưu trữ dữ liệu để sử dụng sau này. Trước khi gửi yêu cầu lưu dữ liệu, hệ thống Android sẽ yêu cầu dịch vụ điền thông tin vào các thành phần hiển thị. Để cho biết ý định lưu dữ liệu, dịch vụ đưa vào một đối tượng

fun traverseStructure(structure: AssistStructure) {

val windowNodes: List =
        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?.run {
            (0 until childCount).map { getChildAt(it) }
        }
children?.forEach { childNode: ViewNode ->
    traverseNode(childNode)
}
}

4 trong phản hồi cho yêu cầu điền. Đối tượng

fun traverseStructure(structure: AssistStructure) {

val windowNodes: List =
        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?.run {
            (0 until childCount).map { getChildAt(it) }
        }
children?.forEach { childNode: ViewNode ->
    traverseNode(childNode)
}
}

4 ít nhất phải chứa dữ liệu sau:

  • Loại dữ liệu người dùng được lưu. Để biết danh sách giá trị fun traverseStructure(structure: AssistStructure) {
    val windowNodes: List =  
            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?.run {  
                (0 until childCount).map { getChildAt(it) }  
            }  
    children?.forEach { childNode: ViewNode ->  
        traverseNode(childNode)  
    }  
    
    } 6 hiện có, hãy xem fun traverseStructure(structure: AssistStructure) {
    val windowNodes: List =  
            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?.run {  
                (0 until childCount).map { getChildAt(it) }  
            }  
    children?.forEach { childNode: ViewNode ->  
        traverseNode(childNode)  
    }  
    
    } 4.
  • Tập hợp thành phần hiển thị tối thiểu cần được thay đổi để kích hoạt yêu cầu lưu. Ví dụ: biểu mẫu đăng nhập thường yêu cầu người dùng cập nhật các thành phần hiển thị fun traverseStructure(structure: AssistStructure) {
    val windowNodes: List =  
            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?.run {  
                (0 until childCount).map { getChildAt(it) }  
            }  
    children?.forEach { childNode: ViewNode ->  
        traverseNode(childNode)  
    }  
    
    } 8 và fun traverseStructure(structure: AssistStructure) {
    val windowNodes: List =  
            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?.run {  
                (0 until childCount).map { getChildAt(it) }  
            }  
    children?.forEach { childNode: ViewNode ->  
        traverseNode(childNode)  
    }  
    
    } 9 để kích hoạt yêu cầu lưu.

Đối tượng

fun traverseStructure(structure: AssistStructure) {

val windowNodes: List =
        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?.run {
            (0 until childCount).map { getChildAt(it) }
        }
children?.forEach { childNode: ViewNode ->
    traverseNode(childNode)
}
}

4 được liên kết với đối tượng

// 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()
7, như trong mã ví dụ sau đây:

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();
...
}

Dịch vụ tự động điền có thể triển khai logic để duy trì dữ liệu người dùng trong phương thức . Phương thức này thường được gọi sau khi hoạt động ứng dụng kết thúc hoặc khi ứng dụng khách gọi . Đoạn mã sau đây cho thấy ví dụ về phương thức

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:

Kotlin


0

Java


1

Dịch vụ tự động điền phải mã hoá dữ liệu nhạy cảm trước khi duy trì sử dụng. Tuy nhiên, dữ liệu người dùng có thể bao gồm cả nhãn hoặc dữ liệu không nhạy cảm. Ví dụ: tài khoản người dùng có thể bao gồm cả nhãn đánh dấu dữ liệu là tài khoản công việc hoặc tài khoản cá nhân. Dịch vụ không được mã hoá nhãn. Bằng cách không mã hoá nhãn, dịch vụ có thể sử dụng nhãn trong thành phần hiển thị bản trình bày nếu người dùng chưa xác thực. Tiếp đó, các dịch vụ có thể thay thế nhãn bằng dữ liệu thực tế sau khi người dùng xác thực.

Trì hoãn giao diện người dùng lưu thông tin tự động điền

Kể từ Android 10, nếu sử dụng nhiều màn hình để triển khai một quy trình tự động điền (ví dụ: một màn hình cho trường tên người dùng và một màn hình khác cho mật khẩu), thì bạn có thể trì hoãn giao diện người dùng lưu thông tin tự động điền bằng cách sử dụng cờ .

Nếu bạn thiết lập cờ này thì giao diện người dùng lưu thông tin tự động điền sẽ không được kích hoạt khi ngữ cảnh tự động điền liên kết với phản hồi

fun traverseStructure(structure: AssistStructure) {

val windowNodes: List =
        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?.run {
            (0 until childCount).map { getChildAt(it) }
        }
children?.forEach { childNode: ViewNode ->
    traverseNode(childNode)
}
}

4 được cam kết. Thay vào đó, bạn có thể sử dụng một hoạt động riêng biệt trong cùng một nhiệm vụ nhằm thực hiện các yêu cầu điền sau này, rồi hiển thị giao diện người dùng lưu thông tin tự động điền qua yêu cầu lưu. Để biết thêm thông tin, hãy xem .

Yêu cầu xác thực người dùng

Dịch vụ tự động điền có thể có thêm một lớp bảo mật bằng cách yêu cầu người dùng xác thực trước khi có thể điền vào các thành phần hiển thị. Sau đây là một số tình huống nên triển khai phương thức xác thực người dùng:

  • Dữ liệu người dùng trong ứng dụng cần được mở khoá bằng mật khẩu chính hoặc quét vân tay.
  • Một tập dữ liệu cụ thể cần được mở khoá (chẳng hạn như thông tin thẻ tín dụng) bằng cách sử dụng mã xác minh thẻ (CVC).

Trong trường hợp dịch vụ yêu cầu xác thực người dùng trước khi mở khoá dữ liệu, dịch vụ có thể trình bày dữ liệu nguyên mẫu (boilerplate) hoặc nhãn và chỉ định

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);
}
}

8 để xử lý hoạt động xác thực. Nếu cần thêm dữ liệu để xử lý yêu cầu sau khi quy trình xác thực hoàn tất thì bạn có thể thêm dữ liệu như vậy vào ý định. Sau đó, hoạt động xác thực của bạn có thể trả về dữ liệu cho lớp

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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)

2 trong ứng dụng.

Ví dụ về mã sau đây cho thấy cách chỉ định rằng yêu cầu cần được xác thực:

Kotlin


2

Java


3

Khi hoàn tất quy trình xác thực, hoạt động phải gọi phương thức , truyền một giá trị

// 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()
2 và đặt bổ sung vào đối tượng

// 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()
7 chứa tập dữ liệu điền sẵn. Mã sau đây là ví dụ về cách trả về kết quả sau khi quy trình xác thực hoàn tất:

Kotlin


4

Java


5

Trong trường hợp cần mở khoá một tập dữ liệu thẻ tín dụng, dịch vụ có thể cho thấy giao diện người dùng yêu cầu cấp thông tin CVC. Bạn có thể ẩn dữ liệu cho đến khi tập dữ liệu được mở khoá bằng cách cho thấy dữ liệu tạo sẵn, chẳng hạn như tên ngân hàng và bốn số cuối của số thẻ tín dụng. Ví dụ sau đây cho thấy cách yêu cầu xác thực một tập dữ liệu và ẩn dữ liệu cho đến khi người dùng cung cấp CVC:

Kotlin


6

Java


7

Sau khi xác thực CVC, hoạt động sẽ gọi phương thức

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()
...
}

0 truyền một giá trị

// 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()
2 và đặt

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()
...
}

2 bổ sung vào một đối tượng

// 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()
5 chứa số thẻ tín dụng và ngày hết hạn. Tập dữ liệu mới thay thế tập dữ liệu yêu cầu xác thực, đồng thời các thành phần hiển thị được điền vào ngay lập tức. Mã sau đây cho thấy ví dụ về cách trả về tập dữ liệu sau khi người dùng cung cấp CVC:

Kotlin


8

Java


9

Sắp xếp dữ liệu theo nhóm logic

Dịch vụ tự động điền phải sắp xếp dữ liệu theo nhóm logic để tách biệt các khái niệm với nhiều miền. Trên trang này, các nhóm logic như vậy được gọi là phân vùng. Danh sách sau đây cho thấy các ví dụ điển hình về phân vùng và trường:

  • Thông tin xác thực, bao gồm cả trường tên người dùng và mật khẩu.
  • Địa chỉ, bao gồm cả trường đường, thành phố, tiểu bang và mã bưu chính.
  • Thông tin thanh toán, bao gồm cả trường số thẻ tín dụng, ngày hết hạn và mã xác minh.

Dịch vụ tự động điền phân vùng dữ liệu chính xác có thể tăng khả năng bảo vệ dữ liệu của người dùng bằng cách không để lộ dữ liệu trên nhiều phân vùng trong một tập dữ liệu. Ví dụ: tập dữ liệu chứa thông tin xác thực không nhất thiết phải chứa thông tin thanh toán. Việc sắp xếp dữ liệu trong các phân vùng cho phép dịch vụ đưa ra lượng thông tin tối thiểu có liên quan và cần thiết để đáp ứng yêu cầu.

Việc sắp xếp dữ liệu trong các phân vùng cho phép dịch vụ điền dữ liệu vào các hoạt động có thành phần hiển thị trên nhiều phân vùng trong khi gửi lượng dữ liệu tối thiểu có liên quan đến ứng dụng khách. Ví dụ: hãy xem xét một hoạt động bao gồm các thành phần hiển thị cho tên người dùng, mật khẩu, đường phố và thành phố, cũng như một dịch vụ tự động điền có những dữ liệu sau:

Phân vùng Trường 1 Trường 2 Thông tin xác thực work_username work_password personal_username personal_password Địa chỉ work_street work_city personal_street personal_city

Dịch vụ có thể chuẩn bị một tập dữ liệu bao gồm cả phân vùng thông tin xác thực cho cả tài khoản công việc và tài khoản cá nhân. Khi người dùng chọn một tập dữ liệu, phản hồi tự động điền tiếp theo có thể cung cấp địa chỉ cơ quan hoặc cá nhân, tuỳ thuộc vào lựa chọn đầu tiên của người dùng.

Dịch vụ có thể xác định trường đã đưa ra yêu cầu bằng cách gọi phương thức trong khi truyền tải đối tượng

// 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()
3. Điều này cho phép dịch vụ chuẩn bị một

// 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()
7 có dữ liệu phân vùng thích hợp.

Tự động điền mã một lần qua SMS

Dịch vụ tự động điền của bạn có thể hỗ trợ người dùng điền mã một lần gửi qua SMS bằng cách sử dụng SMS Retriever API.

Để sử dụng tính năng này, bạn phải đáp ứng các yêu cầu sau đây:

  • Dịch vụ tự động điền đang chạy trên Android 9 (API cấp 28) trở lên
  • Người dùng đồng ý cho phép dịch vụ tự động điền của bạn đọc mã một lần qua SMS.
  • Ứng dụng mà bạn cung cấp dịch vụ tự động điền chưa sử dụng SMS Retriever API để đọc mã một lần.

Dịch vụ tự động điền có thể sử dụng

@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();
...
}

1 hiện có bằng cách gọi

@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();
...
}

2 trên Dịch vụ Google Play 19.0.56 trở lên.

Sau đây là các bước chính để sử dụng API này trong dịch vụ tự động điền:

  1. Trong dịch vụ tự động điền, hãy sử dụng trên @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();  
    ...  
    
    } 1 để xác định xem có yêu cầu nào đang áp dụng cho tên gói của ứng dụng mà bạn tự động điền hay không. Dịch vụ tự động điền chỉ đưa ra lời nhắc đề xuất nếu giá trị trả về là @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();  
    ...  
    
    } 5.
  2. Trong dịch vụ tự động điền, hãy sử dụng trên

    @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();  
    ...  
    
    } 1 để kiểm tra xem dịch vụ tự động điền có quyền tự động điền mã một lần hay không. Trạng thái của quyền này có thể là @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();  
    ...  
    
    } 8, @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();  
    ...  
    
    } 9 hoặc

    00. Dịch vụ Tự động điền phải cho thấy lời nhắc đề xuất đối với các trạng thái @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();  
    ...  
    
    } 8 và @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();  
    ...  
    
    } 9.
  3. Trong hoạt động xác thực tự động điền, hãy sử dụng quyền

    03 để đăng ký tính năng nghe

    04 cho

    05 và nhận kết quả mã SMS khi có.
  4. Gọi trên

    @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();  
    ...  
    
    } 1 để bắt đầu nghe mã một lần được gửi qua SMS. Nếu người dùng cấp quyền cho dịch vụ tự động điền của bạn để truy xuất mã một lần qua SMS, thì hệ thống sẽ tìm các tin nhắn SMS nhận được trong khoảng thời gian từ 1 đến 5 phút vừa xong. Nếu dịch vụ tự động điền của bạn cần yêu cầu người dùng cho phép để đọc mã một lần, thì

    08 do

    06 trả về có thể không thành công và kèm theo một

    10 được trả về. Nếu tình trạng này xảy ra thì bạn phải gọi phương thức

    11 để cho thấy hộp thoại đồng ý đối với yêu cầu cấp quyền.
  5. Nhận kết quả mã SMS của ý định rồi trả về mã SMS để dùng làm phản hồi tự động điền.

Các trường hợp tự động điền nâng cao

Tích hợp với bàn phím Kể từ Android 11, nền tảng này cho phép bàn phím và các trình chỉnh sửa phương thức nhập khác (IME) đưa ra đề xuất tự động điền cùng dòng thay vì sử dụng trình đơn kéo xuống. Để biết thêm thông tin về cách dịch vụ tự động điền có thể hỗ trợ chức năng này, hãy xem phần Tích hợp tính năng tự động điền cho bàn phím. Phân trang tập dữ liệu Một phản hồi lớn trong phần tự động điền có thể vượt quá kích thước giao dịch được phép của đối tượng


12 đại diện cho đối tượng có thể điều khiển từ xa cần thiết để xử lý yêu cầu. Để ngăn hệ thống Android loại bỏ một trường hợp ngoại lệ trong những trường hợp như vậy, bạn có thể giữ kích thước

// 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()
7 bằng cách thêm tối đa 20 đối tượng

// 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()
5 cùng lúc. Nếu câu trả lời của bạn cần thêm tập dữ liệu, thì bạn có thể thêm tập dữ liệu để người dùng biết rằng có thông tin khác và truy xuất nhóm tập dữ liệu tiếp theo khi được chọn. Để biết thêm thông tin, hãy xem . Lưu dữ liệu được chia tách trên nhiều màn hình

Thường thì ứng dụng sẽ chia tách dữ liệu người dùng thành nhiều màn hình trong cùng một hoạt động, đặc biệt là trong các hoạt động dùng để tạo tài khoản người dùng mới. Ví dụ: màn hình đầu tiên yêu cầu đưa ra tên người dùng và nếu tên người dùng có sẵn thì màn hình thứ hai sẽ yêu cầu đưa ra mật khẩu. Trong những trường hợp như vậy, dịch vụ tự động điền phải đợi cho đến khi người dùng nhập cả hai trường trước khi cho thấy giao diện người dùng lưu thông tin tự động điền. Hãy làm theo các bước sau đây để xử lý những trường hợp như vậy:

  1. Trong đầu tiên, hãy thêm (client state bundle) trong phản hồi có chứa mã nhận dạng tự động điền của các trường xuất hiện một phần trên màn hình.
  2. Trong yêu cầu điền thứ hai, hãy truy xuất gói trạng thái ứng dụng, lấy mã nhận dạng tự động điền đã đặt trong yêu cầu trước đó của trạng thái ứng dụng rồi thêm các mã nhận dạng này để gắn cờ đối tượng fun traverseStructure(structure: AssistStructure) {
    val windowNodes: List =  
            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?.run {  
                (0 until childCount).map { getChildAt(it) }  
            }  
    children?.forEach { childNode: ViewNode ->  
        traverseNode(childNode)  
    }  
    
    } 4 dùng trong phản hồi thứ hai.
  3. Trong , hãy sử dụng các đối tượng

    18 phù hợp để nhận giá trị của từng trường. Mỗi yêu cầu điền có một ngữ cảnh điền.

Để biết thêm thông tin, hãy xem phần .

Cung cấp logic khởi động và chia nhỏ cho mỗi yêu cầu

Mỗi khi có yêu cầu tự động điền, hệ thống Android sẽ liên kết với dịch vụ và gọi phương thức . Sau khi dịch vụ xử lý yêu cầu này, hệ thống Android sẽ gọi phương thức và huỷ liên kết với dịch vụ. Bạn có thể triển khai


19 để cung cấp mã chạy trước khi xử lý yêu cầu và


20 để cung cấp mã chạy sau khi xử lý yêu cầu.

Tuỳ chỉnh giao diện người dùng lưu thông tin tự động điền

Dịch vụ tự động điền có thể tuỳ chỉnh giao diện người dùng lưu thông tin tự động điền để giúp người dùng quyết định xem họ có muốn cho phép dịch vụ lưu dữ liệu của họ hay không. Dịch vụ có thể cung cấp thêm thông tin về nội dung được lưu thông qua một văn bản đơn giản hoặc một thành phần hiển thị tuỳ chỉnh. Dịch vụ cũng có thể thay đổi giao diện của nút huỷ yêu cầu lưu và nhận thông báo khi người dùng nhấn vào nút đó. Để biết thêm thông tin, hãy xem trang tham khảo về

fun traverseStructure(structure: AssistStructure) {

val windowNodes: List =
        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?.run {
            (0 until childCount).map { getChildAt(it) }
        }
children?.forEach { childNode: ViewNode ->
    traverseNode(childNode)
}
}

4.

Chế độ tương thích

Chế độ tương thích cho phép dịch vụ tự động điền sử dụng (accessibility virtual structure) cho mục đích tự động điền. Điều này đặc biệt hữu ích khi cung cấp chức năng tự động điền trong các trình duyệt chưa triển khai rõ ràng API tự động điền.

Để kiểm tra dịch vụ tự động điền bằng chế độ tương thích, hãy thể hiện rõ việc đưa trình duyệt hoặc ứng dụng yêu cầu chế độ tương thích vào danh sách cho phép. Bạn có thể kiểm tra gói nào đã có trong danh sách cho phép bằng cách chạy lệnh sau:

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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)

0

Nếu gói bạn đang kiểm thử không có trong danh sách, hãy thêm gói đó bằng cách chạy lệnh sau đây, trong đó


24 là gói của ứng dụng:

override fun onFillRequest(

request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
// Get the structure from the request
val context: List = 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)

1

Nếu ứng dụng là trình duyệt, hãy sử dụng


25 để chỉ định mã nhận dạng tài nguyên của trường nhập dữ liệu chứa URL của trang hiển thị.

Chế độ tương thích có một số hạn chế như sau:

  • Yêu cầu lưu được kích hoạt khi dịch vụ sử dụng cờ

    16 hoặc phương thức được gọi.

    16 được đặt theo mặc định khi sử dụng chế độ tương thích.
  • Giá trị văn bản của các nút có thể không có trong phương thức .

Để biết thêm thông tin về chế độ tương thích, bao gồm cả những giới hạn liên quan đến chế độ này, hãy xem tài liệu tham khảo về lớp .