В этом руководстве вы узнаете об Android-телефонии и SMS-API. Вы узнаете, как совершать вызовы из своего приложения и как отслеживать события телефонных звонков, а также как отправлять и получать SMS.
1. Как позвонить
Для начала я покажу вам, как инициировать вызов из вашего приложения, используя приложение для набора номера телефона или непосредственно из вашего приложения, чтобы упростить его для ваших пользователей.
Создать новый проект Android Studio
MainActivity
Android Studio и создайте новый проект с пустым действием MainActivity
.
Выложить экран
На данный момент наш макет будет просто иметь поле EditText
и кнопку набора номера :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<?xml version=»1.0″ encoding=»utf-8″?>
<LinearLayout
xmlns:android=»https://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»
android:paddingLeft=»@dimen/activity_horizontal_margin»
android:paddingRight=»@dimen/activity_horizontal_margin»
android:paddingTop=»@dimen/activity_vertical_margin»
android:paddingBottom=»@dimen/activity_vertical_margin»
android:gravity=»center_horizontal|center_vertical»
tools:context=»com.chikeandroid.tutsplust_telephony.MainActivity»>
<EditText
android:id=»@+id/et_phone_no»
android:hint=»Enter Phone number»
android:inputType=»phone»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»/>
<Button
android:id=»@+id/btn_dial»
android:layout_gravity=»center_horizontal»
android:text=»Dial»
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»/>
</LinearLayout>
|
Изменить класс MainActivity
В приведенном ниже блоке кода мы создаем намерение ACTION_DIAL
для отображения номера телефона. Номер телефона анализируется по нашей схеме tel
URI: tel:XXXXXXXX
. Обратите внимание, что вам не нужно никакого разрешения для этой работы:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mDialButton = (Button) findViewById(R.id.btn_dial);
final EditText mPhoneNoEt = (EditText) findViewById(R.id.et_phone_no);
mDialButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String phoneNo = mPhoneNoEt.getText().toString();
if(!TextUtils.isEmpty(phoneNo)) {
String dial = «tel:» + phoneNo;
startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(dial)));
}else {
Toast.makeText(MainActivity.this, «Enter a phone number», Toast.LENGTH_SHORT).show();
}
}
});
}
}
|
Если вы запустите приложение и нажмете кнопку набора номера, вы попадете в приложение для набора номера, и оттуда вам придется фактически набрать номер. Вы можете изменить этот поток, чтобы фактически выполнять вызов из вашего приложения, просто изменив намерение ACTION_CALL
на ACTION_CALL
. Для этого потребуется разрешение android.permission.CALL_PHONE
.
2. Мониторинг событий телефонного звонка
В этом разделе мы узнаем, как отслеживать события телефонных звонков в системе Android. Телефон может быть в трех состояниях:
- в режиме ожидания (когда он не используется)
- звонит (при входящем звонке)
- снять трубку (при ответе на звонок)
Добавить разрешение
Нам нужно разрешение READ_PHONE_STATE
чтобы иметь возможность отслеживать состояние телефона. Добавьте его в AndroidManifest.xml:
1
|
<uses-permission android:name=»android.permission.READ_PHONE_STATE»/>
|
Создайте объект PhoneStateListener
Мы создаем объект класса PhoneStateListener
, а затем переопределяем его onCallStateChanged()
(в IntelliJ это легко сделать с помощью Control-O , а затем выбираем или ищем метод для переопределения). Мы будем обрабатывать изменения в состоянии вызова, отображая Toast
. Обратите внимание, что мы также можем получить доступ к номерам входящих телефонов при запуске этого метода:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
// …
PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
Toast.makeText(MainActivity.this, «CALL_STATE_IDLE», Toast.LENGTH_SHORT).show();
break;
case TelephonyManager.CALL_STATE_RINGING:
Toast.makeText(MainActivity.this, «CALL_STATE_RINGING», Toast.LENGTH_SHORT).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Toast.makeText(MainActivity.this, «CALL_STATE_OFFHOOK», Toast.LENGTH_SHORT).show();
break;
}
}
};
// …
|
В зависимости от потребностей вашего приложения вы также можете переопределить один из следующих методов событий: onCellInfoChanged()
, onCallForwardingIndicatorChanged()
, onCellLocationChanged()
или onSignalStrengthChanged()
.
Прослушивание состояния телефонного звонка
Чтобы начать прослушивание состояния телефонного звонка, нам нужно получить TelephonyManager
из системной службы и инициализировать его в onCreate()
.
1
2
3
4
5
6
7
|
// …
private TelephonyManager mTelephonyManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
// …
mTelephonyManager = (TelephonyManager) getSystemService(getApplicationContext().TELEPHONY_SERVICE);
}
|
В onResume()
мы можем начать прослушивание, используя метод listen()
TelephonyManager
, передав ему экземпляр PhoneStateListener
и статический LISTEN_CALL_STATE
. Мы прекращаем прослушивание в onStop()
, передавая LISTEN_NONE
в качестве второго аргумента для listen()
.
01
02
03
04
05
06
07
08
09
10
11
12
|
// …
@Override
protected void onResume() {
super.onResume();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
@Override
protected void onStop() {
super.onStop();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}
// …
|
Другие возможные варианты прослушивания телефона: LISTEN_CELL_LOCATION
, LISTEN_SIGNAL_STRENGTH
, LISTEN_CALL_FORWARDING_INDICATOR
и LISTEN_CELL_INFO
.
Наконец, запустите приложение и убедитесь, что входящий звонок поступает.
Этот мониторинг будет работать только тогда, когда приложение находится на переднем плане. Чтобы это работало в фоновом режиме (когда наше приложение не запущено), нам нужно было бы создать BroadcastReceiver
чтобы даже если приложение не работало, мы могли отслеживать состояние телефонных звонков. В зависимости от требований вашего приложения это может быть гораздо лучшим способом прослушивания изменений состояния телефонных звонков. Я покажу вам, как это сделать, в следующем разделе.
Помните, что мы отслеживаем только входящие звонки. Для мониторинга исходящих звонков нам нужны дополнительные разрешения. Чтобы отслеживать исходящие звонки, включите следующую строку в файл AndroidManifest.xml .
1
|
<uses-permission android:name=»android.permission.PROCESS_OUTGOING_CALLS»/>
|
Как использовать эмулятор для звонков и отправки SMS-сообщений
Вы можете использовать свой эмулятор для имитации звонка или отправки SMS-сообщения, но вам нужно будет немного настроить. Откройте эмулятор, нажмите последнюю кнопку на правой панели навигации, чтобы открыть расширенное диалоговое окно управления, а затем выберите кнопку управления телефоном.
3. Мониторинг телефонных звонков в фоновом режиме
Создать BroadcastReceiver
Как и в предыдущем разделе, нам нужно создать прослушиватель событий для отслеживания изменений состояния телефона. Основное отличие состоит в том, что на этот раз мы расширим базовый класс BroadcastReceiver
чтобы мы могли прослушивать состояние телефонного звонка, даже если приложение не запущено. Не регистрируйте слушателя более одного раза! Наш чек на это находится на линии 36.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.Toast;
public class PhoneCallStateReceiver extends BroadcastReceiver {
private TelephonyManager mTelephonyManager;
public static boolean isListening = false;
@Override
public void onReceive(final Context context, Intent intent) {
mTelephonyManager = (TelephonyManager) context.getSystemService(context.TELEPHONY_SERVICE);
PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
Toast.makeText(context, «CALL_STATE_IDLE», Toast.LENGTH_SHORT).show();
break;
case TelephonyManager.CALL_STATE_RINGING:
Toast.makeText(context, «CALL_STATE_RINGING», Toast.LENGTH_SHORT).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Toast.makeText(context, «CALL_STATE_OFFHOOK», Toast.LENGTH_SHORT).show();
break;
}
}
};
if(!isListening) {
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
isListening = true;
}
}
}
|
Изменить AndroidManifest.xml
Приемник вещания работает, только если он зарегистрирован. Нам нужно сообщить системе Android о нашем широковещательном приемнике, зарегистрировав его в AndroidManifest. XML- файл, подключив наш класс PhoneCallStateReceiver
к <intent-filter>
который описывает системную трансляцию, которую мы хотим получить — в данном случае PHONE_STATE
.
1
2
3
4
5
|
<receiver android:name=».PhoneCallStateReceiver»>
<intent-filter>
<action android:name=»android.intent.action.PHONE_STATE»/>
</intent-filter>
</receiver>
|
Мониторинг исходящих звонков
Для исходящих вызовов вам необходимо включить NEW_OUTGOING_CALL
действия NEW_OUTGOING_CALL
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
в <intent-filter>
получателя в AndroidManifest. XML
Чтобы получить номер телефона предполагаемого исходящего вызова, внутри onReceive(Context, Intent)
мы получаем номер из намерения в качестве дополнительного. Чтобы предотвратить этот намеченный вызов, мы можем вызвать setResultData()
и передать ему нулевой аргумент. resultData
используется в качестве фактического номера для вызова.
1
2
3
4
5
6
7
|
@Override
public void onReceive(final Context context, Intent intent) {
// for outgoing call
String outgoingPhoneNo = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER).toString();
// prevent outgoing call
setResultData(null);
}
|
Вы можете узнать больше о трансляциях и приемниках трансляций в нашем уроке здесь на Envato Tuts +:
4. Отправка SMS-сообщений
У вас есть только два основных варианта отправки SMS: с помощью клиентского приложения SMS на устройстве или пропуска клиента, отправив SMS непосредственно из вашего приложения. Мы рассмотрим оба сценария, и вы сможете решить, какой из них лучше для вашего варианта использования. Начнем с отправки SMS с помощью устройства SMS-клиент.
Настроить макет
Во-первых, нам нужно изменить наш основной макет, чтобы иметь поле EditText
для сообщения и кнопку « Отправить сообщение» .
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<!—/ … /—>
<EditText
android:id=»@+id/et_message»
android:hint=»Enter message»
android:inputType=»textCapSentences|textMultiLine»
android:maxLength=»2000″
android:maxLines=»12″
android:layout_width=»match_parent»
android:layout_height=»wrap_content»/>
<Button
android:id=»@+id/btn_send_message»
android:layout_gravity=»center_horizontal»
android:text=»Send Messange»
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»/>
<!—/ … /—>
|
Изменить MainActivity
Внутри нашего onCreate()
в нашем классе MainActivity
создайте намерение с ACTION_SENDTO
в качестве первого аргумента и smsto:<phone number>
URI в качестве второго аргумента. Текстовое сообщение будет значением дополнительного sms_body
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
// …
Button sendMessageBtn = (Button) findViewById(R.id.btn_send_message);
final EditText messagetEt = (EditText) findViewById(R.id.et_message);
sendMessageBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String message = messagetEt.getText().toString();
String phoneNo = mPhoneNoEt.getText().toString();
if(!TextUtils.isEmpty(message) && !TextUtils.isEmpty(phoneNo)) {
Intent smsIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse(«smsto:» + phoneNo));
smsIntent.putExtra(«sms_body», message);
startActivity(smsIntent);
}
}
});
// …
|
Здесь клиент SMS будет следить за состоянием доставки сообщения.
Запустите приложение
Когда все обязательные поля заполнены, нажатие кнопки « Отправить SMS» откроет клиент SMS пользователя или предоставит пользователю возможность выбрать приложение, если оно еще не было выбрано.
5. Отправка SMS-сообщений напрямую
Далее давайте посмотрим, как отправлять смс прямо из нашего приложения, а не с помощью устройства смс-клиента.
Добавить разрешение в AndroidManifest.xml
Как обычно, нам нужно зарегистрировать разрешение в AndroidManifest.xml .
1
|
<uses-permission android:name=»android.permission.SEND_SMS»/>
|
Изменить класс MainActivity
Далее, для Android 6.0 (уровень API 23) и выше, нам нужно запросить разрешение SEND_SMS
во время выполнения.
Чтобы узнать больше о разрешениях для Android во время выполнения и о том, как они изменились в версии 6.0, ознакомьтесь с нашим руководством по Envato Tuts +:
Чтобы отправить SMS, мы получаем экземпляр SmsManager
по умолчанию и затем вызываем его sendTextMessage()
, передавая номер телефона в качестве первого аргумента и сообщение в качестве второго аргумента:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
// …
final int SEND_SMS_PERMISSION_REQUEST_CODE = 111;
private Button mSendMessageBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
// …
mSendMessageBtn = (Button) findViewById(R.id.btn_send_message);
final EditText messagetEt = (EditText) findViewById(R.id.et_message);
mSendMessageBtn.setEnabled(false);
if(checkPermission(Manifest.permission.SEND_SMS)) {
mSendMessageBtn.setEnabled(true);
}else {
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.SEND_SMS},
SEND_SMS_PERMISSION_REQUEST_CODE);
}
mSendMessageBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String message = messagetEt.getText().toString();
String phoneNo = mPhoneNoEt.getText().toString();
if(!TextUtils.isEmpty(message) && !TextUtils.isEmpty(phoneNo)) {
if(checkPermission(Manifest.permission.SEND_SMS)) {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNo, null, message, null, null);
}else {
Toast.makeText(MainActivity.this, «Permission denied», Toast.LENGTH_SHORT).show();
}
}
}
});
}
private boolean checkPermission(String permission) {
int checkPermission = ContextCompat.checkSelfPermission(this, permission);
return (checkPermission == PackageManager.PERMISSION_GRANTED);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case SEND_SMS_PERMISSION_REQUEST_CODE: {
if(grantResults.length > 0 && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
mSendMessageBtn.setEnabled(true);
}
return;
}
}
}
// …
|
Для контроля за состоянием доставки метод SMSManager
sendTextMessage()
имеет два необязательных параметра sentIntent
: sentIntent
и deliveryIntent
.
1
2
3
4
5
|
void sendTextMessage (String destinationAddress,
String scAddress,
String text,
PendingIntent sentIntent,
PendingIntent deliveryIntent)
|
Если вы хотите использовать sentIntent
, следите за кодом результата Activity.RESULT_OK
в случае успеха или одним из RESULT_ERROR_GENERIC_FAILURE
, RESULT_ERROR_RADIO_OFF
и RESULT_ERROR_NULL_PDU
чтобы указать на ошибку.
6. Получение SMS-сообщения
Чтобы ваше приложение начало получать SMS-сообщения с телефона пользователя, лучше всего зарегистрировать получателя широковещательной рассылки, чтобы он мог получать оповещения о поступлении новых SMS-сообщений, даже если ваше приложение не работает на переднем плане.
Добавить разрешение
Добавьте разрешение RECEIVE_SMS
в AndroidManifest. XML :
1
|
<uses-permission android:name=»android.permission.RECEIVE_SMS»/>
|
Далее нам нужно проверить и посмотреть, есть ли у приложения разрешение на получение SMS-сообщений во время выполнения. Поэтому в классе MainActivity
проверьте разрешение RECEIVE_SMS
. Если он не найден, запросите его.
1
2
3
4
5
6
7
8
9
|
// …
@Override
protected void onCreate(Bundle savedInstanceState) {
// …
if(!checkPermission(Manifest.permission.RECEIVE_SMS)) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECEIVE_SMS}, 222);
}
}
// …
|
Создать широковещательный приемник
Мы SmsMessage
каждый объект класса SmsMessage
, используя метод createFromPdu(byte[] pdu)
, передавая ему PDU (протокольный блок данных). Затем мы добавляем его в наш массив сообщений.
Для поддержки API 23 и выше необходимо включить формат String extra («3gpp» для сообщений GSM / UMTS / LTE в формате 3GPP или «3gpp2» для сообщений CDMA / LTE в формате 3GPP2).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.widget.Toast;
public class SMSReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if(bundle != null) {
Object[] pdus = (Object[]) bundle.get(«pdus»);
String format = bundle.getString(«format»);
final SmsMessage[] messages = new SmsMessage[pdus.length];
for(int i = 0; i < pdus.length; i++) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
}else {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
String senderPhoneNo = messages[i].getDisplayOriginatingAddress();
Toast.makeText(context, «Message » + messages[0].getMessageBody() + «, from » + senderPhoneNo, Toast.LENGTH_SHORT).show();
}
}
}
}
|
Теперь запустите приложение, закройте его и отправьте эмулируемому телефону SMS.
Вывод
В этом уроке вы узнали о:
- звонить из вашего приложения
- мониторинг событий телефонного звонка
- отправка SMS-сообщений с помощью приложения для обмена сообщениями на устройстве или непосредственно из собственного приложения
- получение SMS-сообщений в фоновом режиме
В Android можно сделать гораздо больше с помощью телефонных звонков и SMS-сообщений. Посетите API телефонии Android и документацию API SMSManager, чтобы узнать больше.
А пока посмотрите другие наши посты о разработке для Android!