1package foo.bar.testback; 2 3import android.accessibilityservice.AccessibilityService; 4import android.accessibilityservice.AccessibilityServiceInfo; 5import android.content.Context; 6import android.util.ArraySet; 7import android.util.Log; 8import android.view.WindowManager; 9import android.view.accessibility.AccessibilityEvent; 10import android.view.accessibility.AccessibilityNodeInfo; 11import android.view.accessibility.AccessibilityWindowInfo; 12import android.widget.Button; 13 14import java.util.List; 15import java.util.Set; 16 17public class TestBackService extends AccessibilityService { 18 19 private static final String LOG_TAG = TestBackService.class.getSimpleName(); 20 21 private Button mButton; 22 23 @Override 24 public void onCreate() { 25 super.onCreate(); 26 mButton = new Button(this); 27 mButton.setText("Button 1"); 28 } 29 30 @Override 31 public void onAccessibilityEvent(AccessibilityEvent event) { 32// if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { 33// Log.i(LOG_TAG, event.getText().toString()); 34// //dumpWindows(); 35// } 36 if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) { 37// Log.i(LOG_TAG, "Click event.isChecked()=" + event.isChecked() 38// + ((event.getSource() != null) ? " node.isChecked()=" 39// + event.getSource().isChecked() : " node=null")); 40 41 AccessibilityNodeInfo source = event.getSource(); 42 dumpWindow(source); 43// AccessibilityNodeInfo node = event.getSource(); 44// if (node != null) { 45// node.refresh(); 46// Log.i(LOG_TAG, "Clicked: " + node.getClassName() + " clicked:" + node.isChecked()); 47// } 48 } 49 } 50 51 @Override 52 protected boolean onGesture(int gestureId) { 53 switch (gestureId) { 54 case AccessibilityService.GESTURE_SWIPE_DOWN: { 55 showAccessibilityOverlay(); 56 } break; 57 case AccessibilityService.GESTURE_SWIPE_UP: { 58 hideAccessibilityOverlay(); 59 } break; 60 } 61 return super.onGesture(gestureId); 62 } 63 64 private void showAccessibilityOverlay() { 65 WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 66 params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 67 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 68 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 69 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 70 params.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; 71 72 WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 73 windowManager.addView(mButton, params); 74 } 75 76 private void hideAccessibilityOverlay() { 77 WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 78 windowManager.removeView(mButton); 79 } 80 81 private void dumpWindows() { 82 List<AccessibilityWindowInfo> windows = getWindows(); 83 final int windowCount = windows.size(); 84 for (int i = 0; i < windowCount; i++) { 85 AccessibilityWindowInfo window = windows.get(i); 86 Log.i(LOG_TAG, "============================="); 87 Log.i(LOG_TAG, window.toString()); 88 89 } 90 } 91 92 private void dumpWindow(AccessibilityNodeInfo source) { 93 AccessibilityNodeInfo root = source; 94 while (true) { 95 AccessibilityNodeInfo parent = root.getParent(); 96 if (parent == null) { 97 break; 98 } else if (parent.equals(root)) { 99 Log.i(LOG_TAG, "Node is own parent:" + root); 100 } 101 root = parent; 102 } 103 dumpTree(root, new ArraySet<AccessibilityNodeInfo>()); 104 } 105 106 private void dumpTree(AccessibilityNodeInfo root, Set<AccessibilityNodeInfo> visited) { 107 if (root == null) { 108 return; 109 } 110 111 if (!visited.add(root)) { 112 Log.i(LOG_TAG, "Cycle detected to node:" + root); 113 } 114 115 final int childCount = root.getChildCount(); 116 for (int i = 0; i < childCount; i++) { 117 AccessibilityNodeInfo child = root.getChild(i); 118 if (child != null) { 119 AccessibilityNodeInfo parent = child.getParent(); 120 if (parent == null) { 121 Log.e(LOG_TAG, "Child of a node has no parent"); 122 } else if (!parent.equals(root)) { 123 Log.e(LOG_TAG, "Child of a node has wrong parent"); 124 } 125 Log.i(LOG_TAG, child.toString()); 126 } 127 } 128 129 for (int i = 0; i < childCount; i++) { 130 AccessibilityNodeInfo child = root.getChild(i); 131 dumpTree(child, visited); 132 } 133 } 134 135 @Override 136 public void onInterrupt() { 137 /* ignore */ 138 } 139 140 @Override 141 public void onServiceConnected() { 142 AccessibilityServiceInfo info = getServiceInfo(); 143 info.flags |= AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS; 144 info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; 145 info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS; 146 setServiceInfo(info); 147 } 148} 149