Мониторинг сообщений и звонков в Android

от автора

На разрабатываемом мною проекте под Android возникла необходимость мониторинга входящих и исходящих звонков и сообщений. Полез читать документацию и подручные книги и к сожалению понял, что задача не совсем из тривиальных, так как каждая часть одной проблемы требует различного подхода к реализации. Вообще заметил, что многие книги или же статьи всегда стараются подавать более легкие примеры, например обработка входящего сообщения, а вот про исходящее ни слова если реализация более сложна. Возможно это мое предубеждение, но уже не первый раз обращаю на это внимание
Решил собрать все воедино и поделиться с теми, кто возможно будет набивать себе оскомину пытаясь найти решение данной проблемы. На оптимальность не претендую, так как сам учусь

Мониторинг входящих сообщений

Наверное реализация этой части самая простая и легкая. В манифесте приложения даем разрешение на обработку получения сообщения

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

И регистрируем Receiver, который будет срабатывать по событию входящего сообщения

<receiver android:name="MessageReceiver" android:enabled="true"> 	<intent-filter> 		 <action android:name="android.provider.Telephony.SMS_RECEIVED"/> 	</intent-filter>  </receiver> 

Как видно из примера кода, при получении сообщения управление будет передаваться Receiver-у MessageReceiver.

Примерная реализация:

public class MessageReceiver extends BroadcastReceiver { 	public void onReceive(Context context, Intent intent) { 		Bundle bundle = intent.getExtras();         		if (bundle != null) { 			Object[] pdus = (Object[]) bundle.get("pdus"); 			SmsMessage[] msgs = new SmsMessage[pdus.length]; 			ArrayList<String> numbers = new ArrayList<String>(); 			ArrayList<String> messages = new ArrayList<String>(); 			for (int i=0; i<msgs.length; i++){ //пробегаемся по всем полученным сообщениям 				msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]); 				numbers.add(msgs[i].getOriginatingAddress()); //получаем номер отправителя 				messages.add(msgs[i].getMessageBody().toString());//получаем текст сообщения 			} 			if (messages.size() > 0){ 				//делаем что-то с сообщениями 			} 		}  	} } 

Мониторинг исходящих сообщений

К сожалению с исходящими сообщениями не так просто как со входящими. Telephony API не предоставляет отдельного события исходящего сообщения. Для меня это выглядит весьма странным в довольно отточенном продукте, которым является Android. Тем не менее решение все-таки есть.

В манифесте приложения даем разрешение на чтение сообщений

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

А далее следует создать и зарегистрировать обработчик изменения базы сообщений. Вприцнипе тут можно объеденить обработку входящих и исходящих сообщений, но как-то со входящими сообщениями все намного проще. При обработке исходящих сообщений следует учесть тот факт, что обработчик может быть вызван несколько раз при отправке одного и того-же сообщения. Рекомендуется создать таблицу хешей всех сообщений проверять нету ли обрабатываемого сообщения в базе. В коде ниже показана примитивная проверка по идентификатору сообщения

private static final String CONTENT_SMS = "content://sms/"; private static long id = 0; //регистрируем обработчик изменений контента сообщений ContentResolver contentResolver = getBaseContext().getContentResolver(); contentResolver.registerContentObserver(Uri.parse(CONTENT_SMS),true, new OutgoingSmsObserver(new Handler())); private class OutgoingSmsObserver extends ContentObserver { 	@Override 	public void onChange(boolean selfChange) { 		super.onChange(selfChange); 		Uri uriSMSURI = Uri.parse(CONTENT_SMS); 		Cursor cur = getContentResolver().query(uriSMSURI, null, null,null, null); 		cur.moveToNext(); 		String protocol = cur.getString(cur.getColumnIndex("protocol")); 		if(protocol == null){ 			long messageId = cur.getLong(cur.getColumnIndex("_id")); 			//проверяем не обрабатывали ли мы это сообщение только-что 			if (messageId != id){ 				id = messageId; 				int threadId = cur.getInt(cur.getColumnIndex("thread_id")); 				Cursor c = getContentResolver().query(Uri.parse("content://sms/outbox/" + threadId), null, null, null, null); c.moveToNext(); 				//получаем адрес получателя 				String address = cur.getString(cur.getColumnIndex("address")); 				//получаем текст сообщения 				String body= cur.getString(cur.getColumnIndex("body")); 				//делаем что-то с сообщением 			} 		} 	} }  

Мониторинг звонков

Android предоставляет возможность мониторить состояние телефона посредством действия android.intent.action.PHONE_STATE, но у меня снова таки возникла проблема получения номера абонента при исходящем звонке, поэтому мне пришлось регистрировать ресивер для двух действий.

В манифесте приложения даем разрешение на чтение состояния телефона и обработки исходящих звонков.

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

И регистрируем Receiver, который будет срабатывать по событию входящего сообщения

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

Примерная реализация:

String phoneNumber = "";  public class CallReceiver extends BroadcastReceiver { 	public void onReceive(Context context, Intent intent) { 		if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) { 			//получаем исходящий номер 			phoneNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER"); 		} else if (intent.getAction().equals("android.intent.action.PHONE_STATE")){ 			String phone_state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); 			if (phone_state.equals(TelephonyManager.EXTRA_STATE_RINGING)) { 				//телефон звонит, получаем входящий номер 				phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER); 			} else if (phone_state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){ 				//телефон находится в режиме звонка (набор номера / разговор) 			} else if (phone_state.equals(TelephonyManager.EXTRA_STATE_IDLE)){ 				//телефон находиться в ждущем режиме. Это событие наступает по окончанию разговора, когда мы уже знаем номер и факт звонка 			} 		} 	} } 

Резюмируя проблемы, которые я описал в самом начале, снова таки скажу, что меня не покидало ощущение некоторой недоделанности Android API. Входящее сообщение весьма легко перехватывается, что не скажешь о исходящем. Тоже самое касается и звонков. Конечно можно использовать одно решение и для сообщений, но если для входящих так просто, то почему нету такой простоты для исходящих?

ссылка на оригинал статьи http://habrahabr.ru/post/162181/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *