Affiliate links on Android Authority may earn us a commission. Learn more.
App development: How to make calls, send SMS, and retrieve the user's contacts
With smartphones getting more advanced all the time, it’s sometimes easy to forget that they’re designed for making calls and sending messages, and everything else is just an added extra.
Advanced features like Augmented and Virtual Reality may be grabbing all the headlines, but making calls and sending SMS are some of the most versatile features that you can add to your Android applications. For example, if your app ever displays contact details, such as phone numbers for restaurants, takeaways, tourist attractions, or shops, then there’s a chance that users may actually want to use this information. By default, the user has to copy the telephone number from your application, switch to their smartphone’s built-in dialer, paste this information into the dialer, and then confirm that they want to make the call. This is a clunky, multi-step process – wouldn’t it be easier if the user could initiate the call, simply by tapping the phone number in your application?
In this article, we’ll be creating an application that lets users initiate calls and send text messages, directly from your application’s UI. This functionality is perfect for numbers that are already displayed in your app, or numbers that the user has memorized, but what about numbers that don’t fall into either of those categories? If you scroll through your smartphone’s phonebook, then chances are there’s very few numbers that you’ve actually memorized!
Smartphones have made getting in touch with someone as simple as tapping their name in your phone’s Contacts application, and for the best user experience your app should provide similar functionality.
To make it easy to place calls and send SMS messages to any phone number, I’ll also be giving our app the ability to retrieve and display the complete list of names and numbers stored in the user’s device.
Making the call
Let’s start by looking at how you’d initiate a call from your application’s UI. While the call is still handled by the phone’s default dialer, the user can place this call simply by tapping a button, and without having to manually navigate any screens in the device’s dialer.
Create a new project using the ‘Empty Activity’ template, and build a simple layout where the user can enter a phone number into an EditText, and then dial that number at the tap of a button:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.jessicathornsby.telephonesms.MainActivity">
<EditText
android:id="@+id/phoneNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter phone number"
//Specify the input type, which changes the default keyboard//
android:inputType="phone" />
<Button
android:id="@+id/call"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="call"
android:text="Call" />
</LinearLayout>
Getting access to the dialer
There’s two ways that an application can make a phone call:
- The ACTION_CALL intent. This launches the device’s built-in dialer and automatically dials the number provided. This requires your app to have the CALL_PHONE permission.
- The ACTION_DIAL intent. This launches the device’s built-in dialer with the number already loaded, but the user must confirm whether they want to go ahead with the call. The ACTION_DIAL intent doesn’t require any special permissions.
I’m going to use the ACTION_CALL intent, so you’ll need to add the following to your Manifest:
<uses-permission android:name="android.permission.CALL_PHONE" />
An app can only place calls and send SMS messages if the “telephony” hardware is present on the device. Whenever your app has features that rely on certain hardware or software, you need to consider the kind of experience your app would provide on a device that’s missing this hardware or software. Our app is just an EditText field and a ‘Call’ button, so it has nothing to offer a device that doesn’t feature telephony hardware, such as a tablet.
It’s always better to prevent your app from being installed on a device where it would provide a poor user experience, rather than risk someone downloading your app to an incompatible device, having a bad experience, and then leaving you a negative review on Google Play as a result.
If your app can’t provide a good user experience without telephony hardware, then you should mark this hardware as a requirement, by adding a <uses-feature> declaration to your Manifest, and setting it to “true.”
<uses-feature android:name="android.hardware.telephony" android:required="true" />
You should also declare any hardware and software that your app doesn’t rely on, but will use where available. For example, if our app still had plenty to offer on devices that can’t make calls or send SMS messagess, then you’d use the following instead:
<uses-feature android:name="android.hardware.telephony" android:required="false" />
When you upload your app, Google Play checks your app’s permissions for any implicit hardware or software related features that you haven’t declared in your Manifest. If it discovers any, then it’ll assume your app does require this hardware or software, and will prevent your app from being installed on devices that don’t meet these requirements.
To ensure this behavior doesn’t prevent your app from being installed on devices that it’s perfectly compatible with, you should get into the habit of declaring every hardware and software feature that your app uses, and then tag them as “true” or “false,” as appropriate.
Create an ACTION_CALL intent
In our project’s MainActivity, we need to perform the following:
- Issue the permission request. In Android 6.0 and higher you need to request permissions at runtime, so our app needs to check whether it has the CALL_PHONE permission, and then request it if necessary. Whenever the user denies any permission, it’s best practice to disable all the features that rely on this permission, but this is especially important for any app that uses the ACTION_CALL action. Attempting to perform an ACTION_CALL without the CALL_PHONE permission will result in a SecurityException, so as soon as the user denies CALL_PHONE, I’m going to disable the app’s ‘Call’ button.
- Create a Intent.ACTION_CALL intent. This will initiate the call to the number retrieved by getData().
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Build;
import android.widget.Button;
import android.support.v4.content.ContextCompat;
import android.widget.EditText;
import android.content.Intent;
import android.util.Log;
import android.Manifest;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.widget.Toast;
import android.net.Uri;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 1;
Button callButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= 23) {
if (checkPermission()) {
Log.e("permission", "Permission already granted.");
} else {
//If the app doesn’t have the CALL_PHONE permission, request it//
requestPermission();
}
}
}
public boolean checkPermission() {
int CallPermissionResult = ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CALL_PHONE);
return CallPermissionResult == PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
ActivityCompat.requestPermissions(MainActivity.this, new String[]
{
Manifest.permission.CALL_PHONE
}, PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
callButton = (Button)findViewById(R.id.call);
if (grantResults.length > 0) {
boolean CallPermission = grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (CallPermission ) {
Toast.makeText(MainActivity.this,
"Permission accepted", Toast.LENGTH_LONG).show();
//If the permission is denied….//
} else {
Toast.makeText(MainActivity.this,
//...display the following toast...//
"Permission denied", Toast.LENGTH_LONG).show();
//...and disable the call button.//
callButton.setEnabled(false);
}
break;
}
}
}
public void call(View view)
{
final EditText phoneNumber = (EditText) findViewById(R.id.phoneNumber);
String phoneNum = phoneNumber.getText().toString();
if(!TextUtils.isEmpty(phoneNum)) {
String dial = "tel:" + phoneNum;
//Make an Intent object of type intent.ACTION_CALL//
startActivity(new Intent(Intent.ACTION_CALL,
//Extract the telephone number from the URI//
Uri.parse(dial)));
}else {
Toast.makeText(MainActivity.this, "Please enter a valid telephone number", Toast.LENGTH_SHORT).show();
}
}
}
Install this application on a physical smartphone or tablet, or an Android Virtual Device (AVD). As soon as the app launches, it should request permission to make calls. If you grant this permission request, then you’ll be able to initiate calls by entering any valid telephone number into the EditText, and then tapping ‘Call.’
If you deny the permission request, then the button will be disabled.
Retrieving the contacts list
At this point, our application has one major drawback: the user needs to have memorized the number they want to dial. This isn’t always going to be the case, so let’s give our app the ability to retrieve and display all the phone numbers the user has stored on their device.
Firstly, reading the user’s contact data requires our app to have the READ_CONTACTS permission, so let’s add this to our Manifest:
<uses-permission android:name="android.permission.READ_CONTACTS" />
I’m going to display this information as a scrollable list that the user can access by tapping a ‘Load Contacts’ button. Open the main_activity.xml file and add this button, plus a ListView that’ll eventually display all the contacts information:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.jessicathornsby.telephonesms.MainActivity">
<EditText
android:id="@+id/phoneNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter phone number"
android:inputType="phone" />
<Button
android:id="@+id/call"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="call"
android:text="Call" />
//Add a ‘Load Contacts’ button//
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/contacts"
android:text="Load contacts" />
//Add a ListView//
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Next, we need to create an XML layout that defines the structure for each line in our ListView. This layout can be elaborate, but I’m keeping things simple by including a single TextView and specifying the text size:
- Select ‘New > Layout resource file’ from the Android Studio toolbar.
- Name this file ‘contact_listview.xml,’ and then click ‘OK.’
- Open this file, and then add the following:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Name: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView"
android:textSize="20dp" />
</RelativeLayout>
In our MainActivity, we need to perform the following:
- Request the READ_CONTACTS permission and process the response.
- Initialize an ArrayAdapter. A ListView’s data is populated using an Adapter, which acts as a bridge between the data source and the ListView.
- Bind our ListView to an Adapter. We need to set the Adapter by calling the setAdapter() method on the ListView, and then passing the Adapter. Once the Adapter is attached, it’ll pull content from the source and convert each item into a View, which is then inserted into the ListView container.
Android provides a ContactsContracts class, which defines a database of contact-related information, stored in a ContactsContract.Data table. We’ll use ContactsContracts to access the Contacts Provider without having to define our own constants.
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.os.Bundle;
import android.os.Build;
import android.widget.Button;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.v4.content.ContextCompat;
import android.widget.EditText;
import android.content.Intent;
import android.widget.ListView;
import android.util.Log;
import android.Manifest;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.widget.Toast;
import android.net.Uri;
import android.view.View;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 1;
ListView listView ;
ArrayList<String> contactsArray ;
ArrayAdapter<String> arrayAdapter ;
Button contactsButton;
Button callButton;
Cursor cursor ;
String name, contactNumber ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= 23) {
if (checkPermission()) {
Log.e("permission", "Permission already granted.");
} else {
requestPermission();
}
}
listView = (ListView)findViewById(R.id.listview);
contactsArray = new ArrayList<String>();
contactsButton = (Button)findViewById(R.id.contacts);
contactsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AddContactstoArray();
//Initialize ArrayAdapter and declare that we’re converting Strings into Views//
arrayAdapter = new ArrayAdapter<String>(
MainActivity.this,
//Specify the XML file where you’ve defined the layout for each item//
R.layout.contact_listview, R.id.textView,
//The array of data//
contactsArray
);
//Set the Adapter to the ListView, using setAdapter()//
listView.setAdapter(arrayAdapter);
}
});
}
public void AddContactstoArray(){
//Query the phone number table using the URI stored in CONTENT_URI//
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null, null, null);
while (cursor.moveToNext()) {
//Get the display name for each contact//
name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
//Get the phone number for each contact//
contactNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
//Add each display name and phone number to the Array//
contactsArray.add(name + " " + ":" + " " + contactNumber);
}
cursor.close();
}
public boolean checkPermission() {
int CallPermissionResult = ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CALL_PHONE);
int ContactPermissionResult = ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_CONTACTS);
return CallPermissionResult == PackageManager.PERMISSION_GRANTED &&
ContactPermissionResult == PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
ActivityCompat.requestPermissions(MainActivity.this, new String[]
{
Manifest.permission.READ_CONTACTS,
Manifest.permission.CALL_PHONE
}, PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
callButton = (Button)findViewById(R.id.call);
if (grantResults.length > 0) {
boolean CallPermission = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean ReadContactsPermission = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (CallPermission && ReadContactsPermission) {
Toast.makeText(MainActivity.this,
"Permission accepted", Toast.LENGTH_LONG).show();
//If permission is denied...//
} else {
Toast.makeText(MainActivity.this,
"Permission denied", Toast.LENGTH_LONG).show();
//....disable the Call and Contacts buttons//
callButton.setEnabled(false);
contactsButton.setEnabled(false);
}
break;
}
}
}
public void call(View view)
{
final EditText phoneNumber = (EditText) findViewById(R.id.phoneNumber);
String phoneNum = phoneNumber.getText().toString();
if(!TextUtils.isEmpty(phoneNum)) {
String dial = "tel:" + phoneNum;
startActivity(new Intent(Intent.ACTION_CALL, Uri.parse(dial)));
}else {
Toast.makeText(MainActivity.this, "Please enter a valid telephone number", Toast.LENGTH_SHORT).show();
}
}
}
Install this project on your Android device. Now, when the app launches it’ll request permission to access your contacts and make calls. If you grant these permissions, then you’ll be able to view all your contacts, by giving the app’s ‘Load Contacts’ button a tap.
If you deny these permission requests, then the ‘Call’ and ‘Load Contacts’ buttons will be disabled.
Sending text messages
In this final section, I’ll show you how to send SMS messages directly from your app, using Android’s SmsManager class.
Text messaging can be a handy way of encouraging users to invite friends and family to sign up for your application, for example you could generate bonuses or discount codes that can then be shared over SMS. Messaging is also a convenient way of sharing information that the user has discovered in your app, or for sending notifications, for example if you’ve developed a calendar app then your users might appreciate the ability to send event reminders over SMS.
I’m going to implement this functionality in a separate SMSActivity, so let’s start by creating a new layout resource file:
- Select ‘File > New > Layout resource file’ from the Android Studio toolbar.
- Name this file ‘activity_sms.xml.’
- Open your new layout file, and then add the following:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.jessicathornsby.telephonesms.SMSActivity">
<EditText
android:id="@+id/phoneNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter phone number"
android:inputType="phone" />
<EditText
android:id="@+id/message"
android:hint="Message"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/sendSMS"
android:text="Send SMS"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
This is a simple UI where the user can send SMS messages by entering any valid mobile number, some message text, and then giving the ‘Send SMS’ button a tap.
Our ‘activity_sms.xml’ needs a corresponding Java class, so select ‘File > New > Java class’ from the Android Studio toolbar, and name this class ‘SMSActivity.’
Thanks to the SmsManager, the process of sending the message is fairly straightforward: you simply need to use getDefault() to retrieve the SmsManager, and then pass the phone number and the message to the sendTextMessage method.
We also need to check whether our app currently has access to the SEND_SMS permission and then request it, if necessary. Once again, if the user denies the permission request, then we should disable all related functionality, which in this instance means disabling the ‘Send SMS’ button.
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Build;
import android.widget.Button;
import android.support.v4.content.ContextCompat;
import android.widget.EditText;
import android.util.Log;
import android.Manifest;
import android.content.pm.PackageManager;
import android.telephony.SmsManager;
import android.text.TextUtils;
import android.widget.Toast;
import android.view.View;
public class SMSActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 1;
private Button sendSMS;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sms);
final EditText phoneNumber = (EditText) findViewById(R.id.phoneNumber);
if (Build.VERSION.SDK_INT >= 23) {
if (checkPermission()) {
Log.e("permission", "Permission already granted.");
} else {
requestPermission();
}
}
final EditText smsText = (EditText) findViewById(R.id.message);
sendSMS = (Button) findViewById(R.id.sendSMS);
sendSMS.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String sms = smsText.getText().toString();
String phoneNum = phoneNumber.getText().toString();
if(!TextUtils.isEmpty(sms) && !TextUtils.isEmpty(phoneNum)) {
if(checkPermission()) {
//Get the default SmsManager//
SmsManager smsManager = SmsManager.getDefault();
//Send the SMS//
smsManager.sendTextMessage(phoneNum, null, sms, null, null);
}else {
Toast.makeText(SMSActivity.this, "Permission denied", Toast.LENGTH_SHORT).show();
}
}
}
});
}
private boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(SMSActivity.this, Manifest.permission.SEND_SMS);
if (result == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
}
private void requestPermission() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.SEND_SMS}, PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(SMSActivity.this,
"Permission accepted", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(SMSActivity.this,
"Permission denied", Toast.LENGTH_LONG).show();
Button sendSMS = (Button) findViewById(R.id.sendSMS);
sendSMS.setEnabled(false);
}
break;
}
}
}
Next, open your project’s Manifest and add the SEND_SMS permission. We also need to declare the SMSActivity component:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jessicathornsby.telephonesms">
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-feature android:name="android.hardware.telephony" android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SMSActivity">
</activity>
</application>
</manifest>
The last task is ensuring the user can reach SMSActivity from the app’s MainActivity. Open your project’s activity_main.xml file and add a ‘launchSMS’ button:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.jessicathornsby.telephonesms.MainActivity">
<EditText
android:id="@+id/phoneNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter phone number"
android:inputType="phone" />
<Button
android:id="@+id/call"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="call"
android:text="Call" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/contacts"
android:text="Load contacts" />
//Add the following//
<Button
android:id="@+id/launchSMS"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:onClick="launchSMS"
android:text="Launch SMS Activity" />
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Finally, implement the onClick in MainActivity:
public void launchSMS(View view)
{
Intent intent = new Intent(MainActivity.this, SMSActivity.class);
startActivity(intent);
}
Take this project for a spin by installing it on your Android device. As soon as you navigate to SMSActivity, the app should request permission to view and send text messages. If you grant this permission request then you’ll be able to send SMS messages directly from the application’s UI.
In addition, you can monitor the status of sent messages using the following parameters:
- sentIntent. Watch for Activity.RESULT_OK, which will be returned if the message has been sent successfully, and is therefore queued for delivery. If the message fails, then you’ll receive one of the following result codes instead: RESULT_ERROR_GENERIC_FAILURE, RESULT_ERROR_RADIO_OFF, or RESULT_ERROR_NULL_PDU.
- deliveryIntent. This PendingIntent is broadcast after the recipient receives your SMS. Note that DeliveryIntent is dependent on the carrier providing a DeliveryReport.
For example:
void sendTextMessage (String destinationAddress,
String scAddress,
String text,
PendingIntent sentIntent,
PendingIntent deliveryIntent)
Wrapping up
In this article, we looked at how to initiate calls and send SMS messages directly from your application’s UI, and how to retrieve the complete list of contacts stored on the user’s device.
While they’re easy to overlook in favour of some of Android’s newer, more cutting-edge functionality, sending SMS and making calls are some of the most versatile features you can add to your Android apps. And, unlike newer features, you can be confident that pretty much every Android smartphone has full support for making calls and sending messages!
What are some of the “go-to” features that you regularly add to your Android apps? Let us know in the comments below!