سرویس دسترسی‌پذیری خودتان را ایجاد کنید (Views)

مفاهیم و پیاده‌سازی Jetpack Compose

سرویس دسترسی ، برنامه‌ای است که رابط کاربری را بهبود می‌بخشد تا به کاربران دارای معلولیت یا کسانی که ممکن است موقتاً قادر به تعامل کامل با دستگاه نباشند، کمک کند. به عنوان مثال، کاربرانی که رانندگی می‌کنند، از کودک خردسال مراقبت می‌کنند یا در یک مهمانی بسیار شلوغ شرکت می‌کنند، ممکن است به بازخورد رابط کاربری اضافی یا جایگزین نیاز داشته باشند.

اندروید سرویس‌های دسترسی استاندارد، از جمله TalkBack ، را ارائه می‌دهد و توسعه‌دهندگان می‌توانند سرویس‌های خود را ایجاد و توزیع کنند. این سند اصول اولیه ساخت یک سرویس دسترسی را توضیح می‌دهد.

یک سرویس دسترسی می‌تواند با یک برنامه معمولی همراه شود یا به عنوان یک پروژه اندروید مستقل ایجاد شود. مراحل ایجاد سرویس در هر دو حالت یکسان است.

سرویس دسترسی خود را ایجاد کنید

در پروژه خود، یک کلاس ایجاد کنید که AccessibilityService ارث بری کند:

کاتلین

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 اولیه را از کد منبع خود حذف کنید.

اعلان‌ها و مجوزهای آشکار

برنامه‌هایی که سرویس‌های دسترسی ارائه می‌دهند، باید اعلان‌های خاصی را در مانیفست‌های برنامه خود بگنجانند تا سیستم اندروید آنها را به عنوان یک سرویس دسترسی در نظر بگیرد. این بخش تنظیمات الزامی و اختیاری برای سرویس‌های دسترسی را توضیح می‌دهد.

اعلامیه خدمات دسترسی

برای اینکه برنامه شما به عنوان یک سرویس دسترسی در نظر گرفته شود، یک عنصر service - به جای عنصر activity - را در عنصر application در مانیفست خود قرار دهید. علاوه بر این، در عنصر 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() را بازنویسی کرده و سرویس خود را در آنجا پیکربندی کنید، همانطور که در مثال زیر نشان داده شده است:

کاتلین

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 استفاده می‌کنید، با اضافه کردن یک تگ <meta-data> به اعلان سرویس خود که به فایل XML اشاره می‌کند، آن را در مانیفست خود ارجاع دهید. اگر فایل XML خود را در 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 را ارث‌بری کند و متدهای زیر را از آن کلاس بازنویسی (override) کند. این متدها به ترتیبی که سیستم اندروید آنها را فراخوانی می‌کند، ارائه می‌شوند: از زمانی که سرویس شروع می‌شود ( onServiceConnected() )، تا زمانی که در حال اجرا است ( onAccessibilityEvent() ، onInterrupt() )، تا زمانی که خاموش می‌شود ( onUnbind() ).

  • onServiceConnected() : (اختیاری) سیستم این متد را هنگام اتصال به سرویس دسترسی شما فراخوانی می‌کند. از این متد برای انجام مراحل راه‌اندازی یکباره برای سرویس خود، از جمله اتصال به سرویس‌های سیستم بازخورد کاربر، مانند مدیر صدا یا ویبراتور دستگاه، استفاده کنید. اگر می‌خواهید پیکربندی سرویس خود را در زمان اجرا تنظیم کنید یا تنظیمات یکباره انجام دهید، این مکان مناسبی برای فراخوانی setServiceInfo() است.

  • onAccessibilityEvent() : (الزامی) سیستم این متد را زمانی فراخوانی می‌کند که یک AccessibilityEvent شناسایی کند که با پارامترهای فیلترینگ رویداد مشخص شده توسط سرویس دسترسی شما مطابقت دارد، مانند زمانی که کاربر روی یک دکمه ضربه می‌زند یا روی یک کنترل رابط کاربری در برنامه‌ای که سرویس دسترسی شما برای آن بازخورد ارائه می‌دهد، تمرکز می‌کند. وقتی سیستم این متد را فراخوانی می‌کند، AccessibilityEvent مرتبط را ارسال می‌کند که سرویس می‌تواند آن را تفسیر کرده و برای ارائه بازخورد به کاربر استفاده کند. این متد را می‌توان بارها در طول چرخه عمر سرویس شما فراخوانی کرد.

  • onInterrupt() : (الزامی) سیستم این متد را زمانی فراخوانی می‌کند که می‌خواهد بازخوردی که سرویس شما ارائه می‌دهد را قطع کند، معمولاً در پاسخ به یک اقدام کاربر مانند انتقال فوکوس به یک کنترل دیگر. این متد می‌تواند بارها در طول چرخه حیات سرویس شما فراخوانی شود.

  • onUnbind() : (اختیاری) سیستم این متد را زمانی فراخوانی می‌کند که سیستم در شرف خاموش کردن سرویس دسترسی باشد. از این متد برای انجام هرگونه رویه خاموش کردن یکباره، از جمله لغو تخصیص سرویس‌های سیستم بازخورد کاربر، مانند مدیر صدا یا ویبراتور دستگاه، استفاده کنید.

این متدهای فراخوانی، ساختار اولیه سرویس دسترسی شما را فراهم می‌کنند. شما می‌توانید تصمیم بگیرید که چگونه داده‌های ارائه شده توسط سیستم اندروید را در قالب اشیاء AccessibilityEvent پردازش کنید و به کاربر بازخورد ارائه دهید. برای اطلاعات بیشتر در مورد دریافت اطلاعات از یک رویداد دسترسی، به بخش «دریافت جزئیات رویداد» مراجعه کنید.

برای رویدادهای دسترسی‌پذیری ثبت‌نام کنید

یکی از مهم‌ترین کارکردهای پارامترهای پیکربندی سرویس دسترسی‌پذیری این است که به شما امکان می‌دهد مشخص کنید سرویس شما چه نوع رویدادهای دسترسی‌پذیری را می‌تواند مدیریت کند. تعیین این اطلاعات به سرویس‌های دسترسی‌پذیری اجازه می‌دهد تا با یکدیگر همکاری کنند و به شما انعطاف‌پذیری لازم را می‌دهد تا فقط انواع خاصی از رویدادها را از برنامه‌های خاص مدیریت کنید. فیلتر کردن رویدادها می‌تواند شامل معیارهای زیر باشد:

  • نام‌های بسته: نام‌های بسته برنامه‌هایی را که می‌خواهید رویدادهای دسترسی‌پذیری آنها توسط سرویس شما مدیریت شود، مشخص کنید. اگر این پارامتر حذف شود، سرویس دسترسی‌پذیری شما برای رویدادهای دسترسی‌پذیری سرویس برای هر برنامه‌ای در دسترس در نظر گرفته می‌شود. می‌توانید این پارامتر را در فایل‌های پیکربندی سرویس دسترسی‌پذیری با ویژگی android:packageNames به صورت یک لیست جدا شده با کاما تنظیم کنید یا از عضو AccessibilityServiceInfo.packageNames استفاده کنید.

  • انواع رویداد: انواع رویدادهای دسترسی‌پذیری که می‌خواهید سرویس شما مدیریت کند را مشخص کنید. می‌توانید این پارامتر را در فایل‌های پیکربندی سرویس دسترسی‌پذیری با ویژگی android:accessibilityEventTypes به صورت لیستی که با کاراکتر | جدا شده است، تنظیم کنید - برای مثال، accessibilityEventTypes="typeViewClicked|typeViewFocused" . یا می‌توانید آن را با استفاده از عضو AccessibilityServiceInfo.eventTypes تنظیم کنید.

هنگام تنظیم سرویس دسترسی خود، به دقت در نظر بگیرید که سرویس شما چه رویدادهایی را می‌تواند مدیریت کند و فقط برای آن رویدادها ثبت نام کنید. از آنجایی که کاربران می‌توانند بیش از یک سرویس دسترسی را همزمان فعال کنند، سرویس شما نباید رویدادهایی را که نمی‌تواند مدیریت کند، مصرف کند. به یاد داشته باشید که سرویس‌های دیگر ممکن است این رویدادها را برای بهبود تجربه کاربری مدیریت کنند.

حجم دسترسی

دستگاه‌هایی که اندروید ۸.۰ (سطح API ۲۶) و بالاتر را اجرا می‌کنند، شامل دسته‌ی صدای STREAM_ACCESSIBILITY هستند که به شما امکان می‌دهد صدای خروجی صدای سرویس دسترسی خود را مستقل از سایر صداهای دستگاه کنترل کنید.

سرویس‌های دسترسی می‌توانند با تنظیم گزینه FLAG_ENABLE_ACCESSIBILITY_VOLUME از این نوع جریان استفاده کنند. سپس می‌توانید با فراخوانی متد adjustStreamVolume() در نمونه AudioManager دستگاه، میزان صدای دسترسی دستگاه را تغییر دهید.

قطعه کد زیر نشان می‌دهد که چگونه یک سرویس دسترسی می‌تواند از دسته‌ی ولوم STREAM_ACCESSIBILITY استفاده کند:

کاتلین

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 را که از ساعت ۶:۳۵ شروع می‌شود، ببینید.

میانبر دسترسی

در دستگاه‌هایی که اندروید ۸.۰ (سطح API ۲۶) و بالاتر را اجرا می‌کنند، کاربران می‌توانند با فشار دادن و نگه داشتن همزمان هر دو کلید تنظیم صدا، سرویس دسترسی دلخواه خود را از هر صفحه‌ای فعال و غیرفعال کنند. اگرچه این میانبر به طور پیش‌فرض Talkback را فعال و غیرفعال می‌کند، کاربران می‌توانند دکمه را طوری پیکربندی کنند که هر سرویسی را که روی دستگاهشان نصب شده است، فعال و غیرفعال کند.

برای اینکه کاربران بتوانند از طریق میانبر دسترسی به یک سرویس دسترسی خاص دسترسی پیدا کنند، سرویس باید در زمان اجرا، ویژگی مورد نظر را درخواست کند.

برای اطلاعات بیشتر، ویدیوی جلسه «چه چیزهای جدیدی در اندروید برای دسترسی‌پذیری» از Google I/O 2017 را که از ساعت ۱۳:۲۵ شروع می‌شود، ببینید.

دکمه دسترسی

در دستگاه‌هایی که از ناحیه ناوبری رندر شده توسط نرم‌افزار استفاده می‌کنند و اندروید ۸.۰ (سطح API ۲۶) و بالاتر را اجرا می‌کنند، سمت راست نوار ناوبری شامل یک دکمه دسترسی است. وقتی کاربران این دکمه را فشار می‌دهند، می‌توانند بسته به محتوایی که در حال حاضر روی صفحه نمایش داده می‌شود، یکی از چندین ویژگی و سرویس دسترسی فعال را فراخوانی کنند.

برای اینکه کاربران بتوانند با استفاده از دکمه دسترسی، یک سرویس دسترسی مشخص را فراخوانی کنند، سرویس باید پرچم FLAG_REQUEST_ACCESSIBILITY_BUTTON را در ویژگی android:accessibilityFlags شیء AccessibilityServiceInfo اضافه کند. سپس سرویس می‌تواند با استفاده از registerAccessibilityButtonCallback() فراخوانی‌های برگشتی را ثبت کند.

قطعه کد زیر نشان می‌دهد که چگونه می‌توانید یک سرویس دسترسی را طوری پیکربندی کنید که به کاربر در صورت فشردن دکمه دسترسی پاسخ دهد:

کاتلین

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 را که از ساعت ۱۶:۲۸ شروع می‌شود، ببینید.

حرکات اثر انگشت

سرویس‌های دسترسی در دستگاه‌هایی که اندروید ۸.۰ (سطح API ۲۶) و بالاتر را اجرا می‌کنند، می‌توانند به کشیدن انگشت در جهت‌های مختلف (بالا، پایین، چپ و راست) در امتداد حسگر اثر انگشت دستگاه پاسخ دهند. برای پیکربندی یک سرویس برای دریافت فراخوانی‌های مربوط به این تعاملات، مراحل زیر را تکمیل کنید:

  1. مجوز USE_BIOMETRIC و قابلیت CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES را اعلام کنید.
  2. پرچم FLAG_REQUEST_FINGERPRINT_GESTURES را درون ویژگی android:accessibilityFlags تنظیم کنید.
  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"
    ... />

کاتلین

// 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 را که از ساعت ۹:۰۳ شروع می‌شود، ببینید.

تبدیل متن به گفتار چند زبانه

از اندروید ۸.۰ (سطح API ۲۶)، سرویس تبدیل متن به گفتار (TTS) اندروید می‌تواند عبارات را به چندین زبان در یک بلوک متن شناسایی و بیان کند. برای فعال کردن این قابلیت تغییر خودکار زبان در یک سرویس دسترسی، تمام رشته‌ها را در اشیاء LocaleSpan قرار دهید، همانطور که در قطعه کد زیر نشان داده شده است:

کاتلین

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 را که از ساعت ۱۰:۵۹ شروع می‌شود، ببینید.

از طرف کاربران عمل کنید

از سال ۲۰۱۱، سرویس‌های دسترسی می‌توانند به نمایندگی از کاربران عمل کنند، از جمله تغییر فوکوس ورودی و انتخاب (فعال‌سازی) عناصر رابط کاربری. در سال ۲۰۱۲، طیف اقدامات گسترش یافت و شامل پیمایش لیست‌ها و تعامل با فیلدهای متنی شد. سرویس‌های دسترسی همچنین می‌توانند اقدامات سراسری مانند پیمایش به صفحه اصلی، فشار دادن دکمه بازگشت و باز کردن صفحه اعلان‌ها و لیست برنامه‌های اخیر را انجام دهند. از سال ۲۰۱۲، اندروید شامل فوکوس دسترسی است که باعث می‌شود همه عناصر قابل مشاهده توسط یک سرویس دسترسی قابل انتخاب باشند.

این قابلیت‌ها به توسعه‌دهندگان سرویس‌های دسترسی اجازه می‌دهد تا حالت‌های ناوبری جایگزین، مانند ناوبری حرکتی، ایجاد کنند و به کاربران دارای معلولیت، کنترل بهتری بر دستگاه‌های اندرویدی خود ارائه دهند.

به حرکات گوش دهید

سرویس‌های دسترسی می‌توانند به حرکات خاص گوش دهند و با اقدام از طرف کاربر، پاسخ دهند. این ویژگی مستلزم آن است که سرویس دسترسی شما درخواست فعال‌سازی ویژگی Explore by Touch را داشته باشد. سرویس شما می‌تواند با تنظیم عضو flags از نمونه AccessibilityServiceInfo سرویس به FLAG_REQUEST_TOUCH_EXPLORATION_MODE ، همانطور که در مثال زیر نشان داده شده است، این فعال‌سازی را درخواست کند.

کاتلین

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

پس از اینکه سرویس شما درخواست فعال‌سازی Explore by Touch را می‌دهد، کاربر باید اجازه دهد که این ویژگی فعال شود، اگر از قبل فعال نشده باشد. وقتی این ویژگی فعال است، سرویس شما از طریق متد فراخوانی onGesture() اعلان‌های مربوط به حرکات دسترسی را دریافت می‌کند و می‌تواند به نمایندگی از کاربر پاسخ دهد.

حرکات ادامه‌دار

دستگاه‌هایی که اندروید ۸.۰ (سطح API ۲۶) و بالاتر را اجرا می‌کنند، از حرکات مداوم یا حرکات برنامه‌ریزی‌شده حاوی بیش از یک شیء Path پشتیبانی می‌کنند.

هنگام مشخص کردن دنباله‌ای از strokeها، می‌توانید با استفاده از آرگومان نهایی willContinue در سازنده‌ی GestureDescription.StrokeDescription ، همانطور که در قطعه کد زیر نشان داده شده است، مشخص کنید که آنها به یک ژست برنامه‌نویسی یکسان تعلق دارند:

کاتلین

// 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:canRetrieveWindowContent روی true در فایل پیکربندی سرویس ، درخواست مجوز برای مشاهده محتوای برنامه‌ها را بدهد. هنگامی که رویدادها توسط سرویس شما دریافت می‌شوند، می‌تواند با استفاده از getSource() شیء AccessibilityNodeInfo را از رویداد بازیابی کند. با استفاده از شیء AccessibilityNodeInfo ، سرویس شما می‌تواند سلسله مراتب نماها را بررسی کند تا مشخص کند چه اقدامی باید انجام دهد و سپس با استفاده از performAction() برای کاربر اقدام کند.

کاتلین

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() استفاده کنید.

از انواع فوکوس استفاده کنید

در سال ۲۰۱۲، اندروید یک تمرکز رابط کاربری به نام تمرکز دسترسی (accessibility focus) معرفی کرد. سرویس‌های دسترسی می‌توانند از این تمرکز برای انتخاب هر عنصر رابط کاربری قابل مشاهده و انجام عملیات روی آن استفاده کنند. این نوع تمرکز با تمرکز ورودی (input focus ) متفاوت است، که تعیین می‌کند چه عنصر رابط کاربری روی صفحه نمایش هنگام تایپ کاراکترها، فشردن کلید Enter روی صفحه کلید یا فشردن دکمه مرکزی D-pad، ورودی را دریافت کند.

ممکن است یک عنصر در رابط کاربری، تمرکز ورودی داشته باشد در حالی که عنصر دیگر تمرکز دسترسی داشته باشد. هدف از تمرکز دسترسی، ارائه روشی برای تعامل سرویس‌های دسترسی با عناصر قابل مشاهده روی صفحه نمایش است، صرف نظر از اینکه آیا آن عنصر از دیدگاه سیستم، تمرکز ورودی دارد یا خیر. برای اطمینان از اینکه سرویس دسترسی شما به درستی با عناصر ورودی برنامه‌ها تعامل دارد، دستورالعمل‌های مربوط به آزمایش دسترسی یک برنامه را برای آزمایش سرویس خود هنگام استفاده از یک برنامه معمولی دنبال کنید.

یک سرویس دسترسی‌پذیری می‌تواند با استفاده از متد AccessibilityNodeInfo.findFocus() تعیین کند که کدام عنصر رابط کاربری دارای فوکوس ورودی یا فوکوس دسترسی‌پذیری است. همچنین می‌توانید با استفاده از متد focusSearch() عناصری را که می‌توانند با فوکوس ورودی انتخاب شوند، جستجو کنید. در نهایت، سرویس دسترسی‌پذیری شما می‌تواند با استفاده از متد performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS) فوکوس دسترسی‌پذیری را تنظیم کند.

جمع‌آوری اطلاعات

سرویس‌های دسترسی‌پذیری روش‌های استانداردی برای جمع‌آوری و نمایش واحدهای کلیدی اطلاعات ارائه شده توسط کاربر، مانند جزئیات رویداد، متن و اعداد، دارند.

جزئیات تغییر پنجره را دریافت کنید

اندروید ۹ (سطح API 28) و بالاتر به برنامه‌ها اجازه می‌دهد تا به‌روزرسانی‌های پنجره را هنگامی که یک برنامه چندین پنجره را به طور همزمان ترسیم مجدد می‌کند، پیگیری کنند. هنگامی که یک رویداد TYPE_WINDOWS_CHANGED رخ می‌دهد، از API getWindowChanges() برای تعیین نحوه تغییر پنجره‌ها استفاده کنید. در طول به‌روزرسانی چند پنجره‌ای، هر پنجره مجموعه رویدادهای خود را تولید می‌کند. متد getSource() نمای ریشه پنجره مرتبط با هر رویداد را برمی‌گرداند.

اگر یک برنامه عناوین پنل دسترسی را برای اشیاء View خود تعریف کند، سرویس شما می‌تواند تشخیص دهد که رابط کاربری برنامه چه زمانی به‌روزرسانی می‌شود. هنگامی که یک رویداد TYPE_WINDOW_STATE_CHANGED رخ می‌دهد، از انواع برگردانده شده توسط getContentChangeTypes() برای تعیین نحوه تغییر پنجره استفاده کنید. به عنوان مثال، این چارچوب می‌تواند تشخیص دهد که یک پنل چه زمانی عنوان جدیدی دارد یا چه زمانی ناپدید می‌شود.

جزئیات رویداد را دریافت کنید

اندروید از طریق اشیاء AccessibilityEvent اطلاعاتی در مورد تعامل رابط کاربری به سرویس‌های دسترسی ارائه می‌دهد. در نسخه‌های قبلی اندروید، اطلاعات موجود در یک رویداد دسترسی، در حالی که جزئیات قابل توجهی در مورد کنترل رابط کاربری انتخاب شده توسط کاربران ارائه می‌داد، اطلاعات زمینه‌ای محدودی را ارائه می‌داد. در بسیاری از موارد، این اطلاعات زمینه‌ای از دست رفته ممکن است برای درک معنای کنترل انتخاب شده بسیار مهم باشد.

یک نمونه از رابط کاربری که در آن زمینه بسیار مهم است، یک تقویم یا برنامه‌ریز روزانه است. اگر کاربر یک بازه زمانی ۴:۰۰ بعد از ظهر را در لیست روزهای دوشنبه تا جمعه انتخاب کند و سرویس دسترسی "۴ بعد از ظهر" را اعلام کند، اما نام روز هفته، روز ماه یا نام ماه را اعلام نکند، بازخورد حاصل گیج‌کننده خواهد بود. در این حالت، زمینه یک کنترل رابط کاربری برای کاربری که می‌خواهد یک جلسه را برنامه‌ریزی کند، بسیار مهم است.

از سال ۲۰۱۱، اندروید با ترکیب رویدادهای دسترسی بر اساس سلسله مراتب نما، میزان اطلاعاتی را که یک سرویس دسترسی می‌تواند در مورد تعامل رابط کاربری به دست آورد، به طور قابل توجهی گسترش داده است. سلسله مراتب نما مجموعه‌ای از اجزای رابط کاربری است که شامل مؤلفه (والدین آن) و عناصر رابط کاربری است که ممکن است توسط آن مؤلفه (فرزندان آن) در بر گرفته شوند. به این ترتیب، اندروید می‌تواند جزئیات غنی‌تری در مورد رویدادهای دسترسی ارائه دهد و به سرویس‌های دسترسی اجازه دهد بازخورد مفیدتری را به کاربران ارائه دهند.

یک سرویس دسترسی‌پذیری، اطلاعاتی در مورد یک رویداد رابط کاربری از طریق AccessibilityEvent که توسط سیستم به متد فراخوانی onAccessibilityEvent() سرویس ارسال می‌شود، دریافت می‌کند. این شیء جزئیاتی در مورد رویداد، از جمله نوع شیء مورد نظر، متن توصیفی آن و سایر جزئیات را ارائه می‌دهد.

  • AccessibilityEvent.getRecordCount() و getRecord(int) : این متدها به شما امکان می‌دهند مجموعه‌ای از اشیاء AccessibilityRecord را که در AccessibilityEvent ارسال شده توسط سیستم به شما نقش دارند، بازیابی کنید. این سطح از جزئیات، زمینه بیشتری را برای رویدادی که سرویس دسترسی شما را فعال می‌کند، فراهم می‌کند.

  • AccessibilityRecord.getSource() : این متد یک شیء AccessibilityNodeInfo را برمی‌گرداند. این شیء به شما امکان می‌دهد سلسله مراتب طرح‌بندی نما (والدها و فرزندان) از مؤلفه‌ای که رویداد دسترسی را آغاز می‌کند، درخواست کنید. این ویژگی به یک سرویس دسترسی اجازه می‌دهد تا زمینه کامل یک رویداد، از جمله محتوا و وضعیت هر نمای محصور یا نمای فرزند را بررسی کند.

پلتفرم اندروید این قابلیت را برای AccessibilityService فراهم می‌کند که سلسله مراتب view را بررسی کند و اطلاعاتی در مورد کامپوننت UI که یک رویداد را تولید می‌کند و همچنین والد و فرزندان آن جمع‌آوری کند. برای انجام این کار، خط زیر را در پیکربندی XML خود تنظیم کنید:

android:canRetrieveWindowContent="true"

پس از انجام این کار، با استفاده از getSource() یک شیء AccessibilityNodeInfo دریافت کنید. این فراخوانی فقط در صورتی یک شیء را برمی‌گرداند که پنجره‌ای که رویداد از آن سرچشمه می‌گیرد، هنوز پنجره فعال باشد. در غیر این صورت، مقدار null را برمی‌گرداند، بنابراین مطابق با آن رفتار کنید.

در مثال زیر، کد هنگام دریافت یک رویداد، کارهای زیر را انجام می‌دهد:

  1. بلافاصله والدِ view ای که رویداد از آن سرچشمه می‌گیرد را می‌گیرد.
  2. در آن نما، به دنبال یک برچسب و یک کادر انتخاب در نماهای فرزند می‌گردد.
  3. اگر آنها را پیدا کند، رشته‌ای برای گزارش به کاربر ایجاد می‌کند که برچسب و اینکه آیا علامت‌گذاری شده است یا خیر را نشان می‌دهد.

اگر در هر مرحله‌ای هنگام پیمایش سلسله مراتب view، مقدار null برگردانده شود، متد بی‌سروصدا از کار می‌افتد.

کاتلین

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

حالا شما یک سرویس دسترسی کامل و کارآمد دارید. سعی کنید نحوه تعامل آن با کاربر را با اضافه کردن موتور تبدیل متن به گفتار اندروید یا استفاده از Vibrator برای ارائه بازخورد لمسی پیکربندی کنید.

متن پردازش

دستگاه‌هایی که اندروید ۸.۰ (سطح API ۲۶) و بالاتر را اجرا می‌کنند، شامل چندین ویژگی پردازش متن هستند که شناسایی و کار با واحدهای خاص متنی که روی صفحه نمایش داده می‌شوند را برای سرویس‌های دسترسی آسان‌تر می‌کند.

نکات راهنما

اندروید ۹ (API سطح ۲۸) قابلیت‌های متعددی را معرفی می‌کند که به شما امکان دسترسی به tooltipها در رابط کاربری یک برنامه را می‌دهد. از getTooltipText() برای خواندن متن tooltip استفاده کنید و از ACTION_SHOW_TOOLTIP و ACTION_HIDE_TOOLTIP برای دستور دادن به نمونه‌های View برای نمایش یا پنهان کردن tooltipهای خود استفاده کنید.

متن راهنما

از سال ۲۰۱۷، اندروید چندین روش برای تعامل با متن راهنمای یک شیء مبتنی بر متن ارائه داده است:

  • متدهای isShowingHintText() و setShowingHintText() به ترتیب نشان می‌دهند و تنظیم می‌کنند که آیا محتوای متن فعلی گره، متن راهنمای گره را نشان می‌دهد یا خیر.
  • getHintText() دسترسی به خود متن راهنما را فراهم می‌کند. حتی اگر یک شیء متن راهنما را نمایش ندهد، فراخوانی getHintText() با موفقیت انجام می‌شود.

مکان‌های کاراکترهای متنی روی صفحه

در دستگاه‌هایی که اندروید ۸.۰ (سطح API ۲۶) و بالاتر را اجرا می‌کنند، سرویس‌های دسترسی می‌توانند مختصات صفحه را برای کادر محصورکننده هر کاراکتر قابل مشاهده در یک ویجت TextView تعیین کنند. سرویس‌ها این مختصات را با فراخوانی refreshWithExtraData() پیدا می‌کنند و EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY را به عنوان اولین آرگومان و یک شیء Bundle را به عنوان دومین آرگومان ارسال می‌کنند. همزمان با اجرای متد، سیستم آرگومان Bundle را با یک آرایه قابل تقسیم از اشیاء Rect پر می‌کند. هر شیء Rect نشان دهنده کادر محصورکننده یک کاراکتر خاص است.

مقادیر استاندارد شده‌ی یک‌طرفه برای محدوده

برخی از اشیاء AccessibilityNodeInfo از نمونه‌ای از AccessibilityNodeInfo.RangeInfo استفاده می‌کنند تا نشان دهند که یک عنصر رابط کاربری می‌تواند طیف وسیعی از مقادیر را بپذیرد. هنگام ایجاد یک طیف با استفاده از RangeInfo.obtain() یا هنگام بازیابی مقادیر نهایی طیف با استفاده از getMin() و getMax() ، به خاطر داشته باشید که دستگاه‌هایی که اندروید ۸.۰ (سطح API ۲۶) و بالاتر را اجرا می‌کنند، طیف‌های یک‌طرفه را به روشی استاندارد نمایش می‌دهند:

  • برای محدوده‌هایی که حداقل ندارند، Float.NEGATIVE_INFINITY نشان دهنده حداقل مقدار است.
  • برای محدوده‌هایی که حداکثر ندارند، Float.POSITIVE_INFINITY حداکثر مقدار را نشان می‌دهد.

پاسخ به رویدادهای دسترسی‌پذیری

حالا که سرویس شما برای اجرا و گوش دادن به رویدادها تنظیم شده است، کدی بنویسید تا بداند وقتی یک AccessibilityEvent از راه می‌رسد چه کاری باید انجام دهد. با بازنویسی متد onAccessibilityEvent(AccessibilityEvent) شروع کنید. در آن متد، getEventType() برای تعیین نوع رویداد و getContentDescription() برای استخراج هر متن برچسب مرتبط با نمایی که رویداد را اجرا می‌کند، استفاده کنید:

کاتلین

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

منابع اضافی

برای مطالعه بیشتر، به منابع زیر مراجعه کنید:

راهنماها

کدلبز