سرویس تکمیل خودکار برنامهای است که با تزریق دادهها به نماهای برنامههای دیگر، پر کردن فرمها را برای کاربران آسانتر میکند. سرویسهای تکمیل خودکار همچنین میتوانند دادههای کاربر را از نماهای یک برنامه بازیابی کرده و برای استفاده در زمان بعدی ذخیره کنند. خدمات تکمیل خودکار معمولاً توسط برنامه هایی ارائه می شود که داده های کاربر را مدیریت می کنند، مانند مدیران رمز عبور.
Android با چارچوب تکمیل خودکار موجود در Android 8.0 (سطح API 26) و بالاتر، پر کردن فرمها را آسانتر میکند. کاربران تنها در صورتی می توانند از ویژگی های تکمیل خودکار استفاده کنند که برنامه ای وجود داشته باشد که خدمات تکمیل خودکار را در دستگاه آنها ارائه دهد.
این صفحه نحوه پیاده سازی سرویس تکمیل خودکار را در برنامه خود نشان می دهد. اگر به دنبال نمونه کدی هستید که نحوه اجرای یک سرویس را نشان دهد، نمونه 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
از کاربر درخواست کند تنظیمات تکمیل خودکار را تغییر دهد. اگر کاربر سرویس تکمیل خودکار را انتخاب کند که با بسته تماس گیرنده مطابقت دارد، intent مقدار RESULT_OK
را برمی گرداند.
نماهای مشتری را پر کنید
سرویس تکمیل خودکار درخواستهایی برای پر کردن نماهای مشتری در هنگام تعامل کاربر با برنامههای دیگر دریافت میکند. اگر سرویس تکمیل خودکار دادههای کاربر را داشته باشد که درخواست را برآورده کند، دادههای موجود در پاسخ را ارسال میکند. سیستم اندروید یک UI تکمیل خودکار را با داده های موجود نشان می دهد، همانطور که در شکل 1 نشان داده شده است:
چارچوب تکمیل خودکار یک گردش کار را برای پر کردن نماها تعریف می کند که برای به حداقل رساندن زمان اتصال سیستم Android به سرویس تکمیل خودکار طراحی شده است. در هر درخواست، سیستم اندروید یک شی AssistStructure
با فراخوانی متد onFillRequest()
به سرویس ارسال می کند.
سرویس تکمیل خودکار بررسی می کند که آیا می تواند درخواست را با داده های کاربر که قبلاً ذخیره کرده است برآورده کند یا خیر. اگر بتواند درخواست را برآورده کند، سرویس داده ها را در اشیاء Dataset
بسته بندی می کند. این سرویس متد onSuccess()
را فراخوانی میکند و یک شی FillResponse
ارسال میکند که حاوی اشیاء Dataset
است. اگر سرویس داده ای برای ارضای درخواست نداشته باشد، به متد onSuccess()
null
می دهد.
اگر در پردازش درخواست خطایی وجود داشته باشد، سرویس متد 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()
بازیابی کند.
یک سرویس باید بتواند محتوای یک view را توصیف کند تا بررسی کند که آیا می تواند درخواست را برآورده کند یا خیر. استفاده از ویژگی autofillHints
اولین رویکردی است که یک سرویس باید برای توصیف محتویات یک view استفاده کند. با این حال، برنامه های سرویس گیرنده باید به صراحت این ویژگی را در نماهای خود قبل از اینکه در دسترس سرویس قرار گیرند، ارائه دهند.
اگر یک برنامه مشتری ویژگی autofillHints
را ارائه نکند، یک سرویس باید از اکتشافات خود برای توصیف محتوا استفاده کند. این سرویس میتواند از روشهای کلاسهای دیگر، مانند getText()
یا getHint()
برای دریافت اطلاعات در مورد محتوای view استفاده کند. برای اطلاعات بیشتر، به ارائه نکات برای تکمیل خودکار مراجعه کنید.
مثال زیر نحوه عبور از 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(); }
سرویسهای تکمیل خودکار باید دادههای حساس را قبل از تداوم آن رمزگذاری کنند. با این حال، دادههای کاربر میتواند شامل برچسبها یا دادههایی باشد که حساس نیستند. به عنوان مثال، یک حساب کاربری می تواند دارای برچسبی باشد که داده ها را به عنوان یک حساب کاری یا شخصی علامت گذاری می کند. سرویس ها نباید برچسب ها را رمزگذاری کنند. با رمزگذاری نکردن برچسبها، اگر کاربر احراز هویت نکرده باشد، سرویسها میتوانند از برچسبها در نماهای ارائه استفاده کنند. سپس، سرویسها میتوانند برچسبها را با دادههای واقعی پس از احراز هویت کاربر جایگزین کنند.
UI ذخیره تکمیل خودکار را به تعویق بیندازید
با شروع Android 10، اگر از چندین صفحه برای پیادهسازی یک گردش کار تکمیل خودکار استفاده میکنید - به عنوان مثال، یک صفحه برای قسمت نام کاربری و دیگری برای رمز عبور - میتوانید با استفاده از پرچم SaveInfo.FLAG_DELAY_SAVE
، رابط کاربری ذخیرهسازی تکمیل خودکار را به تعویق بیندازید.
اگر این پرچم تنظیم شود، هنگامی که زمینه تکمیل خودکار مرتبط با پاسخ SaveInfo
متعهد شود، رابط کاربری ذخیره خودکار تکمیل نمی شود. در عوض، میتوانید از یک فعالیت جداگانه در همان کار برای ارائه درخواستهای تکمیل آینده استفاده کنید و سپس UI را از طریق یک درخواست ذخیره نشان دهید. برای اطلاعات بیشتر، SaveInfo.FLAG_DELAY_SAVE
ببینید.
نیاز به احراز هویت کاربر
سرویسهای تکمیل خودکار میتوانند سطح امنیتی بیشتری را با الزام کاربر به احراز هویت قبل از پر کردن نماها فراهم کنند. سناریوهای زیر کاندیدهای خوبی برای اجرای احراز هویت کاربر هستند:
- قفل اطلاعات کاربر در برنامه باید با استفاده از رمز عبور اصلی یا اسکن اثر انگشت باز شود.
- یک مجموعه داده خاص باید باز شود، مانند جزئیات کارت اعتباری با استفاده از کد تأیید کارت (CVC).
در سناریویی که سرویس قبل از باز کردن قفل داده ها نیاز به احراز هویت کاربر دارد، سرویس می تواند داده های دیگ بخار یا برچسبی را ارائه کند و Intent
را که از احراز هویت مراقبت می کند را مشخص کند. اگر برای پردازش درخواست پس از انجام جریان احراز هویت به داده های اضافی نیاز دارید، می توانید چنین داده هایی را به 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);
در سناریویی که یک مجموعه داده کارت اعتباری باید باز شود، سرویس میتواند یک UI را نشان دهد که 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();
زمانی که اکتیویتی 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);
داده ها را در گروه های منطقی سازماندهی کنید
سرویس های تکمیل خودکار باید داده ها را در گروه های منطقی که مفاهیم را از حوزه های مختلف جدا می کنند، سازماندهی کنند. در این صفحه از این گروه های منطقی به عنوان پارتیشن یاد می شود. لیست زیر نمونه های معمولی از پارتیشن ها و فیلدها را نشان می دهد:
- اعتبار، که شامل فیلدهای نام کاربری و رمز عبور است.
- آدرس، که شامل فیلدهای خیابان، شهر، ایالت و کد پستی است.
- اطلاعات پرداخت، که شامل شماره کارت اعتباری، تاریخ انقضا و فیلدهای کد تأیید است.
یک سرویس تکمیل خودکار که دادهها را به درستی پارتیشن بندی میکند، میتواند با افشای نکردن دادههای بیش از یک پارتیشن در یک مجموعه داده، از دادههای کاربران خود بهتر محافظت کند. به عنوان مثال، مجموعه داده ای که شامل اعتبارنامه است، نیازی به درج اطلاعات پرداخت ندارد. سازماندهی دادهها در پارتیشنها به سرویس شما اجازه میدهد تا حداقل اطلاعات مرتبط مورد نیاز برای برآورده کردن یک درخواست را در معرض نمایش بگذارد.
سازماندهی دادهها در پارتیشنها، سرویسها را قادر میسازد تا فعالیتهایی را که دارای نماهایی از چندین پارتیشن هستند پر کنند و در عین حال حداقل دادههای مربوطه را به برنامه مشتری ارسال کنند. به عنوان مثال، فعالیتی را در نظر بگیرید که شامل نماهایی برای نام کاربری، رمز عبور، خیابان و شهر و یک سرویس تکمیل خودکار است که دادههای زیر را دارد:
پارتیشن | فیلد 1 | فیلد 2 |
---|---|---|
اعتبارنامه | نام کاربری_کار | work_password |
نام کاربری_personal | personal_password | |
آدرس | خیابان_کار | کار_شهر |
خیابان_شخصی | شهر شخصی |
این سرویس می تواند مجموعه داده ای را آماده کند که شامل پارتیشن اعتبارنامه برای حساب های کاری و شخصی باشد. هنگامی که کاربر یک مجموعه داده را انتخاب می کند، بسته به انتخاب اول کاربر، پاسخ تکمیل خودکار بعدی می تواند آدرس کاری یا شخصی را ارائه دهد.
یک سرویس میتواند با فراخوانی متد isFocused()
در حین عبور از شی AssistStructure
، فیلدی را که درخواست را آغاز کرده است شناسایی کند. این به سرویس اجازه می دهد تا یک FillResponse
با داده های پارتیشن مناسب آماده کند.
تکمیل خودکار کد یکبار مصرف پیامک
سرویس تکمیل خودکار شما می تواند با استفاده از SMS Retriever API به کاربر در پر کردن کدهای یکبار مصرف ارسال شده از طریق پیامک کمک کند.
برای استفاده از این ویژگی، شرایط زیر باید رعایت شود:
- سرویس تکمیل خودکار روی Android 9 (سطح API 28) یا بالاتر اجرا میشود.
- کاربر برای سرویس تکمیل خودکار شما برای خواندن کدهای یکبار مصرف از پیامک رضایت می دهد.
- برنامهای که تکمیل خودکار آن را ارائه میدهید از قبل از SMS Retriever API برای خواندن کدهای یکبار مصرف استفاده نمیکند.
سرویس تکمیل خودکار شما میتواند از SmsCodeAutofillClient
استفاده کند که با تماس با SmsCodeRetriever.getAutofillClient()
از Google Play Services 19.0.56 یا بالاتر در دسترس است.
مراحل اولیه برای استفاده از این API در یک سرویس تکمیل خودکار عبارتند از:
- در سرویس تکمیل خودکار، از
hasOngoingSmsRequest
ازSmsCodeAutofillClient
استفاده کنید تا تعیین کنید آیا درخواستی برای نام بسته برنامه کاربردی که به صورت خودکار تکمیل می کنید وجود دارد یا خیر. سرویس تکمیل خودکار شما فقط در صورتی باید یک درخواست پیشنهادی را نمایش دهد کهfalse
باشد. - در سرویس تکمیل خودکار، از
checkPermissionState
ازSmsCodeAutofillClient
استفاده کنید تا بررسی کنید که آیا سرویس تکمیل خودکار مجوز تکمیل خودکار کدهای یکبار مصرف را دارد یا خیر. این وضعیت مجوز می تواندNONE
,GRANTED
یاDENIED
شود . سرویس تکمیل خودکار باید یک پیشنهاد برای حالت هایNONE
وGRANTED
نمایش دهد. - در فعالیت احراز هویت تکمیل خودکار، از مجوز
SmsRetriever.SEND_PERMISSION
برای ثبت یکBroadcastReceiver
در حال گوش دادن بهSmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION
استفاده کنید تا نتیجه کد پیامک را در صورت موجود بودن دریافت کنید. با
startSmsCodeRetriever
درSmsCodeAutofillClient
تماس بگیرید تا شروع به گوش دادن به کدهای یکبار مصرف ارسال شده از طریق پیامک کنید. اگر کاربر به سرویس تکمیل خودکار شما اجازه می دهد تا کدهای یکبار مصرف را از پیامک بازیابی کند، این به دنبال پیامک های دریافت شده در یک تا پنج دقیقه گذشته از هم اکنون می باشد.اگر سرویس تکمیل خودکار شما نیاز به درخواست مجوز کاربر برای خواندن کدهای یکبار مصرف داشته باشد، در آن صورت ممکن است
Task
که توسطstartSmsCodeRetriever
بازگردانده شده است باResolvableApiException
بازگردانده شده، شکست بخورد. اگر این اتفاق افتاد، باید متدResolvableApiException.startResolutionForResult()
را فراخوانی کنید تا یک گفتگوی رضایت برای درخواست مجوز نمایش داده شود.نتیجه کد پیامکی را از قصد دریافت کنید و سپس کد پیامک را به عنوان پاسخ تکمیل خودکار برگردانید.
سناریوهای تکمیل خودکار پیشرفته
- ادغام با صفحه کلید
- با شروع اندروید 11، این پلتفرم به صفحهکلیدها و سایر ویرایشگرهای روش ورودی ( IME ) اجازه میدهد بهجای استفاده از منوی کشویی، پیشنهادات تکمیل خودکار را بهصورت درون خطی نمایش دهند. برای اطلاعات بیشتر در مورد اینکه چگونه سرویس تکمیل خودکار شما می تواند از این عملکرد پشتیبانی کند، به ادغام تکمیل خودکار با صفحه کلید مراجعه کنید.
- صفحه بندی مجموعه داده ها
- یک پاسخ تکمیل خودکار بزرگ میتواند از اندازه تراکنش مجاز شی
Binder
که نشاندهنده شی قابل راهحل مورد نیاز برای پردازش درخواست است، بیشتر شود. برای جلوگیری از ایجاد استثنا توسط سیستم اندروید در این سناریوها، می توانیدFillResponse
را با اضافه کردن بیش از 20 شیءDataset
در یک زمان کوچک نگه دارید. اگر پاسخ شما به مجموعه دادههای بیشتری نیاز دارد، میتوانید مجموعه دادهای اضافه کنید که به کاربران اطلاع دهد اطلاعات بیشتری وجود دارد و در صورت انتخاب، گروه بعدی مجموعه دادهها را بازیابی میکند. برای اطلاعات بیشتر،addDataset(Dataset)
ببینید. - تقسیم داده ها را در چند صفحه ذخیره کنید
برنامه ها اغلب در یک فعالیت، داده های کاربر را در چندین صفحه تقسیم می کنند، به خصوص در فعالیت هایی که برای ایجاد یک حساب کاربری جدید استفاده می شود. به عنوان مثال، صفحه اول یک نام کاربری می خواهد و اگر نام کاربری موجود باشد، صفحه دوم یک رمز عبور می خواهد. در این مواقع، سرویس تکمیل خودکار باید منتظر بماند تا کاربر هر دو فیلد را وارد کند تا رابط کاربری ذخیره خودکار تکمیل شود. برای مدیریت چنین سناریوهایی مراحل زیر را دنبال کنید:
- در اولین درخواست پر کردن ، یک بسته حالت کلاینت را در پاسخ اضافه کنید که حاوی شناسههای تکمیل خودکار فیلدهای جزئی موجود در صفحه است.
- در درخواست تکمیل دوم، بسته حالت کلاینت را بازیابی کنید، شناسههای تکمیل خودکار تنظیم شده در درخواست قبلی را از حالت مشتری دریافت کنید و این شناسهها و پرچم
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
را به شیSaveInfo
استفاده شده در پاسخ دوم اضافه کنید. - در درخواست ذخیره ، از اشیاء
FillContext
مناسب برای بدست آوردن مقدار هر فیلد استفاده کنید. برای هر درخواست پر کردن یک زمینه پر کردن وجود دارد.
برای اطلاعات بیشتر، به ذخیره زمانی که داده ها در چندین صفحه تقسیم می شوند، مراجعه کنید.
- برای هر درخواست، منطق اولیه و حذف را ارائه دهید
هر بار که یک درخواست تکمیل خودکار وجود دارد، سیستم Android به سرویس متصل می شود و متد
onConnected()
آن را فراخوانی می کند. هنگامی که سرویس درخواست را پردازش کرد، سیستم اندروید متدonDisconnected()
را فراخوانی می کند و از سرویس جدا می شود. شما می توانیدonConnected()
برای ارائه کدی که قبل از پردازش درخواست اجرا می شود وonDisconnected()
برای ارائه کدی که پس از پردازش درخواست اجرا می شود، پیاده سازی کنید.- UI ذخیره تکمیل خودکار را سفارشی کنید
سرویسهای تکمیل خودکار میتوانند رابط کاربری ذخیرهسازی تکمیل خودکار را سفارشی کنند تا به کاربران کمک کنند تصمیم بگیرند که آیا میخواهند به سرویس اجازه دهند دادههایشان را ذخیره کند یا خیر. سرویسها میتوانند اطلاعات بیشتری در مورد آنچه ذخیره میشوند از طریق یک متن ساده یا از طریق یک نمای سفارشی ارائه کنند. سرویسها همچنین میتوانند ظاهر دکمهای را که درخواست ذخیره را لغو میکند تغییر دهند و هنگامی که کاربر روی آن دکمه ضربه میزند، اعلان دریافت کنند. برای اطلاعات بیشتر، به صفحه مرجع
SaveInfo
مراجعه کنید.- حالت سازگاری
حالت سازگاری به سرویسهای تکمیل خودکار اجازه میدهد از ساختار مجازی دسترسی برای اهداف تکمیل خودکار استفاده کنند. این به ویژه برای ارائه عملکرد تکمیل خودکار در مرورگرهایی که به صراحت APIهای تکمیل خودکار را پیاده سازی نمی کنند مفید است.
برای آزمایش سرویس تکمیل خودکار خود با استفاده از حالت سازگاری، مرورگر یا برنامهای را که به حالت سازگاری نیاز دارد به صراحت فهرست کنید. با اجرای دستور زیر می توانید بررسی کنید که کدام بسته ها قبلاً در لیست مجاز هستند:
$ adb shell settings get global autofill_compat_mode_allowed_packages
اگر بسته ای که آزمایش می کنید در لیست نیست، با اجرای دستور زیر آن را اضافه کنید، جایی که
pkgX
بسته برنامه است:$ adb shell settings put global autofill_compat_mode_allowed_packages pkg1[resId1]:pkg2[resId1,resId2]
اگر برنامه یک مرورگر است، از
resIdx
برای تعیین شناسه منبع فیلد ورودی که حاوی URL صفحه رندر شده است استفاده کنید.
حالت سازگاری دارای محدودیت های زیر است:
- زمانی که سرویس از پرچم
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
استفاده می کند یا متدsetTrigger()
فراخوانی می شود، درخواست ذخیره راه اندازی می شود. هنگام استفاده از حالت سازگاری،FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
به طور پیشفرض تنظیم میشود. - ممکن است مقدار متن گره ها در روش
onSaveRequest(SaveRequest, SaveCallback)
در دسترس نباشد.
برای اطلاعات بیشتر در مورد حالت سازگاری، از جمله محدودیتهای مرتبط با آن، به مرجع کلاس AutofillService
مراجعه کنید.