1. מבוא
שירותי הנגישות הם תכונה של מסגרת Android שנועדה לספק למשתמש משוב חלופי על הניווט באפליקציות שמותקנות במכשירי Android. שירות נגישות יכול לתקשר עם המשתמש בשם האפליקציה, למשל על ידי המרת טקסט לדיבור או מתן משוב הפטי כשמשתמש מעביר את העכבר מעל אזור חשוב במסך. ב-Codelab הזה תלמדו איך ליצור שירות נגישות פשוט מאוד.
מהו שירות נגישות?
שירות נגישות עוזר למשתמשים עם מוגבלויות להשתמש במכשירי Android ובאפליקציות ל-Android. זהו שירות מורשה שפועל לאורך זמן ועוזר למשתמשים לעבד מידע במסך ולקיים אינטראקציה משמעותית עם המכשיר.
דוגמאות לשירותי נגישות נפוצים
- גישה באמצעות מתג: מאפשרת למשתמשי Android עם מגבלות בניידות ליצור אינטראקציה עם מכשירים באמצעות מתג אחד או יותר.
- Voice Access (בטא): מאפשרת למשתמשי Android עם מגבלות בניידות לשלוט במכשיר באמצעות פקודות קוליות.
- Talkback: קורא מסך שנמצא בשימוש נפוץ בקרב משתמשים עם ליקויי ראייה או עיוורים.
פיתוח שירות נגישות
Google מספקת שירותים כמו 'גישה באמצעות מתג', 'גישה קולית' ו-Talkback למשתמשי Android, אבל השירותים האלה לא יכולים לתת מענה לכל המשתמשים עם מוגבלויות. למשתמשים רבים עם מוגבלויות יש צרכים ייחודיים, ולכן ממשקי ה-API של Android ליצירת שירותי נגישות הם פתוחים, ומפתחים יכולים ליצור שירותי נגישות ולהפיץ אותם דרך חנות Play.
מה תפַתחו
ב-codelab הזה תפתחו שירות פשוט שעושה כמה דברים שימושיים באמצעות ה-API לנגישות. אם אתם יודעים לכתוב אפליקציית Android בסיסית, תוכלו לפתח שירות דומה.
ממשק ה-API לנגישות הוא עוצמתי: הקוד של השירות שתבנו נמצא רק בארבעה קבצים, וכולל כ-200 שורות קוד בלבד.
משתמש הקצה
תבנו שירות למשתמש היפותטי עם המאפיינים הבאים:
- למשתמש קשה להגיע ללחצני הצד במכשיר.
- למשתמש יש קושי בגלילה או בהחלקה.
פרטי השירות
השירות שלכם יציג סרגל פעולות גלובלי במסך. המשתמש יכול להקיש על הלחצנים בסרגל הזה כדי לבצע את הפעולות הבאות:
- לכבות את המכשיר בלי ללחוץ על כפתור ההפעלה בצד הטלפון.
- לשנות את עוצמת הקול בלי לגעת בלחצני עוצמת הקול בצד הטלפון.
- ביצוע פעולות גלילה בלי גלילה בפועל.
- מבצעים החלקה בלי להשתמש בתנועת החלקה.
מה תצטרכו
ב-codelab הזה אנחנו יוצאים מנקודת הנחה שתשתמשו בפריטים הבאים:
- מחשב עם Android Studio.
- טרמינל להרצת פקודות Shell פשוטות.
- מכשיר עם Android 7.0 (Nougat) שמחובר למחשב שבו תשתמשו לפיתוח.
קדימה, מתחילים!
2. תהליך ההגדרה
באמצעות המסוף, יוצרים ספרייה שבה תעבדו. עוברים לספרייה הזו.
הורדת הקוד
אפשר לשכפל את מאגר הקוד שמכיל את הקוד של ה-Codelab הזה:
git clone https://github.com/android/codelab-android-accessibility.git
המאגר מכיל כמה פרויקטים של Android Studio. באמצעות Android Studio, פותחים את GlobalActionBarService.
מפעילים את Android Studio בלחיצה על סמל Studio:

בוחרים באפשרות Import project (Eclipse ADT, Gradle, etc.) (ייבוא פרויקט (Eclipse ADT, Gradle וכו')):

עוברים למיקום שבו שיבטתם את המקור ובוחרים באפשרות GlobalActionBarService.
לאחר מכן, משתמשים במסוף כדי לעבור לספריית הבסיס.
3. הסבר על קוד ההתחלה
בודקים את הפרויקט שפתחתם.
השלד הבסיסי של שירות הנגישות כבר נוצר בשבילכם. כל הקוד שתכתבו ב-codelab הזה מוגבל לארבעת הקבצים הבאים:
- 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>:
- סוג המשוב בשירות הזה (ב-codelab הזה נעשה שימוש ב-feedbackGeneric, שהוא ברירת מחדל טובה).
- ההתראות על נגישות בשירות (ב-codelab הזה נעשה שימוש בהתראות ברירת המחדל).
- היכולות שנדרשות לשירות:
- כדי להחליק, הערך של android:canPerformGestures מוגדר כ-true.
- כדי לאחזר תוכן של חלון, המאפיין android:canRetrieveWindowContent מוגדר לערך true.
GlobalActionBarService.java
רוב הקוד של שירות הנגישות נמצא ב-GlobalActionBarService.java. בתחילה, הקובץ מכיל את קוד המינימום ההכרחי לשירות נגישות:
- מחלקה שמרחיבה את AccessibilityService.
- כמה שיטות חובה שנדרסו (הושארו ריקות ב-codelab הזה).
public class GlobalActionBarService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
במהלך ה-codelab, תוסיפו קוד לקובץ הזה.
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 ריק. במהלך ה-codelab תוסיפו תגי עיצוב ללחצנים.
הפעלת האפליקציה
מוודאים שמכשיר מחובר למחשב. לוחצים על סמל ההפעלה הירוק
בסרגל התפריטים בחלק העליון של המסך. האפליקציה שאתם עובדים עליה אמורה להיפתח.
עוברים אל הגדרות > נגישות. Global Action Bar Service מותקן במכשיר.

לוחצים על Global Action Bar Service ומפעילים אותו. תיבת הדו-שיח הבאה של ההרשאות אמורה להופיע:

שירות הנגישות מבקש הרשאה לצפות בפעולות המשתמש, לאחזר תוכן מחלונות ולבצע מחוות בשם המשתמש. כשמשתמשים בשירות נגישות של צד שלישי, חשוב לוודא שהמקור מהימן.
הפעלת השירות לא תעשה הרבה, כי עדיין לא הוספנו פונקציונליות. נתחיל לעשות את זה.
4. יצירת הכפתורים
פותחים את 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().
הפעלת השירות
כדי להפעיל את השירות באמצעות Android Studio, צריך לוודא שהגדרות ההפעלה מוגדרות בצורה נכונה.
עורכים את הגדרות ההרצה (משתמשים באפשרות 'הרצה' מהתפריט העליון ועוברים אל 'עריכת הגדרות'). אחר כך, בתפריט הנפתח, משנים את אפשרות ההפעלה מ-Default Activity (פעילות ברירת המחדל) ל-Nothing (שום דבר).

עכשיו אמורה להיות לכם אפשרות להפעיל את השירות באמצעות Android Studio.
לוחצים על סמל ההפעלה הירוק
בסרגל התפריטים בחלק העליון של המסך. אחר כך, עוברים אל הגדרות > נגישות ומפעילים את שירות סרגל הפעולות הגלובלי.
ארבעת הכפתורים שיוצרים את שכבת-העל של ממשק המשתמש של השירות אמורים להופיע מעל התוכן שמוצג במסך.

עכשיו תוסיפו פונקציונליות לארבעת הלחצנים, כדי שמשתמש יוכל לגעת בהם ולבצע פעולות שימושיות.
5. הגדרת כפתור ההפעלה
מוסיפים את ה-method 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() לסוף ה-method onServiceConnected():
@Override
protected void onServiceConnected() {
...
configurePowerButton();
}
לוחצים על סמל ההפעלה הירוק
בסרגל התפריטים בחלק העליון של המסך. אחר כך, עוברים אל הגדרות > נגישות ומפעילים את שירות סרגל הפעולות הגלובלי.
לוחצים על כפתור ההפעלה כדי להציג את תיבת הדו-שיח של ההפעלה.
6. הגדרת כפתור עוצמת הקול
מוסיפים את השיטה 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();
}
לוחצים על סמל ההפעלה הירוק
בסרגל התפריטים בחלק העליון של המסך. אחר כך, עוברים אל 'הגדרות' > 'נגישות' ומפעילים את השירות של סרגל הפעולות הגלובלי.
לוחצים על כפתור עוצמת קול כדי לשנות את עוצמת הקול.
המשתמש ההיפותטי שלא מצליח להגיע לבקרי עוצמת הקול בצד המכשיר יכול עכשיו להשתמש בסרגל הפעולות הגלובלי כדי לשנות (להגביר) את עוצמת הקול.
7. הגדרת כפתור הגלילה
בקטע הזה נסביר איך לכתוב קוד לשתי שיטות. השיטה הראשונה מוצאת צומת שאפשר לגלול בו, והשיטה השנייה מבצעת את פעולת הגלילה בשם המשתמש.
מוסיפים את השיטה 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();
}
לוחצים על סמל ההפעלה הירוק
בסרגל התפריטים בחלק העליון של המסך. אחר כך, עוברים אל 'הגדרות' > 'נגישות' ומפעילים את השירות של סרגל הפעולות הגלובלי.
לוחצים על הכפתור "הקודם" כדי לעבור אל הגדרות > נגישות. אפשר לגלול בין הפריטים בפעילות של הגדרות הנגישות, ולגעת בלחצן הגלילה כדי לבצע פעולת גלילה. המשתמש ההיפותטי שלנו שלא יכול לבצע בקלות פעולות גלילה יכול עכשיו להשתמש בלחצן הגלילה כדי לגלול ברשימת פריטים.
8. הגדרה של כפתור ההחלקה
מוסיפים את השיטה 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 כדי לציין את הנתיב של תנועת המגע שצריך לבצע (ערכים שמוגדרים מראש משמשים ב-codelab הזה), ואז שולח את תנועת ההחלקה בשם המשתמש באמצעות השיטה dispatchGesture() של AccessibilityService.
עכשיו מוסיפים את configureSwipeButton() אל onServiceConnected():
@Override
protected void onServiceConnected() {
...
configureSwipeButton();
}
לוחצים על סמל ההפעלה הירוק
בסרגל התפריטים בחלק העליון של המסך. אחר כך, עוברים אל 'הגדרות' > 'נגישות' ומפעילים את השירות של סרגל הפעולות הגלובלי.
הדרך הכי קלה לבדוק את הפונקציונליות של ההחלקה היא לפתוח את אפליקציית מפות Google שמותקנת בטלפון. אחרי שהמפה נטענת, הקשה על לחצן ההחלקה מחליקה את המסך ימינה.
9. סיכום
מעולה! יצרתם שירות נגישות פשוט ופונקציונלי.
יש כמה דרכים להרחיב את השירות הזה. לדוגמה:
- להפוך את סרגל הפעולות לנייד (כרגע הוא נמצא רק בחלק העליון של המסך).
- המשתמש יכול להגביר ולהנמיך את עוצמת הקול.
- המשתמש יכול להחליק ימינה ושמאלה.
- הוספת תמיכה בתנועות נוספות שסרגל הפעולות יכול להגיב להן.
ה-codelab הזה מתייחס רק לחלק קטן מהפונקציונליות שמסופקת על ידי ממשקי ה-API לנגישות. ה-API כולל גם את הפעולות הבאות (רשימה חלקית):
- תמיכה במספר חלונות.
- תמיכה ב-AccessibilityEvents. כשממשק המשתמש משתנה, שירותי הנגישות מקבלים הודעה על השינויים האלה באמצעות אובייקטים של AccessibilityEvent. לאחר מכן, השירות יכול להגיב לשינויים בממשק המשתמש בצורה המתאימה.
- אפשרות לשלוט בהגדלה.
Codelab זה יעזור לכם להתחיל לכתוב שירות נגישות. אם אתם מכירים משתמש עם בעיות נגישות ספציפיות שאתם רוצים לפתור, עכשיו אתם יכולים ליצור שירות שיעזור למשתמש הזה.