안드로이드 SMS 인증 - andeuloideu SMS injeung

안드로이드 - SMS 수신시 인증번호 자동 추출하기

안드로이드 SMS 인증 - andeuloideu SMS injeung

안드로이드 앱을 구현하면서 SMS 문자 메시지를 수신했을 경우 인증번호를 추출하여 특정 위치의 입력박스나 레이아웃, 또는 웹뷰(WebView) 내부의 웹페이지에 Javascript 인터페이스를 통하여 입력해주어야 할 때가 있습니다. 사실 이러한 것을 해주는 소스코드는 검색해보면 국내이든 해외이든 많이 나오긴 합니다. 이미 이런 기능이 지원되는 앱들이 많기도 하구요.

저도 제 나름대로 검색하여 편리하게 사용할 수 있는 코드로 정리하여 작성해 보았습니다. 사실 로직이 크게 복잡하지는 않습니다.

아래와 같은 SMS 문자 메시지를 전송한다고 가정해봅시다.

휴대전화 인증

보호자 휴대전화 인증

아이디 찾기 인증

비밀번호 찾기 인증

[앱스] 휴대전화
인증번호는
145796 입니다. 정확히 입력해주세요.

[앱스] 보호자 휴대전화
인증번호는 
668594 입니다. 정확히 입력해주세요.

[앱스] 아이디 찾기
인증번호는 
683921 입니다. 정확히 입력해주세요.

[앱스] 비밀번호 찾기
인증번호는 
194303 입니다. 정확히 입력해주세요.

해당 기능을 이용하려면 안드로이드 운영체제로부터 아래의 권한을 요청해야 합니다.

권한

상수 코드

API 레벨

READ_SMS

android.permission.READ_SMS
앱이 SMS 메시지를 읽을 수 있게 합니다.

1 이상

RECEIVE_SMS

android.permission.RECEIVE_SMS
앱이 SMS 메시지를 수신할 수 있게 합니다.

1 이상

READ_PHONE_STATE

android.permission.READ_PHONE_STATE
앱이 장치의 전화번호, 현재 셀룰러 네트워크 정보, 진행 중인 통화의 상태에 대한 읽기 전용 액세스를 허용합니다.

1 이상

위의 권한을 아래의 AndroidManifest.xml에 추가합니다.

manifests/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myapps.app">
    . . . .
    <!-- 권한 -->
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="
android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="
android.permission.READ_PHONE_STATE"/>
    . . . .
</manifest>

실제 SMS 문자 메시지를 처리하는 코드를 작성합니다.

SmsReceiverCallback.java (패키지: com.myapps.sms)

/**
  * Copyright © 2018 Arumizz Artworks. All Rights Reserved.
  */

package com.myapps.sms;

/**
  * SMS Receiver Callback Interface
  */
public interface SmsReceiverCallback {
    /**
      * SMS 수신시 호출되는 콜백 메서드
      * @param senderNo 발신 번호
      * @param messageBody 발신 메시지 문자열
      */
    void onReceive(String senderNo, String messageBody);
}

SmsReceiver.java (패키지: com.myapps.sms)

/**
  * Copyright © 2018 Arumizz Artworks. All Rights Reserved.
  */

package com.myapps.sms;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.telephony.SmsMessage;

import java.util.ArrayList;
import java.util.List;

/**
  * SMS Receiver Class
  */
public class SmsReceiver extends BroadcastReceiver {
    /**
     * 클래스 태그 이름
     */
    private static final String TAG = SmsReceiver.class.getName();

    /**
     * SMS 수신 액션 문자열
     */
    private static final String SMS_ACTION = "android.provider.Telephony.SMS_RECEIVED";

    /**
      * 상위 액티비티
      */
    private Activity mActivity = null;

    /**
      * 콜백
      */
    private SmsReceiverCallback mCallback = null;

    /**
      * 발신번호 화이트 리스트
      */
    private List<String> mSenderNoList = null;

    /**
      * SMS 수신 일시정지 여부
      */
    private boolean mPause = false;

    /**
      * 액션 필터용
      */
    private IntentFilter mIntentFilter = null;

    /**
      * 생성자
      */
    public SmsReceiver() {
        mSenderNoList = new ArrayList<>();
    }

    /**
      * 생성자
      * @param activity 부모 액티비티
      */
    public SmsReceiver(Activity activity) {
        mActivity = activity;
        mSenderNoList = new ArrayList<>();
    }

    /**
      * 생성자
      * @param activity 부모 액티비티
      * @param callback 콜백
      */
    public SmsReceiver(Activity activity, SmsReceiverCallback callback) {
        mActivity = activity;
        mCallback = callback;
        mSenderNoList = new ArrayList<>();
    }

    /**
      * 신규 인스턴스를 반환한다.
      * @param activity 부모 액티비티
      * @return 신규 인스턴스
      */
    public static SmsReceiver getInstance(Activity activity) {
        return new SmsReceiver(activity);
    }

    /**
      * 신규 인스턴스를 반환한다.
      * @param activity 부모 액티비티
      * @param callback 콜백
      * @return 신규 인스턴스
      */
    public static SmsReceiver getInstance(Activity activity, SmsReceiverCallback callback) {
        return new SmsReceiver(activity, callback);
    }

    /**
      * 콜백을 반환한다.
      * @return 콜백
      */
    public SmsReceiverCallback getCallback() {
        return mCallback;
    }

    /**
      * 콜백을 설정한다.
      * @param callback 콜백
      */
    public void setCallback(SmsReceiverCallback callback) {
        mCallback = callback;
    }

    /**
      * 부모 액티비티를 반환한다.
      * @return 부모 액티비티
      */
    public Activity getParentActivity() {
        return mActivity;
    }

    /**
      * 부모 액티비티를 설정한다.
      * @param activity 부모 액티비티
      */
    public void setParentActivity(Activity activity) {
        mActivity = activity;
    }

    /**
      * 화이트 리스트에 발신번호를 추가한다.
      * @param senderNo 발신번호
      * @return 추가에 성공하면 true, 실패하면 false를 반환
      */
    public boolean addSenderNo(String senderNo) {
        for (String no : mSenderNoList{
            if (no.equals(senderNo)) return false;
        }
        mSenderNoList.add(senderNo);
        return true;
    }

    /**
      * SMS 수신의 일시정지 여부를 설정한다.
      * @param pause 일시정지 하려면 true, 해제하려면 false
      */
    public void setPause(boolean pause) {
        mPause = pause;
    }

    /**
      * 수신 항목에 본 클래스를 등록한다.
      */
    public void register() {
        if (mActivity != null{
            if (mIntentFilter == null) { // 이전에 등록한 적이 없을 경우에만
                mIntentFilter = new IntentFilter(SMS_ACTION);
                mActivity.registerReceiver(this, mIntentFilter);
            }
        }
    }

    /**
      * 수신 항목에서 본 클래스를 등록 해제한다.
      */
    public void unregister() {
        if (mActivity != null{
            if (mIntentFilter != null) { // 이전에 등록한 적이 있을 경우에만
                mActivity.unregisterReceiver(this);
                mIntentFilter = null;
            }
        }
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (!mPause) { // 일시정지 상태가 아닐 경우에만 처리
            if (intent.getAction().equals(SMS_ACTION)) {
                Bundle bundle = intent.getExtras();
                if (bundle != null{
                    Object[] pduObj = (Object[]) bundle.get("pdus");
                    if (pduObj != null{
                        for (Object obj : pduObj) {
                            SmsMessage smsMsg = getIncomingMessage(obj, bundle);

                            // 발신 번호 및 발신 내용 가져오기
                            String senderNo = smsMsg.getDisplayOriginatingAddress();
                            String messageBody = smsMsg.getDisplayMessageBody();

                            // 발신 내용 맨 앞의 '[Web발신]' 문자열을 제거한다.
                            if (messageBody != null && messageBody.length() > 0{
                                messageBody = messageBody.replace("[Web발신]", "");
                            }

                            boolean isExistsSenderNo = false;

                            // 화이트 리스트 검사
                            if (mSenderNoList.size() > 0{
                                for (String no : mSenderNoList{
                                    if (no.equals(senderNo)) {
                                        isExistsSenderNo = true;
                                        break;
                                    }
                                }
                            }

                            if (mCallback != null && isExistsSenderNo) {
                                mCallback.onReceive(senderNo, messageBody);
                            }
                        }
                        this.abortBroadcast();
                    }
                }
            }
        }
    }

    /**
      * 안드로이드 버전에 따라 적절히 SMS 메시지를 처리한다.
      * @param obj PDUS 객체
      * @param bundle 번들 객체
      * @return SMS 메시지 객체
      */
    @SuppressWarnings("deprecation")
    private SmsMessage getIncomingMessage(Object obj, Bundle bundle) {
        SmsMessage smsMsg;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 마시멜로 이상 버전
            String fmt = bundle.getString("format");
            smsMsg = SmsMessage.createFromPdu((byte[])obj, fmt);
        }
        else { // 마시멜로 미만 버전
            smsMsg = SmsMessage.createFromPdu((byte[])obj);
        }
        return smsMsg;
    }
}

MainActivity.java (패키지: com.myapps.app)

/**
  * Copyright © 2018 Arumizz Artworks. All Rights Reserved.
  */

package com.myapps.app;
....
import com.myapps.sms.SmsReceiver;
import com.myapps.sms.SmsReceiverCallback;

....
public class MainActivity extends AppCompatActivity {
    ....
    /**
      * SMS 리시버
      */
   
private SmsReceiver mSmsReceiver = null;
    ....
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ....

        mSmsReceiver = new SmsReceiver(this, new SmsReceiverCallback() {
            @Override
            public void onReceive(String senderNo, String messageBody) {
                if (messageBody != null && messageBody.length() > 0) {

                    // 개행문자는 반드시 제거
                    messageBody = messageBody.replaceAll("\n", "");

                    boolean validMessage = (
                        messageBody.startsWith("[앱스] 휴대전화") ||
                        messageBody.startsWith("[앱스] 보호자 휴대전화") ||
                        messageBody.startsWith("[앱스] 아이디 찾기") ||
                        messageBody.startsWith("[앱스] 비밀번호 찾기")
                    ) && messageBody.contains("인증번호는");

                    // 인증번호 추출
                    if (validMessage) {
                        String authCode = messageBody;

                        authCode = authCode.replace("[Web발신]", "");
                        authCode = authCode.replace("[앱스] 휴대전화", "");
                        authCode = authCode.replace("[앱스] 보호자 휴대전화", "");
                        authCode = authCode.replace("[앱스] 아이디 찾기", "");
                        authCode = authCode.replace("[앱스] 비밀번호 찾기", "");
                        authCode = authCode.replace("인증번호는", "");
                        authCode = authCode.replace("입니다. 정확히 입력해주세요.", "");

                        // 앞뒤 공백 제거
                        authCode = authCode.trim();

                        if (authCode.length() > 0) {

                            // 여기에 authCode 값을 이용하여 본인이 원하는 처리코드를 작성하면 된다.
                        }
                    }
                }
            }
        });
        mSmsReceiver.addSenderNo("1577-0000"); // 발신자 전화번호를 제한함
        mSmsReceiver.register();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ....
        
mSmsReceiver.unregister();
    }
}