অটোফিল পরিষেবা তৈরি করুন

অটোফিল সার্ভিস হলো এমন একটি অ্যাপ যা অন্য অ্যাপের ভিউতে ডেটা যুক্ত করার মাধ্যমে ব্যবহারকারীদের ফর্ম পূরণ করা সহজ করে তোলে। অটোফিল সার্ভিস কোনো অ্যাপের ভিউ থেকে ব্যবহারকারীর ডেটা সংগ্রহ করে পরবর্তী সময়ে ব্যবহারের জন্য সংরক্ষণও করতে পারে। অটোফিল সার্ভিস সাধারণত সেইসব অ্যাপ দ্বারা সরবরাহ করা হয় যেগুলো ব্যবহারকারীর ডেটা পরিচালনা করে, যেমন পাসওয়ার্ড ম্যানেজার।

অ্যান্ড্রয়েড ৮.০ (এপিআই লেভেল ২৬) এবং এর পরবর্তী সংস্করণগুলোতে উপলব্ধ অটোফিল ফ্রেমওয়ার্কের মাধ্যমে ফর্ম পূরণ করা আরও সহজ হয়ে যায়। ব্যবহারকারীরা কেবল তখনই অটোফিল ফিচারের সুবিধা নিতে পারেন, যখন তাদের ডিভাইসে অটোফিল পরিষেবা প্রদানকারী কোনো অ্যাপ থাকে।

এই পৃষ্ঠায় দেখানো হয়েছে কিভাবে আপনার অ্যাপে একটি অটোফিল সার্ভিস ইমপ্লিমেন্ট করতে হয়। আপনি যদি সার্ভিস ইমপ্লিমেন্ট করার পদ্ধতি দেখানো কোনো কোড স্যাম্পল খুঁজে থাকেন, তাহলে Java বা Kotlin- এ লেখা 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 রিসোর্সটি এমন একটি অ্যাক্টিভিটি নির্দিষ্ট করে যা ব্যবহারকারীদের সার্ভিসটি কনফিগার করার সুযোগ দেয়। নিম্নলিখিত উদাহরণটি service_configuration XML রিসোর্সটি প্রদর্শন করে:

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

এক্সএমএল রিসোর্স সম্পর্কে আরও তথ্যের জন্য, অ্যাপ রিসোর্স ওভারভিউ দেখুন।

পরিষেবাটি সক্রিয় করতে প্রম্পট করুন

একটি অ্যাপ তখনই অটোফিল সার্ভিস হিসেবে ব্যবহৃত হয়, যখন এটি BIND_AUTOFILL_SERVICE পারমিশনটি ঘোষণা করে এবং ব্যবহারকারী ডিভাইস সেটিংসে এটি সক্রিয় করে। একটি অ্যাপ AutofillManager ক্লাসের hasEnabledAutofillServices() মেথডটি কল করার মাধ্যমে যাচাই করতে পারে যে সার্ভিসটি সক্রিয় আছে কি না।

অ্যাপটি যদি বর্তমান অটোফিল সার্ভিস না হয়, তাহলে এটি ACTION_REQUEST_SET_AUTOFILL_SERVICE ইন্টেন্ট ব্যবহার করে ব্যবহারকারীকে অটোফিল সেটিংস পরিবর্তন করার জন্য অনুরোধ করতে পারে। যদি ব্যবহারকারী কলারের প্যাকেজের সাথে মেলে এমন একটি অটোফিল সার্ভিস নির্বাচন করেন, তাহলে ইন্টেন্টটি RESULT_OK ভ্যালুটি রিটার্ন করে।

ক্লায়েন্টের মতামত পূরণ করুন

ব্যবহারকারী যখন অন্যান্য অ্যাপের সাথে ইন্টারঅ্যাক্ট করেন, তখন অটোফিল সার্ভিস ক্লায়েন্ট ভিউ পূরণ করার জন্য অনুরোধ গ্রহণ করে। যদি অটোফিল সার্ভিসের কাছে অনুরোধটি পূরণ করার মতো ব্যবহারকারীর ডেটা থাকে, তবে এটি রেসপন্সে সেই ডেটা পাঠিয়ে দেয়। অ্যান্ড্রয়েড সিস্টেম উপলব্ধ ডেটা সহ একটি অটোফিল UI প্রদর্শন করে, যেমনটি চিত্র ১-এ দেখানো হয়েছে:

অটোফিল সাজেশন ড্রপডাউন একটি উপলব্ধ ডেটাসেট দেখাচ্ছে।
চিত্র ১. একটি ডেটাসেট প্রদর্শনকারী অটোফিল ইউআই।

অটোফিল ফ্রেমওয়ার্কটি ভিউ পূরণ করার জন্য একটি কর্মপ্রবাহ নির্ধারণ করে, যা অ্যান্ড্রয়েড সিস্টেমকে অটোফিল সার্ভিসের সাথে যুক্ত থাকার সময় কমানোর জন্য ডিজাইন করা হয়েছে। প্রতিটি অনুরোধে, অ্যান্ড্রয়েড সিস্টেম onFillRequest() মেথডটি কল করার মাধ্যমে সার্ভিসে একটি AssistStructure অবজেক্ট পাঠায়।

অটোফিল সার্ভিসটি পরীক্ষা করে দেখে যে, এটি পূর্বে সংরক্ষিত ব্যবহারকারীর ডেটা দিয়ে অনুরোধটি পূরণ করতে পারবে কি না। যদি এটি অনুরোধটি পূরণ করতে পারে, তবে সার্ভিসটি ডেটাগুলোকে 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;
}

একটি সার্ভিসের একাধিক ডেটাসেট থাকতে পারে যা অনুরোধটি পূরণ করে। এক্ষেত্রে, অ্যান্ড্রয়েড সিস্টেম অটোফিল UI-তে একাধিক অপশন দেখায়—প্রতিটি ডেটাসেটের জন্য একটি করে। নিচের কোড উদাহরণটি দেখায় কিভাবে একটি রেসপন্সে একাধিক ডেটাসেট সরবরাহ করতে হয়:

কোটলিন

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

অটোফিল সার্ভিসগুলো অনুরোধটি পূরণ করার জন্য প্রয়োজনীয় অটোফিল ডেটা সংগ্রহ করতে AssistStructure এর ViewNode অবজেক্টগুলো নেভিগেট করতে পারে। একটি সার্ভিস ViewNode ক্লাসের মেথড, যেমন getAutofillId() , ব্যবহার করে অটোফিল ডেটা সংগ্রহ করতে পারে।

একটি সার্ভিসকে অবশ্যই একটি ভিউ-এর বিষয়বস্তু বর্ণনা করতে সক্ষম হতে হবে, যাতে এটি অনুরোধটি পূরণ করতে পারবে কিনা তা যাচাই করা যায়। একটি ভিউ-এর বিষয়বস্তু বর্ণনা করার জন্য সার্ভিসকে অবশ্যই প্রথম যে পদ্ধতিটি ব্যবহার করতে হবে তা হলো autofillHints অ্যাট্রিবিউট। তবে, সার্ভিসটির কাছে অ্যাট্রিবিউটটি উপলব্ধ হওয়ার আগে ক্লায়েন্ট অ্যাপগুলোকে তাদের ভিউ-তে এটি সুস্পষ্টভাবে প্রদান করতে হবে।

যদি কোনো ক্লায়েন্ট অ্যাপ ` autofillHints অ্যাট্রিবিউটটি প্রদান না করে, তবে একটি সার্ভিসকে অবশ্যই বিষয়বস্তু বর্ণনা করার জন্য নিজস্ব হিউরিস্টিকস ব্যবহার করতে হবে। সার্ভিসটি ভিউ-এর বিষয়বস্তু সম্পর্কে তথ্য পেতে অন্যান্য ক্লাসের মেথড, যেমন getText() বা getHint() , ব্যবহার করতে পারে। আরও তথ্যের জন্য, `Provide hints for autofill` দেখুন।

নিম্নলিখিত উদাহরণটি দেখায় কিভাবে AssistStructure মধ্যে দিয়ে যেতে হয় এবং একটি ViewNode অবজেক্ট থেকে অটোফিল ডেটা পুনরুদ্ধার করতে হয়:

কোটলিন

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

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

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

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

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

জাভা

public void traverseStructure(AssistStructure structure) {
    int nodes = structure.getWindowNodeCount();

    for (int i = 0; i < nodes; i++) {
        WindowNode windowNode = structure.getWindowNodeAt(i);
        ViewNode viewNode = windowNode.getRootViewNode();
        traverseNode(viewNode);
    }
}

public void traverseNode(ViewNode viewNode) {
    if(viewNode.getAutofillHints() != null && viewNode.getAutofillHints().length > 0) {
        // If the client app provides autofill hints, you can obtain them using
        // viewNode.getAutofillHints();
    } else {
        // Or use your own heuristics to describe the contents of a view
        // using methods such as getText() or getHint()
    }

    for(int i = 0; i < viewNode.getChildCount(); i++) {
        ViewNode childNode = viewNode.getChildAt(i);
        traverseNode(childNode);
    }
}

ব্যবহারকারীর ডেটা সংরক্ষণ করুন

অ্যাপের ভিউগুলো পূরণ করার জন্য একটি অটোফিল সার্ভিসের ব্যবহারকারীর ডেটা প্রয়োজন হয়। ব্যবহারকারীরা যখন ম্যানুয়ালি কোনো ভিউ পূরণ করেন, তখন তাদেরকে বর্তমান অটোফিল সার্ভিসে ডেটাটি সেভ করার জন্য অনুরোধ জানানো হয়, যেমনটি চিত্র ২-এ দেখানো হয়েছে।

সিস্টেম ডায়ালগ ব্যবহারকারীকে সার্ভিসটিতে অটোফিল ডেটা সংরক্ষণ করতে অনুরোধ করে।
চিত্র ২. অটোফিল সেভ UI।

ডেটা সংরক্ষণ করার জন্য, সার্ভিসটিকে অবশ্যই জানাতে হবে যে এটি ভবিষ্যতের ব্যবহারের জন্য ডেটা সংরক্ষণ করতে আগ্রহী। অ্যান্ড্রয়েড সিস্টেম ডেটা সংরক্ষণের জন্য অনুরোধ পাঠানোর আগে, একটি ফিল রিকোয়েস্ট থাকে যেখানে সার্ভিসটি ভিউগুলো পূরণ করার সুযোগ পায়। ডেটা সংরক্ষণে আগ্রহ প্রকাশ করার জন্য, সার্ভিসটি ফিল রিকোয়েস্টের রেসপন্সে একটি 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 স্থগিত করুন

অ্যান্ড্রয়েড ১০ থেকে, যদি আপনি একটি অটোফিল ওয়ার্কফ্লো বাস্তবায়নের জন্য একাধিক স্ক্রিন ব্যবহার করেন—উদাহরণস্বরূপ, ইউজারনেম ফিল্ডের জন্য একটি স্ক্রিন এবং পাসওয়ার্ডের জন্য আরেকটি—তাহলে আপনি SaveInfo.FLAG_DELAY_SAVE ফ্ল্যাগটি ব্যবহার করে অটোফিল সেভ UI-কে স্থগিত করতে পারেন।

এই ফ্ল্যাগটি সেট করা থাকলে, SaveInfo রেসপন্সের সাথে যুক্ত অটোফিল কনটেক্সট কমিট করা হলে অটোফিল সেভ UI ট্রিগার হয় না। এর পরিবর্তে, আপনি ভবিষ্যতের ফিল রিকোয়েস্টগুলো ডেলিভার করার জন্য একই টাস্কের মধ্যে একটি আলাদা অ্যাক্টিভিটি ব্যবহার করতে পারেন এবং তারপর একটি সেভ রিকোয়েস্টের মাধ্যমে UI-টি দেখাতে পারেন। আরও তথ্যের জন্য, SaveInfo.FLAG_DELAY_SAVE দেখুন।

ব্যবহারকারীর প্রমাণীকরণ প্রয়োজন

অটোফিল পরিষেবাগুলি ভিউ পূরণ করার আগে ব্যবহারকারীকে প্রমাণীকরণের প্রয়োজন করে নিরাপত্তার একটি অতিরিক্ত স্তর প্রদান করতে পারে। ব্যবহারকারী প্রমাণীকরণ বাস্তবায়নের জন্য নিম্নলিখিত পরিস্থিতিগুলি উপযুক্ত:

  • অ্যাপের ব্যবহারকারীর তথ্য একটি প্রাথমিক পাসওয়ার্ড বা ফিঙ্গারপ্রিন্ট স্ক্যানের মাধ্যমে আনলক করতে হবে।
  • একটি নির্দিষ্ট ডেটাসেট আনলক করতে হবে, যেমন কার্ড ভেরিফিকেশন কোড (CVC) ব্যবহার করে ক্রেডিট কার্ডের বিবরণ।

এমন পরিস্থিতিতে যেখানে ডেটা আনলক করার আগে পরিষেবাটির জন্য ব্যবহারকারীর প্রমাণীকরণের প্রয়োজন হয়, সেখানে পরিষেবাটি বয়লারপ্লেট ডেটা বা একটি লেবেল উপস্থাপন করতে পারে এবং প্রমাণীকরণ পরিচালনা করে এমন Intent নির্দিষ্ট করে দিতে পারে। প্রমাণীকরণ প্রক্রিয়া সম্পন্ন হওয়ার পর অনুরোধটি প্রক্রিয়া করার জন্য যদি আপনার অতিরিক্ত ডেটার প্রয়োজন হয়, তবে আপনি সেই ডেটা ইন্টেন্টটিতে যোগ করতে পারেন। এরপর আপনার প্রমাণীকরণ অ্যাক্টিভিটি আপনার অ্যাপের AutofillService ক্লাসে সেই ডেটা ফেরত পাঠাতে পারে।

নিম্নলিখিত কোড উদাহরণটি দেখায় যে কীভাবে নির্দিষ্ট করতে হয় যে অনুরোধটির জন্য প্রমাণীকরণ প্রয়োজন:

কোটলিন

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

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

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

জাভা

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

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

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

অ্যাক্টিভিটিটি অথেনটিকেশন ফ্লো সম্পন্ন করার পর, এটিকে অবশ্যই একটি RESULT_OK ভ্যালু পাস করে setResult() মেথডটি কল করতে হবে এবং পপুলেটেড ডেটাসেট অন্তর্ভুক্ত FillResponse অবজেক্টে EXTRA_AUTHENTICATION_RESULT এক্সট্রাটি সেট করতে হবে। অথেনটিকেশন ফ্লো সম্পন্ন হওয়ার পর কীভাবে ফলাফলটি রিটার্ন করতে হয়, তার একটি উদাহরণ নিচের কোডে দেখানো হলো:

কোটলিন

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

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

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

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

setResult(Activity.RESULT_OK, replyIntent)

জাভা

Intent intent = getIntent();

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

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

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

Intent replyIntent = new Intent();

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

setResult(RESULT_OK, replyIntent);

যখন কোনো ক্রেডিট কার্ড ডেটাসেট আনলক করার প্রয়োজন হয়, তখন সার্ভিসটি CVC চেয়ে একটি UI প্রদর্শন করতে পারে। ডেটাসেটটি আনলক না হওয়া পর্যন্ত আপনি কিছু গতানুগতিক ডেটা, যেমন ব্যাংকের নাম এবং ক্রেডিট কার্ড নম্বরের শেষ চারটি অঙ্ক, প্রদর্শন করে ডেটা লুকিয়ে রাখতে পারেন। নিম্নলিখিত উদাহরণটি দেখায় কিভাবে একটি ডেটাসেটের জন্য অথেনটিকেশন বাধ্যতামূলক করা যায় এবং ব্যবহারকারী 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 যাচাই করার পর, RESULT_OK ভ্যালুটি পাস করে setResult() মেথডটি কল করবে এবং 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);

তথ্যগুলোকে যৌক্তিক দলে সাজান।

অটোফিল পরিষেবাগুলিকে অবশ্যই ডেটাগুলিকে যৌক্তিক গোষ্ঠীতে সাজাতে হবে, যা বিভিন্ন ক্ষেত্রের ধারণাগুলিকে আলাদা করে। এই পৃষ্ঠায়, এই যৌক্তিক গোষ্ঠীগুলিকে পার্টিশন হিসাবে উল্লেখ করা হয়েছে। নিম্নলিখিত তালিকাটিতে পার্টিশন এবং ফিল্ডের সাধারণ উদাহরণ দেখানো হয়েছে:

  • পরিচয়পত্র, যার মধ্যে ব্যবহারকারীর নাম এবং পাসওয়ার্ড ক্ষেত্র অন্তর্ভুক্ত রয়েছে।
  • ঠিকানা, যার মধ্যে রাস্তা, শহর, রাজ্য এবং জিপ কোড অন্তর্ভুক্ত রয়েছে।
  • পেমেন্টের তথ্য, যার মধ্যে ক্রেডিট কার্ড নম্বর, মেয়াদ শেষ হওয়ার তারিখ এবং যাচাইকরণ কোডের ক্ষেত্রগুলো অন্তর্ভুক্ত।

একটি অটোফিল পরিষেবা যা ডেটাকে সঠিকভাবে পার্টিশন করে, তা একটি ডেটাসেটের একাধিক পার্টিশন থেকে ডেটা প্রকাশ না করার মাধ্যমে তার ব্যবহারকারীদের ডেটাকে আরও ভালোভাবে সুরক্ষিত করতে পারে। উদাহরণস্বরূপ, যে ডেটাসেটে ক্রেডেনশিয়াল অন্তর্ভুক্ত থাকে, সেখানে পেমেন্টের তথ্য অন্তর্ভুক্ত করার প্রয়োজন নেই। ডেটাকে পার্টিশনে সাজানোর ফলে আপনার পরিষেবাটি একটি অনুরোধ পূরণ করার জন্য প্রয়োজনীয় ন্যূনতম পরিমাণ প্রাসঙ্গিক তথ্য প্রকাশ করতে পারে।

ডেটাকে পার্টিশনে সাজানোর ফলে সার্ভিসগুলো ক্লায়েন্ট অ্যাপে ন্যূনতম পরিমাণ প্রাসঙ্গিক ডেটা পাঠানোর পাশাপাশি এমন অ্যাক্টিভিটিগুলোও পূরণ করতে পারে যেগুলোতে একাধিক পার্টিশনের ভিউ রয়েছে। উদাহরণস্বরূপ, এমন একটি অ্যাক্টিভিটির কথা ভাবুন যেখানে ইউজারনেম, পাসওয়ার্ড, স্ট্রিট এবং সিটির জন্য ভিউ রয়েছে, এবং একটি অটোফিল সার্ভিসের কথা ভাবুন যেখানে নিম্নলিখিত ডেটা আছে:

বিভাজন মাঠ ১ মাঠ ২
যোগ্যতা কাজের_ব্যবহারকারীর নাম কাজের পাসওয়ার্ড
ব্যক্তিগত_ব্যবহারকারীর নাম ব্যক্তিগত পাসওয়ার্ড
ঠিকানা কাজের রাস্তা কর্ম_শহর
ব্যক্তিগত_রাস্তা ব্যক্তিগত_শহর

এই পরিষেবাটি এমন একটি ডেটাসেট প্রস্তুত করতে পারে, যাতে কর্মক্ষেত্র এবং ব্যক্তিগত উভয় অ্যাকাউন্টের ক্রেডেনশিয়াল পার্টিশন অন্তর্ভুক্ত থাকে। ব্যবহারকারী যখন একটি ডেটাসেট নির্বাচন করেন, তখন তার প্রথম পছন্দের ওপর নির্ভর করে পরবর্তী অটোফিল প্রতিক্রিয়া হিসেবে কর্মক্ষেত্র বা ব্যক্তিগত ঠিকানা প্রদান করা হতে পারে।

একটি সার্ভিস AssistStructure অবজেক্টটি ট্র্যাভার্স করার সময় isFocused() মেথডটি কল করার মাধ্যমে অনুরোধটি যে ফিল্ড থেকে এসেছে তা শনাক্ত করতে পারে। এটি সার্ভিসটিকে উপযুক্ত পার্টিশন ডেটা সহ একটি FillResponse প্রস্তুত করার সুযোগ দেয়।

এসএমএস ওয়ান-টাইম কোড অটোফিল

আপনার অটোফিল পরিষেবা ব্যবহারকারীকে এসএমএস রিট্রিভার এপিআই (SMS Retriever API) ব্যবহার করে পাঠানো ওয়ান-টাইম কোড পূরণ করতে সহায়তা করতে পারে।

এই বৈশিষ্ট্যটি ব্যবহার করার জন্য, নিম্নলিখিত শর্তগুলি অবশ্যই পূরণ করতে হবে:

  • অটোফিল পরিষেবাটি অ্যান্ড্রয়েড ৯ (এপিআই লেভেল ২৮) বা তার উচ্চতর সংস্করণে চলছে।
  • ব্যবহারকারী আপনার অটোফিল পরিষেবাটিকে এসএমএস থেকে ওয়ান-টাইম কোড পড়ার জন্য সম্মতি দিচ্ছেন।
  • যে অ্যাপ্লিকেশনটির জন্য আপনি অটোফিল সরবরাহ করছেন, সেটি ওয়ান-টাইম কোড পড়ার জন্য আগে থেকেই এসএমএস রিট্রিভার এপিআই ব্যবহার করছে না।

আপনার অটোফিল পরিষেবা SmsCodeAutofillClient ব্যবহার করতে পারে, যা Google Play services 19.0.56 বা তার উচ্চতর সংস্করণ থেকে SmsCodeRetriever.getAutofillClient() কল করে পাওয়া যায়।

একটি অটোফিল পরিষেবাতে এই এপিআই ব্যবহার করার প্রধান ধাপগুলো হলো:

  1. অটোফিল সার্ভিসে, আপনি যে অ্যাপ্লিকেশনটি অটোফিল করছেন তার প্যাকেজ নামের জন্য কোনো সক্রিয় অনুরোধ আছে কিনা তা নির্ধারণ করতে SmsCodeAutofillClient এর hasOngoingSmsRequest ব্যবহার করুন। যদি এটি false রিটার্ন করে, তবেই আপনার অটোফিল সার্ভিস একটি সাজেশন প্রম্পট প্রদর্শন করবে।
  2. অটোফিল সার্ভিসে, ওয়ান-টাইম কোড অটোফিল করার অনুমতি আছে কিনা তা পরীক্ষা করতে SmsCodeAutofillClient এর checkPermissionState ব্যবহার করুন। এই অনুমতির অবস্থা NONE , GRANTED বা DENIED হতে পারে। NONE এবং GRANTED অবস্থার জন্য অটোফিল সার্ভিসকে অবশ্যই একটি সাজেশন প্রম্পট প্রদর্শন করতে হবে।
  3. অটোফিল অথেন্টিকেশন অ্যাক্টিভিটিতে, SmsRetriever.SEND_PERMISSION পারমিশনটি ব্যবহার করে একটি BroadcastReceiver রেজিস্টার করুন যা SmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION এর জন্য লিসেন করবে, যাতে এসএমএস কোডের ফলাফল উপলব্ধ হলে তা গ্রহণ করা যায়।
  4. এসএমএস ব্যবহার করে পাঠানো ওয়ান-টাইম কোড শোনা শুরু করতে SmsCodeAutofillClientstartSmsCodeRetriever কল করুন। যদি ব্যবহারকারী আপনার অটোফিল পরিষেবাটিকে এসএমএস থেকে ওয়ান-টাইম কোড সংগ্রহ করার অনুমতি দেন, তবে এটি এখন থেকে গত এক থেকে পাঁচ মিনিটের মধ্যে প্রাপ্ত এসএমএস বার্তাগুলি খুঁজে বের করে।

    আপনার অটোফিল সার্ভিসের যদি ওয়ান-টাইম কোড পড়ার জন্য ব্যবহারকারীর অনুমতির প্রয়োজন হয়, তাহলে startSmsCodeRetriever দ্বারা রিটার্ন করা Task টি ResolvableApiException রিটার্ন করে ব্যর্থ হতে পারে। এমনটা ঘটলে, অনুমতির অনুরোধের জন্য একটি সম্মতি ডায়ালগ প্রদর্শন করতে আপনাকে ResolvableApiException.startResolutionForResult() মেথডটি কল করতে হবে।

  5. ইন্টেন্ট থেকে এসএমএস কোডের ফলাফল গ্রহণ করুন এবং তারপর সেই এসএমএস কোডটি একটি অটোফিল প্রতিক্রিয়া হিসাবে ফেরত দিন।

ক্রোমে অটোফিল চালু করুন

ক্রোম থার্ড-পার্টি অটোফিল সার্ভিসগুলোকে ফর্ম স্বয়ংক্রিয়ভাবে পূরণ করার সুযোগ দেয়, যা ব্যবহারকারীদের আরও মসৃণ ও সহজ অভিজ্ঞতা প্রদান করে। পাসওয়ার্ড, পাসকি এবং ঠিকানা ও পেমেন্টের তথ্যের মতো অন্যান্য তথ্য অটোফিল করার জন্য থার্ড-পার্টি অটোফিল সার্ভিস ব্যবহার করতে হলে, ব্যবহারকারীকে ক্রোম সেটিংসে ‘Autofill using another service’ অপশনটি নির্বাচন করতে হবে।

ক্রোম সেটিংসে 'অন্য পরিষেবা ব্যবহার করে স্বয়ংক্রিয়ভাবে পূরণ করুন' টগলটি সক্রিয় দেখানো হচ্ছে।
চিত্র ৩. ক্রোম সেটিংসে "অন্য পরিষেবা ব্যবহার করে স্বয়ংক্রিয়ভাবে পূরণ করুন" টগলটি সক্রিয় দেখানো হচ্ছে।

আপনার পরিষেবা এবং অ্যান্ড্রয়েডে ক্রোমের সাথে ব্যবহারকারীদের সর্বোত্তম অটোফিল অভিজ্ঞতা দিতে, অটোফিল পরিষেবা প্রদানকারীদের উচিত তাদের ব্যবহারকারীদের ক্রোম সেটিংসে পছন্দের পরিষেবা প্রদানকারী নির্দিষ্ট করতে উৎসাহিত করা।

ব্যবহারকারীদের টগলটি চালু করতে সাহায্য করার জন্য, ডেভেলপাররা নিম্নলিখিত কাজগুলো করতে পারেন:

  • ক্রোম সেটিংস অনুসন্ধান করে জানুন ব্যবহারকারী কোনো তৃতীয় পক্ষের অটোফিল পরিষেবা ব্যবহার করতে চান কিনা।
  • ক্রোম সেটিংস পৃষ্ঠার ডিপ লিঙ্ক, যেখানে ব্যবহারকারীরা তৃতীয় পক্ষের অটোফিল পরিষেবাগুলি চালু করতে পারেন।

কম্প্যাটিবিলিটি মোডের জন্য সর্বোচ্চ ক্রোম সংস্করণ সংখ্যা নির্দিষ্ট করুন।

অ্যান্ড্রয়েড অটোফিলের সুবিধার জন্য ক্রোম ১৩৭ সংস্করণ থেকে কম্প্যাটিবিলিটি মোডের সমর্থন বন্ধ করে দিয়েছে। কম্প্যাটিবিলিটি মোড চালু রাখলে স্থিতিশীলতার সমস্যা দেখা দিতে পারে। স্থিতিশীলতার জন্য, নিম্নলিখিতভাবে কম্প্যাটিবিলিটি মোড সমর্থনকারী ক্রোম প্যাকেজগুলির সর্বোচ্চ সংস্করণ নির্দিষ্ট করুন।

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

ক্রোম সেটিংস পড়ুন

যেকোনো অ্যাপই জানতে পারে যে ক্রোম সেই 3P অটোফিল মোড ব্যবহার করছে কিনা, যা সেটিকে অ্যান্ড্রয়েড অটোফিল ব্যবহার করার সুযোগ দেয়। এই তথ্যটি জানানোর জন্য ক্রোম অ্যান্ড্রয়েডের ContentProvider ব্যবহার করে। আপনার অ্যান্ড্রয়েড ম্যানিফেস্টে ঘোষণা করুন যে আপনি কোন চ্যানেলগুলো থেকে সেটিংস পড়তে চান:

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

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

তারপর, অ্যান্ড্রয়েডের ContentResolver ব্যবহার করে কন্টেন্ট URI তৈরি করার মাধ্যমে সেই তথ্যের জন্য অনুরোধ করুন:

কোটলিন

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

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

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

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

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

জাভা

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

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

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

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

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

int index = cursor.getColumnIndex(THIRD_PARTY_MODE_COLUMN);

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

ক্রোম সেটিংস পৃষ্ঠায় ডিপ লিঙ্ক করতে, যেখানে ব্যবহারকারীরা থার্ড-পার্টি অটোফিল পরিষেবাগুলি চালু করতে পারেন, একটি অ্যান্ড্রয়েড Intent ব্যবহার করুন। এই উদাহরণে দেখানো অনুযায়ী অ্যাকশন এবং ক্যাটাগরিগুলি কনফিগার করতে ভুলবেন না:

কোটলিন

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

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

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

জাভা

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

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

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

উন্নত অটোফিল পরিস্থিতি

নিম্নলিখিত পরিস্থিতিগুলিতে অটোফিল ব্যবহার করুন:

কিবোর্ডের সাথে সংযুক্ত করুন

অ্যান্ড্রয়েড ১১ থেকে শুরু করে, প্ল্যাটফর্মটি কিবোর্ড এবং অন্যান্য ইনপুট-মেথড এডিটর ( IME )-কে পুলডাউন মেনু ব্যবহার না করে ইনলাইনে অটোফিল সাজেশন প্রদর্শন করার সুযোগ দেয়। আপনার অটোফিল পরিষেবা কীভাবে এই কার্যকারিতা সমর্থন করতে পারে সে সম্পর্কে আরও তথ্যের জন্য, ‘কিবোর্ডের সাথে অটোফিল ইন্টিগ্রেট করুন ’ দেখুন।

ডেটাসেট পৃষ্ঠাঙ্কন করুন

একটি বড় অটোফিল রেসপন্স, অনুরোধটি প্রসেস করার জন্য প্রয়োজনীয় রিমোটেবল অবজেক্টকে প্রতিনিধিত্বকারী Binder অবজেক্টের অনুমোদিত ট্রানজ্যাকশন সাইজ অতিক্রম করতে পারে। এই ধরনের পরিস্থিতিতে অ্যান্ড্রয়েড সিস্টেম যাতে কোনো এক্সেপশন থ্রো না করে, সেজন্য আপনি একবারে ২০টির বেশি Dataset অবজেক্ট যোগ না করে FillResponse কে ছোট রাখতে পারেন। যদি আপনার রেসপন্সে আরও ডেটাসেটের প্রয়োজন হয়, তবে আপনি এমন একটি ডেটাসেট যোগ করতে পারেন যা ব্যবহারকারীদের জানিয়ে দেয় যে আরও তথ্য রয়েছে এবং সেটি নির্বাচন করা হলে ডেটাসেটের পরবর্তী গ্রুপটি নিয়ে আসে। আরও তথ্যের জন্য, addDataset(Dataset) দেখুন।

একাধিক স্ক্রিনে বিভক্ত ডেটা সংরক্ষণ করুন

অ্যাপগুলো প্রায়শই একই অ্যাক্টিভিটির মধ্যে ব্যবহারকারীর ডেটা একাধিক স্ক্রিনে ভাগ করে দেয়, যেমন নতুন ব্যবহারকারী অ্যাকাউন্ট তৈরি করার ক্ষেত্রে। উদাহরণস্বরূপ, প্রথম স্ক্রিনে একটি ইউজারনেম এবং দ্বিতীয় স্ক্রিনে একটি পাসওয়ার্ড চাওয়া হতে পারে। এই পরিস্থিতিতে, অটোফিল সেভ UI দেখানোর আগে আপনার অটোফিল সার্ভিসকে অবশ্যই অপেক্ষা করতে হবে যতক্ষণ না ব্যবহারকারী সমস্ত প্রাসঙ্গিক ফিল্ডে ডেটা প্রবেশ করান। এই ধরনের পরিস্থিতি সামাল দিতে নিম্নলিখিত ধাপগুলো অনুসরণ করুন:

  1. প্রথম ফিল রিকোয়েস্টের রেসপন্সে একটি ক্লায়েন্ট স্টেট বান্ডেল যোগ করুন, যাতে স্ক্রিনে উপস্থিত পার্শিয়াল ফিল্ডগুলোর অটোফিল আইডিগুলো থাকবে।
  2. দ্বিতীয় ফিল রিকোয়েস্টে, ক্লায়েন্ট স্টেট বান্ডেলটি পুনরুদ্ধার করুন, ক্লায়েন্ট স্টেট থেকে পূর্ববর্তী রিকোয়েস্টে সেট করা অটোফিল আইডিগুলো নিন, এবং এই আইডিগুলো ও FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE ফ্ল্যাগটি দ্বিতীয় রেসপন্সে ব্যবহৃত SaveInfo অবজেক্টে যোগ করুন।
  3. সেভ রিকোয়েস্টে, প্রতিটি ফিল্ডের মান পেতে যথাযথ FillContext অবজেক্ট ব্যবহার করুন। প্রতিটি ফিল রিকোয়েস্টের জন্য একটি করে ফিল কনটেক্সট থাকে।

    আরও তথ্যের জন্য, ‘একাধিক স্ক্রিনে ডেটা বিভক্ত হলে সংরক্ষণ করুন’ দেখুন।

প্রতিটি অনুরোধের জন্য প্রারম্ভিককরণ এবং সমাপ্তিকরণ যুক্তি প্রদান করুন

প্রতিবার কোনো অটোফিল অনুরোধ এলে, অ্যান্ড্রয়েড সিস্টেম সার্ভিসটির সাথে যুক্ত হয় এবং এর onConnected() মেথডটি কল করে। সার্ভিসটি অনুরোধটি প্রসেস করার পর, অ্যান্ড্রয়েড সিস্টেম onDisconnected() মেথডটি কল করে এবং সার্ভিসটি থেকে সংযোগ বিচ্ছিন্ন করে দেয়। আপনি অনুরোধ প্রসেস করার আগে চালানোর জন্য onConnected() এবং অনুরোধ প্রসেস করার পরে চালানোর জন্য onDisconnected() ইমপ্লিমেন্ট করতে পারেন।

অটোফিল সেভ UI কাস্টমাইজ করুন

অটোফিল সার্ভিসগুলো অটোফিল সেভ UI কাস্টমাইজ করতে পারে, যাতে ব্যবহারকারীরা তাদের ডেটা সার্ভিসটিকে সেভ করার অনুমতি দেবেন কিনা, সেই সিদ্ধান্ত নিতে পারেন। সার্ভিসগুলো টেক্সটের মাধ্যমে অথবা একটি কাস্টমাইজড ভিউয়ের মাধ্যমে কী সেভ করা হচ্ছে সে সম্পর্কে অতিরিক্ত তথ্য প্রদান করতে পারে। সার্ভিসগুলো সেভ রিকোয়েস্ট বাতিল করার বাটনটির চেহারাও পরিবর্তন করতে পারে এবং ব্যবহারকারী সেই বাটনটি ট্যাপ করলে একটি নোটিফিকেশন পেতে পারে। আরও তথ্যের জন্য, 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]

অ্যাপটি যদি একটি ব্রাউজার হয়, তাহলে রেন্ডার করা পৃষ্ঠার URL ধারণকারী ইনপুট ফিল্ডের রিসোর্স আইডি নির্দিষ্ট করতে resIdx ব্যবহার করুন।

সামঞ্জস্য মোডের নিম্নলিখিত সীমাবদ্ধতাগুলো রয়েছে:

  • যখন সার্ভিসটি FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE ফ্ল্যাগ ব্যবহার করে অথবা setTrigger() মেথডটি কল করা হয়, তখন একটি সেভ রিকোয়েস্ট ট্রিগার হয়। কম্প্যাটিবিলিটি মোড ব্যবহার করার সময় FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE ডিফল্টরূপে সেট করা থাকে।
  • onSaveRequest(SaveRequest, SaveCallback) মেথডে নোডগুলোর টেক্সট ভ্যালু পাওয়া নাও যেতে পারে।

কম্প্যাটিবিলিটি মোড এবং এর সাথে সম্পর্কিত সীমাবদ্ধতাগুলো সম্পর্কে আরও তথ্যের জন্য AutofillService ক্লাস রেফারেন্স দেখুন।