How to implement voice call in Android?

Adding voice calling capabilities allows Android apps to provide an enhanced user experience by enabling real-time voice communication directly within the app interface. This eliminates the need for users to switch out of the app to make calls. Implementing in-app calling can lead to increased user engagement, longer session times, and reduced friction in the onboarding process.

There are several ways to implement voice calling in an Android app. The app can initiate calls by launching the default dialer with an intent. For full control over the calling experience, the app can make calls programmatically using the Android framework APIs. The app needs to request the necessary audio permissions and implement components to detect incoming and outgoing calls.

This guide will provide an overview of the main steps involved in adding voice calling capabilities to an Android app. It will cover the permissions, intents, APIs, and broadcast receivers required to place calls, detect call state changes, and display call details.

Permissions Needed

To enable voice calling capabilities in an Android app, the app must request the REQUEST_CALL_PHONE permission. This allows the app to initiate phone calls without going through the default phone dialer app. Without this permission, the app will not be able to directly make phone calls.

To request this permission in your AndroidManifest.xml file, add the following line inside the manifest tags:

<uses-permission android:name="android.permission.CALL_PHONE" />

You can also request permissions at runtime. Here is an example using the checkSelfPermission() method:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {

  // Permission not yet granted, request it
  ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL_PERMISSION);

}

Then handle the user’s permission response in onRequestPermissionsResult(). Users may deny the permission, so your app should handle this gracefully and disable calling features if needed.

For more details on requesting permissions, see the Android developer documentation on Requesting Permissions at Run Time.

Make a Call with Intent

The easiest way to initiate a phone call in Android is by using the Intent.ACTION_CALL action. This will directly open the dialer and call the provided phone number without requiring any additional user interaction. For example:


Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:123456789"));
startActivity(intent); 

As per the Android documentation, using Intent.ACTION_CALL requires the CALL_PHONE permission in your AndroidManifest.xml. This allows your app to directly call a phone number without the user’s intervention.

However, for security reasons, it’s recommended to use Intent.ACTION_DIAL instead. This will open the dialer with the number prefilled, but still allow the user to confirm placing the call.

Make a Call Programmatically

You can also make a phone call programmatically without using an intent. This can be achieved using the TelephonyManager class.

To make a call programmatically:

  1. Get an instance of TelephonyManager using getSystemService()
  2. Call TelephonyManager’s dial() method, passing the phone number to dial as a parameter

For example:

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 
tm.dial(phoneNumber);

This will directly dial the given phone number without any user intervention. However, your app needs to request the CALL_PHONE permission in the manifest.

Incoming Call Broadcast Receiver

To detect incoming calls in an Android app, we need to create a broadcast receiver that listens for the ACTION_PHONE_STATE_CHANGED intent. This intent is broadcast by the system whenever the phone state changes, such as when a call comes in.

Here is an example broadcast receiver to detect incoming calls:


public class IncomingCallReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {

    String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

    if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)){
      // Incoming call received
    }

  }

}

We check the state extra in the intent to see if it is TelephonyManager.EXTRA_STATE_RINGING. If so, we know there is an incoming call.

To register this receiver in our AndroidManifest.xml:


<receiver android:name=".IncomingCallReceiver">
  <intent-filter>
    <action android:name="android.intent.action.PHONE_STATE"/>
  </intent-filter>
</receiver>  

Now whenever there is an incoming call, our IncomingCallReceiver will be notified.

We can then implement logic to react accordingly, like updating the UI, creating a notification, or taking other actions.

Outgoing Call Broadcast Receiver

To detect when an outgoing call is made in Android, we need to create a broadcast receiver that listens for the ACTION_NEW_OUTGOING_CALL intent. This intent is broadcast by the system whenever the user initiates an outgoing call by dialing a number.

Here is an example broadcast receiver that logs the outgoing phone number:

<receiver android:name=".OutgoingCallReceiver" >
  <intent-filter>
    <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
  </intent-filter> 
</receiver>

And in OutgoingCallReceiver.java:

public class OutgoingCallReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
    Log.i("Outgoing Call", "Call to number: " + phoneNumber); 
  }

}  

This allows us to detect whenever the user makes an outgoing call from the device. Some key points:

  • Need to declare the NEW_OUTGOING_CALL receiver in AndroidManifest.xml
  • Get the dialed phone number from Intent.EXTRA_PHONE_NUMBER
  • Must only use this for informational/logging purposes, not blocking calls

For more details, see the Android documentation on monitoring outgoing calls at https://stackoverflow.com/questions/9569118/how-do-you-receive-outgoing-call-in-broadcastreceiver.

Detect Call State Changes

To detect call state changes in Android, we need to register a PhoneStateListener by calling TelephonyManager.listen(). The PhoneStateListener will receive callbacks when the call state changes, allowing us to monitor events like when a call is incoming, outgoing, disconnected etc.

Here is an example of registering a PhoneStateListener:


TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

PhoneStateListener callStateListener = new PhoneStateListener() {
  @Override
  public void onCallStateChanged(int state, String incomingNumber) {
    // Called when call state changes
  }
};

// Register the listener with the telephony manager
// Listen for changes to the device call state
tm.listen(callStateListener, PhoneStateListener.LISTEN_CALL_STATE);

The onCallStateChanged() callback will be invoked when the call state changes. We can check the state parameter to determine if it is an incoming, outgoing etc call. The incomingNumber provides the phone number for incoming calls.

This allows our app to monitor the phone call activities and respond accordingly. For example, we may want to mute music playback when a call is incoming.

See the PhoneStateListener documentation for more details.

Display Call Details

To display details about incoming and outgoing calls in your Android app, you need to retrieve the call log information. The Phone app stores call history including phone numbers, contact names (if available), call types, call durations and timestamps.

You can get access to this call log data by querying the CallLog provider using a ContentResolver. The CallLog class provides handy methods to retrieve this information like getCallType() to get the type of call (incoming, outgoing or missed).

To show the caller ID, you would display the phone number or contact name using CallLog.getNumber() or CallLog.getCachedName(). The call duration can be retrieved with CallLog.getDuration() which returns the duration in seconds. Format this as desired, like showing mm:ss. The timestamp of the call is available via CallLog.getDate() which you can format and display as needed.

So by querying the call log content provider, you can easily show useful call details like name, number, call type, duration and date/time in your app’s UI.

Add Call Log Feature

To add a call log feature that displays call history, we need to query the call log database on the Android device. This can be done by using the CallLog ContentProvider. First, we need to request the READ_CALL_LOG permission in our AndroidManifest.xml file:


<uses-permission android:name="android.permission.READ_CALL_LOG"/>

Then in our activity, we can query the call log database like this:


Cursor cursor = getContentResolver().query(CallLog.Calls.CONTENT_URI, null, null, null, null);

This will return a Cursor with all of the call log entries. We can then loop through the cursor to get details like the phone number, duration, type (incoming, outgoing, missed) and date for each call. To display a call history log, we would render each call in the cursor to a ListView or RecyclerView.

Here is some sample code to print the call details:


while (cursor.moveToNext()) {

String number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));

int type = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE));

String callType = (type == CallLog.Calls.OUTGOING_TYPE) ? "Outgoing" :
(type == CallLog.Calls.INCOMING_TYPE) ? "Incoming" : "Missed";

String duration = cursor.getString(cursor.getColumnIndex(CallLog.Calls.DURATION));

long dateLong = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE));
Date date = new Date(dateLong);

SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");

System.out.println("Number: " + number + ", Call Type: " + callType +
", Duration: " + duration + ", Date: " + dateFormat.format(date));

}

cursor.close();

This allows us to easily build a call log feature in an Android app!

Conclusion

In this post, we covered the essential elements needed to implement voice calling in an Android application. We explored the permissions required, making calls with intents or programmatically, handling incoming and outgoing call broadcasts, detecting call state changes, displaying call details, and adding a call log.

Some additional features that could be implemented include integrating contacts, adding video calling, or implementing VoIP calling. With the core components covered here, you should have a solid foundation for building a fully-featured calling app on Android. The capabilities of the Android telephony APIs allow for a wide range of possibilities.

In summary, Android provides powerful tools to integrate voice calling into apps. By requesting the right permissions, monitoring call states, and leveraging intents or programmatic calls, voice features can be added to engage users and provide a more complete experience. With the knowledge from this guide, you are now equipped to start building the next great Android calling app.

Leave a Reply

Your email address will not be published. Required fields are marked *