سرویس دسترسپذیری برنامهای است که میانای کاربر را برای کمک به کاربران دارای ناتوانی یا کاربرانی که ممکن است بهطور موقت نتوانند بهطور کامل با دستگاه تعامل داشته باشند بهبود میبخشد. این سرویسها در پسزمینه اجرا میشوند و با سیستم ارتباط برقرار میکنند تا محتوای صفحه را بررسی کنند و ازجانب کاربر با برنامهها تعامل داشته باشند. نمونهها شامل صفحهخوانها (مانند 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 مراجعه کنید.
پیکربندی زمان اجرا
درحالیکه پیکربندی XML ثابت است، میتوانید پیکربندی سرویس خود را بهصورت پویا در زمان اجرا نیز تغییر دهید. این کار برای تغییر وضعیت ویژگیها براساس اولویتهای کاربر مفید است.
لغو کردن onServiceConnected() برای اعمال بهروزرسانیهای زمان اجرا بااستفاده از
setServiceInfo():
override fun onServiceConnected() { val info = AccessibilityServiceInfo() // Set the type of events that this service wants to listen to. info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED // Set the type of feedback your service provides. info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN // Set flags at runtime. info.flags = AccessibilityServiceInfo.FLAG_DEFAULT or AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES this.setServiceInfo(info) }
تفسیر محتوای میانای کاربر
وقتی 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 }
اقدام ازطرف کاربران
سرویسهای دسترسپذیری میتوانند ازطرف کاربر کارهایی مثل کلیک کردن روی دکمهها یا پیمایش فهرستها را انجام دهند.
برای انجام کنش، performAction() را در شیء AccessibilityNodeInfo
فراخوانی کنید.
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 را در پیکربندیتان، چه در XML یا ازطریق setServiceInfo در زمان اجرا، اضافه کنید.
ویژگیهای پیشرفته
اشارههای اثر انگشت
در دستگاههایی که از Android 10 (سطح میانای برنامهسازی کاربردی ۲۹) یا بالاتر استفاده میکنند، سرویس شما میتواند تند کشیدنهای جهتدار را روی حسگر اثر انگشت ضبط کند. این ویژگی برای ارائه کنترلهای پیمایش جایگزین مفید است.
منطق زیر را به روش 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 را پردازش میکند، ویژگی text را برای اشیاء LocaleSpan بررسی کنید تا زبان نوشتار به گفتار صحیح را تعیین کنید.
منابع بیشتر
برای اطلاعات بیشتر، منابع زیر را ببینید: