۱. مقدمه
سرویسهای دسترسی یکی از ویژگیهای چارچوب اندروید هستند که برای ارائه بازخورد ناوبری جایگزین به کاربر از طرف برنامههای نصب شده روی دستگاههای اندروید طراحی شدهاند. یک سرویس دسترسی میتواند از طرف برنامه با کاربر ارتباط برقرار کند، به عنوان مثال با تبدیل متن به گفتار یا ارائه بازخورد لمسی هنگامی که کاربر روی یک قسمت مهم از صفحه نمایش حرکت میکند. این آزمایشگاه کد به شما نشان میدهد که چگونه یک سرویس دسترسی بسیار ساده ایجاد کنید.
سرویس دسترسی چیست؟
سرویس دسترسی (Accessibility Service) به کاربران دارای معلولیت در استفاده از دستگاهها و برنامههای اندروید کمک میکند. این یک سرویس ممتاز و قدیمی است که به کاربران کمک میکند تا اطلاعات روی صفحه را پردازش کنند و به آنها امکان تعامل معنادار با دستگاه را میدهد.
نمونههایی از سرویسهای دسترسیپذیری رایج
- دسترسی سوئیچ : به کاربران اندروید با محدودیتهای حرکتی اجازه میدهد تا با استفاده از یک یا چند سوئیچ با دستگاهها تعامل داشته باشند.
- دسترسی صوتی (بتا): به کاربران اندروید با محدودیتهای حرکتی اجازه میدهد تا دستگاه را با دستورات صوتی کنترل کنند.
- Talkback : یک صفحهخوان که معمولاً توسط کاربران کمبینا یا نابینا استفاده میشود.
ساخت یک سرویس دسترسیپذیری
اگرچه گوگل سرویسهایی مانند Switch Access، Voice Access و Talkback را برای کاربران اندروید ارائه میدهد، اما این سرویسها نمیتوانند به همه کاربران دارای معلولیت خدمات ارائه دهند. از آنجایی که بسیاری از کاربران دارای معلولیت نیازهای منحصر به فردی دارند، APIهای اندروید برای ایجاد سرویسهای دسترسی باز هستند و توسعهدهندگان میتوانند آزادانه سرویسهای دسترسی ایجاد کرده و آنها را از طریق فروشگاه Play توزیع کنند.
آنچه خواهید ساخت
در این آزمایشگاه کد، شما یک سرویس ساده توسعه خواهید داد که با استفاده از API دسترسیپذیری، چند کار مفید انجام میدهد. اگر میتوانید یک برنامه اندروید ساده بنویسید، میتوانید یک سرویس مشابه توسعه دهید.
رابط برنامهنویسی کاربردی دسترسی قدرتمند است: کد سرویسی که خواهید ساخت، تنها در چهار فایل قرار دارد و از حدود ۲۰۰ خط کد استفاده میکند!
کاربر نهایی
شما برای یک کاربر فرضی با مشخصات زیر، سرویسی خواهید ساخت:
- کاربر برای رسیدن به دکمههای کناری دستگاه مشکل دارد.
- کاربر در اسکرول کردن یا کشیدن انگشت مشکل دارد.
جزئیات خدمات
سرویس شما یک نوار عملیات سراسری روی صفحه نمایش میدهد. کاربر میتواند با لمس دکمههای روی این نوار، اقدامات زیر را انجام دهد:
- بدون اینکه به دکمه پاور کنار گوشی دست بزنید، دستگاه را خاموش کنید.
- بدون لمس دکمههای تنظیم صدا در کنار گوشی، صدا را تنظیم کنید.
- انجام عملیات اسکرول بدون اسکرول کردن واقعی.
- بدون نیاز به استفاده از ژست کشیدن انگشت، کشیدن انگشت را انجام دهید.
آنچه نیاز دارید
این آزمایشگاه کد فرض میکند که شما از موارد زیر استفاده خواهید کرد:
- یک کامپیوتر که اندروید استودیو روی آن نصب باشد.
- یک ترمینال برای اجرای دستورات ساده shell.
- دستگاهی که اندروید ۷.۰ (نوقا) روی آن نصب شده و به کامپیوتری که برای توسعه از آن استفاده خواهید کرد، متصل باشد.
بیایید شروع کنیم!
۲. راهاندازی
با استفاده از ترمینال، یک دایرکتوری ایجاد کنید که در آن کار خواهید کرد. به این دایرکتوری بروید.
کد را دانلود کنید
میتوانید مخزنی که شامل کد این codelab است را کلون کنید:
git clone https://github.com/android/codelab-android-accessibility.git
این مخزن شامل چندین پروژه اندروید استودیو است. با استفاده از اندروید استودیو، GlobalActionBarService را باز کنید.
با کلیک روی آیکون استودیو، اندروید استودیو را اجرا کنید:

گزینه Import project (Eclipse ADT، Gradle و غیره) را انتخاب کنید:

به محلی که سورس را کپی کردهاید بروید و GlobalActionBarService را انتخاب کنید.
سپس، با استفاده از ترمینال، به دایرکتوری ریشه بروید.
۳. درک کد شروع
پروژهای را که باز کردهاید، بررسی کنید.
اسکلت اصلی سرویس دسترسی از قبل برای شما ایجاد شده است. تمام کدی که در این آزمایشگاه کد خواهید نوشت به چهار فایل زیر محدود میشود:
- app/src/main/ فایل AndroidManifest.xml
- app/src/main/res/layout/ action_bar.xml
- app/src/main/res/xml/ global_action_bar_service.xml
- app/src/main/java/com/example/android/globalactionbarservice/ GlobalActionBarService.java
در اینجا خلاصهای از محتویات هر فایل ارائه شده است.
فایل AndroidManifest.xml
اطلاعات مربوط به سرویس دسترسی در مانیفست اعلام شده است:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.globalactionbarservice">
<application>
<service
android:name=".GlobalActionBarService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/global_action_bar_service" />
</service>
</application>
</manifest>
سه مورد الزامی زیر در AndroidManifest.xml تعریف شدهاند:
- مجوز اتصال به یک سرویس دسترسی:
<service
...
android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
...
</service>
- هدف AccessibilityService :
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
- محل فایلی که شامل فرادادههای سرویسی است که ایجاد میکنید:
<meta-data
...
android:resource="@xml/global_action_bar_service" />
</service>
global_action_bar_service.xml
این فایل شامل متادیتای مربوط به سرویس است.
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true" />
با استفاده از عنصر <accessibility-service> ، فرادادههای زیر تعریف شدهاند:
- نوع بازخورد برای این سرویس (این آزمایشگاه کد از feedbackGeneric استفاده میکند که پیشفرض خوبی است).
- پرچمهای دسترسی برای سرویس (این آزمایشگاه کد از پرچمهای پیشفرض استفاده میکند).
- قابلیتهای مورد نیاز برای سرویس:
- برای انجام عمل کشیدن انگشت، android:canPerformGestures روی true تنظیم شده است.
- برای بازیابی محتوای پنجره، android:canRetrieveWindowContent روی true تنظیم شده است.
GlobalActionBarService.java
بیشتر کد مربوط به سرویس دسترسی در GlobalActionBarService.java قرار دارد. در ابتدا، این فایل شامل حداقل کد لازم برای یک سرویس دسترسی است:
- کلاسی که AccessibilityService را ارثبری میکند.
- چند متد مورد نیاز که باید بازنویسی شوند (در این آزمایشگاه کد، خالی گذاشته شدهاند).
public class GlobalActionBarService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
در طول جلسه کدنویسی، کدی را به این فایل اضافه خواهید کرد.
action_bar.xml
این سرویس یک رابط کاربری با چهار دکمه ارائه میدهد و فایل طرحبندی action_bar.xml شامل نشانهگذاری برای نمایش این دکمهها است:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
این فایل فعلاً حاوی یک LinearLayout خالی است. در طول کدنویسی، نشانهگذاری دکمهها را اضافه خواهید کرد.
راهاندازی برنامه
مطمئن شوید که دستگاه به رایانه شما متصل است. روی نماد سبز پخش (Play) کلیک کنید.
از نوار منو به سمت بالای صفحه. این باید برنامهای را که روی آن کار میکنید، اجرا کند.
به تنظیمات > دسترسی بروید. سرویس نوار اقدام سراسری روی دستگاه شما نصب شده است.

روی سرویس نوار اقدام جهانی کلیک کنید و آن را فعال کنید. باید کادر محاورهای مجوز زیر را ببینید:

سرویس دسترسی، برای مشاهدهی اعمال کاربر، بازیابی محتوای پنجره و انجام حرکات از طرف کاربر، درخواست مجوز میکند! هنگام استفاده از یک سرویس دسترسی شخص ثالث، مطمئن شوید که واقعاً به منبع آن اعتماد دارید !
اجرای سرویس کار زیادی انجام نمیدهد، زیرا هنوز هیچ قابلیتی به آن اضافه نکردهایم. بیایید شروع به انجام این کار کنیم.
۴. ایجاد دکمهها
فایل action_bar.xml را در res/layout باز کنید. کد زیر را درون LinearLayout که در حال حاضر خالی است، اضافه کنید:
<LinearLayout ...>
<Button
android:id="@+id/power"
android:text="@string/power"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/volume_up"
android:text="@string/volume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/scroll"
android:text="@string/scroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/swipe"
android:text="@string/swipe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
این دکمههایی ایجاد میکند که کاربر برای اجرای اقدامات روی دستگاه آنها را فشار میدهد.
فایل GlobalActionBarService.java را باز کنید و یک متغیر برای ذخیره طرحبندی نوار اکشن اضافه کنید:
public class GlobalActionBarService extends AccessibilityService {
FrameLayout mLayout;
...
}
حالا یک متد onServiceStarted() اضافه کنید:
public class GlobalActionBarService extends AccessibilityService {
FrameLayout mLayout;
@Override
protected void onServiceConnected() {
// Create an overlay and display the action bar
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
mLayout = new FrameLayout(this);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
lp.format = PixelFormat.TRANSLUCENT;
lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.TOP;
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.action_bar, mLayout);
wm.addView(mLayout, lp);
}
}
این کد، طرحبندی را حجیم میکند و نوار اکشن را به بالای صفحه اضافه میکند.
متد onServiceConnected() زمانی اجرا میشود که سرویس متصل شده باشد. در این زمان، سرویس دسترسی تمام مجوزهای لازم برای عملکرد خود را دارد. مجوز کلیدی که در اینجا استفاده خواهید کرد، مجوز WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY است. این مجوز به شما امکان میدهد بدون نیاز به طی کردن مراحل پیچیدهی مجوزها، مستقیماً روی صفحه نمایش، بالای محتوای موجود، رسم کنید.
چرخه حیات سرویس دسترسیپذیری
چرخه حیات یک سرویس دسترسی منحصراً توسط سیستم مدیریت میشود و از چرخه حیات سرویس تعیینشده پیروی میکند.
- یک سرویس دسترسی زمانی شروع میشود که کاربر صراحتاً سرویس را در تنظیمات دستگاه روشن کند.
- پس از اینکه سیستم به یک سرویس متصل شد، متد onServiceConnected() را فراخوانی میکند. این متد میتواند توسط سرویسهایی که میخواهند تنظیمات پس از اتصال را انجام دهند، بازنویسی شود.
- یک سرویس دسترسی یا زمانی متوقف میشود که کاربر آن را در تنظیمات دستگاه خاموش کند یا زمانی که disableSelf() را فراخوانی کند.
اجرای سرویس
قبل از اینکه بتوانید سرویس را با استفاده از اندروید استودیو راهاندازی کنید، باید مطمئن شوید که تنظیمات Run شما به درستی پیکربندی شده است.
پیکربندی اجرای خود را ویرایش کنید (از منوی بالا از Run استفاده کنید و به Edit Configurations بروید. سپس، با استفاده از منوی کشویی، گزینه راهاندازی را از "فعالیت پیشفرض" به "هیچ چیز" تغییر دهید.

اکنون باید بتوانید سرویس را با استفاده از اندروید استودیو راهاندازی کنید.
نماد سبز پخش را فشار دهید
از نوار منو به سمت بالای صفحه. سپس، به تنظیمات > دسترسی بروید و سرویس نوار اقدام جهانی را فعال کنید.
شما باید چهار دکمه را که رابط کاربری سرویس را تشکیل میدهند، در بالای محتوای نمایش داده شده روی صفحه ببینید.

اکنون به چهار دکمه، قابلیتهایی اضافه خواهید کرد تا کاربر بتواند با لمس آنها، اقدامات مفیدی انجام دهد.
۵. پیکربندی دکمه پاور
متد configurePowerButton() را به GlobalActionBarService.java اضافه کنید:
private void configurePowerButton() {
Button powerButton = (Button) mLayout.findViewById(R.id.power);
powerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
performGlobalAction(GLOBAL_ACTION_POWER_DIALOG);
}
});
}
برای دسترسی به منوی دکمه پاور، تابع configurePowerButton() از متد performGlobalAction() استفاده میکند که توسط AccessibilityService ارائه میشود. کدی که اضافه کردید ساده است: کلیک کردن روی دکمه، یک onClickListener() را فعال میکند. این تابع performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) را فراخوانی میکند و کادر محاورهای پاور را به کاربر نمایش میدهد.
توجه داشته باشید که اقدامات سراسری به هیچ نمایی وابسته نیستند. زدن دکمه برگشت، دکمه خانه، دکمه برنامههای اخیر نمونههای دیگری از اقدامات سراسری هستند.
حالا configurePowerButton() را به انتهای متد onServiceConnected() اضافه کنید:
@Override
protected void onServiceConnected() {
...
configurePowerButton();
}
نماد سبز پخش را فشار دهید
از نوار منو به سمت بالای صفحه. سپس، به تنظیمات > دسترسی مراجعه کرده و سرویس نوار اقدام سراسری را راهاندازی کنید.
دکمه پاور را فشار دهید تا کادر محاورهای پاور نمایش داده شود.
۶. پیکربندی دکمه تنظیم صدا
متد configureVolumeButton() را به GlobalActionBarService.java اضافه کنید:
private void configureVolumeButton() {
Button volumeUpButton = (Button) mLayout.findViewById(R.id.volume_up);
volumeUpButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
}
});
}
متد configureVolumeButton() یک onClickListener() اضافه میکند که وقتی کاربر دکمهی صدا را فشار میدهد، فعال میشود. درون این شنونده، configureVolumeButton() از یک AudioManager برای تنظیم صدای استریم استفاده میکند.
توجه داشته باشید که هر کسی میتواند میزان صدا را کنترل کند (برای انجام این کار نیازی نیست که حتماً یک سرویس دسترسی باشید).
حالا configureVolumeButton() را به انتهای متد onServiceConnected() اضافه کنید:
@Override
protected void onServiceConnected() {
...
configureVolumeButton();
}
نماد سبز پخش را فشار دهید
از نوار منو به سمت بالای صفحه. سپس، به تنظیمات > دسترسی مراجعه کرده و سرویس نوار اقدام سراسری را راهاندازی کنید.
برای تغییر میزان صدا، دکمهی تنظیم صدا را فشار دهید.
کاربر فرضی که قادر به دسترسی به کنترلهای صدا در کنار دستگاه نیست، اکنون میتواند از سرویس نوار اقدام جهانی برای تغییر (افزایش) صدا استفاده کند.
۷. پیکربندی دکمه پیمایش
این بخش شامل کدنویسی دو متد است. متد اول یک گره قابل پیمایش را پیدا میکند و متد دوم عمل پیمایش را از طرف کاربر انجام میدهد.
متد findScrollableNode را به GlobalActionBarService.java اضافه کنید:
private AccessibilityNodeInfo findScrollableNode(AccessibilityNodeInfo root) {
Deque<AccessibilityNodeInfo> deque = new ArrayDeque<>();
deque.add(root);
while (!deque.isEmpty()) {
AccessibilityNodeInfo node = deque.removeFirst();
if (node.getActionList().contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD)) {
return node;
}
for (int i = 0; i < node.getChildCount(); i++) {
deque.addLast(node.getChild(i));
}
}
return null;
}
یک سرویس دسترسیپذیری به نماهای واقعی روی صفحه نمایش دسترسی ندارد. در عوض، بازتابی از آنچه روی صفحه نمایش است را به شکل درختی متشکل از اشیاء AccessibilityNodeInfo میبیند. این اشیاء حاوی اطلاعاتی در مورد نمایی هستند که نشان میدهند (مکان نما، هر متنی که با نما مرتبط است، ابردادههایی که برای دسترسیپذیری اضافه شدهاند، اقدامات پشتیبانی شده توسط نما و غیره). متد findScrollableNode() پیمایش سطح اول این درخت را با شروع از گره ریشه انجام میدهد. اگر یک گره قابل پیمایش پیدا کند (یعنی گرهای که از عمل AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD پشتیبانی میکند ) ، آن را برمیگرداند، در غیر این صورت مقدار null را برمیگرداند.
حالا متد configureScrollButton() را به GlobalActionBarService.java اضافه کنید:
private void configureScrollButton() {
Button scrollButton = (Button) mLayout.findViewById(R.id.scroll);
scrollButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AccessibilityNodeInfo scrollable = findScrollableNode(getRootInActiveWindow());
if (scrollable != null) {
scrollable.performAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId());
}
}
});
}
این متد یک onClickListener() ایجاد میکند که هنگام کلیک روی دکمه اسکرول، اجرا میشود. این متد سعی میکند یک گره قابل اسکرول پیدا کند و در صورت موفقیت، عمل اسکرول را انجام میدهد.
حالا تابع ()configureScrollButton را به تابع ()onServiceConnected اضافه کنید:
@Override
protected void onServiceConnected() {
...
configureScrollButton();
}
نماد سبز پخش را فشار دهید
از نوار منو به سمت بالای صفحه. سپس، به تنظیمات > دسترسی مراجعه کرده و سرویس نوار اقدام سراسری را راهاندازی کنید.
دکمه بازگشت را فشار دهید تا به تنظیمات > دسترسی بروید. موارد موجود در فعالیت تنظیمات دسترسی قابل پیمایش هستند و لمس دکمه پیمایش، عمل پیمایش را انجام میدهد. کاربر فرضی ما که قادر به انجام آسان عملیات پیمایش نیست، اکنون میتواند از دکمه پیمایش برای پیمایش در لیستی از موارد استفاده کند.
۸. پیکربندی دکمهی کشیدن انگشت
متد configureSwipeButton() را به GlobalActionBarService.java اضافه کنید:
private void configureSwipeButton() {
Button swipeButton = (Button) mLayout.findViewById(R.id.swipe);
swipeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Path swipePath = new Path();
swipePath.moveTo(1000, 1000);
swipePath.lineTo(100, 1000);
GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
gestureBuilder.addStroke(new GestureDescription.StrokeDescription(swipePath, 0, 500));
dispatchGesture(gestureBuilder.build(), null, null);
}
});
}
متد configureSwipeButton() از یک API جدید که در N اضافه شده است استفاده میکند که حرکات را از طرف کاربر انجام میدهد. این کد از یک شیء GestureDescription برای مشخص کردن مسیر انجام حرکت استفاده میکند (مقادیر hardcoded در این codelab استفاده میشوند) و سپس حرکت swipe را از طرف کاربر با استفاده از متد AccessibilityService dispatchGesture() ارسال میکند.
حالا تابع ()configureSwipeButton را به تابع ()onServiceConnected اضافه کنید:
@Override
protected void onServiceConnected() {
...
configureSwipeButton();
}
نماد سبز پخش را فشار دهید
از نوار منو به سمت بالای صفحه. سپس، به تنظیمات > دسترسی مراجعه کرده و سرویس نوار اقدام سراسری را راهاندازی کنید.
سادهترین راه برای آزمایش قابلیت کشیدن انگشت روی صفحه، باز کردن برنامه نقشه نصب شده روی گوشی شماست. پس از بارگذاری نقشه، با لمس دکمه کشیدن انگشت، صفحه به سمت راست کشیده میشود.
۹. خلاصه
تبریک میگویم! شما یک سرویس دسترسی ساده و کاربردی ساختهاید.
شما میتوانید این سرویس را به روشهای مختلفی گسترش دهید. برای مثال:
- نوار اکشن را قابل حرکت کنید (فعلاً فقط بالای صفحه قرار میگیرد).
- به کاربر اجازه دهید هم صدا را کم و هم زیاد کند.
- به کاربر اجازه دهید هم به چپ و هم به راست سوایپ کند.
- پشتیبانی از حرکات اضافی که نوار اکشن میتواند به آنها پاسخ دهد را اضافه کنید.
این آزمایشگاه کد تنها زیرمجموعه کوچکی از قابلیتهای ارائه شده توسط APIهای دسترسیپذیری را پوشش میدهد. این API موارد زیر (لیست ناقص) را نیز پوشش میدهد:
- پشتیبانی از چندین پنجره.
- پشتیبانی از AccessibilityEvents . هنگامی که رابط کاربری تغییر میکند، سرویسهای دسترسی با استفاده از اشیاء AccessibilityEvent از این تغییرات مطلع میشوند. سپس سرویس میتواند به تغییرات رابط کاربری به طور مناسب پاسخ دهد.
- قابلیت کنترل بزرگنمایی.
این آزمایشگاه کدنویسی به شما کمک میکند تا نوشتن یک سرویس دسترسیپذیری را شروع کنید. اگر کاربری را میشناسید که مشکلات دسترسیپذیری خاصی دارد و میخواهید به آن رسیدگی کنید، اکنون میتوانید سرویسی برای کمک به آن کاربر بسازید.