今天想來寫關於「簡訊讀取」的功能

雖然現代這個時代手機簡訊(SMS)幾本上已淪落為詐騙訊息集散地(笑)

不過換個角度想,以現在機號約等於個人身份的情況下,透過簡訊驗證手機號碼其實還是蠻可靠的

而在手機驗證的UX中,我們也會期望APP在收到簡訊的瞬間就幫我們將號碼填進去...不能說是一般常識,但只能說「有的話更好」吧!

於是今天的實作就是要來模擬「接收簡訊動態驗證碼並填入」的一個實作,來看範例吧

然後Github

->https://github.com/thumbb13555/AndroidBlogExamples/tree/main/SMSReader

 


 

1. 前言

 

首先今天的這個功能我的重點會放在「如何讀取簡訊」,所以關於那個四位數的UI的函式庫我也只是隨便找個來用而已

在這邊我用的是這個

-> https://github.com/ChaosLeung/PinView

不過如果是做開發的話,我個人會使用這兩個

-> https://github.com/Wynsbin/VerificationCodeInputView

-> https://github.com/wei-gong/VerifyCodeView

 

總之這部分自己評斷使用,我就不深入說明

 


 

2. 載入需要的庫&介面

 

好的,那麼接下來就是載入需要的函式庫與介面

剛剛也說了,這次我的重點會在讀簡訊,而非函式庫

所以這部分我就簡單帶過

 

首先一樣build.gradle加入

implementation 'io.github.chaosleung:pinview:1.4.4'

 

記得Sync(笑)

 

然後介面簡單直接給

 

截圖 2023-05-04 下午4.03.39

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.chaos.view.PinView
        android:id="@+id/pin_view"
        style="@style/PinWidget.PinView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:cursorVisible="true"
        android:hint="0000"
        android:inputType="number"
        android:textColor="#000000"
        app:lineColor="#4DB6AC"
        app:cursorColor="#4DB6AC"
        app:cursorWidth="2dp"
        app:hideLineWhenFilled="false"
        app:itemCount="4"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:viewType="line" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.54" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

 


 

3. 寫入程式

 

再來來看一下這次的檔案結構

截圖 2023-05-04 下午4.09.59

 

除了MainActivity之外,我另外寫了SMSContent這隻檔案作為讀取簡訊的模組

但在這之前..首先我們要替讀取簡訊加入權限

 

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SMSReader"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>

</manifest>

 

再來我直接PO讀取簡訊的程式內容

 

public class SMSContent extends ContentObserver {

    private final OnCallback callback;
    private final Context context;

    public SMSContent(Handler handler,Context context,OnCallback callback) {
        super(handler);
        this.context = context;
        this.callback = callback;
    }

    @Override
    public void onChange(boolean selfChange, @Nullable Uri uri) {
        super.onChange(selfChange, uri);
        Cursor cursor = null;
        ContentResolver resolver = context.getContentResolver();
        //確認權限後,設置讀取SMS        if (ContextCompat.checkSelfPermission(context, "android.permission.READ_SMS")
                == PackageManager.PERMISSION_GRANTED) {
            cursor = resolver.query(Uri.parse("content://sms"), null, null
                    , null, "_id desc");
        }
        if (cursor != null && cursor.getCount() > 0) {
            //讀取簡訊後,並將之設為已讀
            ContentValues values = new ContentValues();
            values.put("read", "1");
            cursor.moveToNext();

            assert uri != null;
            if ("content://sms/raw".equals(uri.toString())) {
                //取得動態簡訊內容
                int smsBodyColumn = cursor.getColumnIndex("body");
                String code = getDynamicCode(cursor.getString(smsBodyColumn));
                wait(1500);
                callback.callback(code);
                cursor.close();
            }
        }
    }
    /**驗證簡訊內容*/
    public String getDynamicCode(String str) {
        //檢測簡訊內容,以正規表達式抓出數字
        Pattern continuousNumberPattern = Pattern.compile("[0-9\\.]");
        Matcher m = continuousNumberPattern.matcher(str);
        //填入數字
        StringBuilder stringBuilder = new StringBuilder();
        while (m.find()) {
            stringBuilder.append(m.group());
        }
        return stringBuilder.toString();
    }

    /**收到簡訊後的延遲時間*/
    private void wait(int ms) {
        try {
            TimeUnit.MILLISECONDS.sleep(ms);
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
    }
    public interface OnCallback{
        void callback(String code);
    }
}

 

...我想我已經把說明寫在註解了,應該沒什麼好講的吧哈哈XD

不過還是說明一下,基本上繼承了ContentObserver的類,他都會去偵測系統通知所發生的變化

而簡訊的收發基本就是系統變化的一類,因此我們複寫onChange去抓取該狀態

再來的內容我在程式內的註解就都有寫了,再麻煩自個兒研究一下囉:D

 

最後完成這部分的模組後,我們就拿這程式來使用囉

 

 

public class MainActivity extends AppCompatActivity {
    final int REQUEST_CODE_ASK_PERMISSIONS = 123;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        PinView pinView = findViewById(R.id.pin_view);
        ActivityCompat.requestPermissions(this, new String[]{"android.permission.READ_SMS"}
                , REQUEST_CODE_ASK_PERMISSIONS);
        SMSContent content = new SMSContent(new Handler(), this, new SMSContent.OnCallback() {
            @Override
            public void callback(String code) {
                pinView.setText(code);
            }
        });
        this.getContentResolver().registerContentObserver(Uri.parse("content://sms/")
                , true, content);
    }
}

 

首先在粉底白字的部分,這部分是取得讀取簡訊的權限;不過一開始我也說了這次的文章重點不在這裡,所以回調部分我就不寫了

再來是黃(橘)底白字的部分就是告訴系統密切注意簡訊的收發狀態(就是監聽他的意思

最後綠底白字就是使用我寫好的模組並應用之

 

寫到這邊,按下執行就能夠跑囉~

 


 

今天撰寫的功能雖然是讀取簡訊,不過其實這項功能的核心重點是ContentObserver

ContentObserver是系統監測,能監測的除了簡訊之外,飛行模式的開啟等等也都是可被監測的內容

這部分我就以後再寫吧!

好的,最後..

TK

arrow
arrow

    碼農日常 發表在 痞客邦 留言(1) 人氣()