본문 바로가기

Android

Android : 문자캐치

문자캐치

회원가입 등에서 핸드폰인증을 할 때 인증문자가 오면, 문자에 쓰인 인증번호가 자동으로 폼에 입력되는 것을 보았을 것이다.
4대 컴포넌트중 하나인 BroadCastReceiver 이용하여 사용자의 상태를 읽고 문자가 오면 내용을 받아오는 예제를 구현해본다.




New - Other - Broadcast Receiver 로 리시버를 만든다.




Manifest.xml에 가서 receiver가 생긴것을 확인하고 컴포넌트의 중간다리인 intent 태그 안에 문자캐치를 위해 아래 코드를 추가한다.
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>

퍼미션을 추가한다.
<uses-permission android:name="android.permission.RECEIVE_SMS"/>


마쳤으면 smsReceiver.java 액티비티를 생성하고 인텐트객체를 생성한다.
setFlags() 메소드를 통해 어플이 꺼져있거나, 백그라운드에서 실행되는 경우 등을 통제한다.

smsReceiver.java
@Override
public void onReceive(Context context, Intent intent) {
Intent it = new Intent(context, MainActivity.class); //현재 화면 정보, 실행시킬 액티비티

//어플이 꺼져있어도 강제실행, 이미 생성된 액티비티가 있으면 그 액티비티 그대로 사용, 액티비티 위에 있는 다른 액티비티 모두 종료
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(it);

}

MainActivity.java의 onCreate()에 권한메시지 요청을 위해 아래 코드를 추가한다.
사용자가 이미 허용을 눌렀다면 권한메시지가 뜨지 않을 것이다.
if(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.RECEIVE_SMS},0);
}



smsReceiver.java에서 인텐트의 정보꺼내 문자내용을 얻어올 것이다.
Bundle bundle = intent.getExtras(); // 인텐트 안의 모든 정보 받기
getExtras()메소드로 bundle 객체를 얻어오는데, 비유하자면 인텐트는 택배차고 번들은 내 택배와 같다.


번들객체를 SmsMessage 타입으로 바꾸기 위한 메소드 parseMsg(bundle)을 추가한다.
SmsMessage[] msg = parseMsg(bundle); //번들객체를 SmsMessage로 바꾸기위한 메소드추가

아래 코드를 추가한다.
private SmsMessage[] parseMsg(Bundle bundle) {
Object[] obj = (Object[]) bundle.get("pdus"); // 번들에서 pdus꺼내면 메시지를 얻을 수 있음 / 한번에 여러개 올 수 있어서 배열로 처리함
SmsMessage[] temp = new SmsMessage[obj.length]; //Object 배열을 SmsMessage 배열로 변환하기 위한 배열생성

String format = bundle.getString("format"); //문자포맷 방식을 꺼내옴

for (int i =0; i<obj.length; i++){ // 변환을 위한 반복문
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
temp[i] = SmsMessage.createFromPdu((byte[])obj[i], format); //pdu방식으로 되어있는 obj배열을 byte 배열로 변환하고 문자포맷 방식을 두번째 파라미터로 넣음
}else{ // 마시멜로 이전버전은 format이 필요없으므로
temp[i]=SmsMessage.createFromPdu((byte[])obj[i]);
}
}

return temp; //변환완료되면 리턴


}


확인용으로 로그를 찍어본다. 문자를 보내보았다.
//문자캐치를 잘 하는지 확인
if(msg.length>0){
String sender = msg[0].getOriginatingAddress(); //보낸사람 전화번호
String contents = msg[0].getMessageBody(); //문자내용
Date time = new Date(msg[0].getTimestampMillis()); //도착시간

Log.v("Message", sender);
Log.v("Message", contents);
Log.v("Message", time.toString());

}

잘 전송된 모습.
12-12 14:34:13.840 28478-28478/com.example.pc_20.catchmessage V/Message: (삭제)
12-12 14:34:13.840 28478-28478/com.example.pc_20.catchmessage V/Message: 이얍!
12-12 14:34:13.840 28478-28478/com.example.pc_20.catchmessage V/Message: Tue Dec 12 14:34:14 GMT+09:00 2017

확인되었으면 putExtra()로 올린다.
it.putExtra("sender", sender);
it.putExtra("sender", contents);
it.putExtra("sender", time);

MainActivity.java에 오버라이드로 onNewIntent() 메소드를 추가하고 인텐트정보를 얻어 뿌려주면 끝.
//smsReceiver.java를 거치지 않았기 때문에 getIntent()를 써서는 얻을 수 없음.
//onNewIntent는 새로운 intent가 등장할때 호출되는 메소드
@Override
protected void onNewIntent(Intent intent) {
String sender = intent.getStringExtra("sender");
String contents = intent.getStringExtra("contents");
String time = intent.getStringExtra("time");
textView.setText(sender+"\n"+contents+"\n"+time);
}




전체 코드. 

MainActivity.java
package com.example.pc_20.catchmessage;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

TextView textView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.tv_msg);

if(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.RECEIVE_SMS},0);
}
}

//smsReceiver.java를 거치지 않았기 때문에 getIntent()를 써서는 얻을 수 없음.
//onNewIntent는 새로운 intent가 등장할때 호출되는 메소드
@Override
protected void onNewIntent(Intent intent) {
String sender = intent.getStringExtra("sender");
String contents = intent.getStringExtra("contents");
String time = intent.getStringExtra("time");
textView.setText("발신자: "+sender+"\n"+"내용: "+contents+"\n"+"시간: "+time);

}
}

smsReceiver.java
package com.example.pc_20.catchmessage;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;

import java.util.Date;

public class smsReceiver extends BroadcastReceiver {

String sender ="";
String contents="";
Date time;

@Override
public void onReceive(Context context, Intent intent) {

Bundle bundle = intent.getExtras(); // 인텐트 안의 모든 정보 받기
SmsMessage[] msg = parseMsg(bundle); //번들객체를 SmsMessage로 바꾸기위한 메소드추가

//문자캐치를 잘 하는지 확인
if(msg.length>0){
sender = msg[0].getOriginatingAddress(); //보낸사람 전화번호
contents = msg[0].getMessageBody(); //문자내용
time = new Date(msg[0].getTimestampMillis()); //도착시간

Log.v("Message", sender);
Log.v("Message", contents);
Log.v("Message", time.toString());

Intent it = new Intent(context, MainActivity.class); //현재 화면 정보, 실행시킬 액티비티
//어플이 꺼져있어도 강제실행, 이미 생성된 액티비티가 있으면 그 액티비티 그대로 사용, 액티비티 위에 있는 다른 액티비티 모두 종료
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

it.putExtra("sender", sender);
it.putExtra("contents", contents);
it.putExtra("time", time.toString());

context.startActivity(it);
}
}

private SmsMessage[] parseMsg(Bundle bundle) {
Object[] obj = (Object[]) bundle.get("pdus"); // 번들에서 pdus꺼내면 메시지를 얻을 수 있음 / 한번에 여러개 올 수 있어서 배열로 처리함
SmsMessage[] temp = new SmsMessage[obj.length]; //Object 배열을 SmsMessage 배열로 변환하기 위한 배열생성

String format = bundle.getString("format"); //문자포맷 방식을 꺼내옴

for (int i =0; i<obj.length; i++){ // 변환을 위한 반복문
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
temp[i] = SmsMessage.createFromPdu((byte[])obj[i], format); //pdu방식으로 되어있는 obj배열을 byte 배열로 변환하고 문자포맷 방식을 두번째 파라미터로 넣음
}else{ // 마시멜로 이전버전은 format이 필요없으므로
temp[i]=SmsMessage.createFromPdu((byte[])obj[i]);
}
}

return temp; //변환완료되면 리턴
}
}


이렇게 앱을 만들고 설치해놓으면, 문자가 올때마다 앱이 실행되므로 한 번 해보고 지우는 것이 좋다. ㅎㅎ;