Erişilebilirlik hizmeti oluşturma

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çin typeAllMask kullanın.
  • android:canRetrieveWindowContent: Hizmetinizin kullanıcı arayüzü hiyerarşisini incelemesi (ör. ekrandaki metni okuması) gerekiyorsa true olmalıdır.
  • android:canPerformGestures: Hareketleri (ör. kaydırma veya dokunma) programatik olarak göndermeyi planlıyorsanız true olmalıdır.
  • android:accessibilityFlags: Özellikleri etkinleştirmek için işaretleri birleştirin. Parmak izi hareketleri için flagRequestFingerprintGestures gereklidir. Yazılım erişilebilirliği düğmesi için flagRequestAccessibilityButton gereklidir.

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

Codelab uygulamaları

İçeriği görüntüleme