অ্যাক্সেসিবিলিটি পরিষেবা তৈরি করা

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

এই গাইডে Android অ্যাক্সেসিবিলিটি পরিষেবা তৈরি করার প্রাথমিক বিষয়গুলি আলোচনা করা হয়েছে।

অ্যাক্সেসিবিলিটি পরিষেবার লাইফসাইকেল

অ্যাক্সেসিবিলিটি পরিষেবা তৈরি করতে, আপনাকে অবশ্যই AccessibilityService ক্লাস বাড়াতে হবে এবং আপনার অ্যাপের ম্যানিফেস্টে পরিষেবাটি ঘোষণা করতে হবে।

পরিষেবা ক্লাস তৈরি করুন

এমন একটি ক্লাস তৈরি করুন যা AccessibilityService-কে এক্সটেন্ড করে। আপনাকে অবশ্যই নিম্নলিখিত পদ্ধতিগুলি ওভাররাইড করতে হবে:

  • onAccessibilityEvent: সিস্টেম যখন আপনার পরিষেবার কনফিগারেশনের সাথে মেলে এমন কোনও ইভেন্ট শনাক্ত করে তখন কল করা হয় (যেমন, ফোকাস পরিবর্তন বা বোতামে ক্লিক)। এখানে আপনার পরিষেবা ইউজার ইন্টারফেস ব্যাখ্যা করে।
  • onInterrupt: সিস্টেম আপনার পরিষেবার প্রতিক্রিয়াকে বাধা দিলে কল করা হয় (যেমন, ব্যবহারকারী দ্রুত ফোকাস সরালে স্পিচ আউটপুট বন্ধ করতে)।
package com.example.android.apis.accessibility

import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.AccessibilityServiceInfo
import android.accessibilityservice.FingerprintGestureController
import android.accessibilityservice.AccessibilityButtonController
import android.accessibilityservice.GestureDescription
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.graphics.Path
import android.os.Build
import android.media.AudioManager
import android.content.Context

class MyAccessibilityService : AccessibilityService() {

    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        // Interpret the event and provide feedback to the user
    }

    override fun onInterrupt() {
        // Interrupt any ongoing feedback
    }

    override fun onServiceConnected() {
        // Perform initialization here
    }
}

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

AndroidManifest.xml ফাইলে আপনার পরিষেবা রেজিস্টার করুন। আপনাকে অবশ্যই BIND_ACCESSIBILITY_SERVICE অনুমতি কঠোরভাবে প্রয়োগ করতে হবে যাতে শুধুমাত্র সিস্টেম আপনার পরিষেবার সাথে বাইন্ড করতে পারে।

সেটিংস বোতাম কাজ করছে কিনা তা নিশ্চিত করতে, ServiceSettingsActivity ঘোষণা করুন।

<application>
  <service android:name=".accessibility.MyAccessibilityService"
      android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
      android:exported="true"
      android:label="@string/accessibility_service_label">
      <intent-filter>
          <action android:name="android.accessibilityservice.AccessibilityService" />
      </intent-filter>
      <meta-data
          android:name="android.accessibilityservice"
          android:resource="@xml/accessibility_service_config" />
  </service>

  <activity android:name=".accessibility.ServiceSettingsActivity"
      android:exported="true"
      android:label="@string/accessibility_service_settings_label" />
</application>

পরিষেবা কনফিগার করুন

res/xml/accessibility_service_config.xml-এ কনফিগারেশন ফাইল তৈরি করুন। এই ফাইলটি আপনার পরিষেবা কোন কোন ইভেন্ট ম্যানেজ করে এবং কী কী মতামত প্রদান করে তা নির্ধারণ করে। আপনার ম্যানিফেস্টে যে ServiceSettingsActivity উল্লেখ করেছেন সেটি রেফারেন্স হিসেবে ব্যবহার করতে ভুলবেন না:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagDefault|flagRequestFingerprintGestures|flagRequestAccessibilityButton"
    android:accessibilityFeedbackType="feedbackSpoken"
    android:notificationTimeout="100"
    android:canRetrieveWindowContent="true"
    android:canPerformGestures="true"
    android:settingsActivity="com.example.android.apis.accessibility.ServiceSettingsActivity" />

কনফিগারেশন ফাইলে নিম্নলিখিত মূল অ্যাট্রিবিউটগুলি অন্তর্ভুক্ত থাকে:

  • android:accessibilityEventTypes: আপনি যেসব ইভেন্ট পেতে চান। সাধারণ-উদ্দেশ্যের পরিষেবার জন্য typeAllMask ব্যবহার করুন।
  • android:canRetrieveWindowContent: আপনার পরিষেবার UI হায়ারার্কি (যেমন, স্ক্রিন থেকে টেক্সট পড়া) পরীক্ষা করার প্রয়োজন হলে, অবশ্যই true হতে হবে।
  • android:canPerformGestures: আপনি প্রোগ্রাম্যাটিকভাবে জেসচার (যেমন সোয়াইপ বা ট্যাপ) ডিসপ্যাচ করতে চাইলে, অবশ্যই true হতে হবে।
  • android:accessibilityFlags: ফিচার চালু করতে ফ্ল্যাগ একত্রিত করুন। ফিঙ্গারপ্রিন্ট জেসচারের জন্য flagRequestFingerprintGestures প্রয়োজন। সফ্টওয়্যার অ্যাক্সেসিবিলিটি বোতামের জন্য flagRequestAccessibilityButton প্রয়োজন।

কনফিগারেশন বিকল্পের সম্পূর্ণ তালিকা দেখতে, AccessibilityServiceInfo দেখুন।

রানটাইম কনফিগারেশন

XML কনফিগারেশন স্ট্যাটিক হলেও, রানটাইমে আপনি নিজের পরিষেবা কনফিগারেশন ডায়নামিক্যালি পরিবর্তন করতে পারবেন। ব্যবহারকারীর পছন্দের উপর ভিত্তি করে ফিচার টগল করার জন্য এটি উপযোগী।

onServiceConnected() ওভাররাইড করে রানটাইম আপডেট প্রয়োগ করুন, এর জন্য ব্যবহার করুন setServiceInfo():

override fun onServiceConnected() {
    val info = AccessibilityServiceInfo()

    // Set the type of events that this service wants to listen to.
    info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED

    // Set the type of feedback your service provides.
    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN

    // Set flags at runtime.
    info.flags = AccessibilityServiceInfo.FLAG_DEFAULT or
            AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES

    this.setServiceInfo(info)
}

UI কন্টেন্ট ব্যাখ্যা করা

onAccessibilityEvent() ট্রিগার করলে, সিস্টেম একটি AccessibilityEvent প্রদান করে। এই ইভেন্টটি অ্যাক্সেসিবিলিটি ট্রি-এর এন্ট্রি পয়েন্ট হিসেবে কাজ করে, যা স্ক্রিন কন্টেন্টের একটি হায়ারার্কিক্যাল উপস্থাপনা।

আপনার পরিষেবা মূলত AccessibilityNodeInfo অবজেক্টের সাথে ইন্টার‍্যাক্ট করে, যা বোতাম, তালিকা এবং টেক্সটের মতো UI এলিমেন্টকে দেখায়। এইসব UI এলিমেন্ট সম্পর্কিত ডেটা AccessibilityNodeInfo-এ নর্ম্যালাইজ করা হয়।

নিম্নলিখিত উদাহরণে দেখানো হয়েছে কীভাবে কোনও ইভেন্টের সোর্স পুনরুদ্ধার করতে হয় এবং তথ্য খুঁজে পেতে অ্যাক্সেসিবিলিটি ট্রি ট্রাভার্স করতে হয়।

override fun onAccessibilityEvent(event: AccessibilityEvent) {
    // Get the source node of the event
    val sourceNode: AccessibilityNodeInfo? = event.source

    if (sourceNode == null) return

    // Inspect properties
    if (sourceNode.isCheckable) {
        val state = if (sourceNode.isChecked) "Checked" else "Unchecked"
        val label = sourceNode.text ?: sourceNode.contentDescription
        
        // Provide feedback (for example, speak to the user)
        speakToUser("$label is $state")
    }

    // Always recycle nodes to prevent memory leaks
    sourceNode.recycle()
}

private fun speakToUser(text: String) {
    // Your text-to-speech implementation goes here
}

ব্যবহারকারীদের হয়ে কাজ করা

অ্যাক্সেসিবিলিটি পরিষেবা ব্যবহারকারীর হয়ে বোতামে ক্লিক করা বা তালিকা স্ক্রল করার মতো অ্যাকশন পারফর্ম করতে পারে।

কোনও অ্যাকশন পারফর্ম করতে, AccessibilityNodeInfo অবজেক্টে performAction() কল করুন।

fun performClick(node: AccessibilityNodeInfo) {
    if (node.isClickable) {
        node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
    }
}

গ্লোবাল অ্যাকশন যা সম্পূর্ণ সিস্টেমকে প্রভাবিত করে (যেমন, ফিরে যান বোতাম প্রেস করা বা বিজ্ঞপ্তি শেড খোলা), তার জন্য performGlobalAction() ব্যবহার করুন।

// Navigate back
fun navigateBack() {
    performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK)
}

ফোকাস ম্যানেজ করুন

Android-এ দুই ধরনের ফোকাস আছে: ইনপুট ফোকাস (যেখানে কীবোর্ড ইনপুট যায়) এবং অ্যাক্সেসিবিলিটি ফোকাস (অ্যাক্সেসিবিলিটি পরিষেবা যা পরীক্ষা করছে)।

বর্তমানে অ্যাক্সেসিবিলিটি ফোকাস আছে এমন এলিমেন্ট কীভাবে খুঁজে পাবেন তা নিম্নলিখিত স্নিপেট থেকে দেখানো হয়েছে:

// Find the node that currently has accessibility focus
// Note: rootInActiveWindow can be null if the window is not available
val root = rootInActiveWindow
if (root != null) {
    val focusedNode = root.findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY)

    // Do something with focusedNode

    // Always recycle nodes
    focusedNode?.recycle()
    // rootInActiveWindow doesn't need to be recycled, but obtained nodes do.
}

নিম্নলিখিত স্নিপেট থেকে বোঝা যায় যে কীভাবে অ্যাক্সেসিবিলিটি ফোকাস কোনও নির্দিষ্ট এলিমেন্টে সরানো যায়:

// Request that the system give focus to a given node
fun focusNode(node: AccessibilityNodeInfo) {
    node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)
}

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

অঙ্গভঙ্গির কাজগুলি সম্পাদন

আপনার পরিষেবা স্ক্রিনে কাস্টম জেসচার পাঠাতে পারে, যেমন সোয়াইপ, ট্যাপ বা মাল্টি-টাচ ইন্টার‍্যাকশন। এটি করতে, আপনার কনফিগারেশনে android:canPerformGestures="true" ঘোষণা করুন যাতে আপনি dispatchGesture() API ব্যবহার করতে পারেন।

সহজ জেসচার

সহজ জেসচার পারফর্ম করতে, প্রদত্ত জেসচারের সাথে সম্পর্কিত মুভমেন্ট দেখানোর জন্য Path অবজেক্ট তৈরি করে শুরু করুন। তারপর, স্ট্রোকের বিবরণ দিতে Path-কে GestureDescription-এর মধ্যে র‍্যাপ করুন। অবশেষে, জেসচার পাঠাতে dispatchGesture-এ কল করুন।

fun swipeRight() {
    // Create a path for the swipe (from x=100 to x=500)
    val swipePath = Path()
    swipePath.moveTo(100f, 500f)
    swipePath.lineTo(500f, 500f)

    // Build the stroke description (0ms delay, 500ms duration)
    val stroke = GestureDescription.StrokeDescription(swipePath, 0, 500)

    // Build the gesture description
    val gestureBuilder = GestureDescription.Builder()
    gestureBuilder.addStroke(stroke)

    // Dispatch the gesture
    dispatchGesture(gestureBuilder.build(), object : AccessibilityService.GestureResultCallback() {
        override fun onCompleted(gestureDescription: GestureDescription?) {
            super.onCompleted(gestureDescription)
            // Gesture finished successfully
        }
    }, null)
}

চলমান জেসচার

জটিল ইন্টার‍্যাকশনের (যেমন L আকৃতি আঁকা বা নিখুঁতভাবে মাল্টি-স্টেপ ড্র্যাগ করা) জন্য, আপনি willContinue প্যারামিটার ব্যবহার করে স্ট্রোক একসাথে চেইন করতে পারবেন।

fun performLShapedGesture() {
    val path1 = Path().apply {
        moveTo(200f, 200f)
        lineTo(400f, 200f)
    }

    val path2 = Path().apply {
        moveTo(400f, 200f)
        lineTo(400f, 400f)
    }

    // First stroke: willContinue = true
    val stroke1 = GestureDescription.StrokeDescription(path1, 0, 500, true)

    // Second stroke: continues immediately after stroke1
    val stroke2 = stroke1.continueStroke(path2, 0, 500, false)

    val builder = GestureDescription.Builder()
    builder.addStroke(stroke1)
    builder.addStroke(stroke2)

    dispatchGesture(builder.build(), null, null)
}

অডিও ম্যানেজমেন্ট

অ্যাক্সেসিবিলিটি পরিষেবা (বিশেষ করে স্ক্রিন রিডার) তৈরি করার সময়, STREAM_ACCESSIBILITY অডিও স্ট্রিম ব্যবহার করুন। এটি ব্যবহারকারীদের সিস্টেম মিডিয়া ভলিউম থেকে স্বাধীনভাবে পরিষেবার ভলিউম নিয়ন্ত্রণ করতে দেয়।

fun increaseAccessibilityVolume() {
    val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
    audioManager.adjustStreamVolume(
        AudioManager.STREAM_ACCESSIBILITY,
        AudioManager.ADJUST_RAISE,
        0
    )
}

আপনার কনফিগারেশনে FLAG_ENABLE_ACCESSIBILITY_VOLUME ফ্ল্যাগ অন্তর্ভুক্ত করতে ভুলবেন না, হয় XML-এ অথবা রানটাইমে setServiceInfo-এর মাধ্যমে।

উন্নত ফিচার

আঙ্গুলের ছাপ সেন্সরের উপর করা জেসচার

Android 10 (API লেভেল ২৯) বা তার পরবর্তী যেকোনও ভার্সনে চলা ডিভাইসে, আপনার পরিষেবা ফিঙ্গারপ্রিন্ট সেন্সরে দিকনির্দেশক সোয়াইপ ক্যাপচার করতে পারে। এটি বিকল্প নেভিগেশন কন্ট্রোল প্রদান করার জন্য উপযোগী।

আপনার onServiceConnected() পদ্ধতিতে নিম্নলিখিত লজিক যোগ করুন:

// Import: android.os.Build
// Import: android.accessibilityservice.FingerprintGestureController

private var gestureController: FingerprintGestureController? = null

override fun onServiceConnected() {
    // Check if the device is running Android 10 (Q) or higher
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        gestureController = fingerprintGestureController

        val callback = object : FingerprintGestureController.FingerprintGestureCallback() {
            override fun onGestureDetected(gesture: Int) {
                when (gesture) {
                    FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN -> {
                        // Handle swipe down
                    }
                    FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP -> {
                        // Handle swipe up
                    }
                }
            }
        }

        gestureController?.registerFingerprintGestureCallback(callback, null)
    }
}

অ্যাক্সেসিবিলিটি বোতাম

সফ্টওয়্যার নেভিগেশন কী ব্যবহার করা ডিভাইসে, ব্যবহারকারীরা নেভিগেশন বারে অ্যাক্সেসিবিলিটি বোতামের মাধ্যমে আপনার পরিষেবা চালু করতে পারেন।

এই ফিচার ব্যবহার করতে, আপনার পরিষেবা কনফিগারেশনে FLAG_REQUEST_ACCESSIBILITY_BUTTON ফ্ল্যাগ যোগ করুন। তারপর আপনার onServiceConnected() পদ্ধতিতে রেজিস্ট্রেশন লজিক যোগ করুন।

// Import: android.accessibilityservice.AccessibilityButtonController

override fun onServiceConnected() {
    // ... existing initialization code ...

    val controller = accessibilityButtonController

    controller.registerAccessibilityButtonCallback(
        object : AccessibilityButtonController.AccessibilityButtonCallback() {
            override fun onClicked(controller: AccessibilityButtonController) {
                // Respond to button tap
            }
        }
    )
}

বহুভাষিক টেক্সট-টু-স্পিচ

যে পরিষেবা জোরে জোরে টেক্সট পড়ে, সেটি অটোমেটিক ভাষা পরিবর্তন করতে পারে, যদি সোর্স টেক্সট LocaleSpan-এর সাথে ট্যাগ করা থাকে। এর ফলে আপনার পরিষেবা ম্যানুয়ালি পরিবর্তন না করেই মিশ্র-ভাষার কন্টেন্ট সঠিকভাবে উচ্চারণ করতে পারে।

import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.LocaleSpan
import java.util.Locale

// Wrap text in LocaleSpan to indicate language
val spannable = SpannableStringBuilder("Bonjour")
spannable.setSpan(
    LocaleSpan(Locale.FRANCE),
    0,
    spannable.length,
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)

আপনার পরিষেবা AccessibilityNodeInfo প্রসেস করার সময়, সঠিক টেক্সট-টু-স্পিচ ভাষা নির্ধারণ করতে LocaleSpan অবজেক্টের জন্য text প্রপার্টি চেক করুন।

অতিরিক্ত রিসোর্স

আরও জানতে, নিম্নলিখিত রিসোর্স দেখুন:

নির্দেশিকা

কোডল্যাবস

কন্টেন্ট দেখে