USSDController.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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.AccessibilityServiceInfo;
  8. import android.annotation.SuppressLint;
  9. import android.app.Activity;
  10. import android.app.AlertDialog;
  11. import android.content.Context;
  12. import android.content.DialogInterface;
  13. import android.content.Intent;
  14. import android.content.pm.ApplicationInfo;
  15. import android.net.Uri;
  16. import android.os.Build;
  17. import android.provider.Settings;
  18. import android.telecom.PhoneAccountHandle;
  19. import android.telecom.TelecomManager;
  20. import android.telephony.SubscriptionManager;
  21. import android.telephony.TelephonyManager;
  22. import android.text.TextUtils;
  23. import android.view.accessibility.AccessibilityManager;
  24. import android.widget.Toast;
  25. import com.elvishew.xlog.XLog;
  26. import java.util.HashMap;
  27. import java.util.HashSet;
  28. import java.util.List;
  29. /**
  30. * @author Romell Dominguez
  31. * @version 1.1.c 27/09/2018
  32. * @since 1.0.a
  33. */
  34. public class USSDController implements USSDInterface, USSDApi {
  35. protected static USSDController instance;
  36. protected Context context;
  37. protected HashMap<String, HashSet<String>> map;
  38. protected CallbackInvoke callbackInvoke;
  39. protected CallbackMessage callbackMessage;
  40. protected static final String KEY_LOGIN = "KEY_LOGIN";
  41. protected static final String KEY_ERROR = "KEY_ERROR";
  42. protected Boolean isRunning = false;
  43. protected Boolean send = false;
  44. private USSDInterface ussdInterface;
  45. /**
  46. * The Singleton building method
  47. *
  48. * @param context An activity that could call
  49. * @return An instance of USSDController
  50. */
  51. public static USSDController getInstance(Context context) {
  52. if (instance == null)
  53. instance = new USSDController(context);
  54. return instance;
  55. }
  56. private USSDController(Context context) {
  57. ussdInterface = this;
  58. this.context = context;
  59. }
  60. /**
  61. * Invoke a dial-up calling a ussd number
  62. *
  63. * @param ussdPhoneNumber ussd number
  64. * @param map Map of Login and problem messages
  65. * @param callbackInvoke a callback object from return answer
  66. */
  67. public void callUSSDInvoke(String ussdPhoneNumber, HashMap<String, HashSet<String>> map, CallbackInvoke callbackInvoke) {
  68. XLog.i("=====================callUSSDInvoke=====================");
  69. callUSSDInvoke(ussdPhoneNumber, 0, map, callbackInvoke);
  70. }
  71. /**
  72. * Invoke a dial-up calling a ussd number and
  73. * you had a overlay progress widget
  74. *
  75. * @param ussdPhoneNumber ussd number
  76. * @param map Map of Login and problem messages
  77. * @param callbackInvoke a callback object from return answer
  78. */
  79. public void callUSSDOverlayInvoke(String ussdPhoneNumber, HashMap<String, HashSet<String>> map, CallbackInvoke callbackInvoke) {
  80. XLog.i("=====================callUSSDOverlayInvoke=====================");
  81. callUSSDOverlayInvoke(ussdPhoneNumber, 0, map, callbackInvoke);
  82. }
  83. /**
  84. * Invoke a dial-up calling a ussd number
  85. *
  86. * @param ussdPhoneNumber ussd number
  87. * @param subId subId number to use
  88. * @param map Map of Login and problem messages
  89. * @param callbackInvoke a callback object from return answer
  90. */
  91. @SuppressLint("MissingPermission")
  92. public void callUSSDInvoke(String ussdPhoneNumber, int subId, HashMap<String, HashSet<String>> map, CallbackInvoke callbackInvoke) {
  93. send = false;
  94. this.callbackInvoke = callbackInvoke;
  95. this.map = map;
  96. if (verifyAccesibilityAccess(context)) {
  97. dialUp(ussdPhoneNumber, subId);
  98. } else {
  99. this.callbackInvoke.over("Check your accessibility");
  100. }
  101. }
  102. /**
  103. * Invoke a dial-up calling a ussd number and
  104. * you had a overlay progress widget
  105. *
  106. * @param ussdPhoneNumber ussd number
  107. * @param subId subId number to use
  108. * @param map Map of Login and problem messages
  109. * @param callbackInvoke a callback object from return answer
  110. */
  111. @SuppressLint("MissingPermission")
  112. public void callUSSDOverlayInvoke(String ussdPhoneNumber, int subId, HashMap<String, HashSet<String>> map, CallbackInvoke callbackInvoke) {
  113. send = false;
  114. this.callbackInvoke = callbackInvoke;
  115. this.map = map;
  116. if (verifyAccesibilityAccess(context) && verifyOverLay(context)) {
  117. dialUp(ussdPhoneNumber, subId);
  118. } else if (!verifyAccesibilityAccess(context)) {
  119. this.callbackInvoke.over("Check your accessibility -> error");
  120. } else if (!verifyOverLay(context)) {
  121. this.callbackInvoke.over("overlay permission -> error");
  122. } else {
  123. this.callbackInvoke.over("other -> error");
  124. }
  125. }
  126. private void dialUp(String ussdPhoneNumber, int subId) {
  127. if (map == null || (!map.containsKey(KEY_ERROR) || !map.containsKey(KEY_LOGIN))) {
  128. this.callbackInvoke.over("Bad Mapping structure");
  129. return;
  130. }
  131. if (ussdPhoneNumber.isEmpty()) {
  132. this.callbackInvoke.over("Bad ussd number");
  133. return;
  134. }
  135. String uri = Uri.encode("#");
  136. if (uri != null)
  137. ussdPhoneNumber = ussdPhoneNumber.replace("#", uri);
  138. Uri uriPhone = Uri.parse("tel:" + ussdPhoneNumber);
  139. if (uriPhone != null)
  140. isRunning = true;
  141. Intent intent = getActionCallIntent(uriPhone, subId);
  142. if (intent != null)
  143. this.context.startActivity(getActionCallIntent(uriPhone, subId));
  144. }
  145. /**
  146. * get action call Intent
  147. *
  148. * @param uri parsed uri to call
  149. */
  150. @SuppressLint("MissingPermission")
  151. private Intent getActionCallIntent(Uri uri, int subId) {
  152. Intent intent = new Intent(Intent.ACTION_CALL, uri);
  153. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  154. intent.putExtra("com.android.phone.force.slot", true);
  155. intent.putExtra("Cdma_Supp", true);
  156. PhoneAccountHandle phoneAccountHandle = null;
  157. int slotIndex = -1;
  158. TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
  159. if (telecomManager != null) {
  160. List<PhoneAccountHandle> phoneAccountHandleList = telecomManager.getCallCapablePhoneAccounts();
  161. TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  162. if (phoneAccountHandleList != null) {
  163. XLog.i("phoneAccountHandleList.size() == " + phoneAccountHandleList.size());
  164. }
  165. for (int i = 0; i < phoneAccountHandleList.size(); i++) {
  166. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
  167. if (subId == telephonyManager.getSubscriptionId(phoneAccountHandleList.get(i))) {
  168. XLog.i("subId 匹配成功, subId == " + subId);
  169. phoneAccountHandle = phoneAccountHandleList.get(i);
  170. slotIndex = SubscriptionManager.getSlotIndex(subId);
  171. break;
  172. } else {
  173. XLog.i("subId == " + subId + ", telephonyManager.getSubscriptionId(phoneAccountHandleList.get(i)) == " + telephonyManager.getSubscriptionId(phoneAccountHandleList.get(i)));
  174. }
  175. }
  176. }
  177. if (phoneAccountHandle != null) {
  178. intent.putExtra("android.telecom.extra.PHONE_ACCOUNT_HANDLE", phoneAccountHandle);
  179. }
  180. if (phoneAccountHandle == null) {
  181. if (callbackInvoke != null) {
  182. callbackInvoke.over("phoneAccountHandle == " + phoneAccountHandle);
  183. }
  184. return null;
  185. }
  186. }
  187. if (slotIndex == -1) {
  188. if (callbackInvoke != null) {
  189. callbackInvoke.over("slotIndex == " + slotIndex);
  190. }
  191. return null;
  192. }
  193. final String simSlotName[] = {
  194. "extra_asus_dial_use_dualsim",
  195. "com.android.phone.extra.slot",
  196. "slot",
  197. "simslot",
  198. "sim_slot",
  199. "subscription",
  200. "Subscription",
  201. "phone",
  202. "com.android.phone.DialingMode",
  203. "simSlot",
  204. "slot_id",
  205. "simId",
  206. "simnum",
  207. "phone_type",
  208. "slotId",
  209. "slotIdx"
  210. };
  211. for (String s : simSlotName)
  212. intent.putExtra(s, slotIndex);
  213. return intent;
  214. }
  215. /**
  216. * Send a string by using inputText from USSD Dialog
  217. *
  218. * @param text string which contains info to be sent
  219. */
  220. public void sendData(String text) {
  221. USSDService.send(text);
  222. }
  223. /**
  224. * Send a string by using the inputText from USSD Dialog
  225. *
  226. * @param text string which contains info to be sent
  227. * @param callbackMessage response callback
  228. */
  229. public void send(String text, CallbackMessage callbackMessage) {
  230. this.callbackMessage = callbackMessage;
  231. this.send = true;
  232. ussdInterface.sendData(text);
  233. }
  234. /**
  235. * Press the first button od USSD Dialog as to cancel operation
  236. */
  237. @Override
  238. public void cancel() {
  239. USSDService.cancel();
  240. }
  241. /**
  242. * Check whether USSD Accessibility was enabled or not
  243. *
  244. * @param context Application context
  245. * @return
  246. */
  247. public static boolean verifyAccesibilityAccess(Context context) {
  248. boolean isEnabled = USSDController.isAccessiblityServicesEnable(context);
  249. if (!isEnabled) {
  250. if (context instanceof Activity) {
  251. openSettingsAccessibility((Activity) context);
  252. } else {
  253. Toast.makeText(context, R.string.accessibility_service_is_not_enabled, Toast.LENGTH_LONG).show();
  254. }
  255. }
  256. return isEnabled;
  257. }
  258. /**
  259. * Check whether the overlay capacity was enabled or not
  260. *
  261. * @param context Application context
  262. * @return
  263. */
  264. public static boolean verifyOverLay(Context context) {
  265. boolean m_android_doesnt_grant = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
  266. && !Settings.canDrawOverlays(context);
  267. if (m_android_doesnt_grant) {
  268. if (context instanceof Activity) {
  269. openSettingsOverlay((Activity) context);
  270. } else {
  271. Toast.makeText(context, R.string.overlay_permission_have_not_grant_permission, Toast.LENGTH_LONG).show();
  272. }
  273. return false;
  274. } else
  275. return true;
  276. }
  277. private static void openSettingsAccessibility(final Activity activity) {
  278. AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
  279. alertDialogBuilder.setTitle(R.string.tip1);
  280. ApplicationInfo applicationInfo = activity.getApplicationInfo();
  281. int stringId = applicationInfo.labelRes;
  282. String name = applicationInfo.labelRes == 0 ?
  283. applicationInfo.nonLocalizedLabel.toString() : activity.getString(stringId);
  284. alertDialogBuilder.setMessage("Accessibility permission must be granted to use " + name);
  285. alertDialogBuilder.setCancelable(false);
  286. alertDialogBuilder.setPositiveButton("Go", new DialogInterface.OnClickListener() {
  287. public void onClick(DialogInterface dialog, int id) {
  288. activity.startActivityForResult(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 1);
  289. }
  290. });
  291. alertDialogBuilder.setNeutralButton("Close", new DialogInterface.OnClickListener() {
  292. public void onClick(DialogInterface dialog, int id) {
  293. dialog.dismiss();
  294. }
  295. });
  296. AlertDialog alertDialog = alertDialogBuilder.create();
  297. if (alertDialog != null) {
  298. alertDialog.show();
  299. }
  300. }
  301. private static void openSettingsOverlay(final Activity activity) {
  302. AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
  303. alertDialogBuilder.setTitle(R.string.tip2);
  304. ApplicationInfo applicationInfo = activity.getApplicationInfo();
  305. int stringId = applicationInfo.labelRes;
  306. String name = applicationInfo.labelRes == 0 ?
  307. applicationInfo.nonLocalizedLabel.toString() : activity.getString(stringId);
  308. alertDialogBuilder
  309. .setMessage("You must allow " + name + " to appear on top of other applications");
  310. alertDialogBuilder.setCancelable(false);
  311. alertDialogBuilder.setPositiveButton("Go", new DialogInterface.OnClickListener() {
  312. public void onClick(DialogInterface dialog, int id) {
  313. Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
  314. Uri.parse("package:" + activity.getPackageName()));
  315. activity.startActivity(intent);
  316. }
  317. });
  318. alertDialogBuilder.setNeutralButton("Close", new DialogInterface.OnClickListener() {
  319. public void onClick(DialogInterface dialog, int id) {
  320. dialog.dismiss();
  321. }
  322. });
  323. AlertDialog alertDialog = alertDialogBuilder.create();
  324. if (alertDialog != null) {
  325. alertDialog.show();
  326. }
  327. }
  328. protected static boolean isAccessiblityServicesEnable(Context context) {
  329. AccessibilityManager am = (AccessibilityManager) context
  330. .getSystemService(Context.ACCESSIBILITY_SERVICE);
  331. if (am != null) {
  332. for (AccessibilityServiceInfo service : am.getInstalledAccessibilityServiceList()) {
  333. if (service.getId().contains(context.getPackageName())) {
  334. return USSDController.isAccessibilitySettingsOn(context, service.getId());
  335. }
  336. }
  337. }
  338. return false;
  339. }
  340. protected static boolean isAccessibilitySettingsOn(Context context, final String service) {
  341. int accessibilityEnabled = 0;
  342. try {
  343. accessibilityEnabled = Settings.Secure.getInt(
  344. context.getApplicationContext().getContentResolver(),
  345. android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
  346. } catch (Settings.SettingNotFoundException e) {
  347. //
  348. }
  349. if (accessibilityEnabled == 1) {
  350. String settingValue = Settings.Secure.getString(
  351. context.getApplicationContext().getContentResolver(),
  352. Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
  353. if (settingValue != null) {
  354. TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(':');
  355. splitter.setString(settingValue);
  356. while (splitter.hasNext()) {
  357. String accessabilityService = splitter.next();
  358. if (accessabilityService.equalsIgnoreCase(service)) {
  359. return true;
  360. }
  361. }
  362. }
  363. }
  364. return false;
  365. }
  366. public interface CallbackInvoke {
  367. void responseInvoke(String message);
  368. void over(String message);
  369. }
  370. public interface CallbackMessage {
  371. void responseMessage(String message);
  372. void over(String message);
  373. }
  374. }