ऐक्सेसिबिलिटी सेवा (व्यू) बनाना

सिद्धांत और Jetpack Compose में लागू करने का तरीका

सुलभता सेवा एक ऐसा ऐप्लिकेशन है जो उपयोगकर्ता इंटरफ़ेस को बेहतर बनाता है, ताकि दिव्यांग लोगों की मदद की जा सके. इसके अलावा, यह उन लोगों की भी मदद करता है जो कुछ समय के लिए डिवाइस का पूरी तरह से इस्तेमाल नहीं कर पाते. उदाहरण के लिए, गाड़ी चलाने वाले, छोटे बच्चे की देखभाल करने वाले या तेज़ आवाज़ वाली पार्टी में शामिल लोगों को इंटरफ़ेस के बारे में ज़्यादा या अलग तरह की जानकारी की ज़रूरत पड़ सकती है.

Android, सुलभता से जुड़ी स्टैंडर्ड सेवाएं उपलब्ध कराता है. इनमें TalkBack शामिल है. साथ ही, डेवलपर अपनी सेवाएं बना सकते हैं और उन्हें डिस्ट्रिब्यूट कर सकते हैं. इस दस्तावेज़ में, सुलभता सेवा बनाने के बारे में बुनियादी जानकारी दी गई है.

सुलभता सेवा को किसी सामान्य ऐप्लिकेशन के साथ बंडल किया जा सकता है या इसे स्टैंडअलोन 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?) {}
...
}

Java

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> एलिमेंट, उस एक्सएमएल फ़ाइल को रेफ़र करता है जिसे आपने अपने ऐप्लिकेशन की रिसॉर्स डायरेक्ट्री में बनाया है: <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"
/>

ऐक्सेसिबिलिटी सेवा कॉन्फ़िगरेशन फ़ाइल में इस्तेमाल किए जा सकने वाले एक्सएमएल एट्रिब्यूट के बारे में ज़्यादा जानने के लिए, यहां दिया गया रेफ़रंस दस्तावेज़ देखें:

कॉन्फ़िगरेशन की किन सेटिंग को रनटाइम के दौरान डाइनैमिक तौर पर सेट किया जा सकता है, इस बारे में ज़्यादा जानने के लिए 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

}

Java

@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);

}

दूसरा विकल्प, एक्सएमएल फ़ाइल का इस्तेमाल करके सेवा को कॉन्फ़िगर करना है. canRetrieveWindowContent जैसे कुछ कॉन्फ़िगरेशन विकल्प सिर्फ़ तब उपलब्ध होते हैं, जब आपने एक्सएमएल का इस्तेमाल करके सेवा को कॉन्फ़िगर किया हो. पिछले उदाहरण में दिए गए कॉन्फ़िगरेशन के विकल्प, एक्सएमएल का इस्तेमाल करके इस तरह से तय किए जाते हैं:

<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"
/>

अगर एक्सएमएल का इस्तेमाल किया जाता है, तो उसे अपने मेनिफ़ेस्ट में शामिल करें. इसके लिए, अपनी सेवा की जानकारी में <meta-data> टैग जोड़ें. यह टैग, एक्सएमएल फ़ाइल की ओर इशारा करता है. अगर आपने एक्सएमएल फ़ाइल को res/xml/serviceconfig.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 (एपीआई लेवल 26) और इसके बाद के वर्शन पर काम करने वाले डिवाइसों में, 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)
        }
    }
}

Java

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 (एपीआई लेवल 26) और इसके बाद के वर्शन वाले डिवाइसों पर, उपयोगकर्ता किसी भी स्क्रीन से अपनी पसंदीदा सुलभता सेवा को चालू और बंद कर सकते हैं. इसके लिए, उन्हें दोनों वॉल्यूम बटन को एक साथ दबाकर रखना होगा. इस शॉर्टकट से, TalkBack डिफ़ॉल्ट रूप से चालू और बंद होता है. हालांकि, उपयोगकर्ता इस बटन को अपने डिवाइस पर इंस्टॉल की गई किसी भी सेवा को चालू और बंद करने के लिए कॉन्फ़िगर कर सकते हैं.

अगर उपयोगकर्ताओं को सुलभता शॉर्टकट से किसी सुलभता सेवा को ऐक्सेस करना है, तो सेवा को रनटाइम के दौरान इस सुविधा का अनुरोध करना होगा.

ज़्यादा जानकारी के लिए, Google I/O 2017 में हुए Android की सुलभता से जुड़ी नई सुविधाएं सेशन का वीडियो देखें. यह वीडियो 13:25 से शुरू होता है.

सुलभता बटन

सॉफ़्टवेयर से रेंडर किए गए नेविगेशन एरिया का इस्तेमाल करने वाले डिवाइसों पर, Android 8.0 (एपीआई लेवल 26) और इसके बाद के वर्शन पर चलने वाले डिवाइसों पर, नेविगेशन बार के दाईं ओर सुलभता बटन होता है. जब उपयोगकर्ता इस बटन को दबाते हैं, तो वे सुलभता से जुड़ी कई सुविधाओं और सेवाओं में से किसी एक को चालू कर सकते हैं. यह इस बात पर निर्भर करता है कि फ़िलहाल स्क्रीन पर कौनसा कॉन्टेंट दिख रहा है.

उपयोगकर्ताओं को सुलभता बटन का इस्तेमाल करके, सुलभता सेवा को चालू करने की सुविधा देने के लिए, सेवा को 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)
    }
}

Java

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 की सुलभता से जुड़ी नई सुविधाएं सेशन का वीडियो देखें. यह वीडियो 16:28 से शुरू होता है.

फ़िंगरप्रिंट जेस्चर

Android 8.0 (एपीआई लेवल 26) और इसके बाद के वर्शन पर चलने वाले डिवाइसों पर, सुलभता सेवाएं डिवाइस के फ़िंगरप्रिंट सेंसर पर दिशा के हिसाब से किए गए स्वाइप (ऊपर, नीचे, बाएं, और दाएं) का जवाब दे सकती हैं. इन इंटरैक्शन के बारे में कॉलबैक पाने के लिए, किसी सेवा को कॉन्फ़िगर करने के लिए, यह तरीका अपनाएं:

  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)
        }
    }
}

Java

// 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 (एपीआई लेवल 26) से, Android की टेक्स्ट को ऑडियो में बदलने की सुविधा (टीटीएस) एक ही ब्लॉक के टेक्स्ट में, कई भाषाओं के वाक्यांशों की पहचान कर सकती है और उन्हें बोल सकती है. भाषा अपने-आप बदलने की इस सुविधा को ऐक्सेसिबिलिटी सेवा में चालू करने के लिए, सभी स्ट्रिंग को 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)
    }
}

Java

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 की सुलभता से जुड़ी नई सुविधाएं सेशन का वीडियो देखें. यह वीडियो 10:59 से शुरू होता है.

उपयोगकर्ताओं की ओर से कार्रवाई करना

साल 2011 से, ऐक्सेसिबिलिटी सेवाएं उपयोगकर्ताओं की ओर से कार्रवाई कर सकती हैं. इनमें इनपुट फ़ोकस बदलना और यूज़र इंटरफ़ेस एलिमेंट चुनना (चालू करना) शामिल है. साल 2012 में, कार्रवाइयों की रेंज को बड़ा किया गया. इसमें स्क्रोल करने वाली सूचियां और टेक्स्ट फ़ील्ड के साथ इंटरैक्ट करना शामिल है. सुलभता सेवाएं, ग्लोबल ऐक्शन भी कर सकती हैं. जैसे, होम स्क्रीन पर जाना, 'वापस जाएं' बटन दबाना, और सूचनाओं वाली स्क्रीन और हाल ही में इस्तेमाल किए गए ऐप्लिकेशन की सूची खोलना. Android में 2012 से, ऐक्सेसिबिलिटी फ़ोकस की सुविधा शामिल है. इससे दिखने वाले सभी एलिमेंट को ऐक्सेसिबिलिटी सेवा की मदद से चुना जा सकता है.

इन सुविधाओं की मदद से, सुलभता सेवाएं देने वाले डेवलपर, नेविगेशन के अन्य मोड बना सकते हैं. जैसे, जेस्चर नेविगेशन. साथ ही, ये सुविधाएं दिव्यांग लोगों को Android डिवाइसों को बेहतर तरीके से कंट्रोल करने में मदद करती हैं.

जेस्चर के लिए सुनना

सुलभता सेवाएं, खास जेस्चर को पहचान सकती हैं और उपयोगकर्ता की ओर से कार्रवाई करके जवाब दे सकती हैं. इस सुविधा के लिए, ऐक्सेसिबिलिटी सेवा को 'छूकर एक्सप्लोर करें' सुविधा चालू करने का अनुरोध करना होगा. आपकी सेवा, इस सुविधा को चालू करने का अनुरोध कर सकती है. इसके लिए, सेवा के AccessibilityServiceInfo इंस्टेंस के flags सदस्य को FLAG_REQUEST_TOUCH_EXPLORATION_MODE पर सेट करें. इस बारे में ज़्यादा जानकारी यहां दी गई है.

Kotlin

class MyAccessibilityService : AccessibilityService() {

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

Java

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

जब आपकी सेवा, 'छूकर एक्सप्लोर करें' सुविधा को चालू करने का अनुरोध करती है, तो उपयोगकर्ता को इस सुविधा को चालू करना होगा. ऐसा तब करना होगा, जब यह सुविधा पहले से चालू न हो. इस सुविधा के चालू होने पर, आपकी सेवा को सुलभता से इस्तेमाल करने के लिए किए गए जेस्चर की सूचना मिलती है. यह सूचना, आपकी सेवा के onGesture() कॉलबैक तरीके से मिलती है. इसके बाद, आपकी सेवा उपयोगकर्ता की ओर से कार्रवाई करके जवाब दे सकती है.

हाथ के जेस्चर जारी रखना

Android 8.0 (एपीआई लेवल 26) और इसके बाद के वर्शन पर चलने वाले डिवाइसों में, जारी रखे गए जेस्चर या एक से ज़्यादा 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)
    }
}

Java

// 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 से शुरू होता है.

सुलभता से जुड़ी कार्रवाइयों का इस्तेमाल करना

सुलभता सेवाएं, उपयोगकर्ताओं की ओर से कार्रवाई कर सकती हैं. इससे ऐप्लिकेशन के साथ इंटरैक्शन को आसान बनाया जा सकता है और ज़्यादा काम किया जा सकता है. सुलभता सेवाओं को कार्रवाइयां करने की सुविधा साल 2011 में जोड़ी गई थी. इसके बाद, साल 2012 में इसे काफ़ी हद तक बढ़ा दिया गया था.

उपयोगकर्ताओं की ओर से कार्रवाई करने के लिए, आपकी ऐक्सेसिबिलिटी सेवा को रजिस्टर करना होगा, ताकि वह ऐप्लिकेशन से इवेंट पा सके. साथ ही, उसे ऐप्लिकेशन का कॉन्टेंट देखने की अनुमति का अनुरोध करना होगा. इसके लिए, सेवा के कॉन्फ़िगरेशन फ़ाइल में 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()
        }
    }
    ...
}

Java

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 ने 2012 में, यूज़र इंटरफ़ेस फ़ोकस की सुविधा पेश की थी. इसे सुलभता फ़ोकस कहा जाता है. सुलभता सेवाएं, इस फ़ोकस का इस्तेमाल करके, दिखने वाले किसी भी उपयोगकर्ता इंटरफ़ेस एलिमेंट को चुन सकती हैं और उस पर कार्रवाई कर सकती हैं. यह फ़ोकस टाइप, इनपुट फ़ोकस से अलग होता है. इनपुट फ़ोकस यह तय करता है कि जब कोई व्यक्ति वर्ण टाइप करता है, कीबोर्ड पर Enter दबाता है या डी-पैड के बीच वाले बटन को दबाता है, तो स्क्रीन पर मौजूद यूज़र इंटरफ़ेस का कौन-सा एलिमेंट इनपुट लेता है.

ऐसा हो सकता है कि यूज़र इंटरफ़ेस में मौजूद किसी एक एलिमेंट पर इनपुट फ़ोकस हो, जबकि दूसरे एलिमेंट पर सुलभता फ़ोकस हो. सुलभता फ़ोकस का मकसद, सुलभता सेवाओं को स्क्रीन पर दिखने वाले एलिमेंट के साथ इंटरैक्ट करने का तरीका उपलब्ध कराना है. इससे कोई फ़र्क़ नहीं पड़ता कि सिस्टम के हिसाब से एलिमेंट इनपुट-फ़ोकस करने लायक है या नहीं. यह पक्का करने के लिए कि आपकी सुलभता सेवा, ऐप्लिकेशन के इनपुट एलिमेंट के साथ सही तरीके से इंटरैक्ट करती है, ऐप्लिकेशन की सुलभता की जांच करने से जुड़े दिशा-निर्देशों का पालन करें. इससे आपको किसी सामान्य ऐप्लिकेशन का इस्तेमाल करते समय, अपनी सेवा की जांच करने में मदद मिलेगी.

सुलभता सेवा, AccessibilityNodeInfo.findFocus() तरीके का इस्तेमाल करके यह तय कर सकती है कि किस यूज़र इंटरफ़ेस एलिमेंट पर इनपुट फ़ोकस या सुलभता फ़ोकस है. focusSearch() तरीके का इस्तेमाल करके, इनपुट फ़ोकस के साथ चुने जा सकने वाले एलिमेंट भी खोजे जा सकते हैं. आखिर में, आपकी सुलभता सेवा, performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS) तरीके का इस्तेमाल करके, सुलभता फ़ोकस सेट कर सकती है.

जानकारी इकट्ठा करना

सुलभता सेवाओं में, उपयोगकर्ता से मिली जानकारी की मुख्य इकाइयों को इकट्ठा करने और दिखाने के स्टैंडर्ड तरीके होते हैं. जैसे, इवेंट की जानकारी, टेक्स्ट, और संख्याएं.

विंडो में हुए बदलावों की जानकारी पाना

Android 9 (एपीआई लेवल 28) और इसके बाद के वर्शन में, ऐप्लिकेशन एक साथ कई विंडो फिर से रेंडर करते हैं. ऐसे में, ऐप्लिकेशन को विंडो अपडेट को ट्रैक करने की सुविधा मिलती है. जब TYPE_WINDOWS_CHANGED इवेंट होता है, तब getWindowChanges() एपीआई का इस्तेमाल करके यह तय करें कि विंडो कैसे बदलती हैं. मल्टी-विंडो अपडेट के दौरान, हर विंडो इवेंट का अपना सेट जनरेट करती है. getSource() तरीके से, हर इवेंट से जुड़ी विंडो का रूट व्यू मिलता है.

अगर कोई ऐप्लिकेशन अपने View ऑब्जेक्ट के लिए सुलभता पैनल के टाइटल तय करता है, तो आपकी सेवा यह पहचान सकती है कि ऐप्लिकेशन का यूज़र इंटरफ़ेस (यूआई) कब अपडेट हुआ है. जब TYPE_WINDOW_STATE_CHANGED इवेंट होता है, तब getContentChangeTypes() से मिले टाइप का इस्तेमाल करके यह तय करें कि विंडो में कैसे बदलाव किया जाए. उदाहरण के लिए, फ़्रेमवर्क यह पता लगा सकता है कि किसी पैनल का नया टाइटल कब है या कोई पैनल कब गायब हो जाता है.

इवेंट की जानकारी पाना

Android, सुलभता सेवाओं को AccessibilityEvent ऑब्जेक्ट के ज़रिए, यूज़र इंटरफ़ेस से इंटरैक्ट करने के बारे में जानकारी देता है. Android के पिछले वर्शन में, ऐक्सेसिबिलिटी इवेंट में उपलब्ध जानकारी में, उपयोगकर्ताओं के चुने गए यूज़र इंटरफ़ेस कंट्रोल के बारे में अहम जानकारी मिलती थी. हालांकि, इसमें कॉन्टेक्स्ट के हिसाब से सीमित जानकारी मिलती थी. कई मामलों में, कॉन्टेक्स्ट की यह जानकारी न होने से, चुने गए कंट्रोल का मतलब समझने में मुश्किल हो सकती है.

कैलेंडर या दिन की योजना बनाने वाले ऐप्लिकेशन जैसे इंटरफ़ेस में, कॉन्टेक्स्ट बहुत ज़रूरी होता है. अगर उपयोगकर्ता, सोमवार से शुक्रवार तक के दिनों की सूची में 4:00 PM का टाइम स्लॉट चुनता है और ऐक्सेसिबिलिटी सेवा "4 PM" का एलान करती है, लेकिन हफ़्ते के दिन का नाम, महीने की तारीख या महीने का नाम नहीं बताती है, तो इससे मिलने वाला फ़ीडबैक भ्रमित करने वाला होता है. इस मामले में, मीटिंग शेड्यूल करने वाले व्यक्ति के लिए, यूज़र इंटरफ़ेस कंट्रोल का कॉन्टेक्स्ट जानना ज़रूरी है.

साल 2011 से, Android ने ऐक्सेसिबिलिटी सेवा को उपयोगकर्ता इंटरफ़ेस के साथ इंटरैक्शन के बारे में ज़्यादा जानकारी देने की सुविधा दी है. इसके लिए, व्यू हैरारकी के आधार पर ऐक्सेसिबिलिटी इवेंट बनाए जाते हैं. व्यू हैरारकी, यूज़र इंटरफ़ेस कॉम्पोनेंट का ऐसा सेट होता है जिसमें कॉम्पोनेंट (इसके पैरंट) और यूज़र इंटरफ़ेस के ऐसे एलिमेंट शामिल होते हैं जिन्हें उस कॉम्पोनेंट (इसके चाइल्ड) में शामिल किया जा सकता है. इस तरह, Android ऐक्सेसिबिलिटी इवेंट के बारे में ज़्यादा जानकारी दे सकता है. इससे ऐक्सेसिबिलिटी सेवाएं, लोगों को ज़्यादा काम का सुझाव दे पाती हैं.

ऐक्सेसिबिलिटी सेवा को यूज़र इंटरफ़ेस इवेंट के बारे में जानकारी, सिस्टम से सेवा के AccessibilityEvent कॉलबैक तरीके को पास किए गए AccessibilityEvent के ज़रिए मिलती है.onAccessibilityEvent() इस ऑब्जेक्ट में इवेंट के बारे में जानकारी होती है. जैसे, जिस ऑब्जेक्ट पर कार्रवाई की जा रही है उसका टाइप, उसके बारे में जानकारी देने वाला टेक्स्ट, और अन्य जानकारी.

  • AccessibilityEvent.getRecordCount() और getRecord(int): इन तरीकों से, AccessibilityRecord ऑब्जेक्ट का सेट वापस पाया जा सकता है. ये ऑब्जेक्ट, सिस्टम से मिले AccessibilityEvent में योगदान देते हैं. इस लेवल की जानकारी से, उस इवेंट के बारे में ज़्यादा जानकारी मिलती है जो आपकी ऐक्सेसिबिलिटी सेवा को ट्रिगर करता है.

  • AccessibilityRecord.getSource(): यह तरीका, AccessibilityNodeInfo ऑब्जेक्ट दिखाता है. इस ऑब्जेक्ट की मदद से, उस कॉम्पोनेंट के व्यू लेआउट की हायरार्की (माता-पिता और बच्चे) का अनुरोध किया जा सकता है जिससे सुलभता इवेंट शुरू हुआ है. इस सुविधा की मदद से, सुलभता सेवा किसी इवेंट के पूरे कॉन्टेक्स्ट की जांच कर सकती है. इसमें, इवेंट के कॉन्टेंट के साथ-साथ, इवेंट को शामिल करने वाले किसी भी व्यू या चाइल्ड व्यू की स्थिति भी शामिल है.

Android प्लैटफ़ॉर्म, AccessibilityService को व्यू हैरारकी के बारे में क्वेरी करने की सुविधा देता है. इससे, इवेंट जनरेट करने वाले यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट के साथ-साथ उसके पैरंट और बच्चों के बारे में जानकारी इकट्ठा की जा सकती है. इसके लिए, अपने एक्सएमएल कॉन्फ़िगरेशन में यह लाइन सेट करें:

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)
}

Java

// 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 (एपीआई लेवल 26) और इसके बाद के वर्शन पर काम करने वाले डिवाइसों में, टेक्स्ट को प्रोसेस करने की कई सुविधाएं शामिल होती हैं. इनसे सुलभता सेवाओं को, स्क्रीन पर दिखने वाले टेक्स्ट की खास इकाइयों की पहचान करने और उन पर कार्रवाई करने में आसानी होती है.

टूलटिप

Android 9 (एपीआई लेवल 28) में कई ऐसी सुविधाएं जोड़ी गई हैं जिनकी मदद से, ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में टूलटिप ऐक्सेस की जा सकती हैं. टूलटिप का टेक्स्ट पढ़ने के लिए, getTooltipText() का इस्तेमाल करें. साथ ही, View के इंस्टेंस को टूलटिप दिखाने या छिपाने का निर्देश देने के लिए, ACTION_SHOW_TOOLTIP और ACTION_HIDE_TOOLTIP का इस्तेमाल करें.

संकेत लेख

साल 2017 से, Android में टेक्स्ट पर आधारित ऑब्जेक्ट के हिंट टेक्स्ट के साथ इंटरैक्ट करने के कई तरीके शामिल हैं:

  • isShowingHintText() और setShowingHintText() मेथड, यह बताते हैं कि नोड का मौजूदा टेक्स्ट कॉन्टेंट, नोड के हिंट टेक्स्ट को दिखाता है या नहीं. साथ ही, ये मेथड यह भी सेट करते हैं कि नोड का मौजूदा टेक्स्ट कॉन्टेंट, नोड के हिंट टेक्स्ट को दिखाता है या नहीं.
  • getHintText() से, हिंट वाले टेक्स्ट को ऐक्सेस किया जा सकता है. अगर किसी ऑब्जेक्ट के लिए हिंट टेक्स्ट नहीं दिख रहा है, तब भी getHintText() को कॉल किया जा सकता है.

स्क्रीन पर मौजूद टेक्स्ट वर्णों की जगहें

Android 8.0 (एपीआई लेवल 26) और इसके बाद के वर्शन पर चलने वाले डिवाइसों पर, सुलभता सेवाएं TextView विजेट में दिखने वाले हर वर्ण के बाउंडिंग बॉक्स के स्क्रीन कोऑर्डिनेट तय कर सकती हैं. सेवाएं इन निर्देशांकों को refreshWithExtraData() को कॉल करके ढूंढती हैं. इसमें EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY को पहले आर्ग्युमेंट के तौर पर और Bundle ऑब्जेक्ट को दूसरे आर्ग्युमेंट के तौर पर पास किया जाता है. इस तरीके के लागू होने पर, सिस्टम Bundle आर्ग्युमेंट में Rect ऑब्जेक्ट का पार्सल किया जा सकने वाला कलेक्शन भरता है. हर Rect ऑब्जेक्ट, किसी वर्ण का बाउंडिंग बॉक्स दिखाता है.

एकतरफ़ा रेंज की स्टैंडर्ड वैल्यू

कुछ AccessibilityNodeInfo ऑब्जेक्ट, AccessibilityNodeInfo.RangeInfo के इंस्टेंस का इस्तेमाल करते हैं. इससे यह पता चलता है कि यूज़र इंटरफ़ेस (यूआई) एलिमेंट, वैल्यू की एक रेंज ले सकता है. RangeInfo.obtain() का इस्तेमाल करके रेंज बनाते समय या getMin() और getMax() का इस्तेमाल करके रेंज की सबसे बड़ी और सबसे छोटी वैल्यू वापस पाते समय, ध्यान रखें कि Android 8.0 (एपीआई लेवल 26) और इसके बाद के वर्शन वाले डिवाइस, एकतरफ़ा रेंज को स्टैंडर्ड तरीके से दिखाते हैं:

  • जिन रेंज के लिए कम से कम वैल्यू तय नहीं की गई है उनके लिए, 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)
    ...
}

Java

@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);
    ...
}

अन्य संसाधन

ज़्यादा जानने के लिए, यहां दिए गए संसाधन देखें:

गाइड

कोडलैब