Konsep dan penerapan Jetpack Compose
Layanan aksesibilitas adalah aplikasi yang meningkatkan kualitas antarmuka pengguna untuk membantu pengguna difabel atau yang mungkin tidak dapat berinteraksi sepenuhnya dengan perangkat untuk sementara waktu. Misalnya, pengguna yang sedang mengemudi, merawat anak kecil, atau menghadiri pesta yang sangat ramai mungkin memerlukan masukan antarmuka tambahan atau alternatif.
Android menyediakan layanan aksesibilitas standar, termasuk TalkBack, dan developer dapat membuat serta mendistribusikan layanan mereka sendiri. Dokumen ini menjelaskan dasar-dasar pembuatan layanan aksesibilitas.
Layanan aksesibilitas dapat dipaketkan dengan aplikasi umum atau dibuat sebagai project Android mandiri. Langkah-langkah untuk membuat layanan sama dalam kedua situasi tersebut.
Membuat layanan aksesibilitas Anda
Dalam project, buat class yang memperluas AccessibilityService:
Kotlin
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?) {} ... }
Java
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() { } ... }
Jika Anda membuat project baru untuk Service ini dan tidak berencana memiliki aplikasi yang terkait dengannya, Anda dapat menghapus class Activity awal dari sumber.
Izin dan deklarasi manifes
Aplikasi yang menyediakan layanan aksesibilitas harus menyertakan deklarasi tertentu dalam manifes aplikasi agar diperlakukan sebagai layanan aksesibilitas oleh sistem Android. Bagian ini menjelaskan setelan wajib dan opsional untuk layanan aksesibilitas.
Deklarasi layanan aksesibilitas
Agar aplikasi Anda diperlakukan sebagai layanan aksesibilitas, sertakan elemen service, bukan elemen activity, dalam elemen application di manifes Anda. Selain itu, dalam elemen service, sertakan filter intent layanan aksesibilitas. Manifes juga harus melindungi layanan dengan menambahkan izin BIND_ACCESSIBILITY_SERVICE untuk memastikan bahwa hanya sistem yang dapat terikat ke layanan. Berikut contohnya:
<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>
Konfigurasi layanan aksesibilitas
Layanan aksesibilitas harus menyediakan konfigurasi yang menentukan jenis peristiwa aksesibilitas yang ditangani layanan dan informasi tambahan tentang layanan tersebut. Konfigurasi layanan aksesibilitas terdapat dalam class
AccessibilityServiceInfo. Layanan Anda dapat membuat dan menyetel
konfigurasi menggunakan instance class ini dan setServiceInfo() pada
waktu proses. Namun, tidak semua opsi konfigurasi tersedia dengan metode ini.
Anda dapat menyertakan elemen <meta-data> dalam manifes dengan referensi ke file konfigurasi, yang memungkinkan Anda menetapkan rentang penuh opsi untuk layanan aksesibilitas, seperti yang ditunjukkan dalam contoh berikut:
<service android:name=".MyAccessibilityService"> ... <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service>
Elemen <meta-data> ini mengacu pada file XML yang Anda buat di direktori
resource aplikasi Anda: <project_dir>/res/xml/accessibility_service_config.xml>.
Kode berikut menunjukkan contoh konten file konfigurasi layanan:
<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" />
Untuk mengetahui informasi selengkapnya tentang atribut XML yang dapat digunakan dalam file konfigurasi layanan aksesibilitas, lihat dokumentasi referensi berikut:
android:descriptionandroid:packageNamesandroid:accessibilityEventTypesandroid:accessibilityFlagsandroid:accessibilityFeedbackTypeandroid:notificationTimeoutandroid:canRetrieveWindowContentandroid:settingsActivity
Untuk mengetahui informasi selengkapnya tentang setelan konfigurasi yang dapat disetel secara dinamis pada waktu proses, lihat dokumentasi referensi AccessibilityServiceInfo.
Mengonfigurasi layanan aksesibilitas
Pertimbangkan hal berikut saat menyetel variabel konfigurasi untuk layanan aksesibilitas Anda guna memberi tahu sistem cara dan waktu menjalankannya:
- Jenis peristiwa apa yang ingin Anda respons?
- Apakah layanan harus aktif untuk semua aplikasi, atau hanya nama paket tertentu?
- Jenis masukan apa yang digunakan?
Anda memiliki dua opsi untuk menyetel variabel ini. Opsi yang kompatibel dengan versi lama adalah menyetelnya dalam kode, menggunakan
setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo). Untuk
melakukannya, ganti metode onServiceConnected() dan konfigurasikan
layanan Anda di sana, seperti yang ditunjukkan dalam contoh berikut:
Kotlin
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 }
Java
@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); }
Opsi kedua adalah mengonfigurasi layanan menggunakan file XML. Opsi konfigurasi tertentu, seperti canRetrieveWindowContent, hanya tersedia jika Anda mengonfigurasi layanan menggunakan XML. Opsi konfigurasi dari contoh sebelumnya akan terlihat seperti ini jika ditentukan menggunakan 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" />
Jika Anda menggunakan XML, rujuk di manifes dengan menambahkan tag <meta-data>
ke deklarasi layanan yang mengarah ke file XML. Jika Anda menyimpan file XML
di res/xml/serviceconfig.xml, tag baru akan terlihat seperti ini:
<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>
Metode layanan aksesibilitas
Layanan aksesibilitas harus memperluas class AccessibilityService dan mengganti metode berikut dari class tersebut. Metode ini ditampilkan dalam urutan panggilan oleh sistem Android: mulai dari saat layanan dimulai (onServiceConnected()), saat layanan dijalankan (onAccessibilityEvent(), onInterrupt()), hingga saat layanan dinonaktifkan (onUnbind()).
onServiceConnected(): (opsional) sistem memanggil metode ini saat terhubung ke layanan aksesibilitas Anda. Gunakan metode ini untuk melakukan langkah penyiapan satu kali bagi layanan Anda, termasuk menghubungkan ke layanan sistem masukan pengguna, seperti pengelola audio atau penggetar perangkat. Jika Anda ingin menyetel konfigurasi layanan saat waktu proses atau membuat penyesuaian satu kali, ini adalah lokasi yang mudah digunakan untuk memanggilsetServiceInfo().onAccessibilityEvent(): (wajib) sistem memanggil kembali metode ini saat mendeteksiAccessibilityEventyang cocok dengan parameter pemfilteran peristiwa yang ditentukan oleh layanan aksesibilitas Anda, seperti saat pengguna mengetuk tombol atau berfokus pada kontrol antarmuka pengguna dalam aplikasi yang diberikan masukan oleh layanan aksesibilitas Anda. Saat sistem memanggil metode ini, sistem akan meneruskanAccessibilityEventterkait, yang kemudian dapat ditafsirkan dan digunakan oleh layanan untuk memberikan masukan kepada pengguna. Metode ini dapat dipanggil beberapa kali selama siklus proses layanan Anda.onInterrupt(): (wajib) sistem memanggil metode ini saat sistem ingin mengganggu masukan yang diberikan layanan Anda, biasanya sebagai respons terhadap tindakan pengguna seperti memindahkan fokus ke kontrol yang berbeda. Metode ini dapat dipanggil beberapa kali selama siklus proses layanan Anda.onUnbind(): (opsional) sistem memanggil metode ini saat sistem akan menonaktifkan layanan aksesibilitas. Gunakan metode ini untuk melakukan prosedur penonaktifan satu kali, termasuk membatalkan alokasi layanan sistem masukan pengguna, seperti pengelola audio atau vibrator perangkat.
Metode callback ini menyediakan struktur dasar untuk layanan aksesibilitas Anda. Anda dapat memutuskan cara memproses data yang diberikan oleh sistem Android dalam bentuk objek AccessibilityEvent dan memberikan masukan kepada pengguna. Untuk
mengetahui informasi selengkapnya tentang mendapatkan informasi dari peristiwa aksesibilitas, lihat Mendapatkan
detail peristiwa.
Mendaftar untuk peristiwa aksesibilitas
Salah satu fungsi terpenting dari parameter konfigurasi layanan aksesibilitas adalah memungkinkan Anda menentukan jenis peristiwa aksesibilitas yang dapat ditangani layanan Anda. Dengan menentukan informasi ini, layanan aksesibilitas dapat bekerja sama satu sama lain dan Anda dapat menangani jenis peristiwa tertentu dari aplikasi tertentu saja secara fleksibel. Pemfilteran peristiwa dapat mencakup kriteria berikut:
Nama paket: tentukan nama paket aplikasi yang peristiwa aksesibilitasnya ingin ditangani oleh layanan Anda. Jika parameter ini dihilangkan, layanan aksesibilitas Anda dianggap tersedia untuk peristiwa aksesibilitas layanan bagi aplikasi apa pun. Anda dapat menyetel parameter ini dalam file konfigurasi layanan aksesibilitas dengan atribut
android:packageNamessebagai daftar yang dipisahkan koma atau menggunakan anggotaAccessibilityServiceInfo.packageNames.Jenis peristiwa: menentukan jenis peristiwa aksesibilitas yang ingin ditangani oleh layanan Anda. Anda dapat menyetel parameter ini dalam file konfigurasi layanan aksesibilitas dengan atribut
android:accessibilityEventTypessebagai daftar yang dipisahkan oleh karakter|—misalnya,accessibilityEventTypes="typeViewClicked|typeViewFocused". Atau, Anda dapat menyetelnya menggunakan anggotaAccessibilityServiceInfo.eventTypes.
Saat menyiapkan layanan aksesibilitas, pertimbangkan dengan cermat peristiwa yang dapat ditangani layanan Anda dan hanya daftarkan untuk peristiwa tersebut. Karena pengguna dapat mengaktifkan lebih dari satu layanan aksesibilitas dalam satu waktu, layanan Anda tidak boleh menggunakan peristiwa yang tidak dapat ditangani. Perlu diingat bahwa layanan lain mungkin menangani peristiwa tersebut untuk meningkatkan pengalaman pengguna.
Volume aksesibilitas
Perangkat yang menjalankan Android 8.0 (level API 26) dan yang lebih tinggi menyertakan kategori volume STREAM_ACCESSIBILITY, yang memungkinkan Anda mengontrol volume output audio layanan aksesibilitas Anda secara independen dari suara lain pada perangkat.
Layanan aksesibilitas dapat menggunakan jenis streaming ini dengan menyetel opsi
FLAG_ENABLE_ACCESSIBILITY_VOLUME. Anda kemudian dapat mengubah volume audio aksesibilitas perangkat dengan memanggil metode adjustStreamVolume() pada instance AudioManager perangkat.
Cuplikan kode berikut menunjukkan cara layanan aksesibilitas dapat menggunakan kategori volume STREAM_ACCESSIBILITY:
Kotlin
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) } } }
Java
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); } } }
Untuk informasi selengkapnya, lihat video sesi Yang baru di aksesibilitas Android dari Google I/O 2017, mulai menit ke 6:35.
Pintasan aksesibilitas
Pada perangkat yang menjalankan Android 8.0 (level API 26) dan yang lebih tinggi, pengguna dapat mengaktifkan dan menonaktifkan layanan aksesibilitas pilihannya dari layar mana pun dengan menekan lama kedua tombol volume secara bersamaan. Meskipun pintasan ini mengaktifkan dan menonaktifkan Talkback secara default, pengguna dapat mengonfigurasi tombol untuk mengaktifkan dan menonaktifkan layanan apa pun yang diinstal di perangkat mereka.
Agar pengguna dapat mengakses layanan aksesibilitas tertentu dari pintasan aksesibilitas, layanan perlu meminta fitur saat waktu proses.
Untuk informasi selengkapnya, lihat video sesi Yang baru di aksesibilitas Android dari Google I/O 2017, mulai menit ke 13:25.
Tombol aksesibilitas
Pada perangkat yang menggunakan area navigasi yang dirender software dan menjalankan Android 8.0 (level API 26) dan yang lebih tinggi, di sisi kanan menu navigasi terdapat tombol aksesibilitas. Saat pengguna menekan tombol ini, mereka dapat memanggil salah satu dari beberapa fitur dan layanan aksesibilitas yang diaktifkan, bergantung pada konten yang saat ini ditampilkan di layar.
Agar pengguna dapat mengaktifkan layanan aksesibilitas tertentu menggunakan tombol aksesibilitas, layanan perlu menambahkan flag FLAG_REQUEST_ACCESSIBILITY_BUTTON dalam atribut android:accessibilityFlags objek AccessibilityServiceInfo. Layanan ini kemudian dapat mendaftarkan callback menggunakan
registerAccessibilityButtonCallback().
Cuplikan kode berikut menunjukkan cara mengonfigurasi layanan aksesibilitas untuk merespons penekanan tombol aksesibilitas oleh pengguna:
Kotlin
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) } }
Java
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); } }
Untuk informasi selengkapnya, lihat video sesi Yang baru di aksesibilitas Android dari Google I/O 2017, mulai menit ke 16:28.
Gestur sidik jari
Layanan aksesibilitas pada perangkat yang menjalankan Android 8.0 (API level 26) dan yang lebih tinggi dapat merespons penggeseran terarah (atas, bawah, kiri, dan kanan) di sekitar area sensor sidik jari perangkat. Untuk mengonfigurasi layanan guna menerima callback terkait interaksi ini, selesaikan langkah-langkah berikut:
- Deklarasikan izin
USE_BIOMETRICdan kemampuanCAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES. - Tetapkan flag
FLAG_REQUEST_FINGERPRINT_GESTURESdalam atributandroid:accessibilityFlags. - Daftarkan callback menggunakan
registerFingerprintGestureCallback()).
Ingatlah bahwa tidak semua perangkat menyertakan sensor sidik jari. Untuk mengidentifikasi
apakah perangkat mendukung sensor, gunakan metode isHardwareDetected(). Meskipun perangkat menyertakan sensor sidik jari, layanan Anda tidak dapat menggunakan sensor tersebut jika digunakan untuk tujuan autentikasi. Untuk mengidentifikasi kapan
sensor tersedia, panggil metode isGestureDetectionAvailable()
dan implementasikan callback onGestureDetectionAvailabilityChanged().
Cuplikan kode berikut akan menunjukkan contoh penggunaan gestur sidik jari untuk memilih opsi navigasi di papan game virtual:
// 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" ... />
Kotlin
// 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) } } }
Java
// 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); } } }
Untuk informasi selengkapnya, lihat video sesi Yang baru di aksesibilitas Android dari Google I/O 2017, mulai menit ke 9:03.
Text to speech multibahasa
Mulai dari Android 8.0 (level API 26), layanan text-to-speech (TTS) Android dapat mengidentifikasi dan mengucapkan frasa dalam beberapa bahasa dalam satu blok teks. Untuk mengaktifkan kemampuan pengalihan bahasa otomatis ini dalam layanan
aksesibilitas, gabungkan semua string dalam objek LocaleSpan, seperti yang ditunjukkan dalam
cuplikan kode berikut:
Kotlin
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) } }
Java
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; }
Untuk informasi selengkapnya, lihat video sesi Yang baru di aksesibilitas Android dari Google I/O 2017, mulai menit 10:59.
Bertindak atas nama pengguna
Mulai tahun 2011, layanan aksesibilitas dapat bertindak atas nama pengguna, termasuk mengubah fokus input dan memilih (mengaktifkan) elemen antarmuka pengguna. Pada tahun 2012, rentang tindakan diperluas untuk menyertakan daftar scroll dan berinteraksi dengan bidang teks. Layanan aksesibilitas juga dapat mengambil tindakan global, seperti membuka layar utama, menekan tombol Kembali, serta membuka layar notifikasi dan daftar aplikasi terbaru. Sejak 2012, Android menyertakan fokus aksesibilitas, yang membuat semua elemen yang terlihat dapat dipilih oleh layanan aksesibilitas.
Kemampuan ini memungkinkan developer layanan aksesibilitas membuat mode navigasi alternatif, seperti navigasi gestur, dan memberikan kontrol yang lebih baik kepada pengguna dengan disabilitas atas perangkat yang didukung Android.
Memproses gestur
Layanan aksesibilitas dapat memproses gestur tertentu dan merespons dengan bertindak atas nama pengguna. Fitur ini mengharuskan layanan aksesibilitas Anda meminta
aktivasi fitur Klik untuk Info. Layanan Anda dapat meminta aktivasi ini dengan menyetel anggota flags dari instance AccessibilityServiceInfo layanan ke FLAG_REQUEST_TOUCH_EXPLORATION_MODE, seperti yang ditunjukkan dalam contoh berikut.
Kotlin
class MyAccessibilityService : AccessibilityService() { override fun onCreate() { serviceInfo.flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE } ... }
Java
public class MyAccessibilityService extends AccessibilityService { @Override public void onCreate() { getServiceInfo().flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; } ... }
Setelah layanan Anda meminta aktivasi Klik untuk Info, pengguna harus mengizinkan fitur tersebut diaktifkan, jika belum aktif. Saat fitur ini aktif, layanan Anda akan menerima notifikasi gestur aksesibilitas melalui metode callback onGesture() layanan Anda dan dapat merespons dengan bertindak atas nama pengguna.
Gestur berkelanjutan
Perangkat yang menjalankan Android 8.0 (API level 26) dan yang lebih tinggi mendukung gestur
berkelanjutan, atau gestur terprogram yang berisi lebih dari satu objek Path.
Saat menentukan urutan goresan, Anda dapat menentukan bahwa goresan tersebut tergolong dalam gestur terprogram yang sama dengan menggunakan argumen akhir willContinue dalam konstruktor GestureDescription.StrokeDescription, seperti yang ditunjukkan dalam cuplikan kode berikut:
Kotlin
// 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) } }
Java
// 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); }
Untuk informasi selengkapnya, lihat video sesi Yang baru di aksesibilitas Android dari Google I/O 2017, mulai menit ke 15:47.
Menggunakan tindakan aksesibilitas
Layanan aksesibilitas dapat bertindak atas nama pengguna untuk menyederhanakan interaksi dengan aplikasi dan menjadi lebih produktif. Kemampuan layanan aksesibilitas untuk melakukan tindakan ditambahkan pada tahun 2011 dan diperluas secara signifikan pada tahun 2012.
Untuk bertindak atas nama pengguna, layanan aksesibilitas Anda harus terdaftar untuk
menerima peristiwa dari aplikasi dan meminta izin untuk melihat konten aplikasi dengan
menyetel android:canRetrieveWindowContent ke true dalam file
konfigurasi layanan. Setelah peristiwa diterima oleh layanan Anda, layanan tersebut dapat mengambil objek AccessibilityNodeInfo dari peristiwa menggunakan getSource(). Dengan objek AccessibilityNodeInfo, layanan Anda dapat menjelajahi hierarki tampilan untuk menentukan tindakan yang harus diambil, lalu bertindak atas nama pengguna menggunakan performAction().
Kotlin
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() } } ... }
Java
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(); } ... }
Metode performAction() memungkinkan layanan Anda mengambil tindakan dalam aplikasi. Jika layanan Anda perlu melakukan tindakan global, seperti membuka layar utama, mengetuk tombol Kembali, atau membuka layar notifikasi atau daftar aplikasi terbaru, gunakan metode performGlobalAction().
Menggunakan jenis fokus
Pada tahun 2012, Android memperkenalkan fokus antarmuka pengguna yang disebut fokus aksesibilitas. Layanan aksesibilitas dapat menggunakan fokus ini untuk memilih elemen antarmuka pengguna yang terlihat dan menindaklanjutinya. Jenis fokus ini berbeda dengan fokus input, yang menentukan elemen antarmuka pengguna di layar yang menerima input saat pengguna mengetik karakter, menekan Enter pada keyboard, atau menekan tombol tengah D-pad.
Ada kemungkinan satu elemen di antarmuka pengguna memiliki fokus input, sedangkan elemen lain memiliki fokus aksesibilitas. Tujuan fokus aksesibilitas adalah menyediakan layanan aksesibilitas dengan metode yang berinteraksi dengan elemen yang terlihat di layar, terlepas dari apakah elemen tersebut dapat difokuskan input dari perspektif sistem atau tidak. Untuk membantu memastikan bahwa layanan aksesibilitas Anda berinteraksi dengan benar dengan elemen input aplikasi, ikuti panduan untuk menguji aksesibilitas aplikasi guna menguji layanan Anda saat menggunakan aplikasi umum.
Layanan aksesibilitas dapat menentukan elemen antarmuka pengguna mana yang memiliki fokus
input atau fokus aksesibilitas menggunakan metode AccessibilityNodeInfo.findFocus(). Anda juga dapat menelusuri elemen yang dapat dipilih dengan fokus input menggunakan metode focusSearch(). Terakhir, layanan aksesibilitas Anda dapat
menyetel fokus aksesibilitas menggunakan
metode performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS).
Mengumpulkan informasi
Layanan aksesibilitas memiliki metode standar untuk mengumpulkan dan merepresentasikan unit utama dari informasi yang diberikan oleh pengguna, seperti detail peristiwa, teks, dan angka.
Mendapatkan detail perubahan jendela
Android 9 (API level 28) dan yang lebih tinggi memungkinkan aplikasi melacak pembaruan jendela saat
aplikasi menggambar ulang beberapa jendela secara bersamaan. Saat peristiwa
TYPE_WINDOWS_CHANGED terjadi, gunakan getWindowChanges()
API untuk menentukan bagaimana jendela berubah. Selama update multi-jendela, setiap jendela menghasilkan sekumpulan peristiwanya sendiri. Metode getSource() menampilkan tampilan root jendela yang terkait dengan setiap peristiwa.
Jika aplikasi menentukan judul panel aksesibilitas untuk objek View-nya, layanan Anda dapat mengenali kapan UI aplikasi diperbarui. Saat peristiwa
TYPE_WINDOW_STATE_CHANGED terjadi, gunakan jenis yang ditampilkan oleh
getContentChangeTypes() untuk menentukan bagaimana jendela berubah. Misalnya, framework dapat mendeteksi kapan panel memiliki judul baru atau kapan panel menghilang.
Mendapatkan detail peristiwa
Android menyediakan informasi untuk layanan aksesibilitas tentang interaksi antarmuka pengguna melalui objek AccessibilityEvent. Pada versi Android sebelumnya,
informasi yang tersedia dalam peristiwa aksesibilitas, sekaligus menyediakan banyak
detail tentang kontrol antarmuka pengguna yang dipilih oleh pengguna, menawarkan informasi kontekstual
yang terbatas. Dalam banyak kasus, informasi konteks yang hilang ini mungkin sangat penting untuk memahami arti kontrol yang dipilih.
Contoh antarmuka yang konteksnya sangat penting adalah kalender atau perencana harian. Jika pengguna memilih slot waktu 16.00 dalam daftar hari Senin sampai Jumat dan layanan aksesibilitas mengumumkan "jam 4 sore", tetapi tidak mengumumkan nama hari, hari dalam sebulan, atau nama bulan, hasil dari masukan tersebut akan membingungkan. Dalam hal ini, konteks kontrol antarmuka pengguna sangat penting bagi pengguna yang ingin menjadwalkan rapat.
Sejak tahun 2011, Android secara signifikan memperluas jumlah informasi yang dapat diperoleh layanan aksesibilitas tentang interaksi antarmuka pengguna dengan membuat peristiwa aksesibilitas berdasarkan hierarki tampilan. Hierarki tampilan adalah kumpulan komponen antarmuka pengguna yang berisi komponen (induknya) dan elemen antarmuka pengguna yang mungkin terdapat dalam komponen tersebut (turunannya). Dengan cara ini, Android dapat memberikan detail yang lebih kaya tentang peristiwa aksesibilitas, sehingga memungkinkan layanan aksesibilitas memberikan masukan yang lebih bermanfaat bagi pengguna.
Layanan aksesibilitas mendapatkan informasi tentang peristiwa antarmuka pengguna melalui
AccessibilityEvent yang diteruskan oleh sistem ke metode callback onAccessibilityEvent() layanan. Objek ini memberikan detail tentang
peristiwa, termasuk jenis objek yang sedang ditindaklanjuti, teks deskriptifnya, dan
detail lainnya.
AccessibilityEvent.getRecordCount()dangetRecord(int): Metode ini memungkinkan Anda mengambil kumpulan objekAccessibilityRecordyang berkontribusi padaAccessibilityEventyang diteruskan kepada Anda oleh sistem. Tingkat detail ini memberikan lebih banyak konteks untuk peristiwa yang memicu layanan aksesibilitas Anda.AccessibilityRecord.getSource(): metode ini menampilkan objekAccessibilityNodeInfo. Objek ini memungkinkan Anda meminta hierarki tata letak tampilan (induk dan turunan) dari komponen yang berasal dari peristiwa aksesibilitas. Fitur ini memungkinkan layanan aksesibilitas menyelidiki konteks sepenuhnya dari peristiwa, termasuk konten dan status tampilan yang disertakan atau tampilan turunan.
Platform Android menyediakan kemampuan bagi AccessibilityService untuk membuat kueri hierarki tampilan, mengumpulkan informasi tentang komponen UI yang menghasilkan peristiwa serta induk dan turunannya. Untuk melakukannya, tetapkan baris berikut
dalam konfigurasi XML Anda:
android:canRetrieveWindowContent="true"
Setelah selesai, panggil objek AccessibilityNodeInfo menggunakan getSource().
Panggilan ini hanya menampilkan objek jika jendela tempat peristiwa berasal masih menjadi jendela yang aktif. Jika tidak, jendela akan menampilkan null, jadi sesuaikan perilakunya.
Dalam contoh berikut, kode melakukan hal berikut saat peristiwa diterima:
- Segera panggil induk tampilan dari tempat peristiwa berasal.
- Dalam tampilan tersebut, cari label dan kotak centang sebagai tampilan turunan.
- Jika menemukannya, buat string untuk dilaporkan kepada pengguna, yang menunjukkan label dan apakah label tersebut dicentang.
Jika kapan pun nilai null ditampilkan saat menjelajahi hierarki tampilan, metode akan menghentikannya secara diam-diam.
Kotlin
// 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) }
Java
// 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); }
Kini Anda memiliki layanan aksesibilitas yang lengkap dan berfungsi. Coba konfigurasikan cara layanan aksesibilitas berinteraksi dengan pengguna dengan menambahkan mesin text-to-speech Android atau menggunakan Vibrator untuk memberikan info via sentuhan.
Memproses teks
Perangkat yang menjalankan Android 8.0 (API level 26) dan yang lebih tinggi menyertakan beberapa fitur pemrosesan teks yang mempermudah layanan aksesibilitas untuk mengidentifikasi dan mengoperasikan unit teks tertentu yang muncul di layar.
Tooltip
Android 9 (level API 28) memperkenalkan beberapa kemampuan yang memberi Anda akses ke tooltip di UI aplikasi. Gunakan getTooltipText() untuk membaca teks tooltip, dan gunakan ACTION_SHOW_TOOLTIP dan ACTION_HIDE_TOOLTIP untuk menginstruksikan instance View guna menampilkan atau menyembunyikan tooltipnya.
Teks petunjuk
Mulai tahun 2017, Android menyertakan beberapa metode untuk berinteraksi dengan teks petunjuk objek berbasis teks:
- Metode
isShowingHintText()dansetShowingHintText()menunjukkan dan menetapkan, secara berurutan, apakah konten teks node saat ini mewakili teks petunjuk node. getHintText()memberikan akses ke teks petunjuk itu sendiri. Meskipun objek tidak menampilkan teks petunjuk, panggilangetHintText()akan berhasil.
Lokasi karakter teks di layar
Pada perangkat yang menjalankan Android 8.0 (API level 26) dan yang lebih tinggi, layanan aksesibilitas dapat menentukan koordinat layar untuk setiap kotak pembatas karakter yang terlihat dalam widget TextView. Layanan menemukan koordinat ini dengan memanggil
refreshWithExtraData(), meneruskan
EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY sebagai argumen pertama dan objek
Bundle sebagai argumen kedua. Saat metode dieksekusi, sistem akan mengisi argumen Bundle dengan array parcelable objek Rect.
Setiap objek Rect mewakili kotak pembatas karakter tertentu.
Nilai rentang satu sisi standar
Beberapa objek AccessibilityNodeInfo menggunakan instance
AccessibilityNodeInfo.RangeInfo untuk menunjukkan bahwa elemen UI dapat
memiliki rentang nilai. Saat membuat rentang menggunakan RangeInfo.obtain(), atau
saat mengambil nilai ekstrem dari rentang menggunakan getMin() dan
getMax(), perhatikan bahwa perangkat yang menjalankan Android 8.0 (API level 26)
dan yang lebih tinggi merepresentasikan rentang satu sisi secara standar:
- Untuk rentang yang tidak memiliki nilai minimum,
Float.NEGATIVE_INFINITYmerepresentasikan nilai minimum. - Untuk rentang yang tidak memiliki nilai maksimum,
Float.POSITIVE_INFINITYmerepresentasikan nilai maksimum.
Merespons peristiwa aksesibilitas
Setelah layanan Anda disiapkan untuk menjalankan dan memproses peristiwa, tulis kode agar layanan mengetahui tindakan apa yang harus dilakukan saat AccessibilityEvent tersedia. Mulai dengan mengganti metode
onAccessibilityEvent(AccessibilityEvent). Dalam metode tersebut, gunakan
getEventType() untuk menentukan jenis peristiwa dan
getContentDescription() untuk mengekstrak teks label apa pun yang terkait dengan
tampilan yang mengaktifkan peristiwa:
Kotlin
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) ... }
Java
@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); ... }
Referensi lainnya
Untuk mempelajari lebih lanjut, lihat referensi berikut: