আপনার অ্যাপে AppFunctions API যোগ করা

এই গাইড থেকে আপনি জানতে পারবেন কীভাবে আপনার Android অ্যাপে AppFunctions API ইন্টিগ্রেট করতে হয়, কোনও ফাংশনের জন্য লজিক প্রয়োগ করতে হয় এবং ইন্টিগ্রেশন সঠিকভাবে কাজ করছে কিনা তা যাচাই করতে হয়।

ভার্সন কম্প্যাটিবিলিটি

এই প্রয়োগের জন্য আপনার প্রোজেক্ট compileSdk-কে API লেভেল ৩৬ বা তার পরের যেকোনও ভার্সনে সেট করতে হবে।

AppFunctions কাজ করে কিনা তা যাচাই করার জন্য আপনার অ্যাপের প্রয়োজন নেই; এটি AppFunctions Jetpack লাইব্রেরির মধ্যে অটোমেটিক ম্যানেজ করা হয়। AppFunctionManager ফিচার কাজ করলে একটি ইনস্ট্যান্স রিটার্ন করে এবং কাজ না করলে null রিটার্ন করে।

ডিপেন্ডেন্সি

আপনার মডিউলের build.gradle.kts (বা build.gradle) ফাইলে প্রয়োজনীয় লাইব্রেরি ডিপেন্ডেন্সি যোগ করুন এবং আপনার টপ-লেভেল অ্যাপ মডিউলে KSP প্লাগ-ইনকে দেখানো পদ্ধতিতে কনফিগার করুন:

dependencies {
  implementation("androidx.appfunctions:appfunctions:1.0.0-alpha10")
  // If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
  // See Add the KSP plugin to your project
  ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha10")
}

AppFunctions লজিক প্রয়োগ করা

আপনার Android অ্যাপের জন্য AppFunction প্রয়োগ করতে, এমন একটি ক্লাস তৈরি করুন যা নির্দিষ্ট AppFunctions লজিক প্রয়োগ করে। এর মধ্যে প্যারামিটার ও উত্তরের জন্য সিরিয়ালাইজ করা যায় এমন ডেটা ক্লাস তৈরি করা এবং তারপর ফাংশন পদ্ধতির মধ্যে মূল লজিক প্রদান করা অন্তর্ভুক্ত।

নিচের কোডে TODO অ্যাপে টাস্ক তৈরি করার একটি উদাহরণ দেখানো হয়েছে। এর মধ্যে কাস্টম প্যারামিটার ও রেসপন্স টাইপ এবং একটি রিপোজিটরি ব্যবহার করে মূল ফাংশনের লজিক ডিফাইন করা রয়েছে।

@RequiresApi(36)
@AndroidEntryPoint
@AppFunctionServiceEntryPoint(
    serviceName = "TaskAppFunctionService",
    appFunctionXmlFileName = "task_app_function_service",
)
abstract class BaseTaskAppFunctionService : AppFunctionService() {
    @Inject internal lateinit var taskRepository: TaskRepository

    /**
     * Creates a task based on [createTaskParams].
     *
     * @param createTaskParams The parameter to describe how to create the task.
     */
    @AppFunction(isDescribedByKDoc = true)
    suspend fun createTask(
        createTaskParams: CreateTaskParams,
    ): Task = withContext(Dispatchers.IO) {
        // Developers can use predefined exceptions to let the agent know
        // why it failed.
        if (createTaskParams.title == null && createTaskParams.content == null) {
            throw AppFunctionInvalidArgumentException("Title or content should be non-null")
        }

        val id = taskRepository.createTask(
            createTaskParams.title,
            createTaskParams.content
        )

        return@withContext taskRepository
            .getTask(id)
            ?.toTask()
            ?: throw AppFunctionElementNotFoundException("Task not found for ID = $id")
    }

    // Maps internal TaskEntity
    private fun TaskEntity.toTask() = Task(id = id, title = title, content = description)
}

কোড সম্পর্কে গুরুত্বপূর্ণ পয়েন্ট

  • ডিফল্ট হিসেবে, কোনও AppFunction ইমপ্লিমেন্টেশন Android UI থ্রেডে রান করে। তাই, দীর্ঘ সময় ধরে চলা অপারেশনকে নিম্নলিখিত কাজগুলি করতে হবে:
    • AppFunction-কে সাসপেন্ড ফাংশন হিসেবে ঘোষণা করুন।
    • অপারেশনটি থ্রেড ব্লক করতে পারে এমন পরিস্থিতিতে উপযুক্ত কোরাউটিন ডিসপ্যাচার ব্যবহার করুন।
  • isDescribedByKDoc true হিসেবে সেট করা থাকলে, ফাংশনের বিবরণ বা সিরিয়ালাইজ করা যায় এমন বিবরণ AppFunctionMetadata-এর অংশ হিসেবে এনকোড করা হয়, যাতে অ্যাপের AppFunction কীভাবে ব্যবহার করতে হয় তা এজেন্ট বুঝতে পারে।

আপনার ম্যানিফেস্টে AppFunction পরিষেবা ঘোষণা করুন

আপনার মডিউল ম্যানিফেস্টের মধ্যে KSP-জেনারেটেড পরিষেবা ঘোষণা এবং app_metadata প্রপার্টি রেজিস্টার করুন, যেমন, src/main/AndroidManifest.xml-এ। KSP কম্পাইলার আপনার অ্যাবস্ট্রাক্ট এন্ট্রি পয়েন্ট ক্লাসকে এক্সটেন্ড করে কংক্রিট সার্ভিস ক্লাস (TaskAppFunctionService) তৈরি করে। এর সাথে সাথে আপনার assets/ ডিরেক্টরিতে সংশ্লিষ্ট XML স্কিমাও তৈরি করে।

<service
    android:name="com.example.snippets.ai.TaskAppFunctionService"
    android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"
    android:exported="true"
    tools:targetApi="36">
    <property
        android:name="android.app.appfunctions.schema"
        android:value="app_functions_schema.xsd" />
    <property
        android:name="android.app.appfunctions.v2"
        android:value="task_app_function_service.xml" />
    <intent-filter>
        <action android:name="android.app.appfunctions.AppFunctionService" />
    </intent-filter>
</service>
<property
    android:name="android.app.appfunctions.app_metadata"
    android:resource="@xml/app_metadata" />

ঐচ্ছিক: রানটাইমে AppFunction উপলভ্যতা টগল করা

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

নির্দিষ্ট অ্যাকাউন্ট স্টেট প্রয়োজন এমন AppFunctions নিরাপদে গেট করতে, একটি দুটি ধাপের প্রসেস অনুসরণ করুন:

ধাপ ১. ফাংশনটি ডিফল্ট হিসেবে বন্ধ করে রাখুন

আপনার ফিচার ফ্ল্যাগ যাচাই করার আগে ফাংশন অ্যাক্সেস করা আটকাতে, আপনার @AppFunction অ্যানোটেশনের isEnabled প্যারামিটারকে false হিসেবে সেট করুন।

@AppFunction(isEnabled = false, isDescribedByKDoc = true)
suspend fun createTask(
    createTaskParams: CreateTaskParams,
): Task = TODO()

ধাপ ২. রানটাইমে ফাংশন ডায়নামিক পদ্ধতিতে চালু করা

প্রতিটি AppFunction ক্লাসের জন্য, কম্পাইলার একটি সংশ্লিষ্ট ক্লাস তৈরি করে যার মধ্যে ফাংশন আইডি কনস্ট্যান্ট (Ids সাফিক্স ব্যবহার করে) থাকে। রানটাইমে কোনও ফাংশনের চালু থাকার স্ট্যাটাস পরিবর্তন করতে, আপনি setAppFunctionEnabled মেথডের সাথে AppFunctionManagerCompat এই জেনারেট করা আইডি কনস্ট্যান্ট ব্যবহার করতে পারবেন।

suspend fun onFeatureEnabled(context: Context) {
    try {
        AppFunctionManager.getInstance(context)
            ?.setAppFunctionEnabled(
                BaseTaskAppFunctionServiceIds.CREATE_TASK_ID,
                AppFunctionManager.APP_FUNCTION_STATE_ENABLED,
            )
    } catch (e: Exception) {
        // Handle exception: AppFunctions indexation may not be fully completed
        // upon initial app startup.
    }
}

suspend fun onFeatureDisabled(context: Context) {
    try {
        AppFunctionManager.getInstance(context)
            ?.setAppFunctionEnabled(
                BaseTaskAppFunctionServiceIds.CREATE_TASK_ID,
                AppFunctionManager.APP_FUNCTION_STATE_DISABLED,
            )
    } catch (e: Exception) {
        // Handle exception
    }
}

উপলভ্য করার জন্য ফাংশনালিটির ধরন সংক্রান্ত বিবেচনা

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

ব্যবহারকারীদের দুর্দান্ত অভিজ্ঞতা প্রদান করতে এবং একই সাথে সংবেদনশীল তথ্য প্রকাশ করা এড়াতে, আমরা এইসব নির্দেশিকা অনুসরণ করার পরামর্শ দিই:

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

AppFunction ইন্টিগ্রেশন যাচাই করা

আপনি AppFunctions সঠিকভাবে ইন্টিগ্রেট করেছেন কিনা তা যাচাই করতে, adb shell cmd app_function ব্যবহার করতে পারেন।

আপনার অ্যাপ প্রদান করে এমন AppFunctions-এর বিবরণ দেখতে adb shell cmd app_function list-app-functions | grep --after-context 10 $myPackageName ব্যবহার করুন।

এছাড়াও, আপনি কমান্ড লাইন থেকে সরাসরি AppFunction এক্সিকিউট করতে পারবেন, এর জন্য আপনাকে স্পষ্ট শনাক্তকারী ("$enclosingClassName#$methodName") ব্যবহার করতে হবে:

adb shell "cmd app_function execute-app-function \
  --package com.example.android.appfunctions \
  --function 'com.example.android.appfunctions.BaseTaskAppFunctionService#createTask' \
  --parameters '{\"createTaskParams\": {\"title\": \"Buy milk\", \"content\": \"From grocery store\"}}'"

Android MCP কীভাবে কাজ করে তা দেখতে এবং কোনও প্রম্পটের প্রয়োজন ছাড়াই এন্ড-টু-এন্ড ওয়ার্কফ্লো যাচাই করতে, আপনার ডিভাইসে AppFunctions টেস্টিং এজেন্ট Android অ্যাপ ইনস্টল করে রান করান।

আপনি যদি Android Studio-তে Gemini-র মতো চ্যাট-ভিত্তিক অ্যাসিস্ট্যান্ট ব্যবহার করে আপনার ইন্টিগ্রেশন যাচাই করেন, তাহলে AppFunctions ডেভেলপমেন্ট স্কিল ব্যবহার করুন অথবা নিম্নলিখিত প্রম্পটের মতো কোনও প্রম্পট দিন:

Execute `adb shell cmd app_function` to learn how the tool works, then act as a
chat agent aiming to invoke AppFunctions to fulfil user prompts for this app.
Rely on the AppFunction description as instructions.

নিম্ন API ভার্সন থেকে মাইগ্রেট করা

1.0.0-alpha10 ভার্সনে, AppFunctions একটি কম্পাইল-টাইম @AppFunctionServiceEntryPoint আর্কিটেকচার নিয়ে এসেছে যা লাইব্রেরি ডিপেন্ডেন্সিগুলিকে একত্রিত করে এবং পুরনো কনফিগারেশন প্রোভাইডার (AppFunctionConfiguration.Provider) পরিবর্তন করে।

আপনার অ্যাপ বর্তমানে AppFunctions-এর আগের কোনও ভার্সন (যেমন 1.0.0-alpha09) ব্যবহার করলে, আপনি Android Studio-তে Gemini-এর মতো AI IDE-তে AppFunctions এজেন্ট স্কিল ব্যবহার করে আপনার মাইগ্রেশন অটোমেট করতে পারবেন। স্কিলে ডেডিকেটেড মাইগ্রেশন সংক্রান্ত নিয়ম আছে যা আপনার বিল্ড ডিপেন্ডেন্সি একত্রিত করতে, প্রয়োজনীয় @AppFunctionServiceEntryPoint সার্ভিস র‍্যাপার তৈরি করতে, কনটেক্সট প্যারামিটার আলাদা করতে এবং আপনার ম্যানিফেস্ট ঘোষণা আপডেট করতে এজেন্টকে গাইড করে।