スマートS​​MSサービスハンドラーの仕組み(重要な情報のみを表示)

この記事には、スマートサービス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。 簡単に



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" } ] } 


サービスワードは、 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" } 

その結果、サーバーは保存されません



完全なJSONはここで見ることができます


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ロジックの詳細:



コード例のあるロジック


パラメーターとして、メソッドに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" } ] } 

次に、リストを取得したい



次はメインロジックです


  // 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_0SUM_1のみが見つかりました
なぜなら これは、SMSのテキストに実際に含まれるデータです。
したがって、最初のロジックの後に、2つの要素の配列があります(この場合、 212,3020537,96 )。
しかし、出力に4行を送信する必要があります(これら2つにEXTRA_PURCHASEEXTRA_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" } ] } 

次に、出力で取得します



ここでメインロジックが終了します。
次に、このメソッドを使用してアクティビティポップアップに表示します。


 showPopupDialog(context, messageLines, sender != null ? sender.iconUrl : ""); 

messageLinesテキストmessageLines 、テキストビューに単に表示されます。
iconUrlGlideを使用して画像ビューにロードされます-ここではすべてが非常に簡単です。


おわりに


明らかに、アルゴリズムは原始的であり、改善することができます。
アイデアの



しかし、アプリケーションは問題を解決します。


画像


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



Source: https://habr.com/ru/post/J303990/


All Articles