خدمات تکمیل خودکار را بسازید

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

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

این صفحه نحوه پیاده‌سازی سرویس تکمیل خودکار را در برنامه شما نشان می‌دهد. اگر به دنبال نمونه کدی هستید که نحوه پیاده‌سازی یک سرویس را نشان دهد، به نمونه AutofillFramework در جاوا یا کاتلین مراجعه کنید. برای جزئیات بیشتر در مورد نحوه عملکرد سرویس‌های تکمیل خودکار، به صفحات مرجع کلاس‌های 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 را برمی‌گرداند.

نظرات مشتریان را پر کنید

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

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

چارچوب تکمیل خودکار، یک گردش کار برای پر کردن نماها تعریف می‌کند که به گونه‌ای طراحی شده است که زمان اتصال سیستم اندروید به سرویس تکمیل خودکار را به حداقل برساند. در هر درخواست، سیستم اندروید با فراخوانی متد 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;
}

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

کاتلین

// 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() ، برای دریافت اطلاعات در مورد محتوای view استفاده کند. برای اطلاعات بیشتر، به بخش «ارائه نکات برای autofill» مراجعه کنید.

مثال زیر نحوه پیمایش 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);
    }
}

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

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

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

برای ذخیره داده‌ها، سرویس باید اعلام کند که علاقه‌مند به ذخیره داده‌ها برای استفاده‌های بعدی است. قبل از اینکه سیستم اندروید درخواستی برای ذخیره داده‌ها ارسال کند، یک درخواست پر کردن وجود دارد که در آن سرویس فرصت پر کردن نماها را دارد. برای نشان دادن اینکه علاقه‌مند به ذخیره داده‌ها است، سرویس یک شیء 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();
}

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

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

از اندروید ۱۰ به بعد، اگر از چندین صفحه برای پیاده‌سازی گردش کار تکمیل خودکار استفاده می‌کنید - مثلاً یک صفحه برای فیلد نام کاربری و صفحه دیگر برای رمز عبور - می‌توانید با استفاده از فلگ SaveInfo.FLAG_DELAY_SAVE رابط کاربری ذخیره تکمیل خودکار را به تعویق بیندازید.

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

نیاز به احراز هویت کاربر

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

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

در سناریویی که سرویس قبل از باز کردن قفل داده‌ها نیاز به احراز هویت کاربر دارد، سرویس می‌تواند داده‌های تکراری یا یک برچسب ارائه دهد و Intent مربوط به احراز هویت را مشخص کند. اگر پس از اتمام جریان احراز هویت، به داده‌های اضافی برای پردازش درخواست نیاز دارید، می‌توانید چنین داده‌هایی را به Intent اضافه کنید. سپس activity احراز هویت شما می‌تواند داده‌ها را به کلاس 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();

پس از اتمام جریان احراز هویت توسط activity، باید متد 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();

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

کاتلین

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

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

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

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

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

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

پارتیشن فیلد ۱ فیلد ۲
مدارک تحصیلی نام کاربری_کار رمز_کار
نام کاربری_شخصی رمز_شخصی
آدرس خیابان_کار شهر_کار
خیابان_شخصی شهر_شخصی

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

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

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

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

برای استفاده از این قابلیت، باید شرایط زیر رعایت شود:

  • سرویس تکمیل خودکار روی اندروید ۹ (سطح API 28) یا بالاتر اجرا می‌شود.
  • کاربر به سرویس تکمیل خودکار شما اجازه می‌دهد کدهای یکبار مصرف را از طریق پیامک بخواند.
  • برنامه‌ای که برای آن فرم تکمیل خودکار ارائه می‌دهید، از قبل از 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. نتیجه کد پیامکی را از intent دریافت کنید و سپس کد پیامکی را به عنوان پاسخ تکمیل خودکار برگردانید.

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

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

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

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

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

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

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

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

<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>

خواندن تنظیمات کروم

هر برنامه‌ای می‌تواند بفهمد که آیا کروم از حالت تکمیل خودکار ۳P که به آن اجازه می‌دهد از تکمیل خودکار اندروید استفاده کند، استفاده می‌کند یا خیر. کروم ContentProvider اندروید برای انتقال این اطلاعات استفاده می‌کند. در مانیفست اندروید خود اعلام کنید که می‌خواهید تنظیمات را از کدام کانال‌ها بخوانید:

<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>

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

کاتلین

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

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

کاتلین

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

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

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

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

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

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

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

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

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

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

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

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

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

رابط کاربری ذخیره خودکار را سفارشی کنید

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

حالت سازگاری

حالت سازگاری به سرویس‌های تکمیل خودکار اجازه می‌دهد تا از ساختار مجازی دسترسی برای اهداف تکمیل خودکار استفاده کنند. این حالت به ویژه برای ارائه قابلیت تکمیل خودکار در مرورگرهایی که صراحتاً APIهای تکمیل خودکار را پیاده‌سازی نمی‌کنند، مفید است.

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

$ adb shell settings get global autofill_compat_mode_allowed_packages

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

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

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

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

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

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