2014-10-29 12 views
5

Voglio fare clic sul pulsante nelle impostazioni di Android utilizzando AccessibilityService come ha fatto greenify, ma non riesco a trovare il pulsante specifico. mi aiuti per favore.Come fare clic sul pulsante nelle impostazioni utilizzando AccessibilityService?

MyAccessibilityService .java:

public class MyAccessibilityService extends AccessibilityService { 

    private static final String TAG = MyAccessibilityService.class 
      .getSimpleName(); 

    @Override 
    public void onAccessibilityEvent(AccessibilityEvent event) { 
     Log.i(TAG, "ACC::onAccessibilityEvent: " + event.getEventType()); 

     //TYPE_WINDOW_STATE_CHANGED = 32, 
     if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType()) { 
      AccessibilityNodeInfo nodeInfo = event.getSource(); 
      Log.i(TAG, "ACC::onAccessibilityEvent: nodeInfo=" + nodeInfo.getText()); 

      List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId("com.android.settings:id/left_button"); 
      for (AccessibilityNodeInfo node : list) { 
       Log.i(TAG, "ACC::onAccessibilityEvent: " + event.getEventType() 
         + " " + node); 
      } 

EDIT:

Solo quando il tipo è TYPE_WINDOW_STATE_CHANGED, ho potuto ottenere l'oggetto NodeInfo.

+1

Il servizio riceve eventi? GetSource() restituisce valori non nulli? Perché stai provando a eseguire azioni di clic e scorrimento sulla sorgente dell'evento modificato dello stato della finestra (che è sempre la vista principale della finestra)? – alanv

+0

Ho rimosso un po 'di codice confuso (esegui clic e scorri) :) – thecr0w

+1

Sei sicuro che ci sia effettivamente una vista con quell'ID? La stringa "force_stop_button" non viene visualizzata da nessuna parte nella struttura dei sorgenti di Android. – alanv

risposta

12

Aprire un'app AppInfo con la forza pulsante di chiusura ha permesso di prova:

public class MyAccessibilityService extends AccessibilityService { 
    private static final String TAG = MyAccessibilityService.class 
      .getSimpleName(); 

    @Override 
    public void onAccessibilityEvent(AccessibilityEvent event) { 
     Log.i(TAG, "ACC::onAccessibilityEvent: " + event.getEventType()); 

     //TYPE_WINDOW_STATE_CHANGED == 32 
     if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event 
       .getEventType()) { 
      AccessibilityNodeInfo nodeInfo = event.getSource(); 
      Log.i(TAG, "ACC::onAccessibilityEvent: nodeInfo=" + nodeInfo); 
      if (nodeInfo == null) { 
       return; 
      } 

      List<AccessibilityNodeInfo> list = nodeInfo 
        .findAccessibilityNodeInfosByViewId("com.android.settings:id/left_button"); 
      for (AccessibilityNodeInfo node : list) { 
       Log.i(TAG, "ACC::onAccessibilityEvent: left_button " + node); 
       node.performAction(AccessibilityNodeInfo.ACTION_CLICK); 
      } 

      list = nodeInfo 
        .findAccessibilityNodeInfosByViewId("android:id/button1"); 
      for (AccessibilityNodeInfo node : list) { 
       Log.i(TAG, "ACC::onAccessibilityEvent: button1 " + node); 
       node.performAction(AccessibilityNodeInfo.ACTION_CLICK); 
      } 
     } 

    } 

    @Override 
    public void onServiceConnected() { 
     Log.i(TAG, "ACC::onServiceConnected: "); 
    } 

    @Override 
    public void onInterrupt() { 
     // TODO Auto-generated method stub 

    } 
} 
+0

Va notato che questo sembra funzionare solo sui livelli API 14+. E che dire 'recycle'ing? In base alla documentazione, è necessario riciclare sia gli eventi che gli oggetti del nodo. – Stan

+0

riciclando? per il tuo ricordo, lo esaminerò. – thecr0w

+1

findAccessibilityNodeInfosByViewId è disponibile per API 18+. L'utilizzo di findAccessibilityNodeInfosByText è un po 'complicato poiché il testo può cambiare in base alle impostazioni locali. Qualche idea su come superare questo problema? –

2

La risposta selezionata funziona su API 18 e al di sopra dal momento che trasmette su findAccessibilityNodeInfosByViewId che è stata aggiunta nel API 18. ho finito per scrivere questo classe per supportare API 17 e seguenti.

ResourcesCompat classe trova le risorse identificate con l'attività specificata che nel nostro caso dovrebbe essere l'attività Impostazione di Android. È possibile ottenere l'attività ComponentName delle impostazioni chiamando questa funzione quando si gestisce l'evento di accessibilità nel servizio di accessibilità dell'utente.

public static ComponentName getForegroundActivity(Context context) { 
     ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 
     List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1); 
     ComponentName topActivity = taskInfo.get(0).topActivity; 
     return topActivity; 
    } 

Un buon posto per chiamare init(...) è in onAccessibilityEvent quando si è prima la gestione dell'evento TYPE_WINDOW_STATE_CHANGED come thecr0w descritto.

public class ResourcesCompat { 
    private final static String RESOURCE_TYPE = "string"; 

    private String mResourcesPackageName; 
    private Resources mResources; 

    /** 
    * Find the resource file for a specific activity 
    * 
    * @param context 
    * @param settingsPackageName 
    * @param settingsClassName 
    */ 
    public void init(Context context, String settingsPackageName, String settingsClassName) { 
     try { 
      mResourcesPackageName = settingsPackageName; 
      ComponentName settingsComponentName = new ComponentName(settingsPackageName, settingsPackageName + settingsClassName); 
      mResources = context.getPackageManager().getResourcesForActivity(settingsComponentName); 
     } catch (PackageManager.NameNotFoundException e) { 
     } 
    } 

    /** 
    * Return the localised string for the given resource name. 
    * @param resourceName The name of the resource definition in strings.xml 
    */ 
    public String getString(String resourceName) { 
     int resourceId = getIdentifier(resourceName); 
     return resourceId > 0 ? mResources.getString(resourceId) : null; 
    } 

    /** 
    * Return a resource identifier for the given resource name. 
    * @param resourceName The name of the desired resource. 
    * @return int The associated resource identifier. Returns 0 if no such resource was found. (0 is not a valid resource ID.) 
    */ 
    private int getIdentifier(String resourceName) { 
     return mResources.getIdentifier(resourceName, RESOURCE_TYPE, mResourcesPackageName); 
    } 
} 

alcuni produttori come per muoversi intorno classi e rinominare le stringhe di default (tosse Samsung tosse Xiomi tosse) Quindi, assicurarsi di coprire tutti i casi e gestire errori ed eccezioni.

Infine, trova la tua vista per nome. Qui, id può essere 'force_stop' ad esempio

private List<AccessibilityNodeInfo> findAccessibilityNodeInfosByName(AccessibilityNodeInfo source, String id) { 
     String nodeText = mResourcesCompat.getString(id); 
     if (nodeText != null) { 
      return source.findAccessibilityNodeInfosByText(nodeText); 
     } 

     return null; 
} 
+0

'findAccessibilityNodeInfosByViewId' può essere sostituito da' AccessibilityNodeInfoCompat.wrap (getRootInActiveWindow()). FindAccessibilityNodeInfosByViewId' per obiettivi precedenti –

Problemi correlati