안드로이드 - SMS 수신시 인증번호 자동 추출하기
안드로이드 앱을 구현하면서 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="//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();
} }
|