В этом руководстве вы узнаете об 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!





