सुलभता सेवा बनाना

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

इस गाइड में, Android की सुलभता सेवा बनाने के बारे में बुनियादी जानकारी दी गई है.

सुलभता सेवा की लाइफ़साइकल

सुलभता सेवा बनाने के लिए, आपको AccessibilityService क्लास को बढ़ाना होगा. साथ ही, अपने ऐप्लिकेशन के मेनिफ़ेस्ट में सेवा के बारे में बताना होगा.

सेवा क्लास बनाना

AccessibilityService को बढ़ाने वाली क्लास बनाएं. आपको इन तरीकों को बदलना होगा:

  • onAccessibilityEvent: इस फ़ंक्शन को तब कॉल किया जाता है, जब सिस्टम को ऐसा इवेंट मिलता है जो आपकी सेवा के कॉन्फ़िगरेशन से मेल खाता है. उदाहरण के लिए, फ़ोकस में बदलाव या बटन पर क्लिक करना. यहां आपकी सेवा, यूज़र इंटरफ़ेस को समझती है.
  • onInterrupt: इस फ़ंक्शन को तब कॉल किया जाता है, जब सिस्टम आपकी सेवा के फ़ीडबैक में रुकावट डालता है. उदाहरण के लिए, जब उपयोगकर्ता फ़ोकस को तेज़ी से बदलता है, तो आवाज़ को बंद करने के लिए.
package com.example.android.apis.accessibility

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

class MyAccessibilityService : AccessibilityService() {

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

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

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

मेनिफ़ेस्ट में एलान करना

अपनी सेवा को AndroidManifest.xml फ़ाइल में रजिस्टर करें. आपको BIND_ACCESSIBILITY_SERVICE अनुमति को सख्ती से लागू करना होगा, ताकि सिर्फ़ सिस्टम आपकी सेवा से जुड़ सके.

यह पक्का करने के लिए कि सेटिंग बटन काम करे, ServiceSettingsActivity को एलान करें.

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

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

सेवा को कॉन्फ़िगर करना

res/xml/accessibility_service_config.xml में कॉन्फ़िगरेशन फ़ाइल बनाएं. इस फ़ाइल में यह तय किया जाता है कि आपकी सेवा कौनसे इवेंट हैंडल करती है और किस तरह का सुझाव देती है. पक्का करें कि आपने अपने मेनिफ़ेस्ट में जिस ServiceSettingsActivity का एलान किया है उसका रेफ़रंस दिया गया हो:

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

कॉन्फ़िगरेशन फ़ाइल में ये मुख्य एट्रिब्यूट शामिल होते हैं:

  • android:accessibilityEventTypes: वे इवेंट जिनकी सूचनाएं आपको चाहिए. सामान्य मकसद वाली सेवा के लिए, typeAllMask का इस्तेमाल करें.
  • android:canRetrieveWindowContent: अगर आपकी सेवा को यूज़र इंटरफ़ेस (यूआई) के क्रम की जांच करनी है, तो इसे true पर सेट करें. उदाहरण के लिए, स्क्रीन से टेक्स्ट पढ़ने के लिए.
  • android:canPerformGestures: अगर आपको प्रोग्राम के हिसाब से स्वाइप या टैप जैसे जेस्चर भेजने हैं, तो इसे true पर सेट करना होगा.
  • android:accessibilityFlags: सुविधाओं को चालू करने के लिए फ़्लैग मिलाएं. flagRequestFingerprintGestures, फ़िंगरप्रिंट जेस्चर के लिए ज़रूरी है. flagRequestAccessibilityButton सॉफ़्टवेयर के सुलभता बटन के लिए ज़रूरी है.

कॉन्फ़िगरेशन के सभी विकल्पों की पूरी सूची देखने के लिए, AccessibilityServiceInfo देखें.

रनटाइम कॉन्फ़िगरेशन

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

onServiceConnected() को ओवरराइड करके, setServiceInfo() का इस्तेमाल करके रनटाइम अपडेट लागू करें:

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

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

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

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

    this.setServiceInfo(info)
}

यूज़र इंटरफ़ेस के कॉन्टेंट को समझना

onAccessibilityEvent() ट्रिगर होने पर, सिस्टम AccessibilityEvent उपलब्ध कराता है. यह इवेंट, सुलभता ट्री का एंट्री पॉइंट होता है. यह स्क्रीन कॉन्टेंट का क्रमवार प्रज़ेंटेशन होता है.

आपकी सेवा मुख्य रूप से AccessibilityNodeInfo ऑब्जेक्ट के साथ इंटरैक्ट करती है. ये ऑब्जेक्ट, यूज़र इंटरफ़ेस (यूआई) एलिमेंट को दिखाते हैं. जैसे, बटन, सूचियां, और टेक्स्ट. इन यूज़र इंटरफ़ेस (यूआई) एलिमेंट के डेटा को AccessibilityNodeInfo में नॉर्मलाइज़ किया जाता है.

यहां दिए गए उदाहरण में, किसी इवेंट के सोर्स को वापस पाने और जानकारी ढूंढने के लिए, ऐक्सेसिबिलिटी ट्री को ट्रैवर्स करने का तरीका बताया गया है.

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

    if (sourceNode == null) return

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

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

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

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

सुलभता सेवाएं, उपयोगकर्ता की ओर से कार्रवाइयां कर सकती हैं. जैसे, बटन पर क्लिक करना या सूचियों को स्क्रोल करना.

कोई कार्रवाई करने के लिए, किसी AccessibilityNodeInfo ऑब्जेक्ट पर performAction() को कॉल करें.

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

ऐसे ग्लोबल ऐक्शन के लिए performGlobalAction() का इस्तेमाल करें जिनका असर पूरे सिस्टम पर पड़ता है. जैसे, 'वापस जाएं' बटन दबाना या सूचना पैनल खोलना.

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

फ़ोकस मैनेज करना

Android में दो तरह के फ़ोकस होते हैं: इनपुट फ़ोकस (जहां कीबोर्ड इनपुट जाता है) और सुलभता फ़ोकस (सुलभता सेवा किस चीज़ की जांच कर रही है).

यहां दिए गए स्निपेट में, उस एलिमेंट का पता लगाने का तरीका बताया गया है जिस पर फ़िलहाल सुलभता फ़ोकस है:

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

    // Do something with focusedNode

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

नीचे दिए गए स्निपेट में, सुलभता फ़ोकस को किसी खास एलिमेंट पर ले जाने का तरीका बताया गया है:

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

सुलभता सेवा बनाते समय, उपयोगकर्ता की फ़ोकस स्थिति का ध्यान रखें. साथ ही, जब तक उपयोगकर्ता कोई कार्रवाई न करे, तब तक फ़ोकस को न चुराएं.

जेस्चर करें

आपकी सेवा, स्क्रीन पर कस्टम जेस्चर भेज सकती है. जैसे, स्वाइप करना, टैप करना या मल्टी-टच इंटरैक्शन. इसके लिए, अपने कॉन्फ़िगरेशन में android:canPerformGestures="true" को शामिल करें, ताकि dispatchGesture() एपीआई का इस्तेमाल किया जा सके.

आसान जेस्चर

आसान जेस्चर करने के लिए, सबसे पहले Path ऑब्जेक्ट बनाएं. यह ऑब्जेक्ट, किसी जेस्चर से जुड़ी गतिविधि को दिखाता है. इसके बाद, स्ट्रोक के बारे में बताने के लिए, Path को GestureDescription में रैप करें. आखिर में, कॉल dispatchGesture करके, जेस्चर को डिसपैच करें.

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

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

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

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

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

जटिल इंटरैक्शन (जैसे कि L शेप बनाना या सटीक मल्टी-स्टेप ड्रैग करना) के लिए, willContinue पैरामीटर का इस्तेमाल करके स्ट्रोक को एक साथ जोड़ा जा सकता है.

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

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

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

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

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

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

ऑडियो मैनेज करना

सुलभता सेवा (खास तौर पर, स्क्रीन रीडर) बनाते समय, STREAM_ACCESSIBILITY ऑडियो स्ट्रीम का इस्तेमाल करें. इससे उपयोगकर्ता, सिस्टम के मीडिया वॉल्यूम से अलग, सेवा के वॉल्यूम को कंट्रोल कर सकते हैं.

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

अपने कॉन्फ़िगरेशन में FLAG_ENABLE_ACCESSIBILITY_VOLUME फ़्लैग को शामिल करना न भूलें. इसे एक्सएमएल में या रनटाइम के दौरान setServiceInfo के ज़रिए शामिल किया जा सकता है.

बेहतर सुविधाएं

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

Android 10 (एपीआई लेवल 29) या इसके बाद के वर्शन वाले डिवाइसों पर, आपकी सेवा फ़िंगरप्रिंट सेंसर पर दिशा के हिसाब से किए गए स्वाइप को कैप्चर कर सकती है. यह नेविगेशन कंट्रोल के विकल्प उपलब्ध कराने के लिए काम आता है.

अपने onServiceConnected() तरीके में यह लॉजिक जोड़ें:

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

private var gestureController: FingerprintGestureController? = null

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

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

        gestureController?.registerFingerprintGestureCallback(callback, null)
    }
}

सुलभता बटन

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

इस सुविधा का इस्तेमाल करने के लिए, अपने सेवा कॉन्फ़िगरेशन में FLAG_REQUEST_ACCESSIBILITY_BUTTON फ़्लैग जोड़ें. इसके बाद, अपने onServiceConnected() तरीके में रजिस्ट्रेशन लॉजिक जोड़ें.

// Import: android.accessibilityservice.AccessibilityButtonController

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

    val controller = accessibilityButtonController

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

कई भाषाओं में लिखाई को बोली में बदलने की सुविधा

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

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

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

जब आपकी सेवा AccessibilityNodeInfo को प्रोसेस करती है, तब LocaleSpan ऑब्जेक्ट के लिए text प्रॉपर्टी की जांच करें. इससे, टेक्स्ट को बोली में बदलने के लिए सही भाषा का पता लगाया जा सकता है.

अन्य संसाधन

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

गाइड

कोडलैब

कॉन्टेंट देखता है