Xamarin.Android SMS(Cメール)受信
1 SMSメッセージの受信
SMSの受信時には Android が SMS_RECEIVED (android.provider.Telephony.SMS_RECEIVED) というブロードキャストを送信しています。
SMSを受信したい場合は、このブロードキャストを受け取るレシーバを実装します。
(1) パーミッション
SMS_RECEIVEDを受信するにはRECEIVE_SMSのパーミッション、SMSを読み取るにはREAD_SMSのパーミッションが必要です。パーミッションを忘れても、受信しないだけでエラー(例外)が発生するわけではないので、うっかりすると嵌るかも知れません。
(2) 受信処理
受信データは、SMS_RECEIVEDインテントのExtra(キー名:"pdus")に格納されており、1通ごとのメッセージは、Java.Lang.Object形式で取得できます。
メッセージ単体はPDU(protocol description unit)形式のbyte文字列ですのでSmsMessageクラスのCreateFromPdu()で、byte[]形式のPDUデータをSmsMessageインスタンスへデコードして取得します。
実装したブロードキャストレシーバでSMS受信処理をしても、最初からインストールされているSMSメッセンジャーにもメールは到着します。
[BroadcastReceiver] [IntentFilter(new string[] { "android.provider.Telephony.SMS_RECEIVED" })] public class SmsReceiver :BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { var bundle = intent.Extras; if (bundle != null) { var pdus = (Java.Lang.Object[])bundle.Get("pdus"); var msgs = new SmsMessage[pdus.Length]; for (var i = 0; i < msgs.Length; i++){ msgs[i] = SmsMessage.CreateFromPdu((byte[]) pdus[i]); Toast.MakeText(context,string.Format("From:{0}\r\nData:{1}", msgs[i].OriginatingAddress, msgs[i].MessageBody),ToastLength.Short).Show(); } } } }
なお、ブロードキャストレシーバは、属性指定の他、RegisterReceiver()でも設置可能です。
var intentFilter = new IntentFilter(); intentFilter.AddAction("android.provider.Telephony.SMS_RECEIVED"); RegisterReceiver(new SmsReceiver(), intentFilter);
2 優先受信
特定のプログラムでこのSMSの機能を利用すれば、簡単にデータの送受が可能です。しかし、使用したメッセージがそのまま既存のSMSメッセンジャーの受信ボックスに残るのは避けたいはずです。
そこで、SMS_RECEIVEDのブロードキャストを高い優先度で受信し、特定のメッセージを処理して、そのブロードキャストを止める処理を実装します。
(1) 優先受信
ブロードキャスト受信の優先度は、マニュフェストのintent-filterに記述します。高い優先度の属性指定は次のとおりです。
[BroadcastReceiver] [IntentFilter(new string[] { "android.provider.Telephony.SMS_RECEIVED" }, Priority = (int)IntentFilterPriority.HighPriority)] class SmsReceiver :BroadcastReceiver {
上記の結果、マージされたマニュフェストには、「android:priority="1000"」が追加されています。
<receiver android:name="androidapplication1.SmsReceiver"> <intent-filter android:priority="1000"> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver>
リファレンスによれば、優先度は -1000 から 1000 の間で設定され、高い数字の方が高い優先度となっています。
(1) ブロードキャストの停止
送信元が090-1234-5678のメッセ−ジだけを表示して、ブロードキャストを停止する処理です。これによって、当該メールはSMSメッセンジャーの受信ボックスには届かなくなります。
OnReceiveに入った時点で、取りあえずブロードキャストを停止し、ターゲットのメールでなかった場合に再開しています。
[BroadcastReceiver] [IntentFilter(new string[] { "android.provider.Telephony.SMS_RECEIVED" }, Priority = (int)IntentFilterPriority.HighPriority)] class SmsReceiver :BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { //取りあえずブロードキャストを止める InvokeAbortBroadcast(); var bundle = intent.Extras; if (bundle != null) { var pdus = (Java.Lang.Object[])bundle.Get("pdus"); var msgs = new SmsMessage[pdus.Length]; for (var i = 0; i < msgs.Length; i++){ msgs[i] = SmsMessage.CreateFromPdu((byte[]) pdus[i]); //送信元が090-1234-5678のメールだけが対象となる if (msgs[i].OriginatingAddress == "09012345678") { //メッセージを表示 Toast.MakeText(context, string.Format("From:{0}\r\nData:{1}", msgs[i].OriginatingAddress, msgs[i].MessageBody), ToastLength.Short).Show(); } else { //該当メールでない場合は、ブロードキャストの停止をキャンセルする ClearAbortBroadcast(); } } } } }
送信元が090-1234-5678のメールは、表示されてSMSメッセンジャでは受信されていない
送信元が090-1234-5678以外のメールは、表示されずにSMSメッセンジャで受信される
3 エミュレータへのSMS送信
デバッグは、telnetでメッセージをエミュレータに送信して行いました。C:\>telnet localhost 5554 Android Console: type 'help' for a list of commands OK sms send 09012345678 TEST OK exit ホストとの接続が切断されました。 C:\>