1 package foo.bar.testback;
2 
3 import android.accessibilityservice.AccessibilityService;
4 import android.accessibilityservice.AccessibilityServiceInfo;
5 import android.content.Context;
6 import android.util.ArraySet;
7 import android.util.Log;
8 import android.view.WindowManager;
9 import android.view.accessibility.AccessibilityEvent;
10 import android.view.accessibility.AccessibilityNodeInfo;
11 import android.view.accessibility.AccessibilityWindowInfo;
12 import android.widget.Button;
13 
14 import java.util.List;
15 import java.util.Set;
16 
17 public class TestBackService extends AccessibilityService {
18 
19     private static final String LOG_TAG = TestBackService.class.getSimpleName();
20 
21     private Button mButton;
22 
23     @Override
onCreate()24     public void onCreate() {
25         super.onCreate();
26         mButton = new Button(this);
27         mButton.setText("Button 1");
28     }
29 
30     @Override
onAccessibilityEvent(AccessibilityEvent event)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
onGesture(int gestureId)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 
showAccessibilityOverlay()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 
hideAccessibilityOverlay()76     private void hideAccessibilityOverlay() {
77         WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
78         windowManager.removeView(mButton);
79     }
80 
dumpWindows()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 
dumpWindow(AccessibilityNodeInfo source)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 
dumpTree(AccessibilityNodeInfo root, Set<AccessibilityNodeInfo> visited)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
onInterrupt()136     public void onInterrupt() {
137         /* ignore */
138     }
139 
140     @Override
onServiceConnected()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