5

I have the following code in my program:

  public static void callPhoneNumber(Context context, String clientPhoneNum) {

    if (isCallingSupported(context)) {
      Intent i = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + clientPhoneNum));
      context.startActivity(i);
    } else {
      final AlertDialog alertDialog =
          new AlertDialog.Builder(context).setMessage(context.getString(R.string.error))
              .setMessage(context.getString(R.string.no_call_functionality))
              .setPositiveButton(context.getString(R.string.ok),
                  new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                      dialog.dismiss();
                    }
                  })

              .create();

      alertDialog.show();
    }
  }

  private static boolean isCallingSupported(Context context) {
    TelephonyManager telephonyManager =
        (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);

    return (telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE);
  }

I am wondering if isCallingSupported() would be necessary at all? I don't remember exactly why I wrote it this way but now when I am reviewing I'm thinking the user may just call a number using his Skype or other VOIP apps. Should I do any other checking instead or is this intent safe without the isCallingSupported() (what I mean by safe is, even if user has a tablet with no calling functionality and no other apps that can handle a call, the intent doesn't cause a crash)?

Nima
  • 6,383
  • 7
  • 46
  • 68

3 Answers3

10

From this question:

PackageManager manager = context.getPackageManager();
List<ResolveInfo> infos = manager.queryIntentActivities(intent, 0);
if (infos.size() > 0) {
       //Then there is application can handle your intent
}else{
       //No Application can handle your intent
}

Start by checking if any app has registered to this intent. If they have, use it. If not, display your dialog.

You would end up just replacing your isCallingSupported function with the code above:

private static boolean isCallingSupported(Context context) {

    boolean result = true;
    PackageManager manager = context.getPackageManager();
    List<ResolveInfo> infos = manager.queryIntentActivities(intent, 0);
    if (infos.size() <= 0) {
        result = false;
    }
    return result;

}

Community
  • 1
  • 1
Robin Eisenberg
  • 1,836
  • 18
  • 26
3

I would use PackageManager and resolveActivity() to see if anything will respond to your configured ACTION_DIAL Intent. That would handle:

  • Devices that do not have native telephony and have no dialer, as resolveActivity() should return null

  • Devices that do not have native telephony but do have a dialer (e.g., VOIP), as hopefully the authors of that dialer have ACTION_DIAL support

  • Devices that do have native telephony, but the current user does not have access to the dialer (e.g., restricted profiles)

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    This is a good read from the commonsware blog :) - http://commonsware.com/blog/2011/02/25/xoom-permissions-android-market.html – Shivam Verma Nov 18 '14 at 15:34
  • @ShivamVerma: `hasSystemFeature()` and kin are needed for cases where there is no `Intent` to check, such as determining if `SmsManager` will be able to send an SMS. – CommonsWare Nov 18 '14 at 15:58
  • Thanks Mark, since the other user has a lower rep I chose to accept his answer but your answer is equally valuable. – Nima Nov 18 '14 at 16:28
  • I just tested the solutions described in this question on a wifi-only Samsung tablet. Strangely, com.android.contacts is returned by queryIntentActivities(). If I click a tel: url in an Html page on the device, I am asked if I want to add the phone number to my contacts. Is there a way to filter this out and only find things that can really place calls? – Michael Levy May 06 '15 at 22:59
  • 1
    @MichaelLevy: You could use `hasSystemFeature(PackageManager.FEATURE_TELEPHONY)`. Or, you could try `ACTION_CALL` instead of `ACTION_DIAL` -- while you need the `CALL_PHONE` permission to actually use such an `Intent` with `startActivity()`, you should be able to `queryIntentActivities()` with it without the permission. – CommonsWare May 06 '15 at 23:12
0

Using Robin's great answer I created a helper in my RunUtilities class:

class RunUtilities {

    companion object {

        fun canMakeCalls(context: Context): Boolean {
            val intent = Intent(Intent.ACTION_DIAL)
            val infos = context.packageManager.queryIntentActivities(intent, 0)
            return infos.size > 0
        }
    }
}

Example usage:

if (!RunUtilities.canMakeCalls(this)) {
    callButton.visibility = View.GONE
}

(I'm fairly new to Kotlin so any pointers would be welcome)

Leon
  • 3,614
  • 1
  • 33
  • 46