Статьи

Как звонить и использовать SMS в приложениях для Android

В этом руководстве вы узнаете об Android-телефонии и SMS-API. Вы узнаете, как совершать вызовы из своего приложения и как отслеживать события телефонных звонков, а также как отправлять и получать SMS.

Для начала я покажу вам, как инициировать вызов из вашего приложения, используя приложение для набора номера телефона или непосредственно из вашего приложения, чтобы упростить его для ваших пользователей.

MainActivity Android Studio и создайте новый проект с пустым действием MainActivity .

Android Studio создает новый скриншот Activity

На данный момент наш макет будет просто иметь поле 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>
Экран набора номера эмулятора с указанным номером телефона

В приведенном ниже блоке кода мы создаем намерение 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 .

Эмулятор экрана по умолчанию, номеронабиратель телефона с номером телефона

В этом разделе мы узнаем, как отслеживать события телефонных звонков в системе Android. Телефон может быть в трех состояниях:

  1. в режиме ожидания (когда он не используется)
  2. звонит (при входящем звонке)
  3. снять трубку (при ответе на звонок)

Нам нужно разрешение READ_PHONE_STATE чтобы иметь возможность отслеживать состояние телефона. Добавьте его в AndroidManifest.xml:

1
<uses-permission android:name=»android.permission.READ_PHONE_STATE»/>

Мы создаем объект класса 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-сообщения, но вам нужно будет немного настроить. Откройте эмулятор, нажмите последнюю кнопку на правой панели навигации, чтобы открыть расширенное диалоговое окно управления, а затем выберите кнопку управления телефоном.

Использование эмулятора для звонка и отправки смс

Как и в предыдущем разделе, нам нужно создать прослушиватель событий для отслеживания изменений состояния телефона. Основное отличие состоит в том, что на этот раз мы расширим базовый класс 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;
        }
    }
}

Приемник вещания работает, только если он зарегистрирован. Нам нужно сообщить системе 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 +:

  • Android SDK
    Android с нуля: понимание трансляций Android
    Ашраф Хатхибелагал

У вас есть только два основных варианта отправки 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»/>
<!—/ … /—>

Внутри нашего 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 пользователя или предоставит пользователю возможность выбрать приложение, если оно еще не было выбрано.

Отправить поток SMS

Далее давайте посмотрим, как отправлять смс прямо из нашего приложения, а не с помощью устройства смс-клиента.

Как обычно, нам нужно зарегистрировать разрешение в AndroidManifest.xml .

1
<uses-permission android:name=»android.permission.SEND_SMS»/>

Далее, для Android 6.0 (уровень API 23) и выше, нам нужно запросить разрешение SEND_SMS во время выполнения.

Чтобы узнать больше о разрешениях для Android во время выполнения и о том, как они изменились в версии 6.0, ознакомьтесь с нашим руководством по Envato Tuts +:

  • Android M
    Понимание разрешений в Android M
    Пол Требилкокс-Руис

Чтобы отправить 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 чтобы указать на ошибку.

Чтобы ваше приложение начало получать 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-сообщений с помощью приложения для обмена сообщениями на устройстве или непосредственно из собственного приложения
  • получение SMS-сообщений в фоновом режиме

В Android можно сделать гораздо больше с помощью телефонных звонков и SMS-сообщений. Посетите API телефонии Android и документацию API SMSManager, чтобы узнать больше.

А пока посмотрите другие наши посты о разработке для Android!

  • Датчики Android в глубине: приближение и гироскоп

  • 6 Что нужно и чего не нужно для отличного пользовательского опыта на Android

  • Фоновое аудио в Android с MediaSessionCompat

  • Перенос приложения Android на дизайн материалов