この記事には、スマートサービスSMSハンドラーの内部構造の説明が含まれています。 
      
        
        
        
      
     アプリケーションは着信SMSを解析し、それらからの重要な情報のみを表示します。 
      
        
        
        
      
     美しく、すばやく、便利に表示されます。 
1.仕組み
マニフェストでSMSを受信して読み取る許可を登録する
<uses-permission android:name="android.permission.RECEIVE_SMS"/> <uses-permission android:name="android.permission.READ_SMS"/>`
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
       receiver
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    登録receiver
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      
        
        
        
      
     テストにaction_sms_received_test
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    パーミッションaction_sms_received_test
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    必要です。 
      
        
        
        
      
     テスト中に実際のSMSにお金を費やさないために、アプリケーションからこのアクションを使用してIntentを送信し、キャッチします。 
 <receiver android:name=".receivers.SmsReceiver"> <intent-filter android:priority="2147483647"> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> <action android:name="action_sms_received_test"/> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      これで、受信者はすべての着信メッセージを受信します
 @Override public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { case ACTION_SMS_RECEIVED: handleIncomingSms(context, intent); break; case ACTION_SMS_RECEIVED_TEST: // do test break; } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       次に、メソッドhandleIncomingSms(context, intent);
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     どのようなSMSが届いたかを把握し、何をすべきかを決定する必要があります。 
      
        
        
        
      
     公式であれば、分析して有用な情報を取得し、美しい形で表示します。 
      
        
        
        
      
     彼女が公式かどうかをどのように理解するか—後で説明します。 
失礼、こんな感じ
 private void handleIncomingSms(Context context, Intent intent) { Li("handleIncomingSms"); Bundle bundle = intent.getExtras(); if (bundle == null) { return; } try { Object[] pdus = (Object[]) bundle.get(PDUS); String smsText = ""; for (Object pdu : pdus) { final SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu); smsText += message.getMessageBody(); } checkTemplates(context, smsText); } catch (Exception e) { Li("handleIncomingSms - Exception", Log.getStackTraceString(e)); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       メソッドcheckTemplates();
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 private void checkTemplates(Context context, String smsText) { Li("checkTemplates", smsText); // get templates List<SmsTemplate> smsTemplates = DatabaseManager.getSmsTemplates(); if (smsTemplates == null) { return; } // check if sms text according to some template for (SmsTemplate smsTemplate : smsTemplates) { List<String> messageLines = SmsNewParser.getMessageLines(smsTemplate, smsText); if (messageLines != null) { Sender sender = DatabaseManager.getSender(smsTemplate.sender); showPopupDialog(context, messageLines, sender != null ? sender.iconUrl : ""); } } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
        showPopupDialog
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    メソッド 
 private void showPopupDialog(Context context, List<String> message, String iconUrl) { Li("showPopupDialog", message, iconUrl); Intent popupIntent = new Intent(context, PopupActivity.class); popupIntent.putExtra(PopupActivity.ICON_URL, iconUrl); popupIntent.putExtra(PopupActivity.MESSAGE_0, message.get(0)); popupIntent.putExtra(PopupActivity.MESSAGE_1, message.get(1)); popupIntent.putExtra(PopupActivity.MESSAGE_2, message.get(2)); popupIntent.putExtra(PopupActivity.MESSAGE_3, message.get(3)); popupIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(popupIntent); }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       その後、ユーザーはそのような画面を見ます 
      
        
        
        
      
     ポイントは、有用な情報をすばやく表示することです。 

2. SMS認識アルゴリズムと重要な情報の発行
2.1。 簡単に
- サーバー上にテンプレートがあります
- 各テンプレートは、a)SMSの外観b)正確に表示する内容を示します
- アプリケーションは、起動するたびにそれらを同期します
- 各着信メッセージは、すべてのテンプレートで実行されます。
- 一致するテンプレートが見つかった場合、重要な情報が必要なフォームに表示されます。
2.2。 モデルの詳細
パターンはこんな感じ
 { "sender": "bank_alfa", "text": "3*8272; Pokupka; Uspeshno; Summa: 212,30 RUR; Ostatok: 20537,96 RUR; RU/MOSKVA/GETT; 15.04.2016 06:02:43", "mask": "~N~*~N4~; ~BANK_ACTION_0~; Uspeshno; Summa: ~SUM_0~ ~CURRENCY_0~; ~BANK_ACTION_1~: ~SUM_1~ ~CURRENCY_1~; ~WORD~; ~N2~.~N2~.~N4~ ~N2~:~N2~:~N2~", "lines": [ { "line": "EXTRA_PURCHASE" }, { "line": "SUM_0" }, { "line": "EXTRA_TOTAL" }, { "line": "SUM_1" } ] }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      -   sender
 
 
 
 -送信者
-   text
 
 
 
 このSMSの初期テキスト。 テストに使用できます
-   mask
 
 
 
 テンプレート自体。~FOO~
 
 
 
 という形式のサービスワード~FOO~
 
 
 
 
-   lines
 
 
 
 画面に表示されるメッセージの行。 テンプレートの一部を指定するか、テンプレートにない単語を使用できます。
 サービスワードは、 extra
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    単語と通常の単語に分かれています。 
      
        
        
        
      
      Extra
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    は、テンプレートにないことを意味します。 
      
        
        
        
      
     例: 
  ~SUM~
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    は一般的なサービスワードです。 ピリオドまたはコンマで区切られた数値を含む式を意味します。 
      
        
        
        
      
     金額を決定するために使用されます。 検索するには、正規表現を使用します 
 { "name": "SUM", "regex": "\\d+[.,]{0,1}\\d+", "values": [], "is_extra": false }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
        ~CURRENCY~
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    は、いくつかの意味を持つことができる普通の単語です。 検索するには、値を繰り返し処理します。 
 { "name": "CURRENCY", "regex": "", "values": [ { "value": "usd" }, { "value": "rur" }, { "value": "eur" }, { "value": "rub" } ], "is_extra": false }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
        ~EXTRA_CODE_WORD~
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    タイプがextra
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    サービスワード。 結果としてテキスト「コードワード」を表示するために使用されます。 
 { "name": "EXTRA_CODE_WORD", "regex": "", "values": [ { "value": " " } ], "is_extra": true }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       メッセージの送信者を正確に示す写真も必要です。 
      
        
        
        
      
     この情報はsender
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    オブジェクトに保存されます。 
 例: 
      
        
        
        
      
     これがアルファバンクとそのアイコンです。 
 { name: "bank_alfa", icon_url: "https://dl.dropboxusercontent.com/u/1816879/CaptainSms/logo_alfa.png" }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      その結果、サーバーは保存されません
- パターン
- サービスワード
- 送信者
2.3。 アルゴリズムの詳細
 モデルをダウンロードして保存します。 
      
        
        
        
      
     次に、SMSを解析し、結果のメッセージを作成する手順に従います。 
 メッセージテキストを解析するには、静的メソッドでSmsParser
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    クラスを使用します。 
      
        
        
        
      
     メインメソッドはgetMessageLines(SmsTemplate smsTemplate, String realSmsText)
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      
        
        
        
      
     すべて問題ない場合はメッセージ行を返し、適切なテンプレートが見つからなかった場合はnull
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    返します。 
      
        
        
        
      
     このメソッドは、上記のcheckTemplates
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    メソッドのこの場所から呼び出されます。 
  // check if sms text according to some template for (SmsTemplate smsTemplate : smsTemplates) { List<String> messageLines = SmsNewParser.getMessageLines(smsTemplate, smsText); if (messageLines != null) { Sender sender = DatabaseManager.getSender(smsTemplate.sender); showPopupDialog(context, messageLines, sender != null ? sender.iconUrl : ""); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       データベースからすべてのテンプレートをmessage lines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     、それぞれのmessage lines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を取得しようとしmessage lines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     。 
      
        
        
        
      
     判明した場合、画面に情報が表示されます.. 
  getMessageLines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ロジックを簡単に 
      
        
        
        
      
     マスクを実行し、文字ごとにSMSテキストと比較し、検出されたサービスワードの値を配列に書き込むか、矛盾がある場合はnull
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    スローしnull
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
  getMessageLines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ロジックの詳細: 
- キャラクターマスクでキャラクターを走らせます
-  シンボルがサービスワード( ~
 
 
 
 )の始まりである場合:
 
 -私たちはそれがどんな種類の単語であるかを理解しています(たとえば、〜SUM_0〜)
 
 255.00
 
 
 
 テキストで値を計算します(たとえば、255.00
 
 
 
 )
 
 -マスクから単語を切り取り、テキストからこの値を(文字ごとに実行し続けるために)
-  それ以外の場合、単純な文字の場合: 
      
 
 -それらが最大値とテキストで一致する場合、そこからそれらをカットしてさらに比較します
 
 -それらが異なる場合、null
 
 
 
 をスローしnull
 
 
 
 -テキストはテンプレートに適合しません
コード例のあるロジック
パラメーターとして、メソッドにsmsテンプレートとテキストを取得します
 public static List<String> getMessageLines(SmsTemplate smsTemplate, String smsText)
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       メソッドの最初で、サービスワードのリストを初期化します。  APIを使用した定期的な更新からデータベースにアクセスしました。 
      
        
        
        
      
     グローバル変数が必要です。なぜなら メソッドは大きく、断片に分割されます。 
 private static void initReservedWords() { Li("initReservedWords"); mReservedWords.clear(); mReservedWords = DatabaseManager.getReservedWords(); }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      次に、指定されたテンプレートからサービスワードのリストを作成します。
  List<ReservedWord> reservedWords = new ArrayList<>(); for (SmsTemplateLine line : smsTemplate.lines) { reservedWords.add(getReservedWordByName(line.line)); }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      つまり テンプレートがある場合
 { "sender": "bank_alfa", "text": "3*8272; Pokupka; Uspeshno; Summa: 212,30 RUR; Ostatok: 20537,96 RUR; RU/MOSKVA/GETT; 15.04.2016 06:02:43", "mask": "~N~*~N4~; ~BANK_ACTION_0~; Uspeshno; Summa: ~SUM_0~ ~CURRENCY_0~; ~BANK_ACTION_1~: ~SUM_1~ ~CURRENCY_1~; ~WORD~; ~N2~.~N2~.~N4~ ~N2~:~N2~:~N2~", "lines": [ { "line": "EXTRA_PURCHASE" }, { "line": "SUM_0" }, { "line": "EXTRA_TOTAL" }, { "line": "SUM_1" } ] }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      次に、リストを取得したい
- EXTRA_PURCHASE
- SUM_0
- EXTRA_TOTAL
- SUM_1
次はメインロジックです
  // check match symbol by symbol try { do { String s = mask.substring(0, 1); if (s.equals(ReservedWord.SYMBOL)) { // found start of a reserved word ReservedWord currentReservedWord = getFirstReservedWord(mask); String valueOfCurrentReservedWord = getValueOfReservedWord(smsText, mask, currentReservedWord); // add value in the list, if reserved word is in the list if (reservedWords.contains(currentReservedWord) && valueOfCurrentReservedWord.length() > 0) { values.put(currentReservedWord.getForm(), valueOfCurrentReservedWord); } // cut text and mask to look next symbols smsText = smsText.substring(valueOfCurrentReservedWord.length()); mask = mask.substring(currentReservedWord.getForm().length()); } else if (s.equals(smsText.substring(0, 1))) { // that symbols matches, go to the next symbol smsText = smsText.substring(1); mask = mask.substring(1); } else { /* * that symbol does not match, so text not match that mask, so method fails * because we cannot return correct values according to that list of reserved word */ return null; } } while (mask.length() > 0); } catch (StringIndexOutOfBoundsException e) { /* * There is some error during parsing. * That mean text does not match mask. */ Li(TAG, "getMessageLines - Exception - " + Log.getStackTraceString(e)); return null; }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       彼女は、「 getMessageLines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     Logic more:」として、上記の説明を正確に実行します。 
 次に、リストを再ソートします。 テキストでは、 message lines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    とは異なる順序で発生しmessage lines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
  // convert list to the right order List<String> valuesList = new ArrayList<>(); for (ReservedWord word : reservedWords) { LLog.e(TAG, "getMessageLines - return list - " + values.get(word.getForm())); if (values.get(word.getForm()) != null) { valuesList.add(values.get(word.getForm())); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       次に、タイプextra
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    補助語を追加します。  SMSのテキストを通過したときにそれらを見つけられませんでした。 
  // add values of all the extra words for (int i = 0; i < reservedWords.size(); i++) { if (reservedWords.get(i).isExtra) { valuesList.add(i, reservedWords.get(i).values.iterator().next().value); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       これが必要な理由です。 
      
        
        
        
      
     入り口でsmsTemplate
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    が与えられsmsTemplate
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     。  messageLineのセットがあります。 たとえば、4つありました。 
  "lines": [ { "line": "EXTRA_PURCHASE" }, { "line": "SUM_0" }, { "line": "EXTRA_TOTAL" }, { "line": "SUM_1" } ] }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       しかし、テンプレートと一致するテキストをチェックするプロセスで、 SUM_0
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    とSUM_1
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    のみが見つかりました 
      
        
        
        
      
     なぜなら これは、SMSのテキストに実際に含まれるデータです。 
      
        
        
        
      
     したがって、最初のロジックの後に、2つの要素の配列があります(この場合、 212,30
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    と20537,96
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     )。 
      
        
        
        
      
     しかし、出力に4行を送信する必要があります(これら2つにEXTRA_PURCHASE
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    とEXTRA_TOTAL
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を追加する必要があります)。さらに正しい順序で。 
      
        
        
        
      
     したがって、メソッドの最後にそれらを追加します。 
その結果、出力では4行の配列を取得します。
たとえば、テンプレートがある場合
 { "sender": "bank_alfa", "text": "3*8272; Pokupka; Uspeshno; Summa: 212,30 RUR; Ostatok: 20537,96 RUR; RU/MOSKVA/GETT; 15.04.2016 06:02:43", "mask": "~N~*~N4~; ~BANK_ACTION_0~; Uspeshno; Summa: ~SUM_0~ ~CURRENCY_0~; ~BANK_ACTION_1~: ~SUM_1~ ~CURRENCY_1~; ~WORD~; ~N2~.~N2~.~N4~ ~N2~:~N2~:~N2~", "lines": [ { "line": "EXTRA_PURCHASE" }, { "line": "SUM_0" }, { "line": "EXTRA_TOTAL" }, { "line": "SUM_1" } ] }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      次に、出力で取得します
- 購入する
- 212.30
- 残った
- 20,537.96
 ここでメインロジックが終了します。 
      
        
        
        
      
     次に、このメソッドを使用してアクティビティポップアップに表示します。 
 showPopupDialog(context, messageLines, sender != null ? sender.iconUrl : "");
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
        messageLines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    テキストmessageLines
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     、テキストビューに単に表示されます。 
      
        
        
        
      
      iconUrl
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     、 Glideを使用して画像ビューにロードされます-ここではすべてが非常に簡単です。 
おわりに
 明らかに、アルゴリズムは原始的であり、改善することができます。 
      
        
        
        
      
     アイデアの 
- APIを異なるJSONファイルに分割します(たとえば、送信者ごとに1つのJSON)
- スマートなテンプレート実行アルゴリズム(最初にコードを使用-最速で必要とされ、次に頻繁に使用され、次にその他すべてのアルゴリズム)
- おそらく、解析コード自体を改善することができます(冗長オブジェクトの作成の確認、ループの数の削減など)。
しかし、アプリケーションは問題を解決します。

 メッセージを解析するためのメインクラスを囲みます。 
      
        
        
        
      
     上記のコードとは少し異なります。 
      
        
        
        
      
     なぜなら 上記のコードは視覚的に改善されています。