خدمة الملء التلقائي هي تطبيق يسهّل على المستخدمين ملء النماذج من خلال إدخال البيانات في طرق عرض التطبيقات الأخرى. يمكن لخدمات الملء التلقائي أيضًا retrieving data user من طرق العرض في أحد التطبيقات وتخزينها لاستخدامها في وقتٍ لاحق. عادةً ما يتم توفير خدمات الملء التلقائي من خلال التطبيقات التي تدير بيانات المستخدمين، مثل كمديري كلمات المرور.
يسهّل Android عملية ملء النماذج باستخدام إطار عمل الملء التلقائي المتاح في Android 8.0 (المستوى 26 من واجهة برمجة التطبيقات) والإصدارات الأحدث لا يمكن للمستخدمين الاستفادة من ميزات الملء التلقائي إلا إذا كان هناك تطبيق يقدّم خدمات الملء التلقائي على أجهزتهم.
تعرض هذه الصفحة كيفية تنفيذ خدمة الملء التلقائي في تطبيقك. إذا كنت
تبحث عن عينة تعليمة برمجية توضح كيفية تنفيذ خدمةٍ ما، راجع
نموذج 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
intent. يعرض الإجراء قيمة RESULT_OK
إذا اختار المستخدم
خدمة ملء تلقائي تتطابق مع حزمة المتصل.
ملء مشاهدات العميل
تتلقّى خدمة الملء التلقائي طلبات لملء عروض العميل عندما يكون المستخدم. يتفاعل مع التطبيقات الأخرى. إذا كانت خدمة الملء التلقائي تتضمّن بيانات مستخدم تلبي الطلب، يتم إرسال البيانات في الاستجابة. يعرض نظام Android واجهة مستخدم لملء البيانات تلقائيًا بالبيانات المتاحة، كما هو موضّح في الشكل 1:
يحدد إطار عمل الملء التلقائي سير عمل لملء طرق العرض المصمّمة من أجل
تقليل وقت ربط نظام Android بخدمة الملء التلقائي ضِمن
كل طلب، يرسِل نظام Android عنصر AssistStructure
إلى الخدمة من خلال
يَتِمُّ الْآنَ الِاتِّصَالْ بِـ onFillRequest()
.
.
تفحص خدمة الملء التلقائي ما إذا كان يمكنها تلبية الطلب ببيانات المستخدم
المخزنة مسبقًا. فإذا تمكنت هذه الميزة من تلبية الطلب، فحينئذٍ يتم تسليم حزم الخدمة
البيانات في Dataset
الأخرى. تطلب الخدمة
onSuccess()
، تمرير FillResponse
يحتوي على الكائنات Dataset
. في حال عدم استجابة الخدمة
بيانات لتلبية الطلب، يتم تمرير null
إلى الطريقة onSuccess()
.
الخدمة
تستدعي onFailure()
بدلاً من ذلك في حالة حدوث خطأ أثناء معالجة الطلب. للحصول على مخطط تفصيلي
لسير العمل، يمكنك الاطّلاع على الوصف في AutofillService
.
المرجعية.
يعرض الرمز البرمجي التالي مثالاً على طريقة onFillRequest()
:
Kotlin
override fun onFillRequest( request: FillRequest, cancellationSignal: CancellationSignal, callback: FillCallback ) { // Get the structure from the request val context: List<FillContext> = request.fillContexts val structure: AssistStructure = context[context.size - 1].structure // Traverse the structure looking for nodes to fill out val parsedStructure: ParsedStructure = parseStructure(structure) // Fetch user data that matches the fields val (username: String, password: String) = fetchUserData(parsedStructure) // Build the presentation of the datasets val usernamePresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1) usernamePresentation.setTextViewText(android.R.id.text1, "my_username") val passwordPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1) passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username") // Add a dataset to the response val fillResponse: FillResponse = FillResponse.Builder() .addDataset(Dataset.Builder() .setValue( parsedStructure.usernameId, AutofillValue.forText(username), usernamePresentation ) .setValue( parsedStructure.passwordId, AutofillValue.forText(password), passwordPresentation ) .build()) .build() // If there are no errors, call onSuccess() and pass the response callback.onSuccess(fillResponse) } data class ParsedStructure(var usernameId: AutofillId, var passwordId: AutofillId) data class UserData(var username: String, var password: String)
Java
@Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) { // Get the structure from the request List<FillContext> context = request.getFillContexts(); AssistStructure structure = context.get(context.size() - 1).getStructure(); // Traverse the structure looking for nodes to fill out ParsedStructure parsedStructure = parseStructure(structure); // Fetch user data that matches the fields UserData userData = fetchUserData(parsedStructure); // Build the presentation of the datasets RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); usernamePresentation.setTextViewText(android.R.id.text1, "my_username"); RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username"); // Add a dataset to the response FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(userData.username), usernamePresentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(userData.password), passwordPresentation) .build()) .build(); // If there are no errors, call onSuccess() and pass the response callback.onSuccess(fillResponse); } class ParsedStructure { AutofillId usernameId; AutofillId passwordId; } class UserData { String username; String password; }
يمكن أن تحتوي الخدمة على أكثر من مجموعة بيانات واحدة تلبّي الطلب. في هذه الحالة، يعرض نظام Android خيارات متعدّدة، خيار واحد لكل مجموعة بيانات، في واجهة مستخدم الملء التلقائي. يوضح مثال التعليمة البرمجية التالي كيفية تقديم مجموعات بيانات متعددة في استجابة:
Kotlin
// Add multiple datasets to the response val fillResponse: FillResponse = FillResponse.Builder() .addDataset(Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(user1Data.username), username1Presentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(user1Data.password), password1Presentation) .build()) .addDataset(Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(user2Data.username), username2Presentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(user2Data.password), password2Presentation) .build()) .build()
Java
// Add multiple datasets to the response FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(user1Data.username), username1Presentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(user1Data.password), password1Presentation) .build()) .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(user2Data.username), username2Presentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(user2Data.password), password2Presentation) .build()) .build();
يمكن لخدمات الملء التلقائي التنقّل في عناصر ViewNode
في
AssistStructure
لاسترداد بيانات الملء التلقائي
اللازمة لمعالجة الطلب. يمكن لأي خدمة استرداد بيانات الملء التلقائي باستخدام
طُرق ViewNode
صف مثل getAutofillId()
.
يجب أن تتمكن الخدمة من وصف محتوى الملف الشخصي للتحقق مما إذا كان
تلبية الطلب. إنّ استخدام سمة autofillHints
هو الأسلوب
الأول الذي يجب أن تستخدمه الخدمة لوصف محتوى العرض. ومع ذلك، يجب أن تقدّم تطبيقات العميل السمة صراحةً في طرق العرض قبل أن تصبح متاحة للخدمة.
إذا لم يوفّر أحد تطبيقات العميل السمة autofillHints
يجب أن تستخدم الخدمة إرشاداتها الخاصة لوصف المحتويات.
يمكن للخدمة استخدام طرق من فئات أخرى، مثل getText()
أو getHint()
،
للحصول على معلومات عن محتوى العرض.
لمزيد من المعلومات، يُرجى الاطّلاع على تقديم نصائح بشأن
الملء التلقائي.
يوضّح المثال التالي كيفية التنقّل في AssistStructure
واسترداد data
الملء التلقائي من عنصر ViewNode
:
Kotlin
fun traverseStructure(structure: AssistStructure) { val windowNodes: List<AssistStructure.WindowNode> = structure.run { (0 until windowNodeCount).map { getWindowNodeAt(it) } } windowNodes.forEach { windowNode: AssistStructure.WindowNode -> val viewNode: ViewNode? = windowNode.rootViewNode traverseNode(viewNode) } } fun traverseNode(viewNode: ViewNode?) { if (viewNode?.autofillHints?.isNotEmpty() == true) { // If the client app provides autofill hints, you can obtain them using // viewNode.getAutofillHints(); } else { // Or use your own heuristics to describe the contents of a view // using methods such as getText() or getHint() } val children: List<ViewNode>? = viewNode?.run { (0 until childCount).map { getChildAt(it) } } children?.forEach { childNode: ViewNode -> traverseNode(childNode) } }
Java
public void traverseStructure(AssistStructure structure) { int nodes = structure.getWindowNodeCount(); for (int i = 0; i < nodes; i++) { WindowNode windowNode = structure.getWindowNodeAt(i); ViewNode viewNode = windowNode.getRootViewNode(); traverseNode(viewNode); } } public void traverseNode(ViewNode viewNode) { if(viewNode.getAutofillHints() != null && viewNode.getAutofillHints().length > 0) { // If the client app provides autofill hints, you can obtain them using // viewNode.getAutofillHints(); } else { // Or use your own heuristics to describe the contents of a view // using methods such as getText() or getHint() } for(int i = 0; i < viewNode.getChildCount(); i++) { ViewNode childNode = viewNode.getChildAt(i); traverseNode(childNode); } }
حفظ بيانات المستخدمين
تحتاج خدمة الملء التلقائي إلى بيانات المستخدمين لملء بيانات المشاهدات في التطبيقات. عندما يملؤون مستخدمو طريقة العرض نموذجًا يدويًا، سيُطلب منهم حفظ البيانات في خدمة الملء التلقائي الحالية، كما هو موضّح في الشكل 2.
لحفظ البيانات، يجب أن تشير الخدمة إلى أنّها مهتمة بتخزين
البيانات لاستخدامها في المستقبل. قبل أن يرسل نظام Android طلبًا لحفظ البيانات،
يكون هناك طلب تعبئة حيث تتاح للخدمة فرصة لملء
طرق العرض. للإشارة إلى أنّها مهتمة بحفظ البيانات، تضمّن الخدمة عنصر SaveInfo
في الاستجابة لطلب الملء. الكائن SaveInfo
يحتوي على البيانات التالية على الأقل:
- نوع بيانات المستخدم التي يتم حفظها للحصول على قائمة
SAVE_DATA
، يُرجى الاطّلاع علىSaveInfo
. - الحد الأدنى لمجموعة المشاهدات التي يجب تغييرها لتشغيل طلب حفظ.
على سبيل المثال، عادةً ما يتطلب نموذج تسجيل الدخول من المستخدم تحديث
username
وpassword
ملف شخصي لتشغيل طلب حفظ.
يرتبط كائن SaveInfo
بكائن FillResponse
، كما هو موضّح في السمة
مثال الرمز التالي:
Kotlin
override fun onFillRequest( request: FillRequest, cancellationSignal: CancellationSignal, callback: FillCallback ) { ... // Builder object requires a non-null presentation val notUsed = RemoteViews(packageName, android.R.layout.simple_list_item_1) val fillResponse: FillResponse = FillResponse.Builder() .addDataset( Dataset.Builder() .setValue(parsedStructure.usernameId, null, notUsed) .setValue(parsedStructure.passwordId, null, notUsed) .build() ) .setSaveInfo( SaveInfo.Builder( SaveInfo.SAVE_DATA_TYPE_USERNAME or SaveInfo.SAVE_DATA_TYPE_PASSWORD, arrayOf(parsedStructure.usernameId, parsedStructure.passwordId) ).build() ) .build() ... }
Java
@Override public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) { ... // Builder object requires a non-null presentation RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, null, notUsed) .setValue(parsedStructure.passwordId, null, notUsed) .build()) .setSaveInfo(new SaveInfo.Builder( SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD, new AutofillId[] {parsedStructure.usernameId, parsedStructure.passwordId}) .build()) .build(); ... }
يمكن لخدمة الملء التلقائي تنفيذ منطق للحفاظ على بيانات المستخدم في onSaveRequest()
الطريقة، والتي يتم استدعاؤها عادةً بعد انتهاء نشاط العميل أو عندما يُطلِب
تطبيق العميل commit()
.
يوضّح الرمز التالي مثالاً على طريقة onSaveRequest()
:
Kotlin
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) { // Get the structure from the request val context: List<FillContext> = request.fillContexts val structure: AssistStructure = context[context.size - 1].structure // Traverse the structure looking for data to save traverseStructure(structure) // Persist the data - if there are no errors, call onSuccess() callback.onSuccess() }
Java
@Override public void onSaveRequest(SaveRequest request, SaveCallback callback) { // Get the structure from the request List<FillContext> context = request.getFillContexts(); AssistStructure structure = context.get(context.size() - 1).getStructure(); // Traverse the structure looking for data to save traverseStructure(structure); // Persist the data - if there are no errors, call onSuccess() callback.onSuccess(); }
يجب أن تشفِّر خدمات الملء التلقائي البيانات الحسّاسة قبل الاحتفاظ بها. ومع ذلك، يمكن أن تشمل بيانات المستخدمين تصنيفات أو بيانات غير حسّاسة. على سبيل المثال، يمكن أن يتضمّن حساب المستخدم تصنيفًا يصنّف البيانات على أنّها حساب عمل أو حساب شخصي. يجب ألا تشفِّر الخدمات التصنيفات. ومن خلال عدم تشفير التصنيفات، استخدام التصنيفات في طرق عرض العروض التقديمية إذا لم يسبق للمستخدم تمت مصادقته. بعد ذلك، يمكن للخدمات استبدال التسميات بالبيانات الفعلية بعد مصادقة المستخدم.
تأجيل واجهة مستخدم حفظ الملء التلقائي
بدءًا من Android 10، إذا كنت تستخدم شاشات متعددة لتنفيذ سير عمل الملء التلقائي، على سبيل المثال، شاشة واحدة لحقل اسم المستخدم وشاشة أخرى لكلمة المرور، يمكنك تأجيل واجهة مستخدم حفظ الملء التلقائي باستخدام العلامة
SaveInfo.FLAG_DELAY_SAVE
.
في حال ضبط هذه العلامة، لن يتم تشغيل واجهة مستخدم الحفظ التلقائي عند استخدام ميزة "الملء التلقائي"
الالتزام بالسياق المرتبط بالاستجابة SaveInfo
. بدلاً من ذلك، يمكنك
استخدام نشاط منفصل ضمن المهمة نفسها لإرسال طلبات الملء المستقبلية ثم
عرض واجهة المستخدم من خلال طلب حفظ. لمزيد من المعلومات، يُرجى الاطّلاع على
SaveInfo.FLAG_DELAY_SAVE
.
طلب مصادقة المستخدم
يمكن أن توفّر خدمات الملء التلقائي مستوى إضافيًا من الأمان من خلال مطالبة العميل بالمصادقة قبل أن تتمكّن من ملء البيانات. إنّ السيناريوهات التالية هي سيناريوهات جيدة لتنفيذ مصادقة المستخدمين:
- ويجب فتح قفل بيانات المستخدمين في التطبيق باستخدام كلمة مرور أساسية أو المسح الضوئي لبصمة الإصبع.
- يجب إلغاء قفل مجموعة بيانات محددة، مثل تفاصيل بطاقة الائتمان من خلال باستخدام رمز التحقق من البطاقة (CVC).
في السيناريو الذي تتطلّب فيه الخدمة مصادقة المستخدم قبل فتح قفلاً على
البيانات، يمكن أن تقدّم الخدمة بيانات نموذجية أو تصنيفًا وتحدّد
Intent
الذي يتولّى
المصادقة. إذا كنت بحاجة إلى بيانات إضافية لمعالجة الطلب بعد اكتمال مسار مصادقة العميل، يمكنك إضافة هذه البيانات إلى الطلب.
بعد ذلك، قد يؤدي نشاط المصادقة إلى إرجاع البيانات إلى الفئة AutofillService
.
في تطبيقك.
يوضح مثال الرمز التالي كيفية تحديد أن الطلب يتطلب المصادقة:
Kotlin
val authPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply { setTextViewText(android.R.id.text1, "requires authentication") } val authIntent = Intent(this, AuthActivity::class.java).apply { // Send any additional data required to complete the request putExtra(MY_EXTRA_DATASET_NAME, "my_dataset") } val intentSender: IntentSender = PendingIntent.getActivity( this, 1001, authIntent, PendingIntent.FLAG_CANCEL_CURRENT ).intentSender // Build a FillResponse object that requires authentication val fillResponse: FillResponse = FillResponse.Builder() .setAuthentication(autofillIds, intentSender, authPresentation) .build()
Java
RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); authPresentation.setTextViewText(android.R.id.text1, "requires authentication"); Intent authIntent = new Intent(this, AuthActivity.class); // Send any additional data required to complete the request authIntent.putExtra(MY_EXTRA_DATASET_NAME, "my_dataset"); IntentSender intentSender = PendingIntent.getActivity( this, 1001, authIntent, PendingIntent.FLAG_CANCEL_CURRENT ).getIntentSender(); // Build a FillResponse object that requires authentication FillResponse fillResponse = new FillResponse.Builder() .setAuthentication(autofillIds, intentSender, authPresentation) .build();
بعد أن يُكمل النشاط مسار المصادقة، يجب أن يستدعي الأسلوب
setResult()
،
مع تمرير قيمة RESULT_OK
، وضبط العنصر EXTRA_AUTHENTICATION_RESULT
الإضافي على عنصر FillResponse
الذي يتضمّن مجموعة البيانات المعبأة. تشير رسالة الأشكال البيانية
التعليمة البرمجية التالية مثالاً على كيفية إرجاع النتيجة بمجرد
تكمل مسارات المصادقة:
Kotlin
// The data sent by the service and the structure are included in the intent val datasetName: String? = intent.getStringExtra(MY_EXTRA_DATASET_NAME) val structure: AssistStructure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE) val parsedStructure: ParsedStructure = parseStructure(structure) val (username, password) = fetchUserData(parsedStructure) // Build the presentation of the datasets val usernamePresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply { setTextViewText(android.R.id.text1, "my_username") } val passwordPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply { setTextViewText(android.R.id.text1, "Password for my_username") } // Add the dataset to the response val fillResponse: FillResponse = FillResponse.Builder() .addDataset(Dataset.Builder() .setValue( parsedStructure.usernameId, AutofillValue.forText(username), usernamePresentation ) .setValue( parsedStructure.passwordId, AutofillValue.forText(password), passwordPresentation ) .build() ).build() val replyIntent = Intent().apply { // Send the data back to the service putExtra(MY_EXTRA_DATASET_NAME, datasetName) putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse) } setResult(Activity.RESULT_OK, replyIntent)
Java
Intent intent = getIntent(); // The data sent by the service and the structure are included in the intent String datasetName = intent.getStringExtra(MY_EXTRA_DATASET_NAME); AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE); ParsedStructure parsedStructure = parseStructure(structure); UserData userData = fetchUserData(parsedStructure); // Build the presentation of the datasets RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); usernamePresentation.setTextViewText(android.R.id.text1, "my_username"); RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username"); // Add the dataset to the response FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() .setValue(parsedStructure.usernameId, AutofillValue.forText(userData.username), usernamePresentation) .setValue(parsedStructure.passwordId, AutofillValue.forText(userData.password), passwordPresentation) .build()) .build(); Intent replyIntent = new Intent(); // Send the data back to the service replyIntent.putExtra(MY_EXTRA_DATASET_NAME, datasetName); replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse); setResult(RESULT_OK, replyIntent);
في السيناريو الذي يجب فيه فتح قفل مجموعة بيانات بطاقة الائتمان، يمكن للخدمة عرض واجهة مستخدم تطلب إدخال رمز التحقّق من البطاقة (CVC). يمكنك إخفاء البيانات إلى أن تتم إلغاء قفل مجموعة البيانات من خلال تقديم بيانات نموذجية، مثل اسم المصرف والأرقام الأربعة الأخيرة من رقم بطاقة الائتمان. يوضّح المثال التالي كيفية طلب المصادقة لمجموعة بيانات وإخفاء البيانات إلى أن يقدّم المستخدم رقم التحقق من البطاقة:
Kotlin
// Parse the structure and fetch payment data val parsedStructure: ParsedStructure = parseStructure(structure) val paymentData: Payment = fetchPaymentData(parsedStructure) // Build the presentation that shows the bank and the last four digits of the // credit card number, such as 'Bank-1234' val maskedPresentation: String = "${paymentData.bank}-" + paymentData.creditCardNumber.substring(paymentData.creditCardNumber.length - 4) val authPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1).apply { setTextViewText(android.R.id.text1, maskedPresentation) } // Prepare an intent that displays the UI that asks for the CVC val cvcIntent = Intent(this, CvcActivity::class.java) val cvcIntentSender: IntentSender = PendingIntent.getActivity( this, 1001, cvcIntent, PendingIntent.FLAG_CANCEL_CURRENT ).intentSender // Build a FillResponse object that includes a Dataset that requires authentication val fillResponse: FillResponse = FillResponse.Builder() .addDataset( Dataset.Builder() // The values in the dataset are replaced by the actual // data once the user provides the CVC .setValue(parsedStructure.creditCardId, null, authPresentation) .setValue(parsedStructure.expDateId, null, authPresentation) .setAuthentication(cvcIntentSender) .build() ).build()
Java
// Parse the structure and fetch payment data ParsedStructure parsedStructure = parseStructure(structure); Payment paymentData = fetchPaymentData(parsedStructure); // Build the presentation that shows the bank and the last four digits of the // credit card number, such as 'Bank-1234' String maskedPresentation = paymentData.bank + "-" + paymentData.creditCardNumber.subString(paymentData.creditCardNumber.length - 4); RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); authPresentation.setTextViewText(android.R.id.text1, maskedPresentation); // Prepare an intent that displays the UI that asks for the CVC Intent cvcIntent = new Intent(this, CvcActivity.class); IntentSender cvcIntentSender = PendingIntent.getActivity( this, 1001, cvcIntent, PendingIntent.FLAG_CANCEL_CURRENT ).getIntentSender(); // Build a FillResponse object that includes a Dataset that requires authentication FillResponse fillResponse = new FillResponse.Builder() .addDataset(new Dataset.Builder() // The values in the dataset are replaced by the actual // data once the user provides the CVC .setValue(parsedStructure.creditCardId, null, authPresentation) .setValue(parsedStructure.expDateId, null, authPresentation) .setAuthentication(cvcIntentSender) .build()) .build();
بعد أن يتحقق النشاط من صحة رمز التحقق من البطاقة (CVC)، يجب أن يطلب طريقة setResult()
.
تمرير قيمة RESULT_OK
، وضبط EXTRA_AUTHENTICATION_RESULT
الإضافية على
عنصر Dataset
يحتوي على رقم بطاقة الائتمان وتاريخ انتهاء الصلاحية. تستبدل مجموعة البيانات
الجديدة مجموعة البيانات التي تتطلّب المصادقة، ويتم ملء الملفات الشخصية
على الفور. يعرض الرمز البرمجي التالي مثالاً على كيفية عرض
مجموعة البيانات بعد أن يقدّم المستخدم رقم التحقق من البطاقة:
Kotlin
// Parse the structure and fetch payment data. val parsedStructure: ParsedStructure = parseStructure(structure) val paymentData: Payment = fetchPaymentData(parsedStructure) // Build a non-null RemoteViews object to use as the presentation when // creating the Dataset object. This presentation isn't actually used, but the // Builder object requires a non-null presentation. val notUsed = RemoteViews(packageName, android.R.layout.simple_list_item_1) // Create a dataset with the credit card number and expiration date. val responseDataset: Dataset = Dataset.Builder() .setValue( parsedStructure.creditCardId, AutofillValue.forText(paymentData.creditCardNumber), notUsed ) .setValue( parsedStructure.expDateId, AutofillValue.forText(paymentData.expirationDate), notUsed ) .build() val replyIntent = Intent().apply { putExtra(EXTRA_AUTHENTICATION_RESULT, responseDataset) }
Java
// Parse the structure and fetch payment data. ParsedStructure parsedStructure = parseStructure(structure); Payment paymentData = fetchPaymentData(parsedStructure); // Build a non-null RemoteViews object to use as the presentation when // creating the Dataset object. This presentation isn't actually used, but the // Builder object requires a non-null presentation. RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1); // Create a dataset with the credit card number and expiration date. Dataset responseDataset = new Dataset.Builder() .setValue(parsedStructure.creditCardId, AutofillValue.forText(paymentData.creditCardNumber), notUsed) .setValue(parsedStructure.expDateId, AutofillValue.forText(paymentData.expirationDate), notUsed) .build(); Intent replyIntent = new Intent(); replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, responseDataset);
تنظيم البيانات في مجموعات منطقية
يجب أن تنظّم خدمات الملء التلقائي البيانات في مجموعات منطقية تعزل المفاهيم من نطاقات مختلفة. في هذه الصفحة، تتم الإشارة إلى هذه المجموعات المنطقية إليها كأقسام. توضح القائمة التالية أمثلة نموذجية الأقسام والحقول:
- بيانات الاعتماد، التي تشمل حقلَي اسم المستخدم وكلمة المرور.
- العنوان، بما في ذلك حقول الشارع والمدينة والولاية والرمز البريدي.
- معلومات الدفع، التي تتضمّن رقم بطاقة الائتمان وتاريخ انتهاء الصلاحية وحقل رمز التحقّق
إنّ خدمة الملء التلقائي التي تقسم البيانات بشكل صحيح يمكنها حماية dataمستخدميها بشكل أفضل من خلال عدم عرض بيانات من أكثر من قسم واحد في مجموعة data. على سبيل المثال، لا تحتاج مجموعة البيانات التي تتضمن بيانات اعتماد إلى تضمين معلومات الدفع يتيح تنظيم البيانات في الأقسام للخدمة عرض الحد الأدنى من المعلومات ذات الصلة المطلوبة لتلبية الطلب.
يتيح تنظيم البيانات على أقسام للخدمات ملء الأنشطة التي من أقسام متعددة مع إرسال أقل عدد ممكن من البيانات إلى تطبيق العميل. على سبيل المثال، يمكنك التفكير في نشاط يتضمّن مشاهدات لاسم المستخدم وكلمة المرور والشارع والمدينة، فضلاً عن خدمة الملء التلقائي التي البيانات التالية:
قسم | الحقل 1 | الحقل 2 |
---|---|---|
بيانات الاعتماد | اسم_المستخدم_للعمل | work_password |
personal_username | Personal_password | |
العنوان | شارع_العمل | مدينة العمل |
personal_street | مدينة_شخصية |
يمكن للخدمة إعداد مجموعة بيانات تتضمّن قسم بيانات الاعتماد لكلٍّ من الحسابَين الشخصي وحساب العمل. عندما يختار المستخدم مجموعة بيانات، يمكن أن يقدّم ردّ الملء التلقائي التالي إما عنوان العمل أو العنوان الشخصي، استنادًا إلى اختيار المستخدم الأول.
يمكن للخدمة تحديد الحقل الذي أنشأ الطلب من خلال استدعاء
isFocused()
أثناء اجتياز الكائن AssistStructure
. يتيح ذلك للخدمة FillResponse
ببيانات التقسيم المناسبة.
الملء التلقائي لرمز الرسالة القصيرة لمرة واحدة
يمكن أن تساعد خدمة الملء التلقائي المستخدم في ملء الرموز لمرة واحدة التي يتم إرسالها عبر الرسائل القصيرة SMS باستخدام واجهة برمجة التطبيقات SMS Retriever API
لاستخدام هذه الميزة، يجب استيفاء المتطلبات التالية:
- تعمل خدمة الملء التلقائي على Android 9 (المستوى 28 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث.
- يمنح المستخدم موافقته على خدمة الملء التلقائي لقراءة الرموز التي تُستخدم لمرة واحدة من رسالة قصيرة
- التطبيق الذي تقدِّم له ميزة الملء التلقائي لا يستخدم حاليًا واجهة برمجة التطبيقات SMS Retriever API لقراءة الرموز التي تُستخدم لمرة واحدة
يمكن لخدمة الملء التلقائي استخدام SmsCodeAutofillClient
،
المتاح من خلال الاتصال بالرقم SmsCodeRetriever.getAutofillClient()
من "خدمات Google Play"
19.0.56 أو الإصدارات الأحدث.
في ما يلي الخطوات الأساسية لاستخدام واجهة برمجة التطبيقات هذه في خدمة الملء التلقائي:
- في خدمة الملء التلقائي، استخدِم
hasOngoingSmsRequest
. منSmsCodeAutofillClient
لتحديد ما إذا كانت هناك أيّ طلبات نشط لاسم حزمة التطبيق الذي تملأه تلقائيًا. يجب ألا تعرض خدمة الملء التلقائي سوى طلب اقتراح إذا كانت هذه القيمة هيfalse
. - في خدمة الملء التلقائي، استخدِم
checkPermissionState
. منSmsCodeAutofillClient
للتحقق مما إذا كانت خدمة الملء التلقائي تحتوي على إذن لملء الرموز التي تُستخدم لمرة واحدة تلقائيًا. يمكن أن تكون حالة الإذن هذهNONE
،GRANTED
أوDENIED
. يجب أن تعرض خدمة الملء التلقائي طلبًا باقتراح لحالةNONE
وGRANTED
- وفي نشاط مصادقة الملء التلقائي، استخدم
إذن "
SmsRetriever.SEND_PERMISSION
" لتسجيلBroadcastReceiver
جارٍ الاستماع لمدةSmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION
لاستلام الرسالة القصيرة SMS الرمز عندما يكون متاحًا. اتصل على
startSmsCodeRetriever
فيSmsCodeAutofillClient
لبدء الاستماع إلى الرموز التي تُستخدم لمرة واحدة والتي يتم إرسالها عبر الرسائل القصيرة. إذا منح المستخدم أذونات لخدمة الملء التلقائي لاسترداد البيانات لمرّة واحدة الرموز من SMS، هذا البحث عن رسائل SMS التي تم استلامها خلال آخر خمس إلى خمس دقائق من الآن.إذا كانت خدمة الملء التلقائي تحتاج إلى طلب إذن المستخدم لقراءة رمز مخصص لمرة واحدة، قد يتعذّر عرض الرمز
Task
الذي يعرضهstartSmsCodeRetriever
مع عرضResolvableApiException
. في هذه الحالة، عليك استدعاء الأسلوبResolvableApiException.startResolutionForResult()
لعرض مربع حوار الموافقة لطلب الإذن.احصل على نتيجة رمز الرسالة القصيرة SMS من رسالة intent ثم اعرض الرسالة القصيرة. الرمز كاستجابة الملء التلقائي.
سيناريوهات الملء التلقائي المتقدّمة
- الدمج مع لوحة المفاتيح
- يسمح النظام الأساسي بلوحات المفاتيح بدءًا من Android 11 ومحررات أسلوب الإدخال الأخرى (أدوات تحرير أسلوب الإدخال) لعرض اقتراحات الملء التلقائي بشكل مضمّن، بدلاً من استخدام قائمة منسدلة. لمزيد من المعلومات حول كيفية إتاحة خدمة الملء التلقائي لهذه الميزة، يُرجى الاطّلاع على مقالة دمج ميزة الملء التلقائي مع اللوحات المفاتيح.
- تقسيم مجموعات البيانات على صفحات
- يمكن أن يتجاوز ردّ الملء التلقائي الكبير حجم المعاملة المسموح به للكائن
Binder
الذي يمثّل الكائن القابل للإزالة المطلوب لمعالجة الطلب. لمنع نظام Android من طرح استثناء في هذه السيناريوهات، يمكنك إبقاءFillResponse
صغيرًا من خلال إضافة 20 عنصرًاDataset
كحد أقصى في المرة الواحدة. إذا كان ردّك بحاجة إلى المزيد من مجموعات البيانات، يمكنك إضافة مجموعة بيانات تُعلم المستخدمين بوجود المزيد من المعلومات وتسترجع المجموعة التالية من مجموعات البيانات عند اختيارها. لمزيد من المعلومات، يُرجى الاطّلاع علىaddDataset(Dataset)
. - حفظ تقسيم البيانات في شاشات متعددة
غالبًا ما تقسّم التطبيقات بيانات المستخدم على شاشات متعددة في النشاط نفسه، خاصةً في الأنشطة المستخدمة لإنشاء حساب مستخدم جديد. على سبيل المثال، تطلب الشاشة الأولى اسم مستخدم، وإذا كان اسم المستخدم متاحًا، تطلب الشاشة الثانية كلمة مرور. في هذه الحالات، يجب أن تنتظر خدمة الملء التلقائي إلى أن يُدخل المستخدم كلاً من الحقلين قبل أن يتم عرض واجهة مستخدِم حفظ الملء التلقائي. اتّبِع الخطوات التالية للتأكّد من معالجة هذه السيناريوهات:
- في طلب الملء الأول، أضِف حِزمة حالة العميل في الاستجابة التي تحتوي على أرقام تعريف الملء التلقائي للحقول الجزئية الظاهرة على الشاشة.
- في طلب الملء الثاني،
استرجع حِزمة حالة العميل، واحصل على معرّفات الملء التلقائي التي تم ضبطها
في الطلب السابق من حالة العميل، وأضِف هذه المعرّفات والعلامة
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
إلى عنصرSaveInfo
المستخدَم في الردّ الثاني. - في طلب الحفظ،
استخدِم عناصر
FillContext
المناسبة للحصول على قيمة كل حقل. هناك سياق ملء واحد لكل طلب ملء.
ولمزيد من المعلومات، يُرجى الاطّلاع على الحفظ عند تقسيم البيانات على عدة شاشات.
- توفير منطق الإعداد والتقسيم لكل طلب
في كل مرة يتم فيها إدخال طلب ملء تلقائي، يرتبط نظام Android بـ الخدمة واستدعاء طريقة
onConnected()
. بعد معالجة الخدمة للطلب، يستدعي نظام AndroidonDisconnected()
وإلغاء الربط من الخدمة. يمكنك تنفيذ السمة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
لتحديد رقم تعريف المورد لحقل الإدخال الذي يحتوي على عنوان URL. في الصفحة المعروضة
يخضع "وضع التوافق" للقيود التالية:
- يتم تشغيل طلب حفظ عندما تستخدم الخدمة
علم
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
أوsetTrigger()
المريض. يتم ضبطFLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
تلقائيًا في الحالات التالية: باستخدام وضع التوافق. - قد لا تكون القيمة النصية للعُقد متاحة في
onSaveRequest(SaveRequest, SaveCallback)
.
لمزيد من المعلومات عن وضع التوافق، بما في ذلك القيود
المرتبطة به، اطّلِع على مرجع فئة
AutofillService
.