1. Introducción
Los servicios de accesibilidad son una función del framework de Android diseñada para proporcionar comentarios de navegación alternativos al usuario en nombre de las aplicaciones instaladas en dispositivos Android. Un servicio de accesibilidad puede comunicarse con el usuario en nombre de la aplicación, por ejemplo, convirtiendo texto en voz o proporcionando comentarios hápticos cuando un usuario se desplaza sobre un área importante de la pantalla. En este codelab, se muestra cómo crear un servicio de accesibilidad muy simple.
¿Qué es un servicio de accesibilidad?
Un servicio de accesibilidad ayuda a los usuarios con discapacidades a usar dispositivos y apps para Android. Es un servicio privilegiado de larga duración que ayuda a los usuarios a procesar la información en la pantalla y les permite interactuar de manera significativa con un dispositivo.
Ejemplos de servicios de accesibilidad comunes
- Accesibilidad con interruptores: Permite que los usuarios de Android con limitaciones de movilidad interactúen con los dispositivos a través de uno o más interruptores.
- Acceso por voz (beta): Permite que los usuarios de Android con limitaciones de movilidad controlen un dispositivo con comandos por voz.
- TalkBack: Es un lector de pantalla que suelen usar las personas ciegas o con discapacidad visual.
Cómo crear un servicio de accesibilidad
Si bien Google proporciona servicios como Accesibilidad con interruptores, Acceso por voz y TalkBack para los usuarios de Android, estos servicios no pueden satisfacer las necesidades de todos los usuarios con discapacidades. Dado que muchos usuarios con discapacidades tienen necesidades únicas, las APIs de Android para crear servicios de accesibilidad son abiertas, y los desarrolladores pueden crear servicios de accesibilidad y distribuirlos a través de Play Store.
Qué compilarás
En este codelab, desarrollarás un servicio simple que hace algunas cosas útiles con la API de accesibilidad. Si puedes escribir una app básica para Android, puedes desarrollar un servicio similar.
La API de accesibilidad es potente: el código del servicio que compilarás se encuentra en solo cuatro archivos y usa alrededor de 200 líneas de código.
El usuario final
Compilarás un servicio para un usuario hipotético con las siguientes características:
- El usuario tiene dificultades para alcanzar los botones laterales de un dispositivo.
- El usuario tiene dificultades para desplazarse o deslizar el dedo.
Detalles del servicio
Tu servicio superpondrá una barra de acciones global en la pantalla. El usuario puede presionar los botones de esta barra para realizar las siguientes acciones:
- Apaga el dispositivo sin alcanzar el botón de encendido real en el lateral del teléfono.
- Ajusta el volumen sin tocar los botones de volumen que se encuentran al costado del teléfono.
- Realiza acciones de desplazamiento sin desplazarte realmente.
- Realiza un deslizamiento sin tener que usar un gesto de deslizamiento.
Requisitos
En este codelab, se supone que usarás lo siguiente:
- Una computadora que ejecute Android Studio
- Una terminal para ejecutar comandos de shell simples
- Un dispositivo que ejecute Android 7.0 (Nougat) conectado a la computadora que usarás para el desarrollo
¡Comencemos!
2. Cómo prepararte
Con la terminal, crea un directorio en el que trabajarás. Cambia a este directorio.
Descarga el código
Puedes clonar el repo que contiene el código para este codelab:
git clone https://github.com/android/codelab-android-accessibility.git
El repo contiene varios proyectos de Android Studio. Con Android Studio, abre GlobalActionBarService.
Haz clic en el ícono de Studio para iniciar Android Studio:

Selecciona la opción Import project (Eclipse ADT, Gradle, etc.):

Navega a la ubicación en la que clonaste la fuente y selecciona GlobalActionBarService.
Luego, con una terminal, cambia al directorio raíz.
3. Comprende el código de inicio
Explora el proyecto que abriste.
Ya se creó el esqueleto básico del servicio de accesibilidad. Todo el código que escribirás en este codelab se limita a los siguientes cuatro archivos:
- 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
A continuación, se incluye una explicación del contenido de cada archivo.
AndroidManifest.xml
En el manifiesto, se declara la información sobre el servicio de accesibilidad:
<?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>
Los siguientes tres elementos obligatorios se declaran en AndroidManifest.xml:
- Permiso para vincularse a un servicio de accesibilidad:
<service
...
android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
...
</service>
- El intent de AccessibilityService:
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
- Ubicación del archivo que contiene los metadatos del servicio que estás creando:
<meta-data
...
android:resource="@xml/global_action_bar_service" />
</service>
global_action_bar_service.xml
Este archivo contiene los metadatos del servicio.
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true" />
Con un elemento <accessibility-service>, se definieron los siguientes metadatos:
- El tipo de comentarios para este servicio (este codelab usa feedbackGeneric, que es un buen valor predeterminado).
- Son las marcas de accesibilidad del servicio (este codelab usa marcas predeterminadas).
- Las capacidades necesarias para el servicio:
- Para realizar el deslizamiento, android:canPerformGestures se establece en true.
- Para recuperar el contenido de la ventana, android:canRetrieveWindowContent se establece en true.
GlobalActionBarService.java
La mayor parte del código del servicio de accesibilidad se encuentra en GlobalActionBarService.java. Inicialmente, el archivo contiene el código mínimo absoluto para un servicio de accesibilidad:
- Una clase que extiende AccessibilityService.
- Un par de métodos anulados obligatorios (se dejan vacíos en este codelab).
public class GlobalActionBarService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
Agregarás código a este archivo durante el codelab.
action_bar.xml
El servicio expone una IU con cuatro botones, y el archivo de diseño action_bar.xml contiene el lenguaje de marcado para mostrar esos botones:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
Por el momento, este archivo contiene un LinearLayout vacío. Agregarás el lenguaje de marcado para los botones durante el codelab.
Cómo iniciar la aplicación
Asegúrate de haber conectado un dispositivo a tu computadora. Presiona el ícono verde de Reproducir
de la barra de menú que se encuentra en la parte superior de la pantalla. Esto debería iniciar la app en la que estás trabajando.
Ve a Configuración > Accesibilidad. El servicio de la barra de acciones global está instalado en tu dispositivo.

Haz clic en Global Action Bar Service y actívalo. Deberías ver el siguiente diálogo de permiso:

El servicio de accesibilidad solicita permiso para observar las acciones del usuario, recuperar el contenido de la ventana y realizar gestos en nombre del usuario. Cuando uses un servicio de accesibilidad de terceros, asegúrate de que confías en la fuente.
Ejecutar el servicio no hace mucho, ya que aún no agregamos ninguna funcionalidad. Comencemos.
4. Cómo crear los botones
Abre action_bar.xml en res/layout. Agrega el lenguaje de marcado dentro del LinearLayout actualmente vacío:
<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>
Esto crea botones que el usuario presionará para activar acciones en el dispositivo.
Abre GlobalActionBarService.java y agrega una variable para almacenar el diseño de la barra de acciones:
public class GlobalActionBarService extends AccessibilityService {
FrameLayout mLayout;
...
}
Ahora, agrega un método 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);
}
}
El código infla el diseño y agrega la barra de acciones cerca de la parte superior de la pantalla.
El método onServiceConnected() se ejecuta cuando se conecta el servicio. En este momento, el servicio de accesibilidad tiene todos los permisos que necesita para funcionar. El permiso clave que usarás aquí es WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY. Este permiso te permite dibujar directamente en la pantalla sobre el contenido existente sin tener que pasar por un flujo de permisos complicado.
Ciclo de vida del servicio de accesibilidad
El sistema administra exclusivamente el ciclo de vida de un servicio de accesibilidad y sigue el ciclo de vida del servicio establecido.
- Un servicio de accesibilidad se inicia cuando el usuario lo activa de forma explícita en la configuración del dispositivo.
- Después de que el sistema se vincula a un servicio, llama a onServiceConnected(). Los servicios que deseen realizar una configuración posterior a la vinculación pueden anular este método.
- Un servicio de accesibilidad se detiene cuando el usuario lo desactiva en la configuración del dispositivo o cuando llama a disableSelf().
Cómo ejecutar el servicio
Antes de iniciar el servicio con Android Studio, debes asegurarte de que la configuración de ejecución esté configurada correctamente.
Edita tu configuración de ejecución (usa Ejecutar en el menú superior y ve a Editar configuraciones. Luego, con el menú desplegable, cambia la opción de inicio de "Actividad predeterminada" a "Nada".

Ahora deberías poder iniciar el servicio con Android Studio.
Presiona el ícono verde de Reproducir
de la barra de menú que se encuentra en la parte superior de la pantalla. Luego, ve a Configuración > Accesibilidad y activa Global Action Bar Service.
Deberías ver los cuatro botones que forman la IU del servicio superpuestos sobre el contenido que se muestra en la pantalla.

Ahora agregarás funcionalidad a los cuatro botones para que el usuario pueda presionarlos y realizar acciones útiles.
5. Cómo configurar el botón de encendido
Agrega el método configurePowerButton() a 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);
}
});
}
Para acceder al menú del botón de encendido, configurePowerButton() usa el método performGlobalAction(), que proporciona AccessibilityService. El código que acabas de agregar es simple: hacer clic en el botón activa un onClickListener(), que llama a performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) y muestra el diálogo de encendido al usuario.
Ten en cuenta que las acciones globales no están vinculadas a ninguna vista. Presionar el botón Atrás, el botón de inicio o el botón Recientes son otros ejemplos de acciones globales.
Ahora, agrega configurePowerButton() al final del método onServiceConnected():
@Override
protected void onServiceConnected() {
...
configurePowerButton();
}
Presiona el ícono verde de Reproducir
de la barra de menú que se encuentra en la parte superior de la pantalla. Luego, ve a Configuración > Accesibilidad y, luego, inicia el Servicio de barra de acciones global.
Presiona el botón de encendido para mostrar el diálogo de encendido.
6. Cómo configurar el botón de volumen
Agrega el método configureVolumeButton() a 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);
}
});
}
El método configureVolumeButton() agrega un onClickListener() que se activa cuando el usuario presiona el botón de volumen. Dentro de este objeto de escucha, configureVolumeButton() usa un AudioManager para ajustar el volumen de la transmisión.
Ten en cuenta que cualquier persona puede controlar el volumen (no es necesario que seas un servicio de accesibilidad para hacerlo).
Ahora, agrega configureVolumeButton() al final del método onServiceConnected():
@Override
protected void onServiceConnected() {
...
configureVolumeButton();
}
Presiona el ícono verde de Reproducir
de la barra de menú que se encuentra en la parte superior de la pantalla. Luego, ve a Configuración > Accesibilidad y, luego, inicia el Servicio de barra de acciones global.
Presiona el botón de volumen para cambiarlo.
El usuario hipotético que no puede alcanzar los controles de volumen en el costado del dispositivo ahora puede usar Global Action Bar Service para cambiar (aumentar) el volumen.
7. Cómo configurar el botón de desplazamiento
En esta sección, se deben codificar dos métodos. El primer método encuentra un nodo desplazable, y el segundo realiza la acción de desplazamiento en nombre del usuario.
Agrega el método findScrollableNode a 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;
}
Un servicio de accesibilidad no tiene acceso a las vistas reales en la pantalla. En cambio, ve un reflejo de lo que se muestra en la pantalla en forma de un árbol compuesto por objetos AccessibilityNodeInfo. Estos objetos contienen información sobre la vista que representan (la ubicación de la vista, cualquier texto asociado a la vista, los metadatos que se agregaron para la accesibilidad, las acciones que admite la vista, etcétera). El método findScrollableNode() realiza un recorrido en amplitud de este árbol, comenzando en el nodo raíz. Si encuentra un nodo desplazable (es decir, un nodo que admite la acción AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD), lo devuelve; de lo contrario, devuelve nulo.
Ahora, agrega el método configureScrollButton() a 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());
}
}
});
}
Este método crea un onClickListener() que se activa cuando se hace clic en el botón de desplazamiento. Intenta encontrar un nodo desplazable y, si lo logra, realiza la acción de desplazamiento.
Ahora, agrega configureScrollButton() a onServiceConnected():
@Override
protected void onServiceConnected() {
...
configureScrollButton();
}
Presiona el ícono verde de Reproducir
de la barra de menú que se encuentra en la parte superior de la pantalla. Luego, ve a Configuración > Accesibilidad y, luego, inicia el Servicio de barra de acciones global.
Presiona el botón Atrás para ir a Configuración > Accesibilidad. Los elementos de la actividad de configuración de accesibilidad se pueden desplazar, y tocar el botón Desplazar realiza una acción de desplazamiento. Nuestro usuario hipotético que no puede realizar acciones de desplazamiento con facilidad ahora puede usar el botón Desplazarse para desplazarse por una lista de elementos.
8. Cómo configurar el botón de deslizamiento
Agrega el método configureSwipeButton() a 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);
}
});
}
El método configureSwipeButton() usa una nueva API que se agregó en N y que realiza gestos en nombre del usuario. El código usa un objeto GestureDescription para especificar la ruta de acceso del gesto que se realizará (en este codelab, se usan valores codificados) y, luego, envía el gesto de deslizamiento en nombre del usuario con el método dispatchGesture() de AccessibilityService.
Ahora, agrega configureSwipeButton() a onServiceConnected():
@Override
protected void onServiceConnected() {
...
configureSwipeButton();
}
Presiona el ícono verde de Reproducir
de la barra de menú que se encuentra en la parte superior de la pantalla. Luego, ve a Configuración > Accesibilidad y, luego, inicia el Servicio de barra de acciones global.
La forma más sencilla de probar la función de deslizamiento es abrir la aplicación de Maps instalada en tu teléfono. Una vez que se carga el mapa, si presionas el botón Deslizar, la pantalla se deslizará hacia la derecha.
9. Resumen
¡Felicitaciones! Compilaste un servicio de accesibilidad simple y funcional.
Puedes ampliar este servicio de varias maneras. Por ejemplo:
- Haz que la barra de acciones se pueda mover (por ahora, solo se encuentra en la parte superior de la pantalla).
- Permitir que el usuario aumente y disminuya el volumen
- Permite que el usuario deslice el dedo hacia la izquierda y la derecha.
- Se agregó compatibilidad con gestos adicionales a los que puede responder la barra de acciones.
En este codelab, solo se abarca un pequeño subconjunto de la funcionalidad que proporcionan las APIs de accesibilidad. La API también abarca lo siguiente (lista parcial):
- Compatibilidad con varias ventanas
- Se agregó compatibilidad con AccessibilityEvents. Cuando cambia la IU, los servicios de accesibilidad reciben notificaciones sobre esos cambios a través de objetos AccessibilityEvent. Luego, el servicio puede responder según corresponda a los cambios en la IU.
- Capacidad para controlar la ampliación
En este codelab, comenzarás a escribir un servicio de accesibilidad. Si conoces a un usuario con problemas de accesibilidad específicos que te gustaría abordar, ahora puedes crear un servicio para ayudarlo.