Androidはショートメッセージの検証コードを実現して自動記入機能(詳細版)を取得する。


現在のアプリケーションでは登録やパスワードの変更にショートメッセージの検証コードが使われていますが、androidではどうやってショートメッセージの認証コードを取得して自動的に記入することができますか?
まず、manifestにメールの受信と読み取りの権限を登録する必要があります。 
<uses-permission android:name=“android.permission.RECEIVE_”SMS"
<uses-permission android:name=“android.permission.READ_”SMS'/> 
SMSB roadcastReceiverを放送してショートメッセージを傍受することを実現する:

package com.example.receive;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.SmsMessage;


/**
 *     
 * @author 
 *
 */
public class SMSBroadcastReceiver extends BroadcastReceiver {
 
 private static MessageListener mMessageListener;
 public static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
 
 public SMSBroadcastReceiver() {
  super();
 }

 @Override
 public void onReceive(Context context, Intent intent) {
   if (intent.getAction().equals(SMS_RECEIVED_ACTION)) {
    Object[] pdus = (Object[]) intent.getExtras().get("pdus");
    for(Object pdu:pdus) {
     SmsMessage smsMessage = SmsMessage.createFromPdu((byte [])pdu);
     String sender = smsMessage.getDisplayOriginatingAddress();
     //    
     String content = smsMessage.getDisplayMessageBody();
     long date = smsMessage.getTimestampMillis();
     Date tiemDate = new Date(date);
     SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     String time = simpleDateFormat.format(tiemDate);

     //               
     if ("+8613450214963".equals(sender)) {
      mMessageListener.onReceived(content);
      abortBroadcast();
     }
    }
   }
  
 }
 
 //    
 public interface MessageListener {
  public void onReceived(String message);
 }
 
 public void setOnReceivedMessageListener(MessageListener messageListener) {
  this.mMessageListener = messageListener;
 }
}

検証コードを記入する必要があるActivityでは、SMSB roadcastReceiverの例を生産し、onReceivedのフィードバックインターフェースを実現します。システムの資源を節約するために、私達は動的登録を使って放送をキャンセルします。 

package com.example.smstest;

import com.example.receive.SMSBroadcastReceiver;

import android.os.Bundle;
import android.app.Activity;
import android.content.IntentFilter;
import android.view.Menu;
import android.widget.EditText;

public class MainActivity extends Activity {
 
 private EditText edtPassword;
 private SMSBroadcastReceiver mSMSBroadcastReceiver;
 
 private static final String ACTION = "android.provider.Telephony.SMS_RECEIVED";

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 edtPassword = (EditText) findViewById(R.id.password);
 }
 
 @Override
 protected void onStart() {
  super.onStart();
  //      
  mSMSBroadcastReceiver = new SMSBroadcastReceiver();

  //               
  IntentFilter intentFilter = new IntentFilter(ACTION);
  intentFilter.setPriority(Integer.MAX_VALUE);
  //    
  this.registerReceiver(mSMSBroadcastReceiver, intentFilter);

  mSMSBroadcastReceiver.setOnReceivedMessageListener(new SMSBroadcastReceiver.MessageListener() {
   @Override
   public void onReceived(String message) {

    edtPassword.setText(message);

   }
  });
 }
 
 @Override
 protected void onDestroy() {
 super.onDestroy();
 //        
  this.unregisterReceiver(mSMSBroadcastReceiver);
 }


}


ショートメッセージの検証コードを取得して自動的に記入するという実現方法が提供されています。しかし、このような方法は欠陥があります。あなたの携帯に他のショートメールアプリケーション(例えばQQ通信録)や携帯電話自体が権限を制限している場合、このような方式は機能しないかもしれません。自動で記入することができません。優先度を高く設定しても、他のアプリケーションに「先鞭」を付けられないとは保証できません。
その後、資料を調べて、ショートメールのデータベースを傍受することで実現できると分かりました。待ち受けメールデータベースは主にContentObserverというカテゴリーで作成されます。ConttentObserverは主にUriによって特定のDatabasesを監視するテーブルであり、ContentObserverで観測されたUriが変化するとトリガされる。メールデータベースの特定番号を待ち受けている未読メールだ。私たちは百度を通じてたくさんのデモを見つけることができますが、たくさんのデモの中にBugが存在していることを発見しました。もう一つのケースは、パソコンと接続しています。パソコンにグリーンピースなどのソフトが入っている場合、携帯のメールが届いたら、グリーンピースなどはこのメールの状態を「既読」に変更することもあります。
デバッグによって、ようやくバグを修正しました。レイアウトとショートメッセージの権限はもう詳細に説明しません。MainActivityに内部種類SmsContottを追加します。

  /**
  *        
  */
 class SmsContent extends ContentObserver {

  private Cursor cursor = null;

  public SmsContent(Handler handler) {
   super(handler);
  }

  @Override
  public void onChange(boolean selfChange) {

   super.onChange(selfChange);
   //             
   cursor = managedQuery(Uri.parse("content://sms/inbox"), new String[]{"_id", "address", "read", "body"},
     " address=? and read=?", new String[]{"1065811201", "0"}, "_id desc");// id  ,   date    ,       ,         
   MyLog.l("cursor.isBeforeFirst() " + cursor.isBeforeFirst() + " cursor.getCount() " + cursor.getCount());
   if (cursor != null && cursor.getCount() > 0) {
    ContentValues values = new ContentValues();
    values.put("read", "1");  //         
    cursor.moveToNext();
    int smsbodyColumn = cursor.getColumnIndex("body");
    String smsBody = cursor.getString(smsbodyColumn);
    MyLog.v("smsBody = " + smsBody);

    edtPassword.setText(MatchesUtil.getDynamicPassword(smsBody));

   }

   //  managedQuery   ,      close()  ,    Android 4.0+    ,      
   if(Build.VERSION.SDK_INT < 14) {
    cursor.close();
   }
  }
 }
 

onCreateにメールの変化モニターを登録してください。 

SmsContent content = new SmsContent(new Handler());
  //        
  this.getContentResolver().registerContentObserver(Uri.parse("content://sms/"), true, content); 
傍受を取り消してください
 this.get ContentResolover().unregister ContentObserver;
この中で、下記の検証コードのショートメッセージは普通は一つの文字列です。6桁の数字が含まれています。この6桁の数字を抽出したいです。正規表現で静的な方法を書いてもいいです。 

 /**
  *          6   
  *             
  * @param str     
  * @return      6     
  */
 public static String getDynamicPassword(String str) {
  Pattern continuousNumberPattern = Pattern.compile("[0-9\\.]+");
  Matcher m = continuousNumberPattern.matcher(str);
  String dynamicPassword = "";
  while(m.find()){
   if(m.group().length() == 6) {
    System.out.print(m.group());
    dynamicPassword = m.group();
   }
  }

  return dynamicPassword;
 }

これで、androidはショートメッセージの検証コードを取得し、自動で記入する機能が実現しました。
補足:上記ショートメールデータベースの傍受に対して、直接にラベルを閉じる操作があります。  
しかし、このまま閉鎖すれば、崩壊の原因になります。例えば、メールのパスワードを取得したら、自動的に記入した後、ホームキーを押してデスクトップに戻り、アプリケーションに入ると、アプリケーションが崩壊します。新聞の間違いは:
android.databases.StleData Exception:Attempted to access a cursor after it has been closed
後に資料を調べて、マンチェスターQueryを使う時、自発的にclose()の方法を呼び出すことができなくて、さもなくばAndroid 4.0+のシステムの上で、崩壊が発生します。バージョンを判断してから、ラベルを閉じる操作を行います。 

//  managedQuery   ,      close()  ,    Android 4.0+    ,      
   if(Build.VERSION.SDK_INT < 14) {
    cursor.close();
   }
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。