USSDService.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. /*
  2. * Copyright (c) 2020. BoostTag E.I.R.L. Romell D.Z.
  3. * All rights reserved
  4. * porfile.romellfudi.com
  5. */
  6. package com.romellfudi.ussdlibrary;
  7. import android.accessibilityservice.AccessibilityService;
  8. import android.content.ClipData;
  9. import android.content.ClipboardManager;
  10. import android.content.Context;
  11. import android.os.Build;
  12. import android.os.Bundle;
  13. import android.os.Parcel;
  14. import android.util.Log;
  15. import android.view.accessibility.AccessibilityEvent;
  16. import android.view.accessibility.AccessibilityNodeInfo;
  17. import com.elvishew.xlog.XLog;
  18. import com.google.gson.Gson;
  19. import java.util.ArrayList;
  20. import java.util.List;
  21. /**
  22. * AccessibilityService for ussd windows on Android mobile Telcom
  23. *
  24. * @author Romell Dominguez
  25. * @version 1.1.c 27/09/2018
  26. * @since 1.0.a
  27. */
  28. public class USSDService extends AccessibilityService {
  29. private static String TAG = "USSDServiceUSSD";
  30. public static AccessibilityEvent event;
  31. @Override
  32. public void onAccessibilityEvent(AccessibilityEvent event) {
  33. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
  34. this.event = new AccessibilityEvent(event);
  35. } else {
  36. Parcel parcel = Parcel.obtain();
  37. event.writeToParcel(parcel, 0);
  38. parcel.setDataPosition(0);
  39. AccessibilityEvent newEvent = AccessibilityEvent.CREATOR.createFromParcel(parcel);
  40. parcel.recycle();
  41. this.event = newEvent;
  42. }
  43. XLog.i("USSDService Event Json : " + getEventNowRootInActiveWindow(event));
  44. XLog.i(String.format("USSDService onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [text] %s" +
  45. " [ContentChangeTypes] %s [WindowChanges] %s",
  46. event.getEventType(), event.getClassName(), event.getPackageName(),
  47. event.getEventTime(), event.getText(), event.getContentChangeTypes(),event.getWindowChanges()));
  48. if (USSDController.instance == null || !USSDController.instance.isRunning) {
  49. XLog.e("USSDService Error : USSDController.instance = " + (USSDController.instance == null));
  50. if (USSDController.instance != null) {
  51. XLog.e("USSDService Error : USSDController.instance.isRunning = " + (USSDController.instance.isRunning));
  52. }
  53. return;
  54. }
  55. String response = event.getText().toString();
  56. if (LoginView(event) && notInputText(event)) {
  57. XLog.i("USSDService: 1");
  58. clickOnButton(event, 0);
  59. USSDController.instance.isRunning = false;
  60. if (USSDController.instance.send)
  61. USSDController.instance.callbackMessage.over(response);
  62. else
  63. USSDController.instance.callbackInvoke.over(response);
  64. } else if (problemView(event) || LoginView(event)) {
  65. XLog.i("USSDService: 2");
  66. clickOnButton(event, 1);
  67. if (USSDController.instance.send)
  68. USSDController.instance.callbackMessage.over(response);
  69. else
  70. USSDController.instance.callbackInvoke.over(response);
  71. } else if (isUSSDWidget(event)) {
  72. XLog.i("USSDService: 3");
  73. if (notInputText(event)) {
  74. XLog.i("USSDService: 4");
  75. clickOnButton(event, 0);
  76. USSDController.instance.isRunning = false;
  77. if (USSDController.instance.send)
  78. USSDController.instance.callbackMessage.over(response);
  79. else
  80. USSDController.instance.callbackInvoke.over(response);
  81. } else {
  82. XLog.i("USSDService: 5");
  83. if (USSDController.instance.send)
  84. USSDController.instance.callbackMessage.responseMessage(response);
  85. else
  86. USSDController.instance.callbackInvoke.responseInvoke(response);
  87. }
  88. } else {
  89. XLog.e("USSDService Error : 未走进任何逻辑循环");
  90. }
  91. }
  92. /**
  93. * Send whatever you want via USSD
  94. *
  95. * @param text any string
  96. */
  97. public static void send(String text) {
  98. setTextIntoField(event, text);
  99. clickOnButton(event, 1);
  100. }
  101. /**
  102. * Cancel USSD
  103. */
  104. public static void cancel() {
  105. clickOnButton(event, 0);
  106. }
  107. /**
  108. * set text into input text at USSD widget
  109. *
  110. * @param event AccessibilityEvent
  111. * @param data Any String
  112. */
  113. private static void setTextIntoField(AccessibilityEvent event, String data) {
  114. USSDController ussdController = USSDController.instance;
  115. Bundle arguments = new Bundle();
  116. arguments.putCharSequence(
  117. AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, data);
  118. for (AccessibilityNodeInfo leaf : getLeaves(event)) {
  119. if (leaf.getClassName().equals("android.widget.EditText")
  120. && !leaf.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)) {
  121. ClipboardManager clipboardManager = ((ClipboardManager) ussdController.context
  122. .getSystemService(Context.CLIPBOARD_SERVICE));
  123. if (clipboardManager != null) {
  124. clipboardManager.setPrimaryClip(ClipData.newPlainText("text", data));
  125. }
  126. leaf.performAction(AccessibilityNodeInfo.ACTION_PASTE);
  127. }
  128. }
  129. }
  130. /**
  131. * Method evaluate if USSD widget has input text
  132. *
  133. * @param event AccessibilityEvent
  134. * @return boolean has or not input text
  135. */
  136. protected static boolean notInputText(AccessibilityEvent event) {
  137. boolean flag = true;
  138. for (AccessibilityNodeInfo leaf : getLeaves(event)) {
  139. if (leaf.getClassName().equals("android.widget.EditText")) flag = false;
  140. }
  141. return flag;
  142. }
  143. /**
  144. * The AccessibilityEvent is instance of USSD Widget class
  145. *
  146. * @param event AccessibilityEvent
  147. * @return boolean AccessibilityEvent is USSD
  148. */
  149. private boolean isUSSDWidget(AccessibilityEvent event) {
  150. return (event.getClassName().equals("amigo.app.AmigoAlertDialog")
  151. || event.getClassName().equals("android.app.AlertDialog")
  152. || event.getClassName().equals("com.android.phone.oppo.settings.LocalAlertDialog")
  153. || event.getClassName().equals("com.zte.mifavor.widget.AlertDialog")
  154. || event.getClassName().equals("color.support.v7.app.AlertDialog")
  155. || event.getClassName().equals("com.transsion.widgetslib.dialog.PromptDialog")
  156. || event.getClassName().equals("miuix.appcompat.app.AlertDialog")
  157. || event.getClassName().equals("androidx.appcompat.app.AlertDialog"));
  158. }
  159. /**
  160. * The View has a login message into USSD Widget
  161. *
  162. * @param event AccessibilityEvent
  163. * @return boolean USSD Widget has login message
  164. */
  165. private boolean LoginView(AccessibilityEvent event) {
  166. return isUSSDWidget(event)
  167. && USSDController.instance.map.get(USSDController.KEY_LOGIN)
  168. .contains(event.getText().get(0).toString());
  169. }
  170. /**
  171. * The View has a problem message into USSD Widget
  172. *
  173. * @param event AccessibilityEvent
  174. * @return boolean USSD Widget has problem message
  175. */
  176. protected boolean problemView(AccessibilityEvent event) {
  177. return isUSSDWidget(event)
  178. && USSDController.instance.map.get(USSDController.KEY_ERROR)
  179. .contains(event.getText().get(0).toString());
  180. }
  181. /**
  182. * click a button using the index
  183. *
  184. * @param event AccessibilityEvent
  185. * @param index button's index
  186. */
  187. protected static void clickOnButton(AccessibilityEvent event, int index) {
  188. int count = -1;
  189. for (AccessibilityNodeInfo leaf : getLeaves(event)) {
  190. if (leaf.getClassName().toString().toLowerCase().contains("button")) {
  191. count++;
  192. if (count == index) {
  193. leaf.performAction(AccessibilityNodeInfo.ACTION_CLICK);
  194. }
  195. }
  196. }
  197. }
  198. private static List<AccessibilityNodeInfo> getLeaves(AccessibilityEvent event) {
  199. List<AccessibilityNodeInfo> leaves = new ArrayList<>();
  200. if (event.getSource() != null) {
  201. getLeaves(leaves, event.getSource());
  202. }
  203. return leaves;
  204. }
  205. private static void getLeaves(List<AccessibilityNodeInfo> leaves, AccessibilityNodeInfo node) {
  206. if (node.getChildCount() == 0) {
  207. leaves.add(node);
  208. return;
  209. }
  210. for (int i = 0; i < node.getChildCount(); i++) {
  211. getLeaves(leaves, node.getChild(i));
  212. }
  213. }
  214. /**
  215. * Active when SO interrupt the application
  216. */
  217. @Override
  218. public void onInterrupt() {
  219. Log.d(TAG, "onInterrupt");
  220. }
  221. /**
  222. * Configure accessibility server from Android Operative System
  223. */
  224. @Override
  225. protected void onServiceConnected() {
  226. super.onServiceConnected();
  227. Log.d(TAG, "onServiceConnected");
  228. }
  229. public static String getEventNowRootInActiveWindow(AccessibilityEvent event) {
  230. String PageJson = "{}";
  231. AccessibilityNodeInfo node = event.getSource();
  232. if (node != null) {
  233. NodeInfoWrapper nodeInfoWrapper = traverseNode(node);
  234. Gson gson = new Gson();
  235. PageJson = gson.toJson(nodeInfoWrapper);
  236. }
  237. return PageJson;
  238. }
  239. private static class NodeInfoWrapper {
  240. public String txt;
  241. public String vId;
  242. public String cName;
  243. public String pName;
  244. public int hashcode;
  245. public int childCount;
  246. //用于判断节点是否可以打开弹出窗口(popup)canOpenPopup()方法用于判断给定的节点是否具有打开弹出窗口的能力
  247. public boolean canOpenPopup;
  248. //判断节点是否对用户可见
  249. public boolean visibleToUser;
  250. //判断节点是否启用
  251. public boolean isEnabled;
  252. public String hintText;
  253. //判断节点是否可点击
  254. public boolean isClickable;
  255. //判断节点是否可长按
  256. public boolean isLongClickable;
  257. //判断节点是否可编辑
  258. public boolean isEditable;
  259. //判断节点是否可选择
  260. public boolean isCheckable;
  261. //判断节点的选择状态
  262. public boolean isChecked;
  263. //判断节点是否具有焦点
  264. public boolean focused;
  265. //判断节点是否可滚动
  266. public boolean scrollable;
  267. public boolean isSelected;
  268. public boolean isAccessibilityFocused;
  269. public boolean isContentInvalid;
  270. public boolean isContextClickable;
  271. public boolean isDismissable;
  272. public boolean isFocusable;
  273. public boolean isFocused;
  274. public boolean isHeading;
  275. public boolean isImportantForAccessibility;
  276. public boolean isMultiLine;
  277. public boolean isPassword;
  278. public boolean isScreenReaderFocusable;
  279. public boolean isScrollable;
  280. public boolean isShowingHintText;
  281. public boolean isTextEntryKey;
  282. public boolean isTextSelectable;
  283. public boolean isVisibleToUser;
  284. public List<NodeInfoWrapper> childNodes;
  285. }
  286. private static NodeInfoWrapper traverseNode(AccessibilityNodeInfo nodeInfo) {
  287. NodeInfoWrapper nodeInfoWrapper = new NodeInfoWrapper();
  288. nodeInfoWrapper.txt = nodeInfo.getText() != null ? nodeInfo.getText().toString() : "";
  289. nodeInfoWrapper.vId = nodeInfo.getViewIdResourceName();
  290. nodeInfoWrapper.cName = nodeInfo.getClassName().toString();
  291. nodeInfoWrapper.pName = nodeInfo.getPackageName().toString();
  292. nodeInfoWrapper.hashcode = nodeInfo.hashCode();
  293. nodeInfoWrapper.childCount = nodeInfo.getChildCount();
  294. nodeInfoWrapper.canOpenPopup = nodeInfo.canOpenPopup();
  295. nodeInfoWrapper.visibleToUser = nodeInfo.isVisibleToUser();
  296. nodeInfoWrapper.isEnabled = nodeInfo.isEnabled();
  297. nodeInfoWrapper.hintText = nodeInfo.getHintText() != null ? nodeInfo.getText().toString() : "";
  298. nodeInfoWrapper.isClickable = nodeInfo.isClickable();
  299. nodeInfoWrapper.isLongClickable = nodeInfo.isLongClickable();
  300. nodeInfoWrapper.isEditable = nodeInfo.isEditable();
  301. nodeInfoWrapper.isCheckable = nodeInfo.isCheckable();
  302. nodeInfoWrapper.isChecked = nodeInfo.isChecked();
  303. nodeInfoWrapper.focused = nodeInfo.isFocused();
  304. nodeInfoWrapper.scrollable = nodeInfo.isScrollable();
  305. nodeInfoWrapper.isSelected = nodeInfo.isSelected();
  306. nodeInfoWrapper.isAccessibilityFocused = nodeInfo.isAccessibilityFocused();
  307. nodeInfoWrapper.isContentInvalid = nodeInfo.isContentInvalid();
  308. nodeInfoWrapper.isContextClickable = nodeInfo.isContextClickable();
  309. nodeInfoWrapper.isDismissable = nodeInfo.isDismissable();
  310. nodeInfoWrapper.isFocusable = nodeInfo.isFocusable();
  311. nodeInfoWrapper.isFocused = nodeInfo.isFocused();
  312. nodeInfoWrapper.isHeading = nodeInfo.isHeading();
  313. nodeInfoWrapper.isImportantForAccessibility = nodeInfo.isImportantForAccessibility();
  314. nodeInfoWrapper.isMultiLine = nodeInfo.isMultiLine();
  315. nodeInfoWrapper.isPassword = nodeInfo.isPassword();
  316. nodeInfoWrapper.isScreenReaderFocusable = nodeInfo.isScreenReaderFocusable();
  317. nodeInfoWrapper.isScrollable = nodeInfo.isScrollable();
  318. nodeInfoWrapper.isShowingHintText = nodeInfo.isShowingHintText();
  319. nodeInfoWrapper.isTextEntryKey = nodeInfo.isTextEntryKey();
  320. nodeInfoWrapper.isVisibleToUser = nodeInfo.isVisibleToUser();
  321. List<NodeInfoWrapper> childNodes = new ArrayList<>();
  322. for (int i = 0; i < nodeInfo.getChildCount(); i++) {
  323. AccessibilityNodeInfo childNode = nodeInfo.getChild(i);
  324. if (childNode != null) {
  325. NodeInfoWrapper childNodeWrapper = traverseNode(childNode);
  326. childNodes.add(childNodeWrapper);
  327. }
  328. }
  329. nodeInfoWrapper.childNodes = childNodes;
  330. return nodeInfoWrapper;
  331. }
  332. }