שירות מילוי אוטומטי הוא אפליקציה שמזריקה נתונים לתצוגות של אפליקציות אחרות כדי לעזור למשתמשים למלא טפסים בקלות רבה יותר. שירותי מילוי אוטומטי יכולים גם לאחזר נתוני משתמשים מהתצוגות באפליקציה ולאחסן אותם לשימוש במועד מאוחר יותר. שירותי מילוי אוטומטי ניתנים בדרך כלל על ידי אפליקציות שמנהלות את נתוני המשתמשים, כמו מנהלי סיסמאות.
עם Android, קל יותר למלא טפסים בעזרת המסגרת למילוי אוטומטי Android מגרסה 8.0 (רמת API 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()
ה-method של המחלקה AutofillManager
.
אם האפליקציה היא לא שירות המילוי האוטומטי הנוכחי, היא יכולה לבקש מהמשתמש לשנות את הגדרות המילוי האוטומטי באמצעות הכוונה ACTION_REQUEST_SET_AUTOFILL_SERVICE
. הכוונה מחזירה את הערך RESULT_OK
אם המשתמש בחר שירות מילוי אוטומטי שתואם לחבילה של מבצע הקריאה החוזרת.
מילוי תצוגות של לקוחות
שירות המילוי האוטומטי מקבל בקשות למלא תצוגות של לקוח כאשר המשתמש מקיימים אינטראקציה עם אפליקציות אחרות. אם בשירות המילוי האוטומטי יש נתוני משתמשים שעומדים בדרישות של הבקשה, המערכת שולחת את הנתונים בתשובה. מערכת Android מציגה ממשק המשתמש של המילוי האוטומטי עם הנתונים הזמינים, כפי שמוצג באיור 1:
המסגרת של המילוי האוטומטי מגדירה תהליך עבודה למילוי תצוגות, שנועד
לצמצם את משך הזמן שבו מערכת Android מקושרת לשירות המילוי האוטומטי. לחשבון
בכל בקשה, מערכת Android שולחת לשירות אובייקט AssistStructure
באמצעות
קריאה ל-onFillRequest()
.
שירות המילוי האוטומטי בודק אם הוא יכול לענות על הבקשה באמצעות נתוני משתמשים ששמורים בו. אם השירות יכול לענות על הבקשה, הוא יארגן את הנתונים באובייקטים של Dataset
. קריאות השירות
onSuccess()
אמצעי תשלום, העברת FillResponse
שמכיל את האובייקטים Dataset
. אם לשירות אין נתונים כדי לענות על הבקשה, הוא מעביר את null
לשיטה onSuccess()
.
אם מתרחשת שגיאה בעיבוד הבקשה, השירות קורא במקום זאת ל-method onFailure()
. הסבר מפורט על תהליך העבודה זמין בתיאור בדף העזרה AutofillService
.
הקוד הבא מציג דוגמה ל-method 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
, השירות חייב להשתמש בהיוריסטיקה משלו כדי לתאר את התוכן.
השירות יכול להשתמש ב-methods של מחלקות אחרות, כמו getText()
או getHint()
,
כדי לקבל מידע על תוכן התצוגה.
מידע נוסף זמין במאמר הוספת רמזים למילוי אוטומטי.
הדוגמה הבאה מראה איך לעבור את AssistStructure
ולאחזר
מילוי אוטומטי של נתונים מאובייקט 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(); }
שירותי מילוי אוטומטי חייבים להצפין מידע אישי רגיש לפני שמאחסנים אותו. אבל, לפעמים נתוני המשתמשים יכולים לכלול תוויות או נתונים לא רגישים. לדוגמה, חשבון משתמש יכול לכלול תווית שמסמנת את הנתונים כחשבון עסקי או אישי. אסור להצפין תוויות בשירותים. אם לא להצפין תוויות, יכולים להשתמש בתוויות בתצוגות של מצגות אם המשתמש לא מאומת. לאחר מכן, שירותים יכולים להחליף את התוויות בנתונים בפועל אחרי שהמשתמש מבצע אימות.
דחיית ממשק המשתמש של שמירת המילוי האוטומטי
החל מגרסה 10 של Android, אם משתמשים במספר מסכים כדי להטמיע תהליך עבודה של מילוי אוטומטי – לדוגמה, מסך אחד לשדה שם המשתמש ומסך אחר לסיכה – אפשר לדחות את ממשק המשתמש לשמירת המילוי האוטומטי באמצעות הדגל SaveInfo.FLAG_DELAY_SAVE
.
אם הדגל הזה מוגדר, ממשק המשתמש של שמירת המילוי האוטומטי לא מופעל כשהמילוי האוטומטי לא מופעל
ההקשר המשויך לתשובה SaveInfo
מחויב. במקום זאת, אפשר להשתמש בפעילות נפרדת באותה משימה כדי לשלוח בקשות מילוי עתידיות, ואז להציג את ממשק המשתמש באמצעות בקשת שמירה. מידע נוסף זמין במאמר הבא:
SaveInfo.FLAG_DELAY_SAVE
דרישת אימות של המשתמש
שירותי מילוי אוטומטי יכולים לספק רמה נוספת של אבטחה על ידי דרישה מהמשתמש לבצע אימות לפני שהוא יכול למלא תצוגות. התרחישים הבאים מתאימים להטמעת אימות משתמשים:
- כדי לבטל את נעילת נתוני המשתמש באפליקציה, צריך להשתמש בסיסמה ראשית או בסריקת טביעת אצבע.
- צריך לבטל את הנעילה של מערך נתונים ספציפי, למשל פרטי כרטיס אשראי עד באמצעות קוד אימות כרטיס (CVC).
בתרחיש שבו השירות דורש אימות משתמש לפני ביטול הנעילה
את הנתונים, השירות יכול להציג נתונים סטנדרטיים או תווית ולציין
Intent
שמטפל ב-
אימות. אם דרושים נתונים נוספים כדי לעבד את הבקשה לאחר
תהליך האימות הסתיים, אפשר להוסיף נתונים כאלה ל-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). אפשר להסתיר את הנתונים עד שמערך הנתונים על ידי הצגת נתונים סטנדרטיים, כמו שם הבנק ארבע הספרות האחרונות של מספר כרטיס האשראי. בדוגמה הבאה מוסבר איך לדרוש אימות של מערך נתונים ולהסתיר את הנתונים עד שהמשתמש יספק את מספר ה-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
שמכיל את מספר כרטיס האשראי ותאריך התפוגה.
מערך נתונים חדש מחליף את מערך הנתונים שדורש אימות, והתצוגות
ימולאו באופן מיידי. הקוד הבא מציג דוגמה לאופן שבו מחזירים את הפונקציה
מערך הנתונים לאחר שהמשתמש מספק את קוד האימות (CVC):
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);
ארגנו את הנתונים בקבוצות לוגיות
שירותי מילוי אוטומטי חייבים לארגן את הנתונים בקבוצות לוגיות שמבודדות מושגים מדומיינים שונים. בדף הזה, הקבוצות הלוגיות האלה נקראות מחיצות. ברשימה הבאה מוצגות דוגמאות אופייניות למחיצות ולשדות:
- פרטי כניסה, שכוללים את השדות 'שם משתמש' ו'סיסמה'.
- כתובת, כולל שדות של רחוב, עיר, מדינה ומיקוד.
- פרטי התשלום, כולל מספר כרטיס האשראי, תאריך התפוגה וקוד האימות.
שירות מילוי אוטומטי שמחלק בצורה נכונה את הנתונים למחיצות יכולה להגן טוב יותר על של המשתמשים שלו בכך שלא יחשפו נתונים מכמה מחיצה של הכיתובים. לדוגמה, מערך נתונים שכולל פרטי כניסה לא צריך לכלול פרטי תשלום. ארגון הנתונים במחיצות מאפשר השירות שלך כדי לחשוף את הכמות המינימלית של מידע רלוונטי שנדרש למלא בקשה.
ארגון נתונים במחיצות מאפשר לשירותים למלא פעילויות צפיות ממחיצות מרובות בזמן שליחת הכמות המינימלית של אל אפליקציית הלקוח. לדוגמה, נבחן פעילות שכוללת צפיות עבור שם משתמש, סיסמה, רחוב ועיר, ושירות מילוי אוטומטי שכולל את הנתונים הבאים:
מחיצה | שדה 1 | שדה 2 |
---|---|---|
פרטי כניסה | שם משתמש_עבודה | work_password |
personal_username | סיסמה_אישית | |
כתובת | רחוב_עבודה | work_city |
Individual_street | Individual_city |
השירות יכול להכין מערך נתונים שכולל את מחיצת פרטי הכניסה עבור גם לחשבון לצורכי עבודה וגם לחשבונות לשימוש אישי. כשהמשתמש בוחר מערך נתונים, תשובה אוטומטית של מילוי אוטומטי יכולה לספק כתובת עבודה או כתובת בהתאם לבחירה הראשונה של המשתמש.
שירות יכול לזהות את השדה שממנו הגיעה הבקשה על ידי קריאה לשיטה isFocused()
במהלך סריקה של האובייקט AssistStructure
. הפעולה הזו מאפשרת
שירות כדי להכין FillResponse
עם נתוני המחיצות המתאימים.
מילוי אוטומטי של קוד חד-פעמי ב-SMS
שירות המילוי האוטומטי יכול לעזור למשתמש למלא קודים חד-פעמיים שנשלחים ב-SMS באמצעות SMS Retriever API.
כדי להשתמש בתכונה הזו, צריכים להתקיים התנאים הבאים:
- שירות המילוי האוטומטי פועל ב-Android מגרסה 9 (API ברמה 28) ואילך.
- המשתמש מביע הסכמה לכך ששירות המילוי האוטומטי יקרא קודים חד-פעמיים מ: הודעת SMS.
- האפליקציה שעבורה אתם מספקים מילוי אוטומטי לא משתמשת ממשק API של SMS Retriever לקריאת קודים חד-פעמיים.
שירות המילוי האוטומטי יכול להשתמש ב-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
כדי לקבל את התוצאה של קוד ה-SMS כשהיא תהיה זמינה. חיוג אל
startSmsCodeRetriever
ב-SmsCodeAutofillClient
כדי להתחיל להאזין לקודים חד-פעמיים שנשלחו ב-SMS. אם המשתמש מעניק הרשאות לשירות המילוי האוטומטי לאחזור חד-פעמי מ-SMS, מתבצע חיפוש של הודעות SMS שהתקבלו באחת עד חמש דקות מעכשיו.אם שירות המילוי האוטומטי צריך לבקש מהמשתמש הרשאה לקרוא קודים חד-פעמיים, יכול להיות שה-
Task
שהחזירstartSmsCodeRetriever
ייכשל ויוחזרResolvableApiException
. במקרה כזה, צריך להפעיל את השיטהResolvableApiException.startResolutionForResult()
כדי להציג תיבת דו-שיח להבעת הסכמה לבקשת ההרשאה.מקבלים את התוצאה של קוד ה-SMS מהכוונה, ואז מחזירים את קוד ה-SMS בתור תגובה למילוי אוטומטי.
תרחישים מתקדמים של מילוי אוטומטי
- שילוב עם מקלדת
- החל מ-Android 11, הפלטפורמה מאפשרת שימוש במקלדות ועורכי שיטות קלט אחרים (IME) להציג הצעות למילוי אוטומטי בתוך השורה, במקום להשתמש בתפריט נפתח. ניתן לקרוא מידע נוסף על האופן שבו שירות המילוי האוטומטי יכול לתמוך בכך הפונקציונליות הזו זמינה במאמר שילוב המילוי האוטומטי עם מקלדות.
- עימוד של מערכי נתונים
- תגובה גדולה של מילוי אוטומטי עלולה לחרוג מגודל העסקה המותר של
אובייקט
Binder
שמייצג את שנדרש כדי לעבד את הבקשה. כדי למנוע ממערכת Android להפעיל חריגה בתרחישים האלה, אפשר להקטין אתFillResponse
על ידי הוספה של לא יותר מ-20 אובייקטים מסוגDataset
בכל פעם. אם התשובה שלכם צריכה עוד מערכי נתונים, תוכלו להוסיף מערך נתונים שיודיע למשתמשים שיש מידע נוסף, ויאחזר את הקבוצה הבאה של מערכי הנתונים כשבוחרים בו. מידע נוסף זמין במאמרaddDataset(Dataset)
. - שמירה של פיצול נתונים בכמה מסכים
אפליקציות מפצלות לעיתים קרובות נתוני משתמשים בכמה מסכים באותה פעילות, במיוחד בפעילויות שמשמשות ליצירת חשבון משתמש חדש. לדוגמה, תתבקשו להזין שם משתמש, ואם שם המשתמש זמין, במסך שני תוצג בקשה לסיסמה. במצבים כאלה, שירות המילוי האוטומטי צריך להמתין עד שהמשתמש יזין את לפני שניתן להציג את השדות שלפני ממשק המשתמש לשמירת המילוי האוטומטי. מעקב כדי לטפל בתרחישים כאלה:
- בבקשה למילוי הבקשה הראשונה, הוספה של חבילת מצב לקוח בתשובה שמכילה את מזהי המילוי האוטומטי של השדות החלקיים שמופיעה במסך.
- בבקשת המילוי השנייה, מאחזרים את החבילה של מצב הלקוח, מקבלים את המזהים של המילוי האוטומטי שהוגדרו בבקשה הקודמת ממצב הלקוח, ומוסיפים את המזהים האלה ואת הדגל
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE
לאובייקטSaveInfo
שמשמש בתגובה השנייה. - בבקשת השמירה, משתמשים באובייקטים המתאימים של
FillContext
כדי לקבל את הערך של כל שדה. יש הקשר מילוי אחד לכל בקשת מילוי.
מידע נוסף זמין במאמר שמירה כשהנתונים מחולקים לכמה מסכים.
- לספק לוגיקה של אתחול ופירוק לכל בקשה
בכל פעם שמופיעה בקשה למילוי אוטומטי, מערכת Android מקשרת את השירות ומפעילה את השיטה
onConnected()
שלו. לאחר שהשירות יעבד את הבקשה, מערכת Android תפעיל את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
הפניה לכיתה.