/* * Copyright (c) 2020. BoostTag E.I.R.L. Romell D.Z. * All rights reserved * porfile.romellfudi.com */ package com.romellfudi.ussdlibrary; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.net.Uri; import android.os.Build; import android.provider.Settings; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.elvishew.xlog.XLog; import java.util.HashMap; import java.util.HashSet; import java.util.List; /** * @author Romell Dominguez * @version 1.1.c 27/09/2018 * @since 1.0.a */ public class USSDController implements USSDInterface, USSDApi { protected static USSDController instance; protected Context context; protected HashMap> map; protected CallbackInvoke callbackInvoke; protected CallbackMessage callbackMessage; protected static final String KEY_LOGIN = "KEY_LOGIN"; protected static final String KEY_ERROR = "KEY_ERROR"; protected Boolean isRunning = false; protected Boolean send = false; private USSDInterface ussdInterface; /** * The Singleton building method * * @param context An activity that could call * @return An instance of USSDController */ public static USSDController getInstance(Context context) { if (instance == null) instance = new USSDController(context); return instance; } private USSDController(Context context) { ussdInterface = this; this.context = context; } /** * Invoke a dial-up calling a ussd number * * @param ussdPhoneNumber ussd number * @param map Map of Login and problem messages * @param callbackInvoke a callback object from return answer */ public void callUSSDInvoke(String ussdPhoneNumber, HashMap> map, CallbackInvoke callbackInvoke) { XLog.i("=====================callUSSDInvoke====================="); callUSSDInvoke(ussdPhoneNumber, 0, map, callbackInvoke); } /** * Invoke a dial-up calling a ussd number and * you had a overlay progress widget * * @param ussdPhoneNumber ussd number * @param map Map of Login and problem messages * @param callbackInvoke a callback object from return answer */ public void callUSSDOverlayInvoke(String ussdPhoneNumber, HashMap> map, CallbackInvoke callbackInvoke) { XLog.i("=====================callUSSDOverlayInvoke====================="); callUSSDOverlayInvoke(ussdPhoneNumber, 0, map, callbackInvoke); } /** * Invoke a dial-up calling a ussd number * * @param ussdPhoneNumber ussd number * @param subId subId number to use * @param map Map of Login and problem messages * @param callbackInvoke a callback object from return answer */ @SuppressLint("MissingPermission") public void callUSSDInvoke(String ussdPhoneNumber, int subId, HashMap> map, CallbackInvoke callbackInvoke) { send = false; this.callbackInvoke = callbackInvoke; this.map = map; if (verifyAccesibilityAccess(context)) { dialUp(ussdPhoneNumber, subId); } else { this.callbackInvoke.over("Check your accessibility"); } } /** * Invoke a dial-up calling a ussd number and * you had a overlay progress widget * * @param ussdPhoneNumber ussd number * @param subId subId number to use * @param map Map of Login and problem messages * @param callbackInvoke a callback object from return answer */ @SuppressLint("MissingPermission") public void callUSSDOverlayInvoke(String ussdPhoneNumber, int subId, HashMap> map, CallbackInvoke callbackInvoke) { send = false; this.callbackInvoke = callbackInvoke; this.map = map; if (verifyAccesibilityAccess(context) && verifyOverLay(context)) { dialUp(ussdPhoneNumber, subId); } else if (!verifyAccesibilityAccess(context)) { this.callbackInvoke.over("Check your accessibility -> error"); } else if (!verifyOverLay(context)) { this.callbackInvoke.over("overlay permission -> error"); } else { this.callbackInvoke.over("other -> error"); } } private void dialUp(String ussdPhoneNumber, int subId) { if (map == null || (!map.containsKey(KEY_ERROR) || !map.containsKey(KEY_LOGIN))) { this.callbackInvoke.over("Bad Mapping structure"); return; } if (ussdPhoneNumber.isEmpty()) { this.callbackInvoke.over("Bad ussd number"); return; } String uri = Uri.encode("#"); if (uri != null) ussdPhoneNumber = ussdPhoneNumber.replace("#", uri); Uri uriPhone = Uri.parse("tel:" + ussdPhoneNumber); if (uriPhone != null) isRunning = true; Intent intent = getActionCallIntent(uriPhone, subId); if (intent != null) this.context.startActivity(getActionCallIntent(uriPhone, subId)); } /** * get action call Intent * * @param uri parsed uri to call */ @SuppressLint("MissingPermission") private Intent getActionCallIntent(Uri uri, int subId) { Intent intent = new Intent(Intent.ACTION_CALL, uri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("com.android.phone.force.slot", true); intent.putExtra("Cdma_Supp", true); PhoneAccountHandle phoneAccountHandle = null; int slotIndex = -1; TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); if (telecomManager != null) { List phoneAccountHandleList = telecomManager.getCallCapablePhoneAccounts(); TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (phoneAccountHandleList != null) { XLog.i("phoneAccountHandleList.size() == " + phoneAccountHandleList.size()); } for (int i = 0; i < phoneAccountHandleList.size(); i++) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (subId == telephonyManager.getSubscriptionId(phoneAccountHandleList.get(i))) { XLog.i("subId 匹配成功, subId == " + subId); phoneAccountHandle = phoneAccountHandleList.get(i); slotIndex = SubscriptionManager.getSlotIndex(subId); break; } else { XLog.i("subId == " + subId + ", telephonyManager.getSubscriptionId(phoneAccountHandleList.get(i)) == " + telephonyManager.getSubscriptionId(phoneAccountHandleList.get(i))); } } } if (phoneAccountHandle != null) { intent.putExtra("android.telecom.extra.PHONE_ACCOUNT_HANDLE", phoneAccountHandle); } if (phoneAccountHandle == null) { if (callbackInvoke != null) { callbackInvoke.over("phoneAccountHandle == " + phoneAccountHandle); } return null; } } if (slotIndex == -1) { if (callbackInvoke != null) { callbackInvoke.over("slotIndex == " + slotIndex); } return null; } final String simSlotName[] = { "extra_asus_dial_use_dualsim", "com.android.phone.extra.slot", "slot", "simslot", "sim_slot", "subscription", "Subscription", "phone", "com.android.phone.DialingMode", "simSlot", "slot_id", "simId", "simnum", "phone_type", "slotId", "slotIdx" }; for (String s : simSlotName) intent.putExtra(s, slotIndex); return intent; } /** * Send a string by using inputText from USSD Dialog * * @param text string which contains info to be sent */ public void sendData(String text) { USSDService.send(text); } /** * Send a string by using the inputText from USSD Dialog * * @param text string which contains info to be sent * @param callbackMessage response callback */ public void send(String text, CallbackMessage callbackMessage) { this.callbackMessage = callbackMessage; this.send = true; ussdInterface.sendData(text); } /** * Press the first button od USSD Dialog as to cancel operation */ @Override public void cancel() { USSDService.cancel(); } /** * Check whether USSD Accessibility was enabled or not * * @param context Application context * @return */ public static boolean verifyAccesibilityAccess(Context context) { boolean isEnabled = USSDController.isAccessiblityServicesEnable(context); if (!isEnabled) { if (context instanceof Activity) { openSettingsAccessibility((Activity) context); } else { Toast.makeText(context, R.string.accessibility_service_is_not_enabled, Toast.LENGTH_LONG).show(); } } return isEnabled; } /** * Check whether the overlay capacity was enabled or not * * @param context Application context * @return */ public static boolean verifyOverLay(Context context) { boolean m_android_doesnt_grant = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context); if (m_android_doesnt_grant) { if (context instanceof Activity) { openSettingsOverlay((Activity) context); } else { Toast.makeText(context, R.string.overlay_permission_have_not_grant_permission, Toast.LENGTH_LONG).show(); } return false; } else return true; } private static void openSettingsAccessibility(final Activity activity) { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); alertDialogBuilder.setTitle(R.string.tip1); ApplicationInfo applicationInfo = activity.getApplicationInfo(); int stringId = applicationInfo.labelRes; String name = applicationInfo.labelRes == 0 ? applicationInfo.nonLocalizedLabel.toString() : activity.getString(stringId); alertDialogBuilder.setMessage("Accessibility permission must be granted to use " + name); alertDialogBuilder.setCancelable(false); alertDialogBuilder.setPositiveButton("Go", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { activity.startActivityForResult(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 1); } }); alertDialogBuilder.setNeutralButton("Close", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); } }); AlertDialog alertDialog = alertDialogBuilder.create(); if (alertDialog != null) { alertDialog.show(); } } private static void openSettingsOverlay(final Activity activity) { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); alertDialogBuilder.setTitle(R.string.tip2); ApplicationInfo applicationInfo = activity.getApplicationInfo(); int stringId = applicationInfo.labelRes; String name = applicationInfo.labelRes == 0 ? applicationInfo.nonLocalizedLabel.toString() : activity.getString(stringId); alertDialogBuilder .setMessage("You must allow " + name + " to appear on top of other applications"); alertDialogBuilder.setCancelable(false); alertDialogBuilder.setPositiveButton("Go", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + activity.getPackageName())); activity.startActivity(intent); } }); alertDialogBuilder.setNeutralButton("Close", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); } }); AlertDialog alertDialog = alertDialogBuilder.create(); if (alertDialog != null) { alertDialog.show(); } } protected static boolean isAccessiblityServicesEnable(Context context) { AccessibilityManager am = (AccessibilityManager) context .getSystemService(Context.ACCESSIBILITY_SERVICE); if (am != null) { for (AccessibilityServiceInfo service : am.getInstalledAccessibilityServiceList()) { if (service.getId().contains(context.getPackageName())) { return USSDController.isAccessibilitySettingsOn(context, service.getId()); } } } return false; } protected static boolean isAccessibilitySettingsOn(Context context, final String service) { int accessibilityEnabled = 0; try { accessibilityEnabled = Settings.Secure.getInt( context.getApplicationContext().getContentResolver(), android.provider.Settings.Secure.ACCESSIBILITY_ENABLED); } catch (Settings.SettingNotFoundException e) { // } if (accessibilityEnabled == 1) { String settingValue = Settings.Secure.getString( context.getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); if (settingValue != null) { TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(':'); splitter.setString(settingValue); while (splitter.hasNext()) { String accessabilityService = splitter.next(); if (accessabilityService.equalsIgnoreCase(service)) { return true; } } } } return false; } public interface CallbackInvoke { void responseInvoke(String message); void over(String message); } public interface CallbackMessage { void responseMessage(String message); void over(String message); } }