Erişilebilirlik hizmeti, kullanıcı arayüzünü geliştirerek engelli kullanıcılara veya geçici olarak bir cihazla tam etkileşim kuramayan kullanıcılara yardımcı olan bir uygulamadır. Bu hizmetler arka planda çalışır ve ekran içeriğini incelemek ve kullanıcının adına uygulamalarla etkileşim kurmak için sistemle iletişim kurar. Örnekler arasında ekran okuyucular (ör. TalkBack), Anahtar Erişimi araçları ve sesle kontrol sistemleri yer alır.
Bu kılavuzda, Android erişilebilirlik hizmeti oluşturmanın temelleri açıklanmaktadır.
Erişilebilirlik hizmeti yaşam döngüsü
Erişilebilirlik hizmeti oluşturmak için AccessibilityService sınıfını genişletmeniz ve hizmeti uygulamanızın manifest dosyasında beyan etmeniz gerekir.
Hizmet sınıfını oluşturma
AccessibilityService sınıfını genişleten bir sınıf oluşturun. Aşağıdaki yöntemleri geçersiz kılmanız gerekir:
onAccessibilityEvent: Sistem, hizmetinizin yapılandırmasıyla eşleşen bir etkinlik (ör. odak değişikliği veya düğme tıklama) algıladığında çağrılır. Hizmetinizin kullanıcı arayüzünü yorumladığı yer burasıdır.onInterrupt: Sistem, hizmetinizin geri bildirimini kesintiye uğrattığında (örneğin, kullanıcı odağı hızlıca hareket ettirdiğinde konuşma çıkışını durdurmak için) çağrılır.
package com.example.android.apis.accessibility import android.accessibilityservice.AccessibilityService import android.accessibilityservice.AccessibilityServiceInfo import android.accessibilityservice.FingerprintGestureController import android.accessibilityservice.AccessibilityButtonController import android.accessibilityservice.GestureDescription import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo import android.graphics.Path import android.os.Build import android.media.AudioManager import android.content.Context class MyAccessibilityService : AccessibilityService() { override fun onAccessibilityEvent(event: AccessibilityEvent) { // Interpret the event and provide feedback to the user } override fun onInterrupt() { // Interrupt any ongoing feedback } override fun onServiceConnected() { // Perform initialization here } }
Manifest dosyasında bildirin
Hizmetinizi AndroidManifest.xml dosyasına kaydedin. Yalnızca sistemin hizmetinize bağlanabilmesi için BIND_ACCESSIBILITY_SERVICE iznini kesinlikle zorunlu kılmalısınız.
Ayarlar düğmesinin çalıştığından emin olmak için ServiceSettingsActivity öğesini tanımlayın.
<application> <service android:name=".accessibility.MyAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:exported="true" android:label="@string/accessibility_service_label"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service> <activity android:name=".accessibility.ServiceSettingsActivity" android:exported="true" android:label="@string/accessibility_service_settings_label" /> </application>
Hizmeti yapılandırma
res/xml/accessibility_service_config.xml içinde bir yapılandırma dosyası oluşturun. Bu dosya, hizmetinizin hangi etkinlikleri işlediğini ve hangi geri bildirimleri sağladığını tanımlar.
Manifest dosyanızda bildirdiğiniz ServiceSettingsActivity öğesine referans verdiğinizden emin olun:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_service_description" android:accessibilityEventTypes="typeAllMask" android:accessibilityFlags="flagDefault|flagRequestFingerprintGestures|flagRequestAccessibilityButton" android:accessibilityFeedbackType="feedbackSpoken" android:notificationTimeout="100" android:canRetrieveWindowContent="true" android:canPerformGestures="true" android:settingsActivity="com.example.android.apis.accessibility.ServiceSettingsActivity" />
Yapılandırma dosyası aşağıdaki temel özellikleri içerir:
android:accessibilityEventTypes: Almak istediğiniz etkinlikler. Genel amaçlı hizmet içintypeAllMaskkullanın.android:canRetrieveWindowContent: Hizmetinizin kullanıcı arayüzü hiyerarşisini incelemesi (ör. ekrandaki metni okuması) gerekiyorsatrueolmalıdır.android:canPerformGestures: Hareketleri (ör. kaydırma veya dokunma) programatik olarak göndermeyi planlıyorsanıztrueolmalıdır.android:accessibilityFlags: Özellikleri etkinleştirmek için işaretleri birleştirin. Parmak izi hareketleri içinflagRequestFingerprintGesturesgereklidir. Yazılım erişilebilirliği düğmesi içinflagRequestAccessibilityButtongereklidir.
Yapılandırma seçeneklerinin tam listesi için AccessibilityServiceInfo sayfasına bakın.
Çalışma zamanı yapılandırması
XML yapılandırması statik olsa da hizmet yapılandırmanızı çalışma zamanında dinamik olarak da değiştirebilirsiniz. Bu, özellikleri kullanıcı tercihlerine göre etkinleştirmek veya devre dışı bırakmak için kullanışlıdır.
Çalışma zamanı güncellemelerini setServiceInfo() kullanarak uygulamak için onServiceConnected()'ı geçersiz kılın:
override fun onServiceConnected() { val info = AccessibilityServiceInfo() // Set the type of events that this service wants to listen to. info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED // Set the type of feedback your service provides. info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN // Set flags at runtime. info.flags = AccessibilityServiceInfo.FLAG_DEFAULT or AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES this.setServiceInfo(info) }
Kullanıcı arayüzü içeriğini yorumlama
onAccessibilityEvent() tetiklendiğinde sistem AccessibilityEvent sağlar. Bu etkinlik, ekran içeriğinin hiyerarşik bir gösterimi olan erişilebilirlik ağacının giriş noktası olarak işlev görür.
Hizmetiniz öncelikle düğmeler, listeler ve metin gibi kullanıcı arayüzü öğelerini temsil eden AccessibilityNodeInfo nesneleriyle etkileşim kurar. Bu kullanıcı arayüzü öğeleriyle ilgili veriler AccessibilityNodeInfo olarak normalleştirilir.
Aşağıdaki örnekte, bir etkinliğin kaynağının nasıl alınacağı ve bilgi bulmak için erişilebilirlik ağacında nasıl gezinileceği gösterilmektedir.
override fun onAccessibilityEvent(event: AccessibilityEvent) { // Get the source node of the event val sourceNode: AccessibilityNodeInfo? = event.source if (sourceNode == null) return // Inspect properties if (sourceNode.isCheckable) { val state = if (sourceNode.isChecked) "Checked" else "Unchecked" val label = sourceNode.text ?: sourceNode.contentDescription // Provide feedback (for example, speak to the user) speakToUser("$label is $state") } // Always recycle nodes to prevent memory leaks sourceNode.recycle() } private fun speakToUser(text: String) { // Your text-to-speech implementation goes here }
Kullanıcılar adına işlem yapma
Erişilebilirlik hizmetleri, kullanıcı adına düğmeleri tıklama veya listeleri kaydırma gibi işlemleri gerçekleştirebilir.
Bir işlem gerçekleştirmek için AccessibilityNodeInfo
nesnesinde performAction() işlevini çağırın.
fun performClick(node: AccessibilityNodeInfo) { if (node.isClickable) { node.performAction(AccessibilityNodeInfo.ACTION_CLICK) } }
Tüm sistemi etkileyen genel işlemler (ör. geri düğmesine basma veya bildirim gölgesini açma) için performGlobalAction() kullanın.
// Navigate back fun navigateBack() { performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) }
Odaklanmayı yönetme
Android'de iki farklı odak türü vardır: Giriş odağı (klavye girişinin gittiği yer) ve erişilebilirlik odağı (erişilebilirlik hizmetinin incelediği yer).
Aşağıdaki snippet'te, şu anda erişilebilirlik odağına sahip olan öğenin nasıl bulunacağı gösterilmektedir:
// Find the node that currently has accessibility focus // Note: rootInActiveWindow can be null if the window is not available val root = rootInActiveWindow if (root != null) { val focusedNode = root.findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) // Do something with focusedNode // Always recycle nodes focusedNode?.recycle() // rootInActiveWindow doesn't need to be recycled, but obtained nodes do. }
Aşağıdaki snippet'te, erişilebilirlik odağının belirli bir öğeye nasıl taşınacağı gösterilmektedir:
// Request that the system give focus to a given node fun focusNode(node: AccessibilityNodeInfo) { node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) }
Erişilebilirlik hizmeti oluştururken kullanıcının odak durumuna saygı gösterin ve kullanıcı işlemiyle açıkça tetiklenmediği sürece odağı çalmaktan kaçının.
Haraketleri yapma
Hizmetiniz, ekrana kaydırma, dokunma veya çoklu dokunma etkileşimleri gibi özel hareketler gönderebilir. Bunu yapmak için yapılandırmanızda android:canPerformGestures="true" öğesini bildirin. Böylece dispatchGesture() API'sini kullanabilirsiniz.
Basit hareketler
Basit hareketler gerçekleştirmek için belirli bir hareketle ilişkili hareketi temsil eden bir Path nesnesi oluşturarak başlayın. Ardından, konturu tanımlamak için Path karakterini GestureDescription içine alın. Son olarak, hareketi göndermek için dispatchGesture işlevini çağırın.
fun swipeRight() { // Create a path for the swipe (from x=100 to x=500) val swipePath = Path() swipePath.moveTo(100f, 500f) swipePath.lineTo(500f, 500f) // Build the stroke description (0ms delay, 500ms duration) val stroke = GestureDescription.StrokeDescription(swipePath, 0, 500) // Build the gesture description val gestureBuilder = GestureDescription.Builder() gestureBuilder.addStroke(stroke) // Dispatch the gesture dispatchGesture(gestureBuilder.build(), object : AccessibilityService.GestureResultCallback() { override fun onCompleted(gestureDescription: GestureDescription?) { super.onCompleted(gestureDescription) // Gesture finished successfully } }, null) }
Devam eden hareketler
Karmaşık etkileşimler (ör. L şekli çizme veya hassas çok adımlı sürükleme) için willContinue parametresini kullanarak vuruşları birbirine bağlayabilirsiniz.
fun performLShapedGesture() { val path1 = Path().apply { moveTo(200f, 200f) lineTo(400f, 200f) } val path2 = Path().apply { moveTo(400f, 200f) lineTo(400f, 400f) } // First stroke: willContinue = true val stroke1 = GestureDescription.StrokeDescription(path1, 0, 500, true) // Second stroke: continues immediately after stroke1 val stroke2 = stroke1.continueStroke(path2, 0, 500, false) val builder = GestureDescription.Builder() builder.addStroke(stroke1) builder.addStroke(stroke2) dispatchGesture(builder.build(), null, null) }
Ses yönetimi
Erişilebilirlik hizmeti (özellikle ekran okuyucu) oluştururken STREAM_ACCESSIBILITY ses akışını kullanın. Bu sayede kullanıcılar, hizmet sesini sistemdeki medya sesinden bağımsız olarak kontrol edebilir.
fun increaseAccessibilityVolume() { val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager audioManager.adjustStreamVolume( AudioManager.STREAM_ACCESSIBILITY, AudioManager.ADJUST_RAISE, 0 ) }
Yapılandırmanıza XML'de veya çalışma zamanında setServiceInfo aracılığıyla FLAG_ENABLE_ACCESSIBILITY_VOLUME işaretini eklediğinizden emin olun.
Gelişmiş özellikler
Parmak izi hareketleri
Android 10 (API düzeyi 29) veya sonraki sürümleri çalıştıran cihazlarda hizmetiniz, parmak izi sensöründe yönlü kaydırmaları yakalayabilir. Bu özellik, alternatif gezinme kontrolleri sağlamak için kullanışlıdır.
onServiceConnected() yönteminize aşağıdaki mantığı ekleyin:
// Import: android.os.Build // Import: android.accessibilityservice.FingerprintGestureController private var gestureController: FingerprintGestureController? = null override fun onServiceConnected() { // Check if the device is running Android 10 (Q) or higher if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { gestureController = fingerprintGestureController val callback = object : FingerprintGestureController.FingerprintGestureCallback() { override fun onGestureDetected(gesture: Int) { when (gesture) { FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN -> { // Handle swipe down } FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP -> { // Handle swipe up } } } } gestureController?.registerFingerprintGestureCallback(callback, null) } }
Erişilebilirlik düğmesi
Yazılım gezinme tuşlarını kullanan cihazlarda, kullanıcılar gezinme çubuğundaki bir erişilebilirlik düğmesi aracılığıyla hizmetinizi çağırabilir.
Bu özelliği kullanmak için hizmet yapılandırmanıza FLAG_REQUEST_ACCESSIBILITY_BUTTON işaretini ekleyin. Ardından, kayıt mantığını onServiceConnected() yönteminize ekleyin.
// Import: android.accessibilityservice.AccessibilityButtonController override fun onServiceConnected() { // ... existing initialization code ... val controller = accessibilityButtonController controller.registerAccessibilityButtonCallback( object : AccessibilityButtonController.AccessibilityButtonCallback() { override fun onClicked(controller: AccessibilityButtonController) { // Respond to button tap } } ) }
Çok dilli metin okuma
Metni yüksek sesle okuyan bir hizmet, kaynak metin LocaleSpan ile etiketlenmişse diller arasında otomatik olarak geçiş yapabilir. Bu sayede hizmetiniz, manuel geçiş yapmadan farklı dillerdeki içerikleri doğru şekilde telaffuz edebilir.
import android.text.Spannable import android.text.SpannableStringBuilder import android.text.style.LocaleSpan import java.util.Locale // Wrap text in LocaleSpan to indicate language val spannable = SpannableStringBuilder("Bonjour") spannable.setSpan( LocaleSpan(Locale.FRANCE), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE )
Hizmetiniz AccessibilityNodeInfo işlerken doğru metin okuma dilini belirlemek için LocaleSpan nesneleri açısından text özelliğini inceleyin.
Ek kaynaklar
Daha fazla bilgi edinmek için aşağıdaki kaynakları inceleyin:
Kılavuzlar
- Erişilebilir uygulamalar geliştirme
- Jetpack Compose'da erişilebilirlik
- Hızlı Kılavuz: Compose'da erişilebilirlik