ساختن سرویس‌های تکمیل خودکار

سرویس تکمیل خودکار برنامه‌ای است که با تزریق داده به نماهای برنامه‌های دیگر، تکمیل فرم‌ها را برای کاربران آسان‌تر می‌کند. سرویس‌های «تکمیل خودکار» همچنین می‌توانند داده‌های کاربر را از نماهای درون برنامه بازیابی کنند و آن را برای استفاده در آینده ذخیره کنند. سرویس‌های تکمیل خودکار معمولاً توسط برنامه‌هایی ارائه می‌شوند که داده‌های کاربر را مدیریت می‌کنند، مثل مدیران گذرواژه.

Android با چارچوب تکمیل خودکار که در Android 8.0 (سطح API‏ ۲۶) و بالاتر دردسترس است، پر کردن فرم‌ها را آسان‌تر می‌کند. کاربران فقط درصورتی می‌توانند از ویژگی‌های تکمیل خودکار استفاده کنند که برنامه‌ای برای ارائه خدمات تکمیل خودکار در دستگاهشان وجود داشته باشد.

این صفحه نحوه پیاده‌سازی سرویس تکمیل خودکار را در برنامه‌تان نشان می‌دهد. اگر به‌دنبال نمونه کدی هستید که نحوه پیاده‌سازی سرویس را نشان دهد، نمونه AutofillFramework را در Java یا Kotlin ببینید. برای جزئیات بیشتر درباره نحوه عملکرد سرویس‌های تکمیل خودکار، صفحه‌های مرجع کلاس‌های AutofillService و AutofillManager را ببینید.

اظهارنامه‌ها و اجازه‌های مانیفست

برنامه‌هایی که سرویس‌های تکمیل خودکار ارائه می‌دهند باید اظهارنامه‌ای داشته باشند که پیاده‌سازی سرویس را شرح دهد. برای مشخص کردن اظهارنامه، عنصر <service> را در مانیفست برنامه اضافه کنید. عنصر <service> باید شامل مشخصه‌ها و عناصر زیر باشد:

  • شاخصه android:name که به زیرکلاس AutofillService در برنامه‌ای که سرویس را پیاده‌سازی می‌کند اشاره دارد.
  • مشخصه android:permission که اجازه BIND_AUTOFILL_SERVICE را اعلام می‌کند.
  • عنصر <intent-filter> که فرزند اجباری <action> آن کنش android.service.autofill.AutofillService را مشخص می‌کند.
  • عنصر اختیاری <meta-data> که می‌توانید از آن برای ارائه پارامترهای پیکربندی اضافی برای سرویس استفاده کنید.

مثال زیر اعلامیه سرویس تکمیل خودکار را نشان می‌دهد:

<service
    android:name=".MyAutofillService"
    android:label="My Autofill Service"
    android:permission="android.permission.BIND_AUTOFILL_SERVICE">
    <intent-filter>
        <action android:name="android.service.autofill.AutofillService" />
    </intent-filter>
    <meta-data
        android:name="android.autofill"
        android:resource="@xml/service_configuration" />
</service>

عنصر <meta-data> شامل مشخصه android:resource است که به منبع XML با جزئیات بیشتر درباره سرویس اشاره می‌کند. منبع service_configuration در مثال قبلی فعالیتی را مشخص می‌کند که به کاربران اجازه می‌دهد سرویس را پیکربندی کنند. مثال زیر منبع XML service_configuration را نشان می‌دهد:

<autofill-service
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:settingsActivity="com.example.android.SettingsActivity" />

برای اطلاعات بیشتر درباره منابع XML، به نمای کلی منابع برنامه مراجعه کنید.

درخواست فعال کردن سرویس

برنامه پس‌از اعلام اجازه BIND_AUTOFILL_SERVICE و فعال شدن آن توسط کاربر در تنظیمات دستگاه، به‌عنوان سرویس تکمیل خودکار استفاده می‌شود. برنامه می‌تواند با فراخوانی روش hasEnabledAutofillServices() از کلاس AutofillManager، بررسی کند که آیا سرویس فعال‌شده است یا نه.

اگر برنامه سرویس تکمیل خودکار فعلی نباشد، می‌تواند از کاربر درخواست کند تنظیمات تکمیل خودکار را بااستفاده از هدف ACTION_REQUEST_SET_AUTOFILL_SERVICE تغییر دهد. اگر کاربر سرویس تکمیل خودکاری را انتخاب کند که با بسته تماس‌گیرنده مطابقت داشته باشد، هدف مقدار RESULT_OK را برمی‌گرداند.

پر کردن بازدیدهای کارخواه

وقتی کاربر با برنامه‌های دیگر تعامل می‌کند، سرویس تکمیل خودکار درخواست‌هایی برای تکمیل نماهای کارخواه دریافت می‌کند. اگر سرویس تکمیل خودکار داده‌های کاربری داشته باشد که درخواست را برآورده کند، آن داده‌ها را در پاسخ ارسال می‌کند. سیستم Android میانای کاربر تکمیل خودکار را با داده‌های دردسترس نشان می‌دهد، همان‌طور که در شکل ۱ نشان داده شده است:

منو کرکره‌ای پیشنهاد تکمیل خودکار که مجموعه داده دردسترسی را نشان می‌دهد
شکل ۱. میانای کاربری تکمیل خودکار که مجموعه داده‌ای را نمایش می‌دهد.

چارچوب تکمیل خودکار گردش کاری را برای تکمیل نماها تعریف می‌کند که برای به‌حداقل رساندن زمانی که سیستم Android به سرویس تکمیل خودکار محدود است طراحی شده است. در هر درخواست، سیستم Android با فراخوانی روش onFillRequest()، شیء AssistStructure را به سرویس ارسال می‌کند.

سرویس تکمیل خودکار بررسی می‌کند که آیا می‌تواند درخواست را با داده‌های کاربر که قبلاً ذخیره کرده است برآورده کند یا نه. اگر بتواند درخواست را برآورده کند، سرویس داده‌ها را در اشیای Dataset بسته‌بندی می‌کند. سرویس روش onSuccess() را فراخوانی می‌کند و شیء FillResponse را که حاوی اشیاء Dataset است ارسال می‌کند. اگر سرویس داده‌ای برای برآورده کردن درخواست نداشته باشد، null را به روش onSuccess() ارسال می‌کند. اگر خطایی در پردازش درخواست وجود داشته باشد، سرویس به‌جای آن روش onFailure() را فراخوانی می‌کند. برای توضیح مفصل درباره گردش کار، شرح موجود در AutofillService صفحه مرجع را ببینید.

کد زیر نمونه‌ای از روش onFillRequest() را نشان می‌دهد:

کاتلین

override fun onFillRequest(
    request: FillRequest,
    cancellationSignal: CancellationSignal,
    callback: FillCallback
) {
    // Get the structure from the request
    val context: List<FillContext> = request.fillContexts
    val structure: AssistStructure = context[context.size - 1].structure

    // Traverse the structure looking for nodes to fill out
    val parsedStructure: ParsedStructure = parseStructure(structure)

    // Fetch user data that matches the fields
    val (username: String, password: String) = fetchUserData(parsedStructure)

    // Build the presentation of the datasets
    val usernamePresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1)
    usernamePresentation.setTextViewText(android.R.id.text1, "my_username")
    val passwordPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1)
    passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username")

    // Add a dataset to the response
    val fillResponse: FillResponse = FillResponse.Builder()
            .addDataset(Dataset.Builder()
                    .setValue(
                            parsedStructure.usernameId,
                            AutofillValue.forText(username),
                            usernamePresentation
                    )
                    .setValue(
                            parsedStructure.passwordId,
                            AutofillValue.forText(password),
                            passwordPresentation
                    )
                    .build())
            .build()

    // If there are no errors, call onSuccess() and pass the response
    callback.onSuccess(fillResponse)
}

data class ParsedStructure(var usernameId: AutofillId, var passwordId: AutofillId)

data class UserData(var username: String, var password: String)

جاوا

@Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    // Get the structure from the request
    List<FillContext> context = request.getFillContexts();
    AssistStructure structure = context.get(context.size() - 1).getStructure();

    // Traverse the structure looking for nodes to fill out
    ParsedStructure parsedStructure = parseStructure(structure);

    // Fetch user data that matches the fields
    UserData userData = fetchUserData(parsedStructure);

    // Build the presentation of the datasets
    RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
    usernamePresentation.setTextViewText(android.R.id.text1, "my_username");
    RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
    passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username");

    // Add a dataset to the response
    FillResponse fillResponse = new FillResponse.Builder()
            .addDataset(new Dataset.Builder()
                    .setValue(parsedStructure.usernameId,
                            AutofillValue.forText(userData.username), usernamePresentation)
                    .setValue(parsedStructure.passwordId,
                            AutofillValue.forText(userData.password), passwordPresentation)
                    .build())
            .build();

    // If there are no errors, call onSuccess() and pass the response
    callback.onSuccess(fillResponse);
}

class ParsedStructure {
    AutofillId usernameId;
    AutofillId passwordId;
}

class UserData {
    String username;
    String password;
}

یک سرویس می‌تواند بیش از یک مجموعه داده داشته باشد که درخواست را برآورده می‌کند. در این حالت، سیستم Android چند گزینه (یکی برای هر مجموعه داده) در میانای کاربری تکمیل خودکار نشان می‌دهد. نمونه کد زیر نشان می‌دهد که چگونه چند مجموعه داده را در یک پاسخ ارائه دهید:

کاتلین

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

جاوا

// Add multiple datasets to the response
FillResponse fillResponse = new FillResponse.Builder()
        .addDataset(new Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(user1Data.username), username1Presentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(user1Data.password), password1Presentation)
                .build())
        .addDataset(new Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(user2Data.username), username2Presentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(user2Data.password), password2Presentation)
                .build())
        .build();

سرویس‌های تکمیل خودکار می‌توانند در اشیاء ViewNode در AssistStructure پیمایش کنند تا داده‌های تکمیل خودکار موردنیاز برای برآورده کردن درخواست را بازیابی کنند. سرویس می‌تواند داده‌های تکمیل خودکار را بااستفاده از روش‌های کلاس ViewNode بازیابی کند، مانند getAutofillId().

سرویس باید بتواند محتوای نما را شرح دهد تا بررسی کند که آیا می‌تواند درخواست را برآورده کند یا نه. استفاده از مشخصه autofillHints اولین رویکردی است که سرویس باید برای توصیف محتوای نما استفاده کند. بااین‌حال، برنامه‌های کارخواه باید صریحاً این مشخصه را در نماهایشان ارائه دهند تا برای سرویس دردسترس قرار گیرد.

اگر برنامه مشتری ویژگی autofillHints را ارائه نکند، سرویس باید از اکتشافات خود برای توصیف محتوا استفاده کند. این سرویس می‌تواند از روش‌های کلاس‌های دیگر، مثل getText() یا getHint()، برای دریافت اطلاعات درباره محتوای نما استفاده کند. برای اطلاعات بیشتر، به ارائه نکته برای تکمیل خودکار مراجعه کنید.

مثال زیر نشان می‌دهد که چگونه می‌توان AssistStructure را پیمایش کرد و داده‌های تکمیل خودکار را از شیء ViewNode بازیابی کرد:

کاتلین

fun traverseStructure(structure: AssistStructure) {
    val windowNodes: List<AssistStructure.WindowNode> =
            structure.run {
                (0 until windowNodeCount).map { getWindowNodeAt(it) }
            }

    windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
        val viewNode: ViewNode? = windowNode.rootViewNode
        traverseNode(viewNode)
    }
}

fun traverseNode(viewNode: ViewNode?) {
    if (viewNode?.autofillHints?.isNotEmpty() == true) {
        // If the client app provides autofill hints, you can obtain them using
        // viewNode.getAutofillHints();
    } else {
        // Or use your own heuristics to describe the contents of a view
        // using methods such as getText() or getHint()
    }

    val children: List<ViewNode>? =
            viewNode?.run {
                (0 until childCount).map { getChildAt(it) }
            }

    children?.forEach { childNode: ViewNode ->
        traverseNode(childNode)
    }
}

جاوا

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

ذخیره کردن داده‌های کاربر

سرویس تکمیل خودکار برای پر کردن نماها در برنامه‌ها به داده‌های کاربر نیاز دارد. وقتی کاربران نمایی را به‌طور دستی تکمیل می‌کنند، از آن‌ها خواسته می‌شود داده‌ها را در سرویس تکمیل خودکار فعلی ذخیره کنند، همان‌طور که در شکل ۲ نشان داده شده است.

کادر گفتگوی سیستم که از کاربر درخواست می‌کند داده‌های تکمیل خودکار را در سرویس ذخیره کند
شکل ۲. واسط کاربر ذخیره تکمیل خودکار.

برای ذخیره کردن داده‌ها، سرویس باید نشان دهد که مایل است داده‌ها را برای استفاده در آینده ذخیره کند. قبل‌از اینکه سیستم Android درخواستی برای ذخیره داده‌ها ارسال کند، درخواست تکمیلی وجود دارد که در آن سرویس فرصت دارد نماها را تکمیل کند. برای نشان دادن اینکه سرویس به ذخیره کردن داده‌ها تمایل دارد، شیء SaveInfo را در پاسخ به درخواست تکمیل اضافه می‌کند. شیء SaveInfo حداقل حاوی داده‌های زیر است:

  • نوع داده‌های کاربر که ذخیره می‌شود. برای مشاهده فهرست مقادیر SAVE_DATA دردسترس، SaveInfo را ببینید.
  • حداقل تعداد نماهایی که باید تغییر کند تا درخواست ذخیره راه‌اندازی شود. برای مثال، فرم ورود به سیستم معمولاً از کاربر می‌خواهد نماهای username و password را به‌روز کند تا درخواست ذخیره را راه‌اندازی کند.

یک شیء SaveInfo با یک شیء FillResponse مرتبط است، همان‌طور که در نمونه کد زیر نشان داده شده است:

کاتلین

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

جاوا

@Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    // ...
    // Builder object requires a non-null presentation
    RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);

    FillResponse fillResponse = new FillResponse.Builder()
            .addDataset(new Dataset.Builder()
                    .setValue(parsedStructure.usernameId, null, notUsed)
                    .setValue(parsedStructure.passwordId, null, notUsed)
                    .build())
            .setSaveInfo(new SaveInfo.Builder(
                    SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD,
                    new AutofillId[] {parsedStructure.usernameId, parsedStructure.passwordId})
                    .build())
            .build();
    // ...
}

سرویس تکمیل خودکار می‌تواند منطقی را برای حفظ داده‌های کاربر در روش onSaveRequest() پیاده‌سازی کند که معمولاً پس‌از پایان فعالیت کارخواه یا وقتی برنامه کارخواه commit() را فراخوانی می‌کند، فراخوانده می‌شود. کد زیر نمونه‌ای از روش onSaveRequest() را نشان می‌دهد:

کاتلین

override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
    // Get the structure from the request
    val context: List<FillContext> = request.fillContexts
    val structure: AssistStructure = context[context.size - 1].structure

    // Traverse the structure looking for data to save
    traverseStructure(structure)

    // Persist the data - if there are no errors, call onSuccess()
    callback.onSuccess()
}

جاوا

@Override
public void onSaveRequest(SaveRequest request, SaveCallback callback) {
    // Get the structure from the request
    List<FillContext> context = request.getFillContexts();
    AssistStructure structure = context.get(context.size() - 1).getStructure();

    // Traverse the structure looking for data to save
    traverseStructure(structure);

    // Persist the data - if there are no errors, call onSuccess()
    callback.onSuccess();
}

سرویس‌های تکمیل خودکار باید داده‌های حساس را قبل‌از ذخیره کردن رمزگذاری کنند. بااین‌حال، داده‌های کاربر می‌تواند شامل برچسب‌ها یا داده‌هایی باشد که حساس نیستند. برای مثال، حساب کاربری می‌تواند شامل برچسبی باشد که داده‌ها را به‌عنوان حساب کاری یا شخصی علامت‌گذاری می‌کند. سرویس‌ها نباید برچسب‌ها را رمزگذاری کنند. با رمزگذاری نکردن برچسب‌ها، سرویس‌ها می‌توانند از برچسب‌ها در نماهای ارائه استفاده کنند، اگر کاربر اصالت‌سنجی نشده باشد. سپس، سرویس‌ها می‌توانند پس‌از اصالت‌سنجی کاربر، برچسب‌ها را با داده‌های واقعی جایگزین کنند.

به‌تعویق انداختن واسط کاربر ذخیره تکمیل خودکار

از Android 10، اگر از چند صفحه برای پیاده‌سازی گردش کار تکمیل خودکار استفاده می‌کنید (برای مثال، یک صفحه برای فیلد نام کاربری و صفحه‌ای دیگر برای گذرواژه)، می‌توانید بااستفاده از پرچم SaveInfo.FLAG_DELAY_SAVE، رابط کاربری ذخیره تکمیل خودکار را به‌تعویق بیندازید.

اگر این پرچم تنظیم شده باشد، وقتی بافت تکمیل خودکار مرتبط با پاسخ SaveInfo تأیید شود، رابط کاربری ذخیره تکمیل خودکار راه‌اندازی نمی‌شود. درعوض، می‌توانید از فعالیت جداگانه‌ای در همان تکلیف برای ارائه درخواست‌های تکمیل آینده استفاده کنید و سپس میانای کاربر را بااستفاده از درخواست ذخیره نشان دهید. برای اطلاعات بیشتر، به SaveInfo.FLAG_DELAY_SAVE مراجعه کنید.

الزامی بودن اصالت‌سنجی کاربر

سرویس‌های تکمیل خودکار می‌توانند با ملزم کردن کاربر به اصالت‌سنجی قبل‌از تکمیل کردن نماها، سطح امنیتی بیشتری را فراهم کنند. سناریوهای زیر گزینه‌های خوبی برای پیاده‌سازی اصالت‌سنجی کاربر هستند:

  • داده‌های کاربر در برنامه باید بااستفاده از گذرواژه اصلی یا اسکن اثر انگشت باز شود.
  • مجموعه داده خاصی باید باز شود، مثلاً جزئیات کارت اعتباری بااستفاده از کد درستی‌سنجی کارت (CVC).

در سناریویی که سرویس قبل‌از باز کردن قفل داده‌ها به اصالت‌سنجی کاربر نیاز دارد، سرویس می‌تواند داده‌های تکراری یا برچسبی را ارائه دهد و Intent را که اصالت‌سنجی را انجام می‌دهد مشخص کند. اگر پس‌از اتمام جریان اصالت‌سنجی به داده‌های بیشتری برای پردازش درخواست نیاز دارید، می‌توانید این داده‌ها را به قصد اضافه کنید. سپس فعالیت اصالت‌سنجی شما می‌تواند داده‌ها را به کلاس AutofillService در برنامه‌تان برگرداند.

نمونه کد زیر نشان می‌دهد که چگونه مشخص کنید که درخواست نیاز به اصالت‌سنجی دارد:

کاتلین

val authPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
    setTextViewText(android.R.id.text1, "requires authentication")
}
val authIntent = Intent(this, AuthActivity::class.java).apply {
    // Send any additional data required to complete the request
    putExtra(MY_EXTRA_DATASET_NAME, "my_dataset")
}

val intentSender: IntentSender = PendingIntent.getActivity(
        this,
        1001,
        authIntent,
        PendingIntent.FLAG_CANCEL_CURRENT
).intentSender

// Build a FillResponse object that requires authentication
val fillResponse: FillResponse = FillResponse.Builder()
        .setAuthentication(autofillIds, intentSender, authPresentation)
        .build()

جاوا

RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
authPresentation.setTextViewText(android.R.id.text1, "requires authentication");
Intent authIntent = new Intent(this, AuthActivity.class);

// Send any additional data required to complete the request
authIntent.putExtra(MY_EXTRA_DATASET_NAME, "my_dataset");
IntentSender intentSender = PendingIntent.getActivity(
                this,
                1001,
                authIntent,
                PendingIntent.FLAG_CANCEL_CURRENT
        ).getIntentSender();

// Build a FillResponse object that requires authentication
FillResponse fillResponse = new FillResponse.Builder()
        .setAuthentication(autofillIds, intentSender, authPresentation)
        .build();

وقتی فعالیت جریان اصالت‌سنجی را تکمیل کرد، باید روش setResult() را فراخوانی کند، مقدار RESULT_OK را ارسال کند، و EXTRA_AUTHENTICATION_RESULT اضافی را روی شیء FillResponse که شامل مجموعه داده‌های پرشده است تنظیم کند. کد زیر نمونه‌ای از نحوه برگرداندن نتیجه پس‌از تکمیل جریان اصالت‌سنجی را نشان می‌دهد:

کاتلین

// The data sent by the service and the structure are included in the intent
val datasetName: String? = intent.getStringExtra(MY_EXTRA_DATASET_NAME)
val structure: AssistStructure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE)
val parsedStructure: ParsedStructure = parseStructure(structure)
val (username, password) = fetchUserData(parsedStructure)

// Build the presentation of the datasets
val usernamePresentation =
        RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
            setTextViewText(android.R.id.text1, "my_username")
        }
val passwordPresentation =
        RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
            setTextViewText(android.R.id.text1, "Password for my_username")
        }

// Add the dataset to the response
val fillResponse: FillResponse = FillResponse.Builder()
        .addDataset(Dataset.Builder()
                .setValue(
                        parsedStructure.usernameId,
                        AutofillValue.forText(username),
                        usernamePresentation
                )
                .setValue(
                        parsedStructure.passwordId,
                        AutofillValue.forText(password),
                        passwordPresentation
                )
                .build()
        ).build()

val replyIntent = Intent().apply {
    // Send the data back to the service
    putExtra(MY_EXTRA_DATASET_NAME, datasetName)
    putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse)
}

setResult(Activity.RESULT_OK, replyIntent)

جاوا

Intent intent = getIntent();

// The data sent by the service and the structure are included in the intent
String datasetName = intent.getStringExtra(MY_EXTRA_DATASET_NAME);
AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
ParsedStructure parsedStructure = parseStructure(structure);
UserData userData = fetchUserData(parsedStructure);

// Build the presentation of the datasets
RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
usernamePresentation.setTextViewText(android.R.id.text1, "my_username");
RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username");

// Add the dataset to the response
FillResponse fillResponse = new FillResponse.Builder()
        .addDataset(new Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(userData.username), usernamePresentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(userData.password), passwordPresentation)
                .build())
        .build();

Intent replyIntent = new Intent();

// Send the data back to the service
replyIntent.putExtra(MY_EXTRA_DATASET_NAME, datasetName);
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse);

setResult(RESULT_OK, replyIntent);

در سناریویی که مجموعه داده کارت اعتباری باید باز شود، سرویس می‌تواند واسط کاربری‌ای را نمایش دهد که کد CVC را درخواست می‌کند. می‌توانید داده‌ها را تا زمانی که قفل مجموعه داده با ارائه داده‌های قالبی، مانند نام بانک و چهار رقم آخر شماره کارت اعتباری، باز شود پنهان کنید. مثال زیر نشان می‌دهد که چگونه برای مجموعه داده‌ای اصالت‌سنجی درخواست کنید و داده‌ها را تا زمانی که کاربر کد CVC را ارائه نکرده است پنهان کنید:

کاتلین

// Parse the structure and fetch payment data
val parsedStructure: ParsedStructure = parseStructure(structure)
val paymentData: Payment = fetchPaymentData(parsedStructure)

// Build the presentation that shows the bank and the last four digits of the
// credit card number, such as 'Bank-1234'
val maskedPresentation: String = "${paymentData.bank}-" +
        paymentData.creditCardNumber.substring(paymentData.creditCardNumber.length - 4)
val authPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
    setTextViewText(android.R.id.text1, maskedPresentation)
}

// Prepare an intent that displays the UI that asks for the CVC
val cvcIntent = Intent(this, CvcActivity::class.java)
val cvcIntentSender: IntentSender = PendingIntent.getActivity(
        this,
        1001,
        cvcIntent,
        PendingIntent.FLAG_CANCEL_CURRENT
).intentSender

// Build a FillResponse object that includes a Dataset that requires authentication
val fillResponse: FillResponse = FillResponse.Builder()
        .addDataset(
                Dataset.Builder()
                        // The values in the dataset are replaced by the actual
                        // data once the user provides the CVC
                        .setValue(parsedStructure.creditCardId, null, authPresentation)
                        .setValue(parsedStructure.expDateId, null, authPresentation)
                        .setAuthentication(cvcIntentSender)
                        .build()
        ).build()

جاوا

// Parse the structure and fetch payment data
ParsedStructure parsedStructure = parseStructure(structure);
Payment paymentData = fetchPaymentData(parsedStructure);

// Build the presentation that shows the bank and the last four digits of the
// credit card number, such as 'Bank-1234'
String maskedPresentation = paymentData.bank + "-" +
    paymentData.creditCardNumber.subString(paymentData.creditCardNumber.length - 4);
RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
authPresentation.setTextViewText(android.R.id.text1, maskedPresentation);

// Prepare an intent that displays the UI that asks for the CVC
Intent cvcIntent = new Intent(this, CvcActivity.class);
IntentSender cvcIntentSender = PendingIntent.getActivity(
        this,
        1001,
        cvcIntent,
        PendingIntent.FLAG_CANCEL_CURRENT
).getIntentSender();

// Build a FillResponse object that includes a Dataset that requires authentication
FillResponse fillResponse = new FillResponse.Builder()
        .addDataset(new Dataset.Builder()
                // The values in the dataset are replaced by the actual
                // data once the user provides the CVC
                .setValue(parsedStructure.creditCardId, null, authPresentation)
                .setValue(parsedStructure.expDateId, null, authPresentation)
                .setAuthentication(cvcIntentSender)
                .build())
        .build();

وقتی فعالیت کد تأیید کارت را اعتبارسنجی کرد، باید روش setResult() را فراخوانی کند، مقدار RESULT_OK را ارسال کند، و EXTRA_AUTHENTICATION_RESULT اضافی را روی شیء Dataset که حاوی شماره کارت اعتباری و تاریخ انقضا است تنظیم کند. مجموعه داده جدید جایگزین مجموعه داده‌ای می‌شود که به اصالت‌سنجی نیاز دارد و نماها بلافاصله پر می‌شوند. کد زیر نمونه‌ای از نحوه برگرداندن مجموعه داده را پس‌از ارائه کد تأیید کارت توسط کاربر نشان می‌دهد:

کاتلین

// Parse the structure and fetch payment data.
val parsedStructure: ParsedStructure = parseStructure(structure)
val paymentData: Payment = fetchPaymentData(parsedStructure)

// Build a non-null RemoteViews object to use as the presentation when
// creating the Dataset object. This presentation isn't actually used, but the
// Builder object requires a non-null presentation.
val notUsed = RemoteViews(packageName, android.R.layout.simple_list_item_1)

// Create a dataset with the credit card number and expiration date.
val responseDataset: Dataset = Dataset.Builder()
        .setValue(
                parsedStructure.creditCardId,
                AutofillValue.forText(paymentData.creditCardNumber),
                notUsed
        )
        .setValue(
                parsedStructure.expDateId,
                AutofillValue.forText(paymentData.expirationDate),
                notUsed
        )
        .build()

val replyIntent = Intent().apply {
    putExtra(EXTRA_AUTHENTICATION_RESULT, responseDataset)
}

جاوا

// Parse the structure and fetch payment data.
ParsedStructure parsedStructure = parseStructure(structure);
Payment paymentData = fetchPaymentData(parsedStructure);

// Build a non-null RemoteViews object to use as the presentation when
// creating the Dataset object. This presentation isn't actually used, but the
// Builder object requires a non-null presentation.
RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);

// Create a dataset with the credit card number and expiration date.
Dataset responseDataset = new Dataset.Builder()
        .setValue(parsedStructure.creditCardId,
                AutofillValue.forText(paymentData.creditCardNumber), notUsed)
        .setValue(parsedStructure.expDateId,
                AutofillValue.forText(paymentData.expirationDate), notUsed)
        .build();

Intent replyIntent = new Intent();
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, responseDataset);

داده‌ها را در گروه‌های منطقی سازمان‌دهی کنید

سرویس‌های تکمیل خودکار باید داده‌ها را در گروه‌های منطقی سازمان‌دهی کنند که مفاهیم را از دامنه‌های مختلف جدا کند. در این صفحه، به این گروه‌های منطقی پارتیشن گفته می‌شود. فهرست زیر نمونه‌های معمول از بخش‌ها و فیلدها را نشان می‌دهد:

  • اعتبارنامه، که شامل فیلدهای نام کاربری و گذرواژه می‌شود.
  • نشانی، که شامل فیلدهای خیابان، شهر، ایالت، و کد پستی می‌شود.
  • اطلاعات پرداخت، که شامل شماره کارت اعتباری، تاریخ انقضا، و فیلدهای کد درستی‌سنجی است.

سرویس تکمیل خودکاری که داده‌ها را به‌درستی تقسیم‌بندی می‌کند، می‌تواند با عدم افشای داده‌های بیش‌از یک بخش در مجموعه داده، از داده‌های کاربرانش بهتر محافظت کند. برای مثال، مجموعه داده‌ای که شامل اطلاعات کاربری است لازم نیست شامل اطلاعات پرداخت باشد. سازمان‌دهی داده‌ها در بخش‌ها به سرویس شما امکان می‌دهد حداقل مقدار اطلاعات مربوط لازم برای برآورده کردن درخواست را افشا کند.

سازمان‌دهی داده‌ها در بخش‌ها به سرویس‌ها امکان می‌دهد فعالیت‌هایی را که دارای نماهایی از بخش‌های متعدد هستند تکمیل کنند و درعین‌حال حداقل مقدار داده‌های مربوطه را به برنامه کارخواه ارسال کنند. برای مثال، فعالیتی را درنظر بگیرید که شامل نماهایی برای نام کاربری، گذرواژه، خیابان، و شهر است، و سرویس تکمیل خودکاری که دارای داده‌های زیر است:

پارتیشن فیلد ۱ فیلد ۲
اطلاعات اعتباری work_username work_password
personal_username personal_password
نشانی work_street work_city
personal_street personal_city

این سرویس می‌تواند مجموعه داده‌ای آماده کند که شامل بخش اطلاعات کاربری برای هر دو حساب کاری و شخصی باشد. وقتی کاربر مجموعه داده‌ای را انتخاب می‌کند، پاسخ تکمیل خودکار بعدی می‌تواند نشانی محل کار یا نشانی شخصی را ارائه دهد، که این به انتخاب اول کاربر بستگی دارد.

سرویس می‌تواند فیلدی را که درخواست از آن منشأ گرفته است با فراخوانی روش isFocused() هنگام پیمایش شیء AssistStructure شناسایی کند. این کار به سرویس اجازه می‌دهد FillResponse را با داده‌های پارتیشن مناسب آماده کند.

تکمیل خودکار کد یک‌بارمصرف پیامکی

سرویس تکمیل خودکار شما می‌تواند به کاربر کمک کند کدهای یک‌بار مصرف ارسال‌شده بااستفاده از SMS Retriever API را تکمیل کند.

برای استفاده کردن از این ویژگی، باید شرایط زیر رعایت شود:

  • سرویس تکمیل خودکار در Android نسخه ۹ (سطح API‏ ۲۸) یا بالاتر اجرا می‌شود.
  • کاربر به سرویس تکمیل خودکار شما اجازه می‌دهد کدهای یک‌بارمصرف را از پیامک بخواند.
  • برنامه‌ای که تکمیل خودکار را برای آن ارائه می‌دهید ازقبل از SMS Retriever API برای خواندن کدهای یک‌بارمصرف استفاده نمی‌کند.

سرویس تکمیل خودکار شما می‌تواند از SmsCodeAutofillClient استفاده کند که با فراخوانی SmsCodeRetriever.getAutofillClient() از «خدمات Google Play» نسخه ۱۹.۰.۵۶ یا بالاتر دردسترس است.

مراحل اصلی استفاده از این API در سرویس تکمیل خودکار عبارتند از:

  1. در سرویس تکمیل خودکار، از hasOngoingSmsRequest از SmsCodeAutofillClient استفاده کنید تا مشخص شود آیا درخواست فعالی برای نام بسته برنامه‌ای که تکمیل خودکار می‌کنید وجود دارد یا نه. سرویس تکمیل خودکار شما فقط درصورتی باید پیام‌واره پیشنهاد را نمایش دهد که این مقدار false را برگرداند.
  2. در سرویس تکمیل خودکار، از checkPermissionState از SmsCodeAutofillClient استفاده کنید تا بررسی کنید آیا سرویس تکمیل خودکار اجازه دارد کدهای یک‌بارمصرف را تکمیل خودکار کند یا نه. این وضعیت اجازه می‌تواند NONE، GRANTED، یا DENIED باشد. سرویس تکمیل خودکار باید پیام‌واره پیشنهادی را برای حالت‌های NONE و GRANTED نمایش دهد.
  3. در فعالیت اصالت‌سنجی تکمیل خودکار، از اجازه SmsRetriever.SEND_PERMISSION برای ثبت BroadcastReceiver گوش دادن به SmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION استفاده کنید تا نتیجه کد پیامک را درصورت دردسترس بودن دریافت کنید.
  4. برای شروع گوش دادن به کدهای یک‌بارمصرف ارسال‌شده ازطریق پیامک، startSmsCodeRetriever را در SmsCodeAutofillClient فراخوانی کنید. اگر کاربر به سرویس تکمیل خودکار شما اجازه دهد کدهای یک‌بارمصرف را از پیامک بازیابی کند، این سرویس پیامک‌های دریافتی در یک تا پنج دقیقه گذشته را جستجو می‌کند.

    اگر سرویس تکمیل خودکار شما برای خواندن کدهای یک‌بارمصرف نیاز به درخواست اجازه کاربر داشته باشد، Task برگردانده‌شده توسط startSmsCodeRetriever ممکن است با برگرداندن ResolvableApiException ناموفق باشد. اگر این اتفاق بیفتد، باید روش ResolvableApiException.startResolutionForResult() را فراخوانی کنید تا کادر گفتگوی رضایت برای درخواست اجازه نمایش داده شود.

  5. نتیجه کد پیامکی را از هدف دریافت کنید و سپس کد پیامکی را به‌عنوان پاسخ تکمیل خودکار برگردانید.

فعال کردن تکمیل خودکار در Chrome

‫Chrome به سرویس‌های تکمیل خودکار شخص ثالث اجازه می‌دهد فرم‌ها را به‌طور بومی تکمیل خودکار کنند تا کاربران تجربه کاربری روان‌تر و ساده‌تری داشته باشند. برای استفاده از سرویس‌های تکمیل خودکار طرف سوم برای تکمیل خودکار گذرواژه‌ها، گذرکلیدها، و اطلاعات دیگر مثل نشانی‌ها و داده‌های پرداخت، کاربران باید تکمیل خودکار بااستفاده از سرویسی دیگر را در تنظیمات Chrome انتخاب کنند.

تنظیمات Chrome که نشان می‌دهد کلید «تکمیل خودکار بااستفاده از سرویسی دیگر» فعال است
شکل ۳.‏ تنظیمات Chrome که نشان می‌دهد کلید «تکمیل خودکار بااستفاده از سرویسی دیگر» فعال است.

برای کمک به کاربران برای داشتن بهترین تجربه تکمیل خودکار ممکن با سرویس شما و Chrome در Android، ارائه‌دهندگان سرویس تکمیل خودکار باید کاربرانشان را تشویق کنند ارائه‌دهنده سرویس دلخواهشان را در تنظیمات Chrome مشخص کنند.

برای کمک به کاربران در روشن کردن کلید، توسعه‌دهندگان می‌توانند:

  • تنظیمات Chrome را جستجو کنید و ببینید آیا کاربر می‌خواهد از سرویس تکمیل خودکار شخص ثالث استفاده کند یا نه.
  • پیوند عمیق به صفحه تنظیمات Chrome که در آنجا کاربران می‌توانند سرویس‌های تکمیل خودکار طرف سوم را فعال کنند.

حداکثر نسخه‌های Chrome را برای حالت سازگاری مشخص کنید

Chrome از نسخه ۱۳۷ پشتیبانی از حالت سازگاری را به‌نفع «تکمیل خودکار Android» متوقف کرد. حفظ حالت سازگاری می‌تواند باعث مشکلات پایداری شود. حداکثر نسخه بسته‌های Chrome را که از حالت سازگاری برای پایداری پشتیبانی می‌کنند به‌صورت زیر مشخص کنید.

<autofill-service>
  ...
  <compatibility-package android:name="com.android.chrome" android:maxLongVersionCode="711900039" />
  <compatibility-package android:name="com.chrome.beta" android:maxLongVersionCode="711900039" />
  <compatibility-package android:name="com.chrome.dev" android:maxLongVersionCode="711900039" />
  <compatibility-package android:name="com.chrome.canary" android:maxLongVersionCode="711900039" />
  ...
</autofill-service>

خواندن تنظیمات Chrome

هر برنامه‌ای می‌تواند بخواند که آیا Chrome از حالت تکمیل خودکار شخص ثالث استفاده می‌کند یا نه، که به آن اجازه می‌دهد از «تکمیل خودکار Android» استفاده کند. ‫Chrome برای انتقال این اطلاعات از ContentProvider Android استفاده می‌کند. در مانیفست Android خود اعلام کنید که می‌خواهید تنظیمات را از کدام کانال‌ها بخوانید:

<uses-permission android:name="android.permission.READ_USER_DICTIONARY"/>
<queries>
 <!-- To Query Chrome Beta: -->
 <package android:name="com.chrome.beta" />

 <!-- To Query Chrome Stable: -->
 <package android:name="com.android.chrome" />
</queries>

سپس، از ContentResolver Android برای درخواست آن اطلاعات با ساختن شناسه منبع یکسان محتوا استفاده کنید:

کاتلین

val CHROME_CHANNEL_PACKAGE = "com.android.chrome" // Chrome Stable.
val CONTENT_PROVIDER_NAME = ".AutofillThirdPartyModeContentProvider"
val THIRD_PARTY_MODE_COLUMN = "autofill_third_party_state"
val THIRD_PARTY_MODE_ACTIONS_URI_PATH = "autofill_third_party_mode"

val uri = Uri.Builder()
    .scheme(ContentResolver.SCHEME_CONTENT)
    .authority(CHROME_CHANNEL_PACKAGE + CONTENT_PROVIDER_NAME)
    .path(THIRD_PARTY_MODE_ACTIONS_URI_PATH)
    .build()

val cursor = contentResolver.query(
    uri,
    arrayOf(THIRD_PARTY_MODE_COLUMN), // projection
    null, // selection
    null, // selectionArgs
    null  // sortOrder
)

if (cursor == null) {
  // Terminate now! Chromium versions older than this don't provide this information.
}

cursor?.use { // Use the safe call operator and the use function for auto-closing
    if (it.moveToFirst()) { // Check if the cursor has any rows
        val index = it.getColumnIndex(THIRD_PARTY_MODE_COLUMN)
        if (index != -1) { // Check if the column exists
          val value = it.getInt(index)
          if (0 == value) {
              // 0 means that the third party mode is turned off. Chrome uses its built-in
              // password manager. This is the default for new users.
          } else {
              // 1 means that the third party mode is turned on. Chrome forwards all
              // autofill requests to Android Autofill. Users have to opt-in for this.
          }
        } else {
          // Handle the case where the column doesn't exist.  Log a warning, perhaps.
          Log.w("Autofill", "Column $THIRD_PARTY_MODE_COLUMN not found in cursor")
        }
    }
} // The cursor is automatically closed here

جاوا

final String CHROME_CHANNEL_PACKAGE = "com.android.chrome";  // Chrome Stable.
final String CONTENT_PROVIDER_NAME = ".AutofillThirdPartyModeContentProvider";
final String THIRD_PARTY_MODE_COLUMN = "autofill_third_party_state";
final String THIRD_PARTY_MODE_ACTIONS_URI_PATH = "autofill_third_party_mode";

final Uri uri = new Uri.Builder()
                  .scheme(ContentResolver.SCHEME_CONTENT)
                  .authority(CHROME_CHANNEL_PACKAGE + CONTENT_PROVIDER_NAME)
                  .path(THIRD_PARTY_MODE_ACTIONS_URI_PATH)
                  .build();

final Cursor cursor = getContentResolver().query(
                  uri,
                  /*projection=*/new String[] {THIRD_PARTY_MODE_COLUMN},
                  /*selection=*/ null,
                  /*selectionArgs=*/ null,
                  /*sortOrder=*/ null);

if (cursor == null) {
  // Terminate now! Chromium versions older than this don't provide this information.
}

cursor.moveToFirst(); // Retrieve the result;

int index = cursor.getColumnIndex(THIRD_PARTY_MODE_COLUMN);

if (0 == cursor.getInt(index)) {
  // 0 means that the third party mode is turned off. Chrome uses its built-in
  // password manager. This is the default for new users.
} else {
  // 1 means that the third party mode is turned on. Chrome forwards all
  // autofill requests to Android Autofill. Users have to opt-in for this.
}

برای پیوند عمیق به صفحه تنظیمات Chrome که کاربران می‌توانند سرویس‌های تکمیل خودکار شخص ثالث را در آن فعال کنند، از Intent Android استفاده کنید. حتماً کنش و دسته‌ها را همان‌طور که در این مثال نشان داده شده است پیکربندی کنید:

کاتلین

val autofillSettingsIntent = Intent(Intent.ACTION_APPLICATION_PREFERENCES)
autofillSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT)
autofillSettingsIntent.addCategory(Intent.CATEGORY_APP_BROWSER)
autofillSettingsIntent.addCategory(Intent.CATEGORY_PREFERENCE)

// Invoking the intent with a chooser allows users to select the channel they
// want to configure. If only one browser reacts to the intent, the chooser is
// skipped.
val chooser = Intent.createChooser(autofillSettingsIntent, "Pick Chrome Channel")
startActivity(chooser)

// If the caller knows which Chrome channel they want to configure,
// they can instead add a package hint to the intent, e.g.
val specificChromeIntent = Intent(Intent.ACTION_APPLICATION_PREFERENCES) // Create a *new* intent
specificChromeIntent.addCategory(Intent.CATEGORY_DEFAULT)
specificChromeIntent.addCategory(Intent.CATEGORY_APP_BROWSER)
specificChromeIntent.addCategory(Intent.CATEGORY_PREFERENCE)
specificChromeIntent.setPackage("com.android.chrome") // Set the package on the *new* intent
startActivity(specificChromeIntent) // Start the *new* intent

جاوا

Intent autofillSettingsIntent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES);
autofillSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
autofillSettingsIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
autofillSettingsIntent.addCategory(Intent.CATEGORY_PREFERENCE);

// Invoking the intent with a chooser allows users to select the channel they
// want to configure. If only one browser reacts to the intent, the chooser is
// skipped.
Intent chooser = Intent.createChooser(autofillSettingsIntent, "Pick Chrome Channel");
startActivity(chooser);

// If the caller knows which Chrome channel they want to configure,
// they can instead add a package hint to the intent, e.g.
autofillSettingsIntent.setPackage("com.android.chrome");
startActivity(autofillSettingsIntent);

سناریوهای تکمیل خودکار پیشرفته

در سناریوهای زیر از تکمیل خودکار استفاده کنید:

ادغام با صفحه‌کلید

از Android نسخه ۱۱، این پلاتفرم به صفحه‌کلیدها و دیگر ویرایشگرهای روش ورودی (IME) اجازه می‌دهد پیشنهادهای تکمیل خودکار را به‌جای استفاده از منو کرکره‌ای، به‌صورت درون‌خطی نمایش دهند. برای اطلاعات بیشتر درباره اینکه سرویس تکمیل خودکار شما چگونه می‌تواند از این عملکرد پشتیبانی کند، به ادغام تکمیل خودکار با صفحه‌کلید مراجعه کنید.

صفحه‌بندی مجموعه‌داده‌ها

پاسخ تکمیل خودکار بزرگ می‌تواند از اندازه تراکنش مجاز شیء Binder که نشان‌دهنده شیء قابل‌کنترل از راه دور موردنیاز برای پردازش درخواست است فراتر رود. برای جلوگیری از اینکه سیستم Android در این سناریوها استثنا قائل شود، می‌توانید با اضافه کردن حداکثر ۲۰ شیء Dataset در هر بار، FillResponse را کوچک نگه دارید. اگر پاسخ شما به مجموعه‌داده‌های بیشتری نیاز دارد، می‌توانید مجموعه‌داده‌ای اضافه کنید که به کاربران اطلاع می‌دهد اطلاعات بیشتری وجود دارد و با انتخاب آن، گروه بعدی مجموعه‌داده‌ها را بازیابی می‌کند. برای اطلاعات بیشتر، به addDataset(Dataset) مراجعه کنید.

ذخیره کردن داده‌های تقسیم‌شده در چند صفحه‌نمایش

برنامه‌ها اغلب داده‌های کاربر را در چندین صفحه در یک فعالیت تقسیم می‌کنند، مثلاً برای ایجاد حساب‌های کاربری جدید. برای مثال، صفحه اول ممکن است نام کاربری را درخواست کند و صفحه دوم گذرواژه را. در این شرایط، سرویس تکمیل خودکار شما باید منتظر بماند تا کاربر داده‌ها را در همه فیلدهای مربوطه وارد کند و سپس واسط کاربری ذخیره تکمیل خودکار را نمایش دهد. برای مدیریت چنین سناریوهایی، این مراحل را دنبال کنید:

  1. در اولین درخواست تکمیل، یک دسته وضعیت کارخواه در پاسخ اضافه کنید که حاوی شناسه‌های تکمیل خودکار فیلدهای جزئی موجود در صفحه باشد.
  2. در دومین درخواست تکمیل، بسته وضعیت مشتری را بازیابی کنید، شناسه‌های تکمیل خودکار تنظیم‌شده در درخواست قبلی را از وضعیت مشتری دریافت کنید، و این شناسه‌ها و پرچم FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE را به شیء SaveInfo مورداستفاده در پاسخ دوم اضافه کنید.
  3. در درخواست ذخیره، از اشیاء FillContext مناسب برای دریافت مقدار هر فیلد استفاده کنید. برای هر درخواست تکمیل، یک بافتار تکمیل وجود دارد.

    برای اطلاعات بیشتر، به ذخیره کردن وقتی داده‌ها در چند صفحه تقسیم شده است مراجعه کنید.

ارائه منطق مقداردهی اولیه و تخریب برای هر درخواست

هربار که درخواست تکمیل خودکار وجود داشته باشد، سیستم Android به سرویس متصل می‌شود و روش onConnected() آن را فراخوانی می‌کند. وقتی سرویس درخواست را پردازش کرد، سیستم Android روش onDisconnected() را فراخوانی می‌کند و اتصالش را با سرویس قطع می‌کند. می‌توانید onConnected() را برای ارائه کدی که قبل‌از پردازش درخواست اجرا می‌شود و onDisconnected() را برای ارائه کدی که بعداز پردازش درخواست اجرا می‌شود پیاده‌سازی کنید.

سفارشی‌سازی واسط کاربر ذخیره تکمیل خودکار

سرویس‌های تکمیل خودکار می‌توانند واسط کاربری ذخیره تکمیل خودکار را سفارشی‌سازی کنند تا به کاربران کمک کنند تصمیم بگیرند آیا می‌خواهند به سرویس اجازه دهند داده‌هایشان را ذخیره کند یا نه. سرویس‌ها می‌توانند اطلاعات بیشتری درباره آنچه ذخیره شده است ازطریق نوشتار یا نمای سفارشی‌شده ارائه دهند. سرویس‌ها همچنین می‌توانند ظاهر دکمه‌ای را که درخواست ذخیره را لغو می‌کند تغییر دهند و وقتی کاربر روی آن دکمه تک‌ضرب می‌زند، اعلانی دریافت کنند. برای اطلاعات بیشتر، به صفحه مرجع SaveInfo مراجعه کنید.

حالت سازگاری

حالت سازگاری به سرویس‌های تکمیل خودکار اجازه می‌دهد از ساختار مجازی دسترس‌پذیری برای اهداف تکمیل خودکار استفاده کنند. این به‌ویژه برای ارائه عملکرد تکمیل خودکار در مرورگرهایی که به‌طور صریح میاناهای برنامه‌سازی کاربردی تکمیل خودکار را پیاده‌سازی نمی‌کنند مفید است.

برای آزمایش سرویس تکمیل خودکار بااستفاده از حالت سازگاری، مرورگر یا برنامه‌ای را که به حالت سازگاری نیاز دارد به‌طور صریح به فهرست مجاز اضافه کنید. با اجرای فرمان زیر می‌توانید بررسی کنید کدام بسته‌ها ازقبل در فهرست مجاز قرار دارند:

$ adb shell settings get global autofill_compat_mode_allowed_packages

اگر بسته‌ای که درحال آزمایش آن هستید در فهرست نیست، آن را با اجرای فرمان زیر اضافه کنید، که در آن pkgX بسته برنامه است:

$ adb shell settings put global autofill_compat_mode_allowed_packages pkg1[resId1]:pkg2[resId1,resId2]

اگر برنامه مرورگر است، از resIdx برای مشخص کردن شناسه منبع فیلد ورودی که حاوی نشانی وب صفحه پردازش‌شده است استفاده کنید.

حالت سازگاری محدودیت‌های زیر را دارد:

  • وقتی سرویس از پرچم FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE استفاده می‌کند یا روش setTrigger() فراخوانی می‌شود، درخواست ذخیره راه‌اندازی می‌شود. FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE به‌طور پیش‌فرض هنگام استفاده از حالت سازگاری تنظیم می‌شود.
  • ممکن است مقدار نوشتاری گره‌ها در روش onSaveRequest(SaveRequest, SaveCallback) دردسترس نباشد.

برای کسب اطلاعات بیشتر درباره حالت سازگاری، ازجمله محدودیت‌های مرتبط با آن، به مرجع کلاس AutofillService مراجعه کنید.