-
-
Save TechNov/889ed015d5428a4259557e8807554a6c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.accessibilityservice.AccessibilityService; | |
import android.accessibilityservice.GestureDescription; | |
import android.content.Context; | |
import android.graphics.Path; | |
import android.graphics.Rect; | |
import android.os.Build; | |
import android.provider.Settings; | |
import android.support.annotation.RequiresApi; | |
import android.text.TextUtils; | |
import android.util.Log; | |
import android.view.accessibility.AccessibilityEvent; | |
import android.view.accessibility.AccessibilityNodeInfo; | |
import java.util.ArrayDeque; | |
import java.util.ArrayList; | |
import java.util.Deque; | |
import java.util.LinkedList; | |
import java.util.List; | |
public class AccessibilityUtils { | |
private static final String TAG = "AccessibilityUtils"; | |
private static AccessibilityUtils instance; | |
public static AccessibilityUtils getInstance() { | |
if (instance == null) { | |
instance = new AccessibilityUtils(); | |
} | |
return instance; | |
} | |
public String getAccessibilityEventType(int eventTypeCode) { | |
switch (eventTypeCode) { | |
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: | |
return "TYPE_NOTIFICATION_STATE_CHANGED"; | |
case AccessibilityEvent.TYPE_VIEW_CLICKED: | |
return "TYPE_VIEW_CLICKED"; | |
case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED: | |
return "TYPE_VIEW_LONG_CLICKED"; | |
case AccessibilityEvent.TYPE_VIEW_SELECTED: | |
return "TYPE_VIEW_SELECTED"; | |
case AccessibilityEvent.TYPE_VIEW_FOCUSED: | |
return "TYPE_VIEW_FOCUSED"; | |
case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: | |
return "TYPE_VIEW_TEXT_CHANGED"; | |
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: | |
return "TYPE_WINDOW_STATE_CHANGED"; | |
case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: | |
return "TYPE_VIEW_HOVER_ENTER"; | |
case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: | |
return "TYPE_VIEW_HOVER_EXIT"; | |
case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: | |
return "TYPE_TOUCH_EXPLORATION_GESTURE_START"; | |
case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: | |
return "TYPE_TOUCH_EXPLORATION_GESTURE_END"; | |
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: | |
return "TYPE_WINDOW_CONTENT_CHANGED"; | |
case AccessibilityEvent.TYPE_VIEW_SCROLLED: | |
return "TYPE_VIEW_SCROLLED"; | |
case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: | |
return "TYPE_VIEW_TEXT_SELECTION_CHANGED"; | |
case AccessibilityEvent.TYPE_ANNOUNCEMENT: | |
return "TYPE_ANNOUNCEMENT"; | |
case AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: | |
return "TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY"; | |
case AccessibilityEvent.TYPE_GESTURE_DETECTION_START: | |
return "TYPE_GESTURE_DETECTION_START"; | |
case AccessibilityEvent.TYPE_GESTURE_DETECTION_END: | |
return "TYPE_GESTURE_DETECTION_END"; | |
case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START: | |
return "TYPE_TOUCH_INTERACTION_START"; | |
case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END: | |
return "TYPE_TOUCH_INTERACTION_END"; | |
case AccessibilityEvent.TYPE_WINDOWS_CHANGED: | |
return "TYPE_WINDOWS_CHANGED"; | |
case AccessibilityEvent.TYPE_VIEW_CONTEXT_CLICKED: | |
return "TYPE_VIEW_CONTEXT_CLICKED"; | |
} | |
return ""; | |
} | |
public final void swipeCoordinatesAction(AccessibilityService service, int centerX, int centerY, int rectWidth) { | |
GestureDescription.Builder builder = null; | |
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { | |
builder = new GestureDescription.Builder(); | |
Path path = new Path(); | |
path.moveTo(((float) centerX) - (((float) rectWidth) / ((float) 2)), (float) centerY); | |
path.lineTo(((float) centerX) + (((float) rectWidth) / ((float) 2)), (float) centerY); | |
builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 400)); | |
service.dispatchGesture(builder.build(), new AccessibilityService.GestureResultCallback() { | |
@RequiresApi(api = Build.VERSION_CODES.N) | |
@Override | |
public void onCompleted(GestureDescription gestureDescription) { | |
Log.e(TAG, "swipeCoordinatesAction Gesture Completed: " + gestureDescription.getStrokeCount()); | |
super.onCompleted(gestureDescription); | |
} | |
}, null); | |
} | |
} | |
public final void tapCoordinatesAction(AccessibilityService service, int centerX, int centerY) { | |
GestureDescription.Builder builder = null; | |
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { | |
builder = new GestureDescription.Builder(); | |
Path path = new Path(); | |
path.moveTo(((float) centerX), (float) centerY); | |
builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 50)); | |
service.dispatchGesture(builder.build(), new AccessibilityService.GestureResultCallback() { | |
@RequiresApi(api = Build.VERSION_CODES.N) | |
@Override | |
public void onCompleted(GestureDescription gestureDescription) { | |
Log.e(TAG, "tapCoordinatesAction Gesture Completed: " + gestureDescription.getStrokeCount()); | |
super.onCompleted(gestureDescription); | |
} | |
}, null); | |
} | |
} | |
public final void tapCoordinatesAction(AccessibilityService service, int centerX, int centerY, final OnProcessClickListener listener) { | |
GestureDescription.Builder builder = null; | |
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { | |
builder = new GestureDescription.Builder(); | |
Path path = new Path(); | |
path.moveTo(((float) centerX), (float) centerY); | |
builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 50)); | |
service.dispatchGesture(builder.build(), new AccessibilityService.GestureResultCallback() { | |
@RequiresApi(api = Build.VERSION_CODES.N) | |
@Override | |
public void onCompleted(GestureDescription gestureDescription) { | |
Log.e(TAG, "tapCoordinatesAction Gesture Completed: " + gestureDescription.getStrokeCount()); | |
super.onCompleted(gestureDescription); | |
if (listener != null) { | |
listener.onClickComplete(); | |
} | |
} | |
}, null); | |
} | |
} | |
public final void tapCoordinatesAction(AccessibilityService service, AccessibilityNodeInfo nodeInfo) { | |
if (nodeInfo == null) { | |
return; | |
} | |
if (nodeInfo.isClickable()) { | |
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); | |
return; | |
} | |
GestureDescription.Builder builder = null; | |
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { | |
builder = new GestureDescription.Builder(); | |
Path path = new Path(); | |
Rect rect = new Rect(); | |
nodeInfo.getBoundsInScreen(rect); | |
path.moveTo(rect.centerX(), rect.centerY()); | |
builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 50)); | |
service.dispatchGesture(builder.build(), new AccessibilityService.GestureResultCallback() { | |
@RequiresApi(api = Build.VERSION_CODES.N) | |
@Override | |
public void onCompleted(GestureDescription gestureDescription) { | |
Log.e(TAG, "tapCoordinatesAction Gesture Completed: " + gestureDescription.getStrokeCount()); | |
super.onCompleted(gestureDescription); | |
} | |
}, null); | |
} | |
} | |
public final void tapCoordinatesAction(AccessibilityService service, AccessibilityNodeInfo nodeInfo, final OnProcessClickListener listener) { | |
if (nodeInfo == null) { | |
return; | |
} | |
GestureDescription.Builder builder = null; | |
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { | |
builder = new GestureDescription.Builder(); | |
Path path = new Path(); | |
Rect rect = new Rect(); | |
nodeInfo.getBoundsInScreen(rect); | |
path.moveTo(rect.centerX(), rect.centerY()); | |
builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 50)); | |
service.dispatchGesture(builder.build(), new AccessibilityService.GestureResultCallback() { | |
@RequiresApi(api = Build.VERSION_CODES.N) | |
@Override | |
public void onCompleted(GestureDescription gestureDescription) { | |
Log.e(TAG, "tapCoordinatesAction Gesture Completed: " + gestureDescription.getStrokeCount()); | |
super.onCompleted(gestureDescription); | |
if (listener != null) { | |
listener.onClickComplete(); | |
} | |
} | |
}, null); | |
} | |
} | |
public void logViewHierarchy(AccessibilityNodeInfo nodeInfo, final int depth, String tag) { | |
if (nodeInfo == null) return; | |
String spacerString = ""; | |
for (int i = 0; i < depth; ++i) { | |
spacerString += "- - "; | |
} | |
//Log the info you care about here... I choose classname and view resource name, because they are simple, but interesting. | |
Log.e(tag, spacerString + nodeInfo.toString()); | |
for (int i = 0; i < nodeInfo.getChildCount(); ++i) { | |
logViewHierarchy(nodeInfo.getChild(i), depth + 1, tag); | |
} | |
} | |
public ArrayList<String> getLogViewHierarchyIterative(AccessibilityNodeInfo nodeInfo) { | |
Deque<AccessibilityNodeInfo> stack = new ArrayDeque<AccessibilityNodeInfo>(); | |
final ArrayList<String> nodeList = new ArrayList<>(); | |
stack.push(nodeInfo); | |
while (!stack.isEmpty()) { | |
AccessibilityNodeInfo info = stack.pop(); | |
int depth = 0; | |
for (int i = 0; i < info.getChildCount(); i++) { | |
AccessibilityNodeInfo childNodeInfo = info.getChild(i); | |
if (childNodeInfo == null) { | |
continue; | |
} | |
if (childNodeInfo.getChildCount() > 0) { | |
stack.push(childNodeInfo); | |
depth++; | |
} | |
if (!TextUtils.isEmpty(childNodeInfo.toString())) { | |
StringBuilder builder = new StringBuilder(); | |
builder.append("[").append(depth).append(", "); | |
builder.append("Class Name: ").append(childNodeInfo.getClassName()).append(", "); | |
String resourceId = "null"; | |
if (!TextUtils.isEmpty(childNodeInfo.getViewIdResourceName())) { | |
resourceId = childNodeInfo.getViewIdResourceName(); | |
} | |
builder.append("ResourceId: ").append(resourceId).append(", "); | |
String text = "null"; | |
if (!TextUtils.isEmpty(childNodeInfo.getText())) { | |
text = childNodeInfo.getText().toString(); | |
} | |
builder.append("Text: ").append(text).append(", "); | |
Rect rect = new Rect(); | |
childNodeInfo.getBoundsInScreen(rect); | |
builder.append("BoundsInScreen: ").append(rect.toString()).append(", "); | |
childNodeInfo.getBoundsInParent(rect); | |
builder.append("BoundsInParent: ").append(rect.toString()).append("]"); | |
nodeList.add(builder.toString()); | |
} | |
} | |
} | |
/*for (String node : nodeList) { | |
Log.e(tag, node); | |
}*/ | |
return nodeList; | |
} | |
public void logViewHierarchyIterative(AccessibilityNodeInfo nodeInfo, String tag) { | |
Deque<AccessibilityNodeInfo> stack = new ArrayDeque<AccessibilityNodeInfo>(); | |
final List<String> nodeList = new LinkedList<>(); | |
stack.push(nodeInfo); | |
while (!stack.isEmpty()) { | |
AccessibilityNodeInfo info = stack.pop(); | |
int depth = 0; | |
for (int i = 0; i < info.getChildCount(); i++) { | |
AccessibilityNodeInfo childNodeInfo = info.getChild(i); | |
if (childNodeInfo == null) { | |
continue; | |
} | |
if (childNodeInfo.getChildCount() > 0) { | |
stack.push(childNodeInfo); | |
depth++; | |
} | |
if (!TextUtils.isEmpty(childNodeInfo.toString())) { | |
nodeList.add(childNodeInfo.toString()); | |
} | |
} | |
} | |
for (String node : nodeList) { | |
Log.e(tag, node); | |
} | |
} | |
public boolean matchAnyTextFromArray(AccessibilityNodeInfo nodeInfo, ArrayList<String> textArray) { | |
Deque<AccessibilityNodeInfo> stack = new ArrayDeque<AccessibilityNodeInfo>(); | |
stack.push(nodeInfo); | |
while (!stack.isEmpty()) { | |
AccessibilityNodeInfo info = stack.pop(); | |
for (int i = 0; i < info.getChildCount(); i++) { | |
AccessibilityNodeInfo childNodeInfo = info.getChild(i); | |
if (childNodeInfo == null) { | |
continue; | |
} | |
if (childNodeInfo.getChildCount() > 0) { | |
stack.push(childNodeInfo); | |
} | |
if (!TextUtils.isEmpty(childNodeInfo.getText())) { | |
if (textArray.contains(childNodeInfo.getText().toString())) { | |
return true; | |
} | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* Check if Accessibility Service is enabled. | |
* | |
* @param mContext | |
* @return <code>true</code> if Accessibility Service is ON, otherwise <code>false</code> | |
*/ | |
public boolean isAccessibilitySettingsOn(Context mContext, Class<?> serviceClass) { | |
int accessibilityEnabled = 0; | |
final String service = mContext.getPackageName() + "/" + serviceClass.getName(); | |
Log.e(TAG, "isAccessibilitySettingsOn: " + service); | |
try { | |
accessibilityEnabled = Settings.Secure.getInt( | |
mContext.getApplicationContext().getContentResolver(), | |
Settings.Secure.ACCESSIBILITY_ENABLED); | |
Log.v(TAG, "accessibilityEnabled = " + accessibilityEnabled); | |
} catch (Settings.SettingNotFoundException e) { | |
Log.e(TAG, "Error finding setting, default accessibility to not found: " | |
+ e.getMessage()); | |
} | |
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':'); | |
if (accessibilityEnabled == 1) { | |
Log.v(TAG, "***ACCESSIBILIY IS ENABLED*** -----------------"); | |
String settingValue = Settings.Secure.getString( | |
mContext.getApplicationContext().getContentResolver(), | |
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); | |
if (settingValue != null) { | |
TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; | |
splitter.setString(settingValue); | |
while (splitter.hasNext()) { | |
String accessabilityService = splitter.next(); | |
Log.v(TAG, "-------------- > accessabilityService :: " + accessabilityService); | |
if (accessabilityService.equalsIgnoreCase(service)) { | |
Log.v(TAG, "We've found the correct setting - accessibility is switched on!"); | |
return true; | |
} | |
} | |
} | |
} else { | |
Log.v(TAG, "***ACCESSIBILIY IS DISABLED***"); | |
} | |
return false; | |
} | |
public AccessibilityNodeInfo getNodeInfoById(AccessibilityNodeInfo nodeInfo, String type, String id) { | |
if (nodeInfo == null) return null; | |
List<AccessibilityNodeInfo> nodeInfoList = nodeInfo.findAccessibilityNodeInfosByViewId(id); | |
if (nodeInfoList != null && !nodeInfoList.isEmpty()) { | |
AccessibilityNodeInfo childNodeInfo = nodeInfoList.get(0); | |
if (childNodeInfo.getClassName().equals(type)) { | |
return childNodeInfo; | |
} | |
} | |
return null; | |
} | |
public AccessibilityNodeInfo getParentNodeFromChild(AccessibilityNodeInfo nodeInfo, String parentId) { | |
if (nodeInfo != null) { | |
AccessibilityNodeInfo info = nodeInfo.getParent(); | |
while (info != null) { | |
if (!TextUtils.isEmpty(info.getViewIdResourceName()) && info.getViewIdResourceName().equals(parentId)) { | |
return info; | |
} | |
info = info.getParent(); | |
} | |
} | |
return null; | |
} | |
public AccessibilityNodeInfo getNodeInfoById(AccessibilityNodeInfo nodeInfo, String id) { | |
if (nodeInfo == null) return null; | |
List<AccessibilityNodeInfo> nodeInfoList = nodeInfo.findAccessibilityNodeInfosByViewId(id); | |
if (nodeInfoList != null && !nodeInfoList.isEmpty()) { | |
return nodeInfoList.get(0); | |
} | |
return null; | |
} | |
public AccessibilityNodeInfo getNodeInfoByIdNew(AccessibilityNodeInfo nodeInfo, String id) { | |
while (nodeInfo != null) { | |
List<AccessibilityNodeInfo> nodeInfoList = nodeInfo.findAccessibilityNodeInfosByViewId(id); | |
if (nodeInfoList != null && !nodeInfoList.isEmpty()) { | |
return nodeInfoList.get(0); | |
} | |
nodeInfo = nodeInfo.getParent(); | |
} | |
return null; | |
} | |
public AccessibilityNodeInfo getNodeInfoByIdArray(AccessibilityNodeInfo nodeInfo, ArrayList<String> idArray) { | |
if (nodeInfo == null) return null; | |
for (String id : idArray) { | |
List<AccessibilityNodeInfo> nodeInfoList = nodeInfo.findAccessibilityNodeInfosByViewId(id); | |
if (nodeInfoList != null && !nodeInfoList.isEmpty()) { | |
return nodeInfoList.get(0); | |
} | |
} | |
return null; | |
} | |
public AccessibilityNodeInfo findNodeFromTextArray(AccessibilityNodeInfo nodeInfo, ArrayList<String> textArray) { | |
Deque<AccessibilityNodeInfo> stack = new ArrayDeque<AccessibilityNodeInfo>(); | |
stack.push(nodeInfo); | |
while (!stack.isEmpty()) { | |
AccessibilityNodeInfo info = stack.pop(); | |
for (int i = 0; i < info.getChildCount(); i++) { | |
AccessibilityNodeInfo childNodeInfo = info.getChild(i); | |
if (childNodeInfo == null) { | |
continue; | |
} | |
if (childNodeInfo.getChildCount() > 0) { | |
stack.push(childNodeInfo); | |
} | |
if (!TextUtils.isEmpty(childNodeInfo.getText())) { | |
if (textArray.contains(childNodeInfo.getText().toString())) { | |
return childNodeInfo; | |
} | |
} | |
} | |
} | |
return null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment