android - Lectura de texto desde(Facebook) RecyclerView con AccessibilityService
android-recyclerview android-accessibility (1)
¿Cómo puedo ver el texto de niños (elementos) RecyclerView no enfocados?
Mi servicio de accesibilidad debe leer el texto de la pantalla que contiene palabras clave. Esto funciona bien en la mayoría de las situaciones, excepto cuando estoy tratando con RecyclerViews, especialmente aquellos en la aplicación de Facebook.
Las pantallas de feed y comentarios de Facebook están basadas en RecyclerView y ninguna parece brindarme acceso al texto que necesito de sus eventos de accesibilidad.
Lo que he intentado:
public void onAccessibilityEvent(AccessibilityEvent event) {
//I''ve tried looping through event.getText() and it does not contain the text entries
//I''ve also tried the following (with various null checks)
AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
AccessibilityNodeInfoCompat source = record.getSource();
List<AccessibilityNodeInfoCompat> nodes = source.findAccessibilityNodeInfosByText("...");
//(and loop through the entries)
//I''ve also tried limiting to the class name "android.support.v7.widget.RecyclerView"
//and looking at parcelableData
//In the facebook app, the following is always null too
source.findFocus(AccessibilityNodeInfoCompat.FOCUS_ACCESSIBILITY);
}
En ninguna parte parece contener los datos que necesito. Como ejemplo al abrir una pantalla de comentarios / superposición de una publicación, puedo ver todo el contenido de texto EXCEPTO los comentarios mismos (que están dentro de la vista de reciclador)
Registros de los detalles del evento que puedo ver:
Esto es lo que veo para la superposición de comentarios en una publicación con un solo comentario
Event: EventType: TYPE_WINDOW_CONTENT_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: android.support.v7.widget.RecyclerView; Text: []; ContentDescription: null; ItemCount: 100; CurrentItemIndex: -1; IsEnabled: true; IsPassword: false; IsChecked: false; IsFullScreen: false; Scrollable: true; BeforeText: null; FromIndex: 13; ToIndex: 24; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
Event: EventType: TYPE_WINDOW_STATE_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: X.BGT; Text: []; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; IsEnabled: true; IsPassword: false; IsChecked: false; IsFullScreen: true; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
Event: EventType: TYPE_WINDOW_CONTENT_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: android.support.v7.widget.RecyclerView; Text: []; ContentDescription: null; ItemCount: 1; CurrentItemIndex: -1; IsEnabled: true; IsPassword: false; IsChecked: false; IsFullScreen: false; Scrollable: false; BeforeText: null; FromIndex: 0; ToIndex: 0; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
Event: EventType: TYPE_WINDOW_CONTENT_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: android.view.ViewGroup; Text: []; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; IsEnabled: true; IsPassword: false; IsChecked: false; IsFullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
Event: EventType: TYPE_WINDOW_CONTENT_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: android.widget.FrameLayout; Text: []; ContentDescription: Photo; ItemCount: -1; CurrentItemIndex: -1; IsEnabled: true; IsPassword: false; IsChecked: false; IsFullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
Event: EventType: TYPE_WINDOW_CONTENT_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: android.widget.FrameLayout; Text: []; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; IsEnabled: true; IsPassword: false; IsChecked: false; IsFullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
Event: EventType: TYPE_WINDOW_CONTENT_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: android.widget.ImageButton; Text: []; ContentDescription: Stickers; ItemCount: -1; CurrentItemIndex: -1; IsEnabled: false; IsPassword: false; IsChecked: false; IsFullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
Event: EventType: TYPE_WINDOW_CONTENT_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: android.widget.FrameLayout; Text: []; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; IsEnabled: true; IsPassword: false; IsChecked: false; IsFullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
Event: EventType: TYPE_WINDOW_CONTENT_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: android.widget.TextView; Text: []; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; IsEnabled: true; IsPassword: false; IsChecked: false; IsFullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
Event: EventType: TYPE_WINDOW_STATE_CHANGED; PackageName: com.facebook.katana; MovementGranularity: 0; Action: 0 [ ClassName: X.BGT; Text: [Be the first to like this., Like Button, Photo Comment, Write a comment…, Open GIF search, Stickers]; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; IsEnabled: true; IsPassword: false; IsChecked: false; IsFullScreen: true; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
También intenté explorar los elementos secundarios de la fuente, pero solo se muestran ViewGroups (sin vistas) y siempre con texto nulo.
Ancestor for 0 = android.view.accessibility.AccessibilityNodeInfo@800a418a; boundsInParent: Rect(0, 0 - 1056, 236); boundsInScreen: Rect(12, 244 - 1068, 480); packageName: com.facebook.katana; className: android.view.ViewGroup; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SHOW_ON_SCREEN - null]
Ancestor for 0, 0 = android.view.accessibility.AccessibilityNodeInfo@800a3647; boundsInParent: Rect(0, 0 - 1056, 236); boundsInScreen: Rect(12, 244 - 1068, 480); packageName: com.facebook.katana; className: android.view.ViewGroup; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: true; contextClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_LONG_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SHOW_ON_SCREEN - null]
Ancestor for 0, 0, 0 = android.view.accessibility.AccessibilityNodeInfo@800a3a08; boundsInParent: Rect(0, 0 - 828, 58); boundsInScreen: Rect(204, 262 - 1032, 320); packageName: com.facebook.katana; className: android.view.ViewGroup; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: true; longClickable: true; contextClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_LONG_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SHOW_ON_SCREEN - null]
Ancestor for 0, 0, 0, 0 = android.view.accessibility.AccessibilityNodeInfo@800a454b; boundsInParent: Rect(0, 0 - 268, 58); boundsInScreen: Rect(204, 262 - 472, 320); packageName: com.facebook.katana; className: android.view.ViewGroup; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: true; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SHOW_ON_SCREEN - null]
Ancestor for 0, 0, 1 = android.view.accessibility.AccessibilityNodeInfo@800a3dc9; boundsInParent: Rect(0, 0 - 828, 48); boundsInScreen: Rect(204, 320 - 1032, 368); packageName: com.facebook.katana; className: android.view.ViewGroup; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: true; longClickable: true; contextClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_LONG_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SHOW_ON_SCREEN - null]
Ancestor for 0, 0, 2 = android.view.accessibility.AccessibilityNodeInfo@800a490c; boundsInParent: Rect(0, 0 - 828, 80); boundsInScreen: Rect(204, 382 - 1032, 462); packageName: com.facebook.katana; className: android.view.ViewGroup; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SHOW_ON_SCREEN - null]
Ancestor for 0, 0, 2, 0 = android.view.accessibility.AccessibilityNodeInfo@800a4ccd; boundsInParent: Rect(0, 0 - 93, 44); boundsInScreen: Rect(507, 394 - 600, 438); packageName: com.facebook.katana; className: android.view.ViewGroup; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: true; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SHOW_ON_SCREEN - null]
Ancestor for 0, 0, 2, 1 = android.view.accessibility.AccessibilityNodeInfo@800a508e; boundsInParent: Rect(0, 0 - 114, 44); boundsInScreen: Rect(632, 394 - 746, 438); packageName: com.facebook.katana; className: android.view.ViewGroup; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: true; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SHOW_ON_SCREEN - null]
No estoy seguro de si esto es definitivamente posible o de que estoy tomando un enfoque completamente equivocado. Me di cuenta en el modo TalkBack que el usuario debe hacer clic / enfocar los elementos individuales de un elemento secundario RecyclerView antes de que se lean, lo cual no es una opción para mi servicio (que se supone que debe encontrar esos elementos de interés)
He enfrentado el problema con las vistas de desplazamiento anidado, resuelto con la búsqueda recursiva de todas las vistas, creo que ya lo ha intentado. Aquí está la solución que funciona en mi caso, puede probar que también puede funcionar para usted.
private AccessibilityNodeInfo accessbiltyContent(AccessibilityNodeInfo parentView,String strAddress) throws Exception {
int childCount = parentView.getChildCount();
for (int i = 0; i < childCount; ++i) {
AccessibilityNodeInfo child = parentView.getChild(i);
// child=null;
if (child != null) {
String strToAddress=strAddress+" > "+child.getClassName().toString();
String nodeText="";
if(child.getText() != null)
nodeText=child.getText().toString();
Log.v(TAG,"values =" +child.getClassName().toString(),nodeText,strToAddress));
accessbiltyContent(child,strToAddress);
}
else {
return null;
}
}
return null;
}
Atravesará todas las vistas en el elemento primario y las imprimirá para encontrarlas.