Muitos usuários do Android passam por diversas situações que os levam a interagir com seus telefones de diferentes maneiras. Para os usuários que não conseguem usar smartphones Android devido à visão, audição ou outros motivos físicos, o Android fornece funções e serviços de acessibilidade para ajudar esses usuários a operar o dispositivo de maneira mais simples, incluindo conversão de texto em fala, feedback tátil, operação por gestos, trackball e Lidar com operação. Os desenvolvedores podem criar seus próprios serviços de acessibilidade, que podem aprimorar a usabilidade dos aplicativos, como avisos sonoros, feedback físico e outros modos operacionais opcionais.
Sua implementação específica é executada em segundo plano por meio do serviço AccessibilityService e recebe retornos de chamada para eventos especificados por meio de AccessibilityEvent. Tais eventos representam algumas transições de estado do usuário na interface, como: foco alterado, botão clicado, etc. Tais serviços podem opcionalmente ter a capacidade de solicitar o conteúdo da janela ativa. Simplificando, AccessibilityService é um serviço de monitoramento em segundo plano. Quando o conteúdo que você monitora muda, o método de retorno de chamada do serviço em segundo plano será chamado.
Escreva sua própria classe de serviço e substitua o método onServiceConnected(), o método onAccessibilityEvent() e o método onInterrupt()
public class QHBAccessibilityService extends AccessibilityService {
/**
* 当启动服务的时候就会被调用
*/
@ Override
protected void onServiceConnected () {
super . onServiceConnected ();
}
/**
* 监听窗口变化的回调
*/
@ Override
public void onAccessibilityEvent ( AccessibilityEvent event ) {
int eventType = event . getEventType ();
//根据事件回调类型进行处理
}
/**
* 中断服务的回调
*/
@ Override
public void onInterrupt () {
}
}
A seguir está uma introdução aos métodos comumente usados em AccessibilityService
Por se tratar de um serviço em segundo plano, precisamos configurar as informações do serviço nos manifestos
< service
android : name = " .AccessibilityService.QHBAccessibilityService "
android : enabled = " true "
android : exported = " true "
android : label = " @string/label "
android : permission = " android.permission.BIND_ACCESSIBILITY_SERVICE " >
< intent-filter >
< action android : name = " android.accessibilityservice.AccessibilityService " />
</ intent-filter >
</ service >
Devemos ficar atentos: qualquer erro na configuração das informações fará com que o serviço pare de responder
Configurar parâmetros de serviço refere-se a: configurar para aceitar eventos de tipos especificados, ouvir pacotes especificados, recuperar conteúdo da janela, obter a hora dos tipos de eventos, etc. Existem duas maneiras de configurar parâmetros de serviço:
Adicione a tag de metadados aos manifestos originais para especificar o arquivo xml
< service
android : name = " .AccessibilityService.QHBAccessibilityService "
android : enabled = " true "
android : exported = " true "
android : label = " @string/label "
android : permission = " android.permission.BIND_ACCESSIBILITY_SERVICE " >
< intent-filter >
< action android : name = " android.accessibilityservice.AccessibilityService " />
</ intent-filter >
< meta-data
android : name = " android.accessibilityservice "
android : resource = " @xml/accessibility_service_config " />
</ service >
A seguir está a configuração do arquivo acessibilidade_service_config
<? xml version = " 1.0 " encoding = " utf-8 " ?>
< accessibility-service xmlns : android = " http://schemas.android.com/apk/res/android "
android : accessibilityEventTypes = " typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged|typeWindowsChanged "
android : accessibilityFeedbackType = " feedbackGeneric "
android : accessibilityFlags = " flagDefault "
android : canRetrieveWindowContent = " true "
android : description = " @string/description "
android : notificationTimeout = " 100 "
android : packageNames = " com.tencent.mm " />
A seguir está uma introdução aos parâmetros xml
acessibilidadeEventTypes: Indica quais alterações na interface o serviço tem interesse, ou seja, quais eventos são notificados, como abertura de janela, deslizamento, mudança de foco, toque longo, etc. O valor específico pode ser encontrado na classe AccessibilityEvent. Por exemplo, typeAllMask significa aceitar todas as notificações de eventos.
acessibilidadeFeedbackType: indica o método de feedback, como reprodução de voz ou vibração
canRetrieveWindowContent: indica se o serviço pode acessar o conteúdo na janela ativa. Ou seja, se você deseja obter o conteúdo do formulário no serviço, é necessário definir seu valor como verdadeiro
description: Uma descrição do recurso de acessibilidade, conforme mostrado na figura abaixo
notificaçãoTimeout: O intervalo de tempo para aceitação de eventos, geralmente definido como 100
packageNames: Indica qual pacote o serviço é usado para monitorar eventos. Aqui tomamos o nome do pacote WeChat como exemplo.
Configure as informações de AccessibilityServiceInfo para nosso AccessibilityService por meio de código. Aqui podemos extraí-las em um método de configuração.
private void settingAccessibilityInfo () {
String [] packageNames = { "com.tencent.mm" };
AccessibilityServiceInfo mAccessibilityServiceInfo = new AccessibilityServiceInfo ();
// 响应事件的类型,这里是全部的响应事件(长按,单击,滑动等)
mAccessibilityServiceInfo . eventTypes = AccessibilityEvent . TYPES_ALL_MASK ;
// 反馈给用户的类型,这里是语音提示
mAccessibilityServiceInfo . feedbackType = AccessibilityServiceInfo . FEEDBACK_SPOKEN ;
// 过滤的包名
mAccessibilityServiceInfo . packageNames = packageNames ;
setServiceInfo ( mAccessibilityServiceInfo );
}
A classe AccessibilityServiceInfo está envolvida aqui. A classe AccessibilityServiceInfo é usada para configurar informações de AccessibilityService. Esta classe contém um grande número de campos constantes para configuração e atributos xml.
Aqui precisamos ativar manualmente esta função na função de acessibilidade, caso contrário não poderemos continuar. Você pode abrir a lista de funções de acessibilidade do sistema através do código a seguir.
Intent intent = new Intent ( Settings . ACTION_ACCESSIBILITY_SETTINGS );
startActivity ( intent );
Como monitoramos a barra de notificação e a interface do evento, quando a barra de notificação ou a interface do packageNames especificado for alterada, nosso evento será chamado de volta por meio de onAccessibilityEvent e, em seguida, o evento será processado.
@ Override
public void onAccessibilityEvent ( AccessibilityEvent event ) {
int eventType = event . getEventType ();
//根据事件回调类型进行处理
switch ( eventType ) {
//当通知栏发生改变时
case AccessibilityEvent . TYPE_NOTIFICATION_STATE_CHANGED :
break ;
//当窗口的状态发生改变时
case AccessibilityEvent . TYPE_WINDOW_STATE_CHANGED :
break ;
}
}
Quando recebermos uma notificação no WeChat, uma mensagem push chegará na barra de status. Neste momento, ela será monitorada por TYPE_NOTIFICATION_STATE_CHANGED e o conteúdo interno será executado. Quando mudarmos a interface do WeChat ou usarmos o WeChat, desta vez ele será executado. ser monitorado por TYPE_WINDOW_STATE_CHANGED Execute o conteúdo dentro.
Métodos AccessibilityEvent
Após obter as alterações na janela da interface, é necessário obter os nós de controle neste momento. Os nós de toda a janela são essencialmente uma estrutura em árvore. As informações do nó são operadas através do seguinte.
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow ();
//通过文本找到对应的节点集合
List < AccessibilityNodeInfo > list = nodeInfo . findAccessibilityNodeInfosByText ( text );
//通过控件ID找到对应的节点集合,如com.tencent.mm:id/gd
List < AccessibilityNodeInfo > list = nodeInfo . findAccessibilityNodeInfosByViewId ( clickId );
Depois de obtermos as informações do nó, podemos realizar cliques simulados, toques longos e outras operações nos nós de controle. A classe AccessibilityNodeInfo fornece o método performAction() para nos permitir realizar operações simuladas. As operações comumente usadas estão listadas aqui.
//模拟点击
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_CLICK );
//模拟长按
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_LONG_CLICK );
//模拟获取焦点
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_FOCUS );
//模拟粘贴
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_PASTE );
Quando nosso telefone estiver conectado ao cabo USB, selecione o dispositivo no Android Device Monitor e abra a ferramenta Dump View Hierarchy for UI Automator, através da qual podemos obter informações de controle
P:
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
Também encontrei um problema como o seu. Acho que está nos "Parâmetros do serviço de configuração". Acabei de começar a configurá-lo no código e também retornou nulo. ", mas não aconteceu. Encontrei a configuração correspondente a android:canRetrieveWindowContent="true", então mudei para configurá-la em xml e ficou tudo bem.
P: Como você obtém esses nomes de classe com.tencent.mm.ui.LauncherUI? R: String className = event.getClassName().toString(); Use Log para imprimi-lo e abra o WeChat para visualizar as informações do Log.
Texto original: blog de Hensen_