ssCAROのブログ

色んなとこで見つけたプログラムのメモ置き場っぽい

Cメール(SMS)を通知に使ってEメールを受信する

前の携帯(ガラケー?)から、Eメールの転送設定で、~@auone.jpとCメールに転送させる。
CメールのアドレスはGoogleで「Cメール 転送」あたりで調べて下さい。
***********.fs@kct.dion.ne.jp (関西地方)で登録した。
(".fs"はfromとsubjectのみを転送の意味)
以下の内容のCメールが届いた

Fr:*****@ezweb.ne.jp\n  
Sub:テストメール
  1. ~@ezweb.ne.jpにメールが届くと、転送設定で~@auone.jpとCメールにも送られる。
  2. Cメールスマホが受信すると、au-oneに接続してメールを取得する。

メリット
IMAP IDLEでずっと待たなくて良いから電池の持ちが良いかもしれない。
デメリット
・転送設定が面倒(最初だけ)
・9月下旬になったら必要ない。

サービスにしたり、ソースを整理したり、やることがいっぱいですがとりあえず動くソースを載せます。
Android用のJavaMailが必要です。(ファイル3つ)

動作確認:au Xperia acro IS11S

プログラムの参考元
SMSの受信
http://meloncake.sblo.jp/article/41242112.html

SMSの削除、ブロードキャストの通知抑制
http://stackoverflow.com/questions/419184/how-to-delete-an-sms-from-the-inbox-in-android-programmatically

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.android.SMSMailDemo"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="10" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
        <activity android:name=".SMSMailDemoActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            </activity>
            <receiver android:name=".SMSReceiver">
                <intent-filter android:priority="100">
                    <action android:name="android.provider.Telephony.SMS_RECEIVED" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
                    <data android:mimeType="*/*" />
                </intent-filter>
            </receiver>
    </application>
</manifest>

"android.provider.Telephony.SMS_RECEIVED"が必要。
<intent-filter android:priority="100">とすることで、ブロードキャストを受信する優先度を上げれるみたい。
上げてどうするのか?一番最初にSMS_RECEIVEDを受け取って、それ以降に流さないようにして通常のCメールアプリにSMSが届かないようにする。

"android.provider.Telephony.WAP_PUSH_RECEIVED"は使ってないので削除可能。

SMSMailDemoActivity.java

package com.example.android.SMSMailDemo;

import java.util.Properties;

import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.telephony.SmsMessage;
import android.widget.TextView;

public class SMSMailDemoActivity extends Activity {
    private static SMSMailDemoActivity me;
    private static TextView messageView;
    
    private static String mUser = "*****@auone.jp";
    private static String mPassword = "*****";
    
    private static String mTargetFolder = "INBOX";
    
    public SMSMailDemoActivity() {
        me = this;
    }
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        messageView = (TextView)findViewById(R.id.messageView);
        Bundle bundle = messageView.getInputExtras(true);
        bundle.putBoolean("allowEmoji", true);
    }
    
    public static Handler hander = new Handler() {
        @Override
        public void handleMessage(android.os.Message msg) {
            try {
                SmsMessage smsMsg = (SmsMessage)msg.obj;
                String body[] = smsMsg.getMessageBody().split("\n");
                // Fr: を抜き出し
                InternetAddress addrFrom = new InternetAddress(body[0].substring(3));
                // Sub: を抜き出し
                String sub = body[1].substring(4);
                //
                //InternetAddress addrFrom = new InternetAddress(msg.getData().get("from").toString());
                //String sub = msg.getData().get("sub").toString();
                viewSubject(addrFrom, sub);
                //
                
                // 受信したSMSを削除する(エラーで失敗したので要確認)
                /*
                Uri deleteUri = Uri.parse("content://sms");
                me.getContentResolver().delete(
                        deleteUri,
                        "address=? and date=?",
                        new String[] {
                                smsMsg.getOriginatingAddress(),
                                String.valueOf(smsMsg.getTimestampMillis())
                        });
                */
            } catch (AddressException e) {
                // TODO 自動生成された catch ブロック
                e.printStackTrace();
            }
        }
    };
    
    private static void viewSubject(InternetAddress cmAddress, String cmSubject) {
        Properties props = new Properties();
        props.setProperty("mail.store.protocol", "imaps");
        
        Session sess = Session.getDefaultInstance(props, null);
        Store store = null;
        Folder folder = null;
        
        try {
            store = sess.getStore("imaps");
            store.connect("imap.gmail.com",
                          993,
                          mUser,
                          mPassword);
            
            folder = store.getFolder(mTargetFolder);
            folder.open(Folder.READ_ONLY);
            
            int totalMsg = folder.getMessageCount(); //INBOXの全件数
            int unreadMsg = folder.getUnreadMessageCount(); //未読数
            //未読数分だけ取得する(最初のほうに未読が残っていれば意味ない)
            Message[] messages = folder.getMessages(totalMsg - unreadMsg, totalMsg);
            StringBuffer sb = new StringBuffer();
            String text = "見つかりませんでした。";
            
            for (int i = unreadMsg; i > 0; i--) {
                Message message = messages[i];
                if (message.isMimeType("text/plain")) {
                    InternetAddress[] addrFrom = (InternetAddress[])message.getFrom();
                    // Cメールで受信したFromと、Eメールで受信したFromが一緒なら表示して抜ける
                    // Subjectも一緒じゃないとダメ!なのが抜けてる
                    if (addrFrom[0].getAddress().equals(cmAddress.getAddress())) {
                        text = String.format("%s\n%s\n", addrFrom[0].getAddress(), message.getSubject());
                        sb.append(text);
                        sb.append(getBody(message));
                        break;
                    }
                }
            }
            messageView.setText(sb.toString());
            // フォルダーを閉じます
            folder.close(false);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (store != null) {
                    store.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    // 絵文字の対応とかをする
    private static String getBody(Message message) {
        String body = null;
        try {
            body = message.getContent().toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return body;
    }
}

SMSReceiver.java

package com.example.android.SMSMailDemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.telephony.SmsMessage;
import android.util.Log;

public class SMSReceiver extends BroadcastReceiver {
    private static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
    private static final String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";
    private static final String TAG = "SMSReceiver";
    
    @Override
    public void onReceive(Context cntxt, Intent intent) {
        // 電話番号でのSMSはこっちで届いた
        // EメールのCメールへの転送設定は、こっちで届いた
        if (SMS_RECEIVED_ACTION.equals(intent.getAction())) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                /* SMS_RECEIVED の受信優先度を上げて(1~100) abortBroadcast() で以降ブロードキャストをとめる
                 * <intent-filter android:priority="100">
                 *     <action android:name="android.provider.Telephony.SMS_RECEIVED" />
                 * </intent-filter>
                 */
                abortBroadcast();
                
                Log.d(TAG, "SMS_RECEIVED");
                // pduのデコードとログ出力
                // サンプルのためBroadcastReceiverで処理(本来はServiceで)
                Object[] pdus = (Object[])extras.get("pdus");
                for (Object pdu: pdus) {
                    SmsMessage smsMessage = SmsMessage.createFromPdu((byte[])pdu);
                    Log.d(TAG, "from:" + smsMessage.getOriginatingAddress());
                    Log.d(TAG, "time:" + Long.toString(smsMessage.getTimestampMillis()));
                    Log.d(TAG, "body:" + smsMessage.getMessageBody().replaceAll("\n", "\t"));
                    
                    //reciveSms(smsMessage.getMessageBody());
                    reciveSms(smsMessage);
                }
            }
        // 使ってない、いつ届くか未確認
        } else if (WAP_PUSH_RECEIVED_ACTION.equals(intent.getAction())) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                Log.d(TAG, "WAP_PUSH_RECEIVED");
                int transactionId = extras.getInt("transactionId");
                int pduType = extras.getInt("pduType");
                byte[] header = extras.getByteArray("header");
                byte[] data = extras.getByteArray("data");
                
                Log.d(TAG, "contentType: " + ((intent.getType() != null) ? intent.getType() : ""));
                Log.d(TAG, "transactionId: " + Integer.toString(transactionId));
                Log.d(TAG, "pduType: " + Integer.toString(pduType));
                
                if (header != null) {
                    for (int i = 0; i < header.length; i++) {
                        Log.d(TAG, String.format("header[%03d]: 0x%02x (%s)", i, header[i], (char)header[i]));
                    }
                } else {
                    Log.d(TAG, "header is null");
                } 
                
                if (data != null) {
                    for (int i = 0; i < data.length; i++) {
                        Log.d(TAG, String.format("data[%03d]: 0x%02x (%s)", i, data[i], (char)data[i]));
                    }
                } else {
                    Log.d(TAG, "data is null");
                }
            }
        }
    }
    
    //Fr:*****@ezweb.ne.jp\n
    //Sub:テストメール
    private void reciveSms(String smsMessage) {
        Message msg = new Message();
        Bundle bundle = new Bundle();
        String[] message = smsMessage.split("\n");
        bundle.putString("from", message[0].substring(3));
        bundle.putString("sub", message[1].substring(4));
        msg.setData(bundle);
        SMSMailDemoActivity.hander.sendMessage(msg);
    }
    
    private void reciveSms(SmsMessage smsMessage) {
        Message msg = new Message();
        msg.obj = smsMessage;
        SMSMailDemoActivity.hander.sendMessage(msg);
    }
}

IS11Sには、デフォルトでCメールアプリがあるので、そっちにも通知が行く。
それをなくす為に、SMSを削除しようとしたけど出来なかった(要確認)。
結局、ブロードキャストの通知を1番最初に取得して消して、以降に流さないようにした。
あと、Handerの使い方がよく分からなかった。