MF99 coding 💻

keep learning; keep coding;

SMS 簡訊自動攔截/解析

現今的服務中,許多登入或是認證的行為,都會用到所謂的 OTP (one-time passcode) 也就是在登入過程中,透過手機驗證的方式來確認用戶身份

有些大一點的公司會使用自家的驗證器(Authenticator),像是 Facebook / Google / Steam ...etc 但是大部分的服務,方便一點的就會使用簡訊服務(SMS)

與其讓用戶收到訊息後還要去查看簡訊,然後把簡訊中的驗證碼記住或是複製,再回到你的 App 做輸入。 一來流程麻煩,二來用戶可能會記錯/貼錯。

所以更方便的方式就是自動擷取簡訊,然後把驗證碼自動幫用戶帶入對應的欄位。

具體作法

  1. 註冊 SMS Receiver
  2. 取得 Runtime Permission
  3. 解析簡訊內容

宣告 Permission

    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />

註冊 Receiver

        <receiver android:name=".SMSListener">
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>

在 Activity 需要時取得 Runtime Permission

如果用戶不同意的話,Receiver 就會收不到訊息

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        if (checkSelfPermission(Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(
                arrayOf(
                    Manifest.permission.READ_SMS,
                    Manifest.permission.RECEIVE_SMS
                ),
                SMS_PERMISSION_REQUEST_CODE
            )
        }
        ...
    }

最後在收到簡訊時,解析簡訊的內容

SMSListener.kt

class SMSListener : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        val bundle = intent.extras
        if (bundle != null) {
            //---retrieve the SMS message received---
            try {
                val pdus = bundle.get("pdus") as Array<Any>
                for (i in 0..pdus.size) {
                    var msg = SmsMessage.createFromPdu(pdus[i] as ByteArray)
                    /* handle message content via 
                        msg.displayMessageBody
                    */
                }
            } catch (e: Exception) {}
        }
    }
}

理論上 SmsMessage.createFromPdu(pdus[i] as ByteArray) 應該還要帶入第二個參數 format GSM/UMTS/LTE 使用 SmsConstants.FORMAT_3GPP CDMA/LTE 使用 SmsConstants.FORMAT_3GPP2

最後就可以從 msg.displayMessageBody 取得完整的見訊文字內容

然後再看看透過 LiveData 或是其他機制通知 View 元件然後幫用戶自動帶入