আপনার নিজস্ব অ্যাক্সেসিবিলিটি পরিষেবা (ভিউ) তৈরি করুন

ধারণা ও Jetpack Compose-এর প্রয়োগ

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

Android স্ট্যান্ডার্ড অ্যাক্সেসিবিলিটি পরিষেবা প্রদান করে, যার মধ্যে টকব্যাক অন্তর্ভুক্ত এবং ডেভেলপাররা তাদের নিজস্ব পরিষেবা তৈরি ও বিতরণ করতে পারেন। এই ডকুমেন্ট থেকে অ্যাক্সেসিবিলিটি পরিষেবা তৈরি করার প্রাথমিক বিষয়গুলি জানা যাবে।

অ্যাক্সেসিবিলিটি পরিষেবা কোনও সাধারণ অ্যাপের সাথে বান্ডেল করা যেতে পারে অথবা একটি স্বতন্ত্র Android প্রোজেক্ট হিসেবে তৈরি করা যেতে পারে। পরিষেবা তৈরি করার ধাপগুলি দুটি ক্ষেত্রেই একই।

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

আপনার প্রোজেক্টের মধ্যে, এমন একটি ক্লাস তৈরি করুন যা AccessibilityService-কে এক্সটেন্ড করে:

Kotlin

package com.example.android.apis.accessibility

import android.accessibilityservice.AccessibilityService
import android.view.accessibility.AccessibilityEvent

class MyAccessibilityService : AccessibilityService() {
...
    override fun onInterrupt() {}

    override fun onAccessibilityEvent(event: AccessibilityEvent?) {}
...
}

জাভা

package com.example.android.apis.accessibility;

import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;

public class MyAccessibilityService extends AccessibilityService {
...
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    }

    @Override
    public void onInterrupt() {
    }

...
}

আপনি যদি এই Service-এর জন্য নতুন প্রোজেক্ট তৈরি করেন এবং এর সাথে কোনও অ্যাপ অ্যাসোসিয়েট করার পরিকল্পনা না করেন, তাহলে আপনার সোর্স থেকে স্টার্টার Activity ক্লাস সরিয়ে দিতে পারেন।

ম্যানিফেস্ট ঘোষণা ও অনুমতি

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

অ্যাক্সেসিবিলিটি পরিষেবা সংক্রান্ত ঘোষণা

আপনার অ্যাপকে অ্যাক্সেসিবিলিটি পরিষেবা হিসেবে গণ্য করতে, আপনার ম্যানিফেস্টের application এলিমেন্টের মধ্যে activity এলিমেন্টের পরিবর্তে service এলিমেন্ট অন্তর্ভুক্ত করুন। এছাড়াও, service এলিমেন্টের মধ্যে, একটি অ্যাক্সেসিবিলিটি পরিষেবা ইনটেন্ট ফিল্টার অন্তর্ভুক্ত করুন। এছাড়াও, ম্যানিফেস্টকে অবশ্যই BIND_ACCESSIBILITY_SERVICE অনুমতি যোগ করে পরিষেবাটিকে সুরক্ষিত করতে হবে যাতে শুধুমাত্র সিস্টেম এটির সাথে বাইন্ড করতে পারে। এখানে একটি উদাহরণ দেওয়া হল:

  <application>
    <service android:name=".MyAccessibilityService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
        android:label="@string/accessibility_service_label">
      <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
      </intent-filter>
    </service>
  </application>

অ্যাক্সেসিবিলিটি পরিষেবা কনফিগারেশন

অ্যাক্সেসিবিলিটি পরিষেবাকে অবশ্যই এমন একটি কনফিগারেশন প্রদান করতে হবে যা পরিষেবার দ্বারা পরিচালিত অ্যাক্সেসিবিলিটি ইভেন্টের ধরন এবং পরিষেবা সম্পর্কে অতিরিক্ত তথ্য নির্দিষ্ট করে। অ্যাক্সেসিবিলিটি পরিষেবার কনফিগারেশন AccessibilityServiceInfo ক্লাসে থাকে। আপনার পরিষেবা এই ক্লাস এবং setServiceInfo()-এর ইনস্ট্যান্স ব্যবহার করে রানটাইমে কনফিগারেশন তৈরি ও সেট করতে পারে। তবে, এই পদ্ধতি ব্যবহার করে সব কনফিগারেশন বিকল্প উপলভ্য নয়।

আপনি কনফিগারেশন ফাইলের রেফারেন্স সহ ম্যানিফেস্টে <meta-data> এলিমেন্ট যোগ করতে পারেন, যা আপনাকে অ্যাক্সেসিবিলিটি পরিষেবার জন্য সম্পূর্ণ রেঞ্জের বিকল্প সেট করতে দেয়, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

<service android:name=".MyAccessibilityService">
  ...
  <meta-data
    android:name="android.accessibilityservice"
    android:resource="@xml/accessibility_service_config" />
</service>

এই <meta-data> এলিমেন্টটি একটি XML ফাইলকে রেফার করে যা আপনি নিজের অ্যাপের রিসোর্স ডিরেক্টরিতে তৈরি করেন: <project_dir>/res/xml/accessibility_service_config.xml>। নিম্নলিখিত কোডটি পরিষেবা কনফিগারেশন ফাইলের কন্টেন্টের একটি উদাহরণ দেখায়:

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

অ্যাক্সেসিবিলিটি পরিষেবা কনফিগারেশন ফাইলে ব্যবহার করা যেতে পারে এমন XML অ্যাট্রিবিউট সম্পর্কে আরও তথ্যের জন্য, নিম্নলিখিত রেফারেন্স ডকুমেন্টেশন দেখুন:

রানটাইমে কোন কনফিগারেশন সেটিংস ডাইনামিকভাবে সেট করা যেতে পারে সেই সম্পর্কে আরও তথ্যের জন্য, AccessibilityServiceInfo রেফারেন্স ডকুমেন্টেশন দেখুন।

আপনার অ্যাক্সেসিবিলিটি পরিষেবা কনফিগার করুন

আপনার অ্যাক্সেসিবিলিটি পরিষেবার জন্য কনফিগারেশন ভেরিয়েবল সেট করার সময় নিম্নলিখিত বিষয়গুলি বিবেচনা করুন সিস্টেমকে কীভাবে এবং কখন চালাতে হবে তা জানাতে:

  • আপনি কোন ধরনের ইভেন্টের উত্তর দিতে চান?
  • পরিষেবাটি কি সব অ্যাপের জন্য চালু থাকতে হবে, নাকি শুধুমাত্র নির্দিষ্ট প্যাকেজের নামের জন্য?
  • এটি কী কী ধরনের মতামত ব্যবহার করে?

এইসব ভেরিয়েবল সেট করার জন্য আপনার কাছে দুটি বিকল্প আছে। ব্যাকওয়ার্ড কম্প্যাটিবল বিকল্প setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) ব্যবহার করে কোডে সেট করতে হয়। এটি করতে, onServiceConnected() পদ্ধতি ওভাররাইড করুন এবং সেখানে আপনার পরিষেবা কনফিগার করুন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

Kotlin

override fun onServiceConnected() {
    info.apply {
        // Set the type of events that this service wants to listen to. Others
        // aren't passed to this service.
        eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED

        // If you only want this service to work with specific apps, set their
        // package names here. Otherwise, when the service is activated, it
        // listens to events from all apps.
        packageNames = arrayOf("com.example.android.myFirstApp", "com.example.android.mySecondApp")

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

        // Default services are invoked only if no package-specific services are
        // present for the type of AccessibilityEvent generated. This service is
        // app-specific, so the flag isn't necessary. For a general-purpose
        // service, consider setting the DEFAULT flag.

        // flags = AccessibilityServiceInfo.DEFAULT;

        notificationTimeout = 100
    }

    this.serviceInfo = info

}

জাভা

@Override
public void onServiceConnected() {
    // Set the type of events that this service wants to listen to. Others
    // aren't passed to this service.
    info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
            AccessibilityEvent.TYPE_VIEW_FOCUSED;

    // If you only want this service to work with specific apps, set their
    // package names here. Otherwise, when the service is activated, it listens
    // to events from all apps.
    info.packageNames = new String[]
            {"com.example.android.myFirstApp", "com.example.android.mySecondApp"};

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

    // Default services are invoked only if no package-specific services are
    // present for the type of AccessibilityEvent generated. This service is
    // app-specific, so the flag isn't necessary. For a general-purpose service,
    // consider setting the DEFAULT flag.

    // info.flags = AccessibilityServiceInfo.DEFAULT;

    info.notificationTimeout = 100;

    this.setServiceInfo(info);

}

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

<accessibility-service
     android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
     android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
     android:accessibilityFeedbackType="feedbackSpoken"
     android:notificationTimeout="100"
     android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
     android:canRetrieveWindowContent="true"
/>

আপনি XML ব্যবহার করলে, XML ফাইলটি নির্দেশ করে আপনার পরিষেবা ঘোষণায় একটি <meta-data> ট্যাগ যোগ করে আপনার ম্যানিফেস্টে এটি উল্লেখ করুন। আপনি যদি res/xml/serviceconfig.xml-এ XML ফাইল স্টোর করেন, তাহলে নতুন ট্যাগটি এইরকম দেখাবে:

<service android:name=".MyAccessibilityService">
     <intent-filter>
         <action android:name="android.accessibilityservice.AccessibilityService" />
     </intent-filter>
     <meta-data android:name="android.accessibilityservice"
     android:resource="@xml/serviceconfig" />
</service>

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

অ্যাক্সেসিবিলিটি পরিষেবাকে অবশ্যই AccessibilityService ক্লাসকে প্রসারিত করতে হবে এবং সেই ক্লাস থেকে নিম্নলিখিত পদ্ধতিগুলিকে ওভাররাইড করতে হবে। Android সিস্টেম যেভাবে কল করে সেই ক্রম অনুযায়ী এইসব পদ্ধতি দেখানো হয়: পরিষেবা শুরু হওয়া থেকে (onServiceConnected()), এটি চলাকালীন (onAccessibilityEvent(), onInterrupt()), এটি বন্ধ হওয়া পর্যন্ত (onUnbind())।

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

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

  • onInterrupt(): (প্রয়োজনীয়) সিস্টেম এই মেথডকে কল করে যখন সিস্টেম আপনার পরিষেবার প্রদান করা ফিডব্যাককে বাধা দিতে চায়, সাধারণত ব্যবহারকারীর অ্যাকশনের উত্তরে যেমন ফোকাসকে অন্য কন্ট্রোলে সরানো। আপনার পরিষেবার লাইফসাইকেল চলাকালীন এই পদ্ধতিটি অনেকবার কল করা যেতে পারে।

  • onUnbind(): (ঐচ্ছিক) সিস্টেম যখন অ্যাক্সেসিবিলিটি পরিষেবা বন্ধ করতে চলেছে, তখন সিস্টেম এই পদ্ধতিকে কল করে। একবার শাটডাউন করার পদ্ধতি সম্পূর্ণ করতে এই পদ্ধতি ব্যবহার করুন, এর মধ্যে অডিও ম্যানেজার বা ডিভাইস ভাইব্রেটরের মতো ব্যবহারকারীর ফিডব্যাক সিস্টেম পরিষেবা ডি-অ্যালোকেট করাও অন্তর্ভুক্ত।

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

অ্যাক্সেসিবিলিটি ইভেন্টের জন্য রেজিস্টার করুন

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

  • প্যাকেজের নাম: অ্যাপের প্যাকেজের নাম উল্লেখ করুন, যেটির অ্যাক্সেসিবিলিটি ইভেন্ট আপনি নিজের পরিষেবার মাধ্যমে ম্যানেজ করতে চান। এই প্যারামিটার বাদ দেওয়া হলে, আপনার অ্যাক্সেসিবিলিটি পরিষেবা যেকোনও অ্যাপের জন্য অ্যাক্সেসিবিলিটি ইভেন্ট পরিষেবা দেওয়ার জন্য উপলভ্য বলে বিবেচনা করা হয়। আপনি অ্যাক্সেসিবিলিটি পরিষেবা কনফিগারেশন ফাইলে কমা দ্বারা আলাদা করা তালিকা হিসেবে android:packageNames অ্যাট্রিবিউট সহ এই প্যারামিটার সেট করতে পারেন অথবা AccessibilityServiceInfo.packageNames মেম্বার ব্যবহার করতে পারেন।

  • ইভেন্টের ধরন: আপনার পরিষেবা যাতে অ্যাক্সেসিবিলিটি ইভেন্ট ম্যানেজ করতে পারে, সেই জন্য ইভেন্টের ধরন নির্দিষ্ট করুন। আপনি এই প্যারামিটার অ্যাক্সেসিবিলিটি পরিষেবা কনফিগারেশন ফাইলে android:accessibilityEventTypes অ্যাট্রিবিউট সহ | অক্ষর দ্বারা আলাদা করা তালিকা হিসেবে সেট করতে পারেন—যেমন, accessibilityEventTypes="typeViewClicked|typeViewFocused"। অথবা আপনি AccessibilityServiceInfo.eventTypes মেম্বার ব্যবহার করে এটি সেট করতে পারেন।

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

অ্যাক্সেসিবিলিটি ভলিউম

Android 8.0 (API লেভেল ২৬) এবং তার পরবর্তী ভার্সনে চলা ডিভাইসে STREAM_ACCESSIBILITY ভলিউম ক্যাটাগরি অন্তর্ভুক্ত থাকে, যা আপনাকে ডিভাইসের অন্যান্য সাউন্ড থেকে আলাদাভাবে আপনার অ্যাক্সেসিবিলিটি পরিষেবার অডিও আউটপুটের ভলিউম কন্ট্রোল করতে দেয়।

অ্যাক্সেসিবিলিটি পরিষেবা FLAG_ENABLE_ACCESSIBILITY_VOLUME বিকল্প সেট করে এই স্ট্রিমের ধরন ব্যবহার করতে পারে। তারপর আপনি AudioManager-এর ডিভাইসের ইনস্ট্যান্সে adjustStreamVolume() মেথড কল করে ডিভাইসের অ্যাক্সেসিবিলিটি অডিও ভলিউম পরিবর্তন করতে পারবেন।

নিম্নলিখিত কোড স্নিপেট থেকে বোঝা যায় যে অ্যাক্সেসিবিলিটি পরিষেবা কীভাবে STREAM_ACCESSIBILITY ভলিউম ক্যাটেগরি ব্যবহার করতে পারে:

Kotlin

import android.media.AudioManager.*

class MyAccessibilityService : AccessibilityService() {

    private val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager

    override fun onAccessibilityEvent(accessibilityEvent: AccessibilityEvent) {
        if (accessibilityEvent.source.text == "Increase volume") {
            audioManager.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY, ADJUST_RAISE, 0)
        }
    }
}

জাভা

import static android.media.AudioManager.*;

public class MyAccessibilityService extends AccessibilityService {
    private AudioManager audioManager =
            (AudioManager) getSystemService(AUDIO_SERVICE);

    @Override
    public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
        AccessibilityNodeInfo interactedNodeInfo =
                accessibilityEvent.getSource();
        if (interactedNodeInfo.getText().equals("Increase volume")) {
            audioManager.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY,
                ADJUST_RAISE, 0);
        }
    }
}

আরও তথ্যের জন্য, Google I/O 2017 থেকে Android অ্যাক্সেসিবিলিটিতে নতুন কী আছে সেশন ভিডিও দেখুন, যা 6:35 থেকে শুরু হচ্ছে।

অ্যাক্সেসিবিলিটি শর্টকাট

Android 8.0 (API লেভেল ২৬) এবং তার পরবর্তী যেকোনও ভার্সন ইনস্টল করা আছে এমন ডিভাইসে, ব্যবহারকারীরা যেকোনও স্ক্রিন থেকে দুটি ভলিউম কী একসাথে প্রেস করে ধরে রেখে তাদের পছন্দের অ্যাক্সেসিবিলিটি পরিষেবা চালু এবং বন্ধ করতে পারবেন। যদিও এই শর্টকাট ডিফল্ট হিসেবে Talkback চালু এবং বন্ধ করে, ব্যবহারকারীরা তাদের ডিভাইসে ইনস্টল করা যেকোনও পরিষেবা চালু এবং বন্ধ করার জন্য বোতামটি কনফিগার করতে পারেন।

অ্যাক্সেসিবিলিটি শর্টকাট থেকে কোনও নির্দিষ্ট অ্যাক্সেসিবিলিটি পরিষেবা অ্যাক্সেস করার জন্য, রানটাইমে পরিষেবাটিকে ফিচারটির জন্য অনুরোধ করতে হবে।

আরও তথ্যের জন্য, Google I/O 2017 থেকে Android অ্যাক্সেসিবিলিটিতে নতুন কী আছে সেশন ভিডিও দেখুন, যা 13:25 থেকে শুরু হচ্ছে।

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

সফ্টওয়্যার-রেন্ডার করা নেভিগেশন এরিয়া ব্যবহার করা এবং Android 8.0 (API লেভেল ২৬) ও তার পরবর্তী যেকোনও ভার্সনে চলা ডিভাইসে, নেভিগেশন বারের ডানদিকে একটি অ্যাক্সেসিবিলিটি বোতাম থাকে। ব্যবহারকারী এই বোতাম প্রেস করলে, স্ক্রিনে বর্তমানে দেখানো কন্টেন্টের উপর নির্ভর করে, তিনি চালু থাকা একাধিক অ্যাক্সেসিবিলিটি ফিচার ও পরিষেবার মধ্যে থেকে যেকোনও একটি চালু করতে পারবেন।

অ্যাক্সেসিবিলিটি বোতাম ব্যবহার করে ব্যবহারকারীদের কোনও নির্দিষ্ট অ্যাক্সেসিবিলিটি পরিষেবা চালু করার অনুমতি দিতে, পরিষেবাটিকে AccessibilityServiceInfo অবজেক্টের android:accessibilityFlags অ্যাট্রিবিউটে FLAG_REQUEST_ACCESSIBILITY_BUTTON ফ্ল্যাগ যোগ করতে হবে। পরিষেবাটি তারপর registerAccessibilityButtonCallback() ব্যবহার করে কলব্যাক রেজিস্টার করতে পারে।

নিম্নলিখিত কোড স্নিপেট থেকে বোঝা যায় যে ব্যবহারকারী অ্যাক্সেসিবিলিটি বোতাম প্রেস করলে, অ্যাক্সেসিবিলিটি পরিষেবা কীভাবে কনফিগার করতে হয়:

Kotlin

private var mAccessibilityButtonController: AccessibilityButtonController? = null
private var accessibilityButtonCallback:
        AccessibilityButtonController.AccessibilityButtonCallback? = null
private var mIsAccessibilityButtonAvailable: Boolean = false

override fun onServiceConnected() {
    mAccessibilityButtonController = accessibilityButtonController
    mIsAccessibilityButtonAvailable =
            mAccessibilityButtonController?.isAccessibilityButtonAvailable ?: false

    if (!mIsAccessibilityButtonAvailable) return

    serviceInfo = serviceInfo.apply {
        flags = flags or AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON
    }

    accessibilityButtonCallback =
        object : AccessibilityButtonController.AccessibilityButtonCallback() {
            override fun onClicked(controller: AccessibilityButtonController) {
                Log.d("MY_APP_TAG", "Accessibility button pressed!")

                // Add custom logic for a service to react to the
                // accessibility button being pressed.
            }

            override fun onAvailabilityChanged(
                    controller: AccessibilityButtonController,
                    available: Boolean
            ) {
                if (controller == mAccessibilityButtonController) {
                    mIsAccessibilityButtonAvailable = available
                }
            }
    }

    accessibilityButtonCallback?.also {
        mAccessibilityButtonController?.registerAccessibilityButtonCallback(it, null)
    }
}

জাভা

private AccessibilityButtonController accessibilityButtonController;
private AccessibilityButtonController
        .AccessibilityButtonCallback accessibilityButtonCallback;
private boolean mIsAccessibilityButtonAvailable;

@Override
protected void onServiceConnected() {
    accessibilityButtonController = getAccessibilityButtonController();
    mIsAccessibilityButtonAvailable =
            accessibilityButtonController.isAccessibilityButtonAvailable();

    if (!mIsAccessibilityButtonAvailable) {
        return;
    }

    AccessibilityServiceInfo serviceInfo = getServiceInfo();
    serviceInfo.flags
            |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
    setServiceInfo(serviceInfo);

    accessibilityButtonCallback =
        new AccessibilityButtonController.AccessibilityButtonCallback() {
            @Override
            public void onClicked(AccessibilityButtonController controller) {
                Log.d("MY_APP_TAG", "Accessibility button pressed!");

                // Add custom logic for a service to react to the
                // accessibility button being pressed.
            }

            @Override
            public void onAvailabilityChanged(
              AccessibilityButtonController controller, boolean available) {
                if (controller.equals(accessibilityButtonController)) {
                    mIsAccessibilityButtonAvailable = available;
                }
            }
        };

    if (accessibilityButtonCallback != null) {
        accessibilityButtonController.registerAccessibilityButtonCallback(
                accessibilityButtonCallback, null);
    }
}

আরও তথ্যের জন্য, Google I/O 2017 থেকে Android অ্যাক্সেসিবিলিটিতে নতুন কী আছে সেশন ভিডিও দেখুন, যা শুরু হচ্ছে ১৬:২৮ থেকে।

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

Android 8.0 (API লেভেল ২৬) এবং তার পরবর্তী ভার্সনে চলা ডিভাইসে অ্যাক্সেসিবিলিটি পরিষেবা ডিভাইসের ফিঙ্গারপ্রিন্ট সেন্সর বরাবর দিকনির্দেশক সোয়াইপে (উপরে, নিচে, বাঁদিকে এবং ডানদিকে) প্রতিক্রিয়া জানাতে পারে। এইসব ইন্টার‍্যাকশন সম্পর্কে কলব্যাক পেতে কোনও পরিষেবা কনফিগার করতে, নিম্নলিখিত ক্রম সম্পূর্ণ করুন:

  1. USE_BIOMETRIC অনুমতি এবং CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES ক্ষমতা ঘোষণা করুন।
  2. android:accessibilityFlags অ্যাট্রিবিউটের মধ্যে FLAG_REQUEST_FINGERPRINT_GESTURES ফ্ল্যাগ সেট করুন।
  3. registerFingerprintGestureCallback() ) ব্যবহার করে কলব্যাকের জন্য রেজিস্টার করুন।

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

নিম্নলিখিত কোড স্নিপেট থেকে ভার্চুয়াল গেম বোর্ডে নেভিগেট করার জন্য ফিঙ্গারপ্রিন্ট জেসচার ব্যবহার করার উদাহরণ দেখানো হয়েছে:

// AndroidManifest.xml
<manifest ... >
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
    ...
    <application>
        <service android:name="com.example.MyFingerprintGestureService" ... >
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/myfingerprintgestureservice" />
        </service>
    </application>
</manifest>
// myfingerprintgestureservice.xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:accessibilityFlags=" ... |flagRequestFingerprintGestures"
    android:canRequestFingerprintGestures="true"
    ... />

Kotlin

// MyFingerprintGestureService.kt
import android.accessibilityservice.FingerprintGestureController.*

class MyFingerprintGestureService : AccessibilityService() {

    private var gestureController: FingerprintGestureController? = null
    private var fingerprintGestureCallback:
            FingerprintGestureController.FingerprintGestureCallback? = null
    private var mIsGestureDetectionAvailable: Boolean = false

    override fun onCreate() {
        gestureController = fingerprintGestureController
        mIsGestureDetectionAvailable = gestureController?.isGestureDetectionAvailable ?: false
    }

    override fun onServiceConnected() {
        if (mFingerprintGestureCallback != null || !mIsGestureDetectionAvailable) return

        fingerprintGestureCallback =
                object : FingerprintGestureController.FingerprintGestureCallback() {
                    override fun onGestureDetected(gesture: Int) {
                        when (gesture) {
                            FINGERPRINT_GESTURE_SWIPE_DOWN -> moveGameCursorDown()
                            FINGERPRINT_GESTURE_SWIPE_LEFT -> moveGameCursorLeft()
                            FINGERPRINT_GESTURE_SWIPE_RIGHT -> moveGameCursorRight()
                            FINGERPRINT_GESTURE_SWIPE_UP -> moveGameCursorUp()
                            else -> Log.e(MY_APP_TAG, "Error: Unknown gesture type detected!")
                        }
                    }

                    override fun onGestureDetectionAvailabilityChanged(available: Boolean) {
                        mIsGestureDetectionAvailable = available
                    }
                }

        fingerprintGestureCallback?.also {
            gestureController?.registerFingerprintGestureCallback(it, null)
        }
    }
}

জাভা

// MyFingerprintGestureService.java
import static android.accessibilityservice.FingerprintGestureController.*;

public class MyFingerprintGestureService extends AccessibilityService {
    private FingerprintGestureController gestureController;
    private FingerprintGestureController
            .FingerprintGestureCallback fingerprintGestureCallback;
    private boolean mIsGestureDetectionAvailable;

    @Override
    public void onCreate() {
        gestureController = getFingerprintGestureController();
        mIsGestureDetectionAvailable =
                gestureController.isGestureDetectionAvailable();
    }

    @Override
    protected void onServiceConnected() {
        if (fingerprintGestureCallback != null
                || !mIsGestureDetectionAvailable) {
            return;
        }

        fingerprintGestureCallback =
               new FingerprintGestureController.FingerprintGestureCallback() {
            @Override
            public void onGestureDetected(int gesture) {
                switch (gesture) {
                    case FINGERPRINT_GESTURE_SWIPE_DOWN:
                        moveGameCursorDown();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_LEFT:
                        moveGameCursorLeft();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_RIGHT:
                        moveGameCursorRight();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_UP:
                        moveGameCursorUp();
                        break;
                    default:
                        Log.e(MY_APP_TAG,
                                  "Error: Unknown gesture type detected!");
                        break;
                }
            }

            @Override
            public void onGestureDetectionAvailabilityChanged(boolean available) {
                mIsGestureDetectionAvailable = available;
            }
        };

        if (fingerprintGestureCallback != null) {
            gestureController.registerFingerprintGestureCallback(
                    fingerprintGestureCallback, null);
        }
    }
}

আরও তথ্যের জন্য, Google I/O 2017 থেকে Android অ্যাক্সেসিবিলিটিতে নতুন কী আছে সেশন ভিডিও দেখুন, যা শুরু হচ্ছে 9:03 থেকে।

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

Android 8.0 (API লেভেল ২৬) থেকে শুরু করে, Android-এর টেক্সট-টু-স্পিচ (TTS) পরিষেবা টেক্সটের একটি ব্লকের মধ্যেই একাধিক ভাষায় শব্দগুচ্ছ শনাক্ত করতে এবং বলতে পারে। অ্যাক্সেসিবিলিটি পরিষেবায় এই অটোমেটিক ভাষা পরিবর্তন করার ক্ষমতা চালু করতে, নিচে দেখানো কোড স্নিপেটের মতো, সব স্ট্রিং LocaleSpan অবজেক্টে র‌্যাপ করুন:

Kotlin

val localeWrappedTextView = findViewById<TextView>(R.id.my_french_greeting_text).apply {
    text = wrapTextInLocaleSpan("Bonjour!", Locale.FRANCE)
}

private fun wrapTextInLocaleSpan(originalText: CharSequence, loc: Locale): SpannableStringBuilder {
    return SpannableStringBuilder(originalText).apply {
        setSpan(LocaleSpan(loc), 0, originalText.length - 1, 0)
    }
}

জাভা

TextView localeWrappedTextView = findViewById(R.id.my_french_greeting_text);
localeWrappedTextView.setText(wrapTextInLocaleSpan("Bonjour!", Locale.FRANCE));

private SpannableStringBuilder wrapTextInLocaleSpan(
        CharSequence originalText, Locale loc) {
    SpannableStringBuilder myLocaleBuilder =
            new SpannableStringBuilder(originalText);
    myLocaleBuilder.setSpan(new LocaleSpan(loc), 0,
            originalText.length() - 1, 0);
    return myLocaleBuilder;
}

আরও তথ্যের জন্য, Google I/O 2017 থেকে Android অ্যাক্সেসিবিলিটিতে নতুন কী আছে সেশন ভিডিও দেখুন, যা ১০:৫৯ থেকে শুরু হচ্ছে।

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

২০১১ সাল থেকে, অ্যাক্সেসিবিলিটি পরিষেবা ব্যবহারকারীদের হয়ে কাজ করতে পারে, যার মধ্যে ইনপুট ফোকাস পরিবর্তন করা এবং ইউজার ইন্টারফেস এলিমেন্ট বেছে নেওয়া (অ্যাক্টিভেট করা) অন্তর্ভুক্ত। ২০১২ সালে, অ্যাকশনের রেঞ্জ আরও বাড়ানো হয়, যার মধ্যে স্ক্রল করা যায় এমন তালিকা এবং টেক্সট ফিল্ডের সাথে ইন্টার‍্যাক্ট করার সুবিধা অন্তর্ভুক্ত করা হয়। অ্যাক্সেসিবিলিটি পরিষেবা গ্লোবাল অ্যাকশনও নিতে পারে, যেমন হোম স্ক্রিনে নেভিগেট করা, ফিরে যাওয়ার বোতাম প্রেস করা এবং বিজ্ঞপ্তি স্ক্রিন ও সাম্প্রতিক অ্যাপের তালিকা খোলা। ২০১২ সাল থেকে, Android-এ অ্যাক্সেসিবিলিটি ফোকাস অন্তর্ভুক্ত করা হয়েছে, যা অ্যাক্সেসিবিলিটি পরিষেবার মাধ্যমে সব দৃশ্যমান এলিমেন্টকে বেছে নেওয়ার উপযুক্ত করে তোলে।

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

জেসচারের জন্য শুনুন

অ্যাক্সেসিবিলিটি পরিষেবা নির্দিষ্ট জেসচার শুনতে এবং ব্যবহারকারীর হয়ে কাজ করে উত্তর দিতে পারে। এই ফিচার ব্যবহার করতে হলে, আপনার অ্যাক্সেসিবিলিটি পরিষেবাকে 'স্পর্শ করে এক্সপ্লোর করুন' ফিচার অ্যাক্টিভেট করার অনুরোধ করতে হবে। আপনার পরিষেবা এই অ্যাক্টিভেশনের জন্য অনুরোধ করতে পারে, এর জন্য পরিষেবার AccessibilityServiceInfo ইনস্ট্যান্সের flags মেম্বারকে FLAG_REQUEST_TOUCH_EXPLORATION_MODE-এ সেট করতে হবে, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে।

Kotlin

class MyAccessibilityService : AccessibilityService() {

    override fun onCreate() {
        serviceInfo.flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE
    }
    ...
}

জাভা

public class MyAccessibilityService extends AccessibilityService {
    @Override
    public void onCreate() {
        getServiceInfo().flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
    }
    ...
}

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

চলমান জেসচার

Android 8.0 (API লেভেল ২৬) এবং তার পরবর্তী যেকোনও ভার্সনে চলা ডিভাইসে কন্টিনিউড জেসচার বা একাধিক Path অবজেক্ট সহ প্রোগ্রাম্যাটিক জেসচার কাজ করে।

স্ট্রোকের ক্রম নির্দিষ্ট করার সময়, আপনি উল্লেখ করতে পারেন যে সেগুলি একই প্রোগ্রাম্যাটিক জেসচারের অংশ। এর জন্য আপনাকে GestureDescription.StrokeDescription কনস্ট্রাক্টরে চূড়ান্ত আর্গুমেন্ট willContinue ব্যবহার করতে হবে। নিচের কোড স্নিপেটে এটি দেখানো হয়েছে:

Kotlin

// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down.
private fun doRightThenDownDrag() {
    val dragRightPath = Path().apply {
        moveTo(200f, 200f)
        lineTo(400f, 200f)
    }
    val dragRightDuration = 500L // 0.5 second

    // The starting point of the second path must match
    // the ending point of the first path.
    val dragDownPath = Path().apply {
        moveTo(400f, 200f)
        lineTo(400f, 400f)
    }
    val dragDownDuration = 500L
    val rightThenDownDrag = GestureDescription.StrokeDescription(
            dragRightPath,
            0L,
            dragRightDuration,
            true
    ).apply {
        continueStroke(dragDownPath, dragRightDuration, dragDownDuration, false)
    }
}

জাভা

// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down.
private void doRightThenDownDrag() {
    Path dragRightPath = new Path();
    dragRightPath.moveTo(200, 200);
    dragRightPath.lineTo(400, 200);
    long dragRightDuration = 500L; // 0.5 second

    // The starting point of the second path must match
    // the ending point of the first path.
    Path dragDownPath = new Path();
    dragDownPath.moveTo(400, 200);
    dragDownPath.lineTo(400, 400);
    long dragDownDuration = 500L;
    GestureDescription.StrokeDescription rightThenDownDrag =
            new GestureDescription.StrokeDescription(dragRightPath, 0L,
            dragRightDuration, true);
    rightThenDownDrag.continueStroke(dragDownPath, dragRightDuration,
            dragDownDuration, false);
}

আরও তথ্যের জন্য, Google I/O 2017 থেকে Android অ্যাক্সেসিবিলিটিতে নতুন কী আছে সেশন ভিডিও দেখুন, যা শুরু হচ্ছে 15:47 থেকে।

অ্যাক্সেসিবিলিটি অ্যাকশন ব্যবহার করা

অ্যাপের সাথে ইন্টার‍্যাকশন আরও সহজ করতে এবং আরও প্রোডাক্টিভ হতে, অ্যাক্সেসিবিলিটি পরিষেবা ব্যবহারকারীর হয়ে কাজ করতে পারে। অ্যাক্সেসিবিলিটি পরিষেবার অ্যাকশন পারফর্ম করার ক্ষমতা ২০১১ সালে যোগ করা হয় এবং ২০১২ সালে তা উল্লেখযোগ্যভাবে বাড়ানো হয়।

ব্যবহারকারীর হয়ে কাজ করতে, আপনার অ্যাক্সেসিবিলিটি পরিষেবাকে অবশ্যই অ্যাপ থেকে ইভেন্ট পেতে রেজিস্টার করতে হবে এবং পরিষেবা কনফিগারেশন ফাইলে android:canRetrieveWindowContent-কে true-এ সেট করে অ্যাপের কন্টেন্ট দেখার অনুমতি চাইতে হবে। আপনার পরিষেবা ইভেন্ট পেলে, এটি getSource() ব্যবহার করে ইভেন্ট থেকে AccessibilityNodeInfo অবজেক্ট উদ্ধার করতে পারে। AccessibilityNodeInfo অবজেক্টের মাধ্যমে, আপনার পরিষেবা তারপরে ভিউ হায়ারার্কি এক্সপ্লোর করে কী অ্যাকশন নিতে হবে তা নির্ধারণ করতে পারে এবং তারপরে performAction() ব্যবহার করে ব্যবহারকারীর জন্য কাজ করতে পারে।

Kotlin

class MyAccessibilityService : AccessibilityService() {

    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        // Get the source node of the event.
        event.source?.apply {

            // Use the event and node information to determine what action to
            // take.

            // Act on behalf of the user.
            performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)

            // Recycle the nodeInfo object.
            recycle()
        }
    }
    ...
}

জাভা

public class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // Get the source node of the event.
        AccessibilityNodeInfo nodeInfo = event.getSource();

        // Use the event and node information to determine what action to take.

        // Act on behalf of the user.
        nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);

        // Recycle the nodeInfo object.
        nodeInfo.recycle();
    }
    ...
}

performAction() মেথড আপনার পরিষেবাকে কোনও অ্যাপের মধ্যে অ্যাকশন নিতে দেয়। আপনার পরিষেবাকে যদি কোনও গ্লোবাল অ্যাকশন নিতে হয়, যেমন হোম স্ক্রিনে নেভিগেট করা, ফিরে যাওয়ার বোতামে ট্যাপ করা, অথবা বিজ্ঞপ্তি স্ক্রিন বা সাম্প্রতিক অ্যাপের তালিকা খোলা, তাহলে performGlobalAction() মেথড ব্যবহার করুন।

ফোকাসের ধরন ব্যবহার করা

২০১২ সালে, Android অ্যাক্সেসিবিলিটি ফোকাস নামে একটি ইউজার ইন্টারফেস ফোকাস চালু করেছে। অ্যাক্সেসিবিলিটি পরিষেবা এই ফোকাস ব্যবহার করে যেকোনও দৃশ্যমান ইউজার ইন্টারফেস এলিমেন্ট বেছে নিতে এবং সেটির উপর কাজ করতে পারে। এই ফোকাসের ধরন ইনপুট ফোকাস থেকে আলাদা, যা নির্ধারণ করে যে ব্যবহারকারী অক্ষর টাইপ করলে, কীবোর্ডে Enter প্রেস করলে বা ডি-প্যাডের মাঝখানের বোতাম পুশ করলে, কোন অন-স্ক্রিন ইউজার ইন্টারফেস এলিমেন্ট ইনপুট গ্রহণ করে।

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

AccessibilityNodeInfo.findFocus() পদ্ধতি ব্যবহার করে, অ্যাক্সেসিবিলিটি পরিষেবা নির্ধারণ করতে পারে যে কোন ইউজার ইন্টারফেস এলিমেন্টে ইনপুট ফোকাস বা অ্যাক্সেসিবিলিটি ফোকাস আছে। এছাড়াও, আপনি focusSearch() পদ্ধতি ব্যবহার করে ইনপুট ফোকাসের মাধ্যমে বেছে নেওয়া যায় এমন এলিমেন্ট সার্চ করতে পারবেন। সবশেষে, আপনার অ্যাক্সেসিবিলিটি পরিষেবা performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS) পদ্ধতি ব্যবহার করে অ্যাক্সেসিবিলিটি ফোকাস সেট করতে পারে।

তথ্য সংগ্রহ করা

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

উইন্ডো পরিবর্তন সংক্রান্ত বিবরণ পান

Android 9 (API লেভেল 28) এবং তার পরবর্তী ভার্সনে, কোনও অ্যাপ একই সাথে একাধিক উইন্ডো রিড্র করলে, অ্যাপকে উইন্ডো আপডেট ট্র্যাক করতে দেয়। TYPE_WINDOWS_CHANGED ইভেন্ট ঘটলে, উইন্ডো কীভাবে পরিবর্তিত হয় তা নির্ধারণ করতে getWindowChanges() API ব্যবহার করুন। মাল্টি-উইন্ডো আপডেট করার সময়, প্রতিটি উইন্ডো নিজস্ব ইভেন্টের সেট তৈরি করে। getSource() পদ্ধতি প্রতিটি ইভেন্টের সাথে যুক্ত উইন্ডোর রুট ভিউ ফেরত পাঠায়।

কোনও অ্যাপ যদি তার View অবজেক্টের জন্য অ্যাক্সেসিবিলিটি প্যান টাইটেল নির্ধারণ করে, তাহলে অ্যাপের UI আপডেট করা হলে আপনার পরিষেবা তা শনাক্ত করতে পারবে। TYPE_WINDOW_STATE_CHANGED ইভেন্ট ঘটলে, উইন্ডো কীভাবে পরিবর্তিত হয় তা নির্ধারণ করতে getContentChangeTypes()-এর মাধ্যমে রিটার্ন করা ধরন ব্যবহার করুন। যেমন, ফ্রেমওয়ার্ক শনাক্ত করতে পারে কখন কোনও প্যানে নতুন শীর্ষক যোগ করা হয়েছে অথবা কখন কোনও প্যান অদৃশ্য হয়ে গেছে।

ইভেন্টের বিবরণ পান

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

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

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

একটি অ্যাক্সেসিবিলিটি পরিষেবা, সিস্টেমের মাধ্যমে পরিষেবার onAccessibilityEvent() কলব্যাক পদ্ধতিতে পাস করা AccessibilityEvent-এর মাধ্যমে ইউজার ইন্টারফেস ইভেন্ট সম্পর্কে তথ্য পায়। এই অবজেক্ট ইভেন্ট সম্পর্কে বিবরণ প্রদান করে, যার মধ্যে যে অবজেক্টের উপর কাজ করা হচ্ছে তার ধরন, এর বর্ণনামূলক টেক্সট এবং অন্যান্য বিবরণ অন্তর্ভুক্ত।

  • AccessibilityEvent.getRecordCount() এবং getRecord(int): এইসব পদ্ধতির মাধ্যমে আপনি AccessibilityRecord অবজেক্টের সেট ফিরিয়ে আনতে পারবেন যা সিস্টেমের মাধ্যমে আপনাকে পাস করা AccessibilityEvent-এ কন্ট্রিবিউট করে। এই লেভেলের বিবরণ সেই ইভেন্টের জন্য আরও প্রসঙ্গ প্রদান করে যা আপনার অ্যাক্সেসিবিলিটি পরিষেবা ট্রিগার করে।

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

Android প্ল্যাটফর্ম AccessibilityService-কে ভিউ হায়ারার্কি কোয়েরি করার ক্ষমতা প্রদান করে, যা UI কম্পোনেন্ট সম্পর্কে তথ্য সংগ্রহ করে যা একটি ইভেন্ট এবং এর পেরেন্ট ও চাইল্ড তৈরি করে। এটি করতে, আপনার XML কনফিগারেশনে নিম্নলিখিত লাইনটি সেট করুন:

android:canRetrieveWindowContent="true"

এটি করার পরে, getSource() ব্যবহার করে AccessibilityNodeInfo অবজেক্ট পান। যে উইন্ডোতে ইভেন্টটি শুরু হয় সেটি এখনও অ্যাক্টিভ উইন্ডো থাকলে, এই কলটি শুধুমাত্র একটি অবজেক্ট রিটার্ন করে। তা না হলে, এটি ফাঁকা রিটার্ন করে, তাই সেই অনুযায়ী কাজ করুন।

নিম্নলিখিত উদাহরণে, কোনও ইভেন্ট পেলে কোডটি নিম্নলিখিত কাজ করে:

  1. যে ভিউ থেকে ইভেন্ট শুরু হয়, তার পেরেন্টকে অবিলম্বে গ্র্যাব করে।
  2. সেই ভিউতে, চাইল্ড ভিউ হিসেবে একটি লেবেল এবং একটি চেকবক্স খোঁজে।
  3. এটি খুঁজে পেলে, ব্যবহারকারীকে রিপোর্ট করার জন্য একটি স্ট্রিং তৈরি করে, যা লেবেল এবং এটি চেক করা হয়েছে কিনা তা নির্দেশ করে।

ভিউ হায়ারার্কি ট্রাভার্স করার সময় কোনও পয়েন্টে যদি নাল ভ্যালু রিটার্ন করা হয়, তাহলে পদ্ধতিটি নীরবে ছেড়ে দেয়।

Kotlin

// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo.

override fun onAccessibilityEvent(event: AccessibilityEvent) {

    val source: AccessibilityNodeInfo = event.source ?: return

    // Grab the parent of the view that fires the event.
    val rowNode: AccessibilityNodeInfo = getListItemNodeInfo(source) ?: return

    // Using this parent, get references to both child nodes, the label, and the
    // checkbox.
    val taskLabel: CharSequence = rowNode.getChild(0)?.text ?: run {
        rowNode.recycle()
        return
    }

    val isComplete: Boolean = rowNode.getChild(1)?.isChecked ?: run {
        rowNode.recycle()
        return
    }

    // Determine what the task is and whether it's complete based on the text
    // inside the label, and the state of the checkbox.
    if (rowNode.childCount < 2 || !rowNode.getChild(1).isCheckable) {
        rowNode.recycle()
        return
    }

    val completeStr: String = if (isComplete) {
        getString(R.string.checked)
    } else {
        getString(R.string.not_checked)
    }
    val reportStr = "$taskLabel$completeStr"
    speakToUser(reportStr)
}

জাভা

// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo.

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {

    AccessibilityNodeInfo source = event.getSource();
    if (source == null) {
        return;
    }

    // Grab the parent of the view that fires the event.
    AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);
    if (rowNode == null) {
        return;
    }

    // Using this parent, get references to both child nodes, the label, and the
    // checkbox.
    AccessibilityNodeInfo labelNode = rowNode.getChild(0);
    if (labelNode == null) {
        rowNode.recycle();
        return;
    }

    AccessibilityNodeInfo completeNode = rowNode.getChild(1);
    if (completeNode == null) {
        rowNode.recycle();
        return;
    }

    // Determine what the task is and whether it's complete based on the text
    // inside the label, and the state of the checkbox.
    if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) {
        rowNode.recycle();
        return;
    }

    CharSequence taskLabel = labelNode.getText();
    final boolean isComplete = completeNode.isChecked();
    String completeStr = null;

    if (isComplete) {
        completeStr = getString(R.string.checked);
    } else {
        completeStr = getString(R.string.not_checked);
    }
    String reportStr = taskLabel + completeStr;
    speakToUser(reportStr);
}

এখন আপনার কাছে একটি সম্পূর্ণ, কার্যকরী অ্যাক্সেসিবিলিটি পরিষেবা আছে। Android-এর টেক্সট-টু-স্পিচ ইঞ্জিন যোগ করে অথবা হ্যাপটিক ফিডব্যাক প্রদান করতে Vibrator ব্যবহার করে, এটি কীভাবে ব্যবহারকারীর সাথে ইন্টার‍্যাক্ট করে তা কনফিগার করার চেষ্টা করুন।

প্রসেস টেক্সট

Android 8.0 (API লেভেল ২৬) এবং তার পরবর্তী ভার্সনে চলা ডিভাইসে বেশ কিছু টেক্সট-প্রসেসিং ফিচার অন্তর্ভুক্ত থাকে যা অ্যাক্সেসিবিলিটি পরিষেবার জন্য স্ক্রিনে দেখানো টেক্সটের নির্দিষ্ট ইউনিট শনাক্ত করা এবং অপারেট করা সহজ করে তোলে।

টুলটিপ

Android 9 (API লেভেল ২৮) বেশ কিছু ক্ষমতা প্রদান করে যা আপনাকে অ্যাপের UI-তে টুলটিপ অ্যাক্সেস করার সুবিধা দেয়। টুলটিপের টেক্সট পড়তে getTooltipText() ব্যবহার করুন এবং View-এর ইনস্ট্যান্সকে তাদের টুলটিপ দেখাতে বা লুকাতে নির্দেশ দিতে ACTION_SHOW_TOOLTIP এবং ACTION_HIDE_TOOLTIP ব্যবহার করুন।

সংকেত পাঠ্য

২০১৭ থেকে, Android-এ টেক্সট-ভিত্তিক অবজেক্টের হিন্ট টেক্সটের সাথে ইন্টার‍্যাক্ট করার জন্য একাধিক পদ্ধতি অন্তর্ভুক্ত করা হয়েছে:

  • isShowingHintText() এবং setShowingHintText() পদ্ধতি যথাক্রমে নির্দেশ করে এবং সেট করে যে নোডের বর্তমান টেক্সট কন্টেন্ট নোডের ইঙ্গিত টেক্সটকে উপস্থাপন করে কিনা।
  • getHintText() হিন্ট টেক্সট অ্যাক্সেস করার সুবিধা দেয়। কোনও অবজেক্টে ইঙ্গিত টেক্সট না দেখালেও, getHintText() কল করা যায়।

স্ক্রিনে দেখানো টেক্সট ক্যারেক্টারের লোকেশন

Android 8.0 (API লেভেল ২৬) এবং তার পরবর্তী যেকোনও ভার্সনে চলা ডিভাইসে, অ্যাক্সেসিবিলিটি পরিষেবা TextView উইজেটের মধ্যে প্রতিটি দৃশ্যমান অক্ষরের বাউন্ডিং বক্সের জন্য স্ক্রিন কোঅর্ডিনেট নির্ধারণ করতে পারে। পরিষেবাগুলি refreshWithExtraData() কল করে, প্রথম আর্গুমেন্ট হিসেবে EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY এবং দ্বিতীয় আর্গুমেন্ট হিসেবে Bundle অবজেক্ট পাস করে এই কোঅর্ডিনেটগুলি খুঁজে পায়। পদ্ধতিটি কার্যকর করার সাথে সাথে, সিস্টেম Bundle আর্গুমেন্টকে Rect অবজেক্টের পার্সেলযোগ্য অ্যারে দিয়ে পূরণ করে। প্রতিটি Rect অবজেক্ট নির্দিষ্ট অক্ষরের বাউন্ডিং বক্সকে দেখায়।

স্ট্যান্ডার্ডাইজড এক-পার্শ্বিক রেঞ্জের মান

কিছু AccessibilityNodeInfo অবজেক্ট AccessibilityNodeInfo.RangeInfo-এর ইনস্ট্যান্স ব্যবহার করে দেখায় যে UI এলিমেন্ট ভ্যালুর রেঞ্জ নিতে পারে। RangeInfo.obtain() ব্যবহার করে রেঞ্জ তৈরি করার সময় অথবা getMin() এবং getMax() ব্যবহার করে রেঞ্জের চরম মান ফিরিয়ে আনার সময়, মনে রাখবেন যে Android 8.0 (API লেভেল ২৬) এবং তার পরবর্তী ভার্সনে চলা ডিভাইসগুলি স্ট্যান্ডার্ড পদ্ধতিতে এক-পার্শ্বিক রেঞ্জ দেখায়:

  • ন্যূনতম সীমা নেই এমন রেঞ্জের ক্ষেত্রে, Float.NEGATIVE_INFINITY ন্যূনতম ভ্যালু প্রতিনিধিত্ব করে।
  • সর্বাধিক সীমা নেই এমন রেঞ্জের ক্ষেত্রে, Float.POSITIVE_INFINITY সর্বাধিক মান দেখায়।

অ্যাক্সেসিবিলিটি ইভেন্টের উত্তর দিন

আপনার পরিষেবা এখন ইভেন্ট রান ও শোনার জন্য সেট-আপ করা হয়েছে, তাই কোড লিখুন যাতে এটি বুঝতে পারে যে AccessibilityEvent এলে কী করতে হবে। onAccessibilityEvent(AccessibilityEvent) পদ্ধতি ওভাররাইড করে শুরু করুন। সেই পদ্ধতিতে, ইভেন্টের ধরন নির্ধারণ করতে getEventType() এবং ইভেন্ট ফায়ার করে এমন ভিউয়ের সাথে সম্পর্কিত যেকোনও লেবেল টেক্সট এক্সট্র্যাক্ট করতে getContentDescription() ব্যবহার করুন:

Kotlin

override fun onAccessibilityEvent(event: AccessibilityEvent) {
    var eventText: String = when (event.eventType) {
        AccessibilityEvent.TYPE_VIEW_CLICKED -> "Clicked: "
        AccessibilityEvent.TYPE_VIEW_FOCUSED -> "Focused: "
        else -> ""
    }

    eventText += event.contentDescription

    // Do something nifty with this text, like speak the composed string back to
    // the user.
    speakToUser(eventText)
    ...
}

জাভা

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    final int eventType = event.getEventType();
    String eventText = null;
    switch(eventType) {
        case AccessibilityEvent.TYPE_VIEW_CLICKED:
            eventText = "Clicked: ";
            break;
        case AccessibilityEvent.TYPE_VIEW_FOCUSED:
            eventText = "Focused: ";
            break;
    }

    eventText = eventText + event.getContentDescription();

    // Do something nifty with this text, like speak the composed string back to
    // the user.
    speakToUser(eventText);
    ...
}

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

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

নির্দেশিকা

কোডল্যাবস