나만의 개발노트

[안드로이드] 브로드캐스트 수신자, onReceiver, SMS메시지 수신 어플 본문

[안드로이드]/[안드로이드] 공부 기록

[안드로이드] 브로드캐스트 수신자, onReceiver, SMS메시지 수신 어플

노트포미 2023. 11. 10. 14:49

메모 : ***22버전 이후 위험권한으로 SMS 수신 안되는 부분 수정 필요***

(아래 코드는 22 이전 버전에서만 작동합니다)


브로드캐스트 수신자 (Broadcast Receiver)

: 앱에서 브로드캐스팅 메시지를 받고 싶을 때, 브로드캐스트 수신자를 등록하여 받는다

 

*브로드캐스팅(Braodcasting)이란,

   메시지를 여러 대상에게 전달하는 

   -> 안드로이드는 여러 어플리케이션 구성요소에게 메시지를 전달하고 싶은 경우 브로드캐스팅을 사용

    ex) SMS 메시지를 받을 때

 

[브로드캐스트 수신자 등록 방법] 

1. new 브로드캐스트 수신자

- Mainfest.xml에 receiver 태그로 등록

 

2. Manifest.xml에 receiver 태그 안에 intent-filter태그를 통해 원하는 브로드캐스팅 메시지를 제한

  ex) SMS 메시지 받기

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

 

3. SMS를 수신할 권한 부여

- Manifest.xml에 RECEIVE_SMS 권한 추가

(마시멜로 이후 버전은, SMS권한을 위험권한으로 승인 필요
  이전 버전으로 낮추려면, build.gradle에서 targetSdkVersion을 22이하로 낮추기)

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

 

*uses-feature 입력하니 가능

 <uses-feature
        android:name="android.hardware.telephony"
        android:required="false" />

 

-> 브로딩캐스팅 메시지가 인텐트 객체로 전달됨 (SMS를 보낸 사람, SMS내용, 보낸 시간)

SMS 메시지를 받아서 SmsActivity에 전달하는 과정

 


[사용법 - 브로드캐스팅 SMS메시지 받아서 띄우는 액티비티 만들기]

1. 브로드캐스트 수신자 등록 (본문 윗쪽 참고)

    1) app 우클릭 -> New -> Other -> Broadcast Receiver

    2) Manifest.xml에 receiver태그 수정

    3) SMS 권한 받기

 

2. 전달받은 SMS메시지를 확인할 수 있는 코드 작성

    1) 전달받은 SMS 객체 내용을 Bundle 형태로 받기

Bundle bundle = intent.getExtras();

   2) Bundle을 SmsMessage[] 로 바꾸는 parseSmsMessage() 만들기

    *SmsMessage는 android.telephony클래스

SmsMessage[] messages = parseSmsMessage(bundle);
private SmsMessage[] parseSmsMessage(Bundle bundle){
    //SMS 데이터를 처리하는 국제표준 프로토콜 smpp에 pdus라는 이름
    //pdus에 SMS 데이터 관련 내용이 들어가 있다 --> 좀 더 공부 필요
    Object[] objects = (Object[]) bundle.get("pdus");
    SmsMessage[] messages = new SmsMessage[objects.length];

    for(int i=0;i<objects.length;i++){
        //마시멜로 (23) 버전 이상의 경우, string 인자가 하나 더 필요
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            String format = bundle.getString("format");
            messages[i] = SmsMessage.createFromPdu((byte[]) objects[i],format);
        } else{
            //마시멜로(23) 이전 버전
            messages[i] = SmsMessage.createFromPdu((byte[]) objects[i]);
        }
    }
    return messages;
}

   3) onReceive()에 전달받은 SmsMessage[] 출력하기 (index 0 만 예시)

if(messages.length > 0){
        //SMS 수신자 정보
        String sender = messages[0].getOriginatingAddress();
        Log.d(TAG,"sender : "+ sender);

        //SMS 내용
        String contents = messages[0].getMessageBody();
        Log.d(TAG,"contents : "+ contents);

        //수신 시간 Date -> String으로 변경
        String receivedDate = dateFormat.format(messages[0].getTimestampMillis());
        Log.d(TAG,"receivedDate : "+ receivedDate);
    }

 

*Date -> String 방법

//원하는 String 형태 format 만들기
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm");

String date = dateFromat.format(Date);

 

3. SMS 메시지를 띄울 액티비티 생성

   1) app 우클릭 -> New -> Activity (SmsActivity생성)

   2) activity_sms.xml 작성

   3) SmsActivity.java 작성

      - activity_sms.xml 에서 editText 불러오기

      - 브로드캐스팅 받은 intent 받기

Intent passsedIntent = getIntent();
//intent에서 내용 표시해주는 함수
processCommand(passsedIntent);

      - onNewIntent() 생성

//이미 생성된 액티비티는 onCreate()가 아닌, onNewIntent()로 시작됨 *생명주기 참고
@Override
protected void onNewIntent(Intent intent) {
    processCommand(intent);
    super.onNewIntent(intent);
}

    - processCommand() 생성

//intent의 내용을 editText에 지정하는 함수
private void processCommand(Intent intent){
    if(intent != null){
        String sender = intent.getStringExtra("sender");
        String contents = intent.getStringExtra("contents");
        String receivedDate = intent.getStringExtra("receivedDate");

        editText1.setText(sender);
        editText2.setText(contents);
        editText3.setText(receivedDate);
    }
}

 

4. 액티비티에 SMS메시지 전달

  - SmsActivity.java에 액티비티로 보내는 함수 sendToActivity() 생성

//액티비티로 메시지 내용을 보내는 함수
//context도 전달받아야함
private void sendToActivity(Context context, String sender, String contents, String receivedDate){
    Intent intent = new Intent(context, SmsActivity.class);
    //보이지 않는 브로드캐스트를 보이는 액티비티로 띄워주기 위해, Flag를 추가해야함
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                    |Intent.FLAG_ACTIVITY_SINGLE_TOP
                    |Intent.FLAG_ACTIVITY_CLEAR_TOP);
    intent.putExtra("sender",sender);
    intent.putExtra("contents",contents);
    intent.putExtra("receivedDate",receivedDate);

    //Context.startActivity로 액티비티에 intent 보내기
    context.startActivity(intent);
}

#최종 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-feature
        android:name="android.hardware.telephony"
        android:required="false" />

    <uses-permission android:name="android.permission.RECEIVE_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.MySMSReceiver"
        tools:targetApi="31">
        <activity
            android:name=".SmsActivity"
            android:exported="false" />

        <receiver
            android:name=".SmsReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.provier.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>

        <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>
        </activity>
    </application>

</manifest>

#최종 SmsReceiver.java

package com.example.mysmsreceiver;

import static androidx.core.content.ContextCompat.startActivity;

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.text.SimpleDateFormat;

public class SmsReceiver extends BroadcastReceiver {

    private static final String TAG = "SMSReciever";
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm");

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG,"onReceiver() 호출됨.");

        //전달받은 SMS 인텐트 객체 내용을 Bundle 형태로 받기
        Bundle bundle = intent.getExtras();
        SmsMessage[] messages = parseSmsMessage(bundle);

        if(messages.length > 0){
            //SMS 수신자 정보
            String sender = messages[0].getOriginatingAddress();
            Log.d(TAG,"sender : "+ sender);

            //SMS 내용
            String contents = messages[0].getMessageBody();
            Log.d(TAG,"contents : "+ contents);

            //수신 시간 Date -> String으로 변경
            String receivedDate = dateFormat.format(messages[0].getTimestampMillis());
            Log.d(TAG,"receivedDate : "+ receivedDate);

            //액티비티로 보내는 함수 호출
            sendToActivity(context, sender,contents,receivedDate);
        }
    }

    //액티비티로 메시지 내용을 보내는 함수
    private void sendToActivity(Context context, String sender, String contents, String receivedDate){
        Intent intent = new Intent(context, SmsActivity.class);
        //보이지 않는 브로드캐스트를 보이는 액티비티로 띄워주기 위해, Flag를 추가해야함
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                        |Intent.FLAG_ACTIVITY_SINGLE_TOP
                        |Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.putExtra("sender",sender);
        intent.putExtra("contents",contents);
        intent.putExtra("receivedDate",receivedDate);

        //Context.startActivity로 액티비티에 intent 보내기
        context.startActivity(intent);
    }

    private SmsMessage[] parseSmsMessage(Bundle bundle){
        //SMS 데이터를 처리하는 국제표준 프로토콜 smpp에 pdus라는 이름
        //pdus에 SMS 데이터 관련 내용이 들어가 있다 --> 좀 더 공부 필요
        Object[] objects = (Object[]) bundle.get("pdus");
        SmsMessage[] messages = new SmsMessage[objects.length];

        for(int i=0;i<objects.length;i++){
            //마시멜로 (23) 버전 이상의 경우, string 인자가 하나 더 필요
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
                String format = bundle.getString("format");
                messages[i] = SmsMessage.createFromPdu((byte[]) objects[i],format);
            } else{
                //마시멜로(23) 이전 버전
                messages[i] = SmsMessage.createFromPdu((byte[]) objects[i]);
            }
        }
        return messages;
    }

}

#최종 activity_sms.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".SmsActivity">

    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="수신자"/>

    <EditText
        android:id="@+id/editText2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="메시지 내용"/>

    <EditText
        android:id="@+id/editText3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="수신 시간"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="확인"/>
</LinearLayout>

#최종 SmsActivity.java

package com.example.mysmsreceiver;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class SmsActivity extends AppCompatActivity {

    EditText editText1;
    EditText editText2;
    EditText editText3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sms);

        editText1 = findViewById(R.id.editText1);
        editText2 = findViewById(R.id.editText2);
        editText3 = findViewById(R.id.editText3);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });

        //Intent 받기
        Intent passsedIntent = getIntent();
        processCommand(passsedIntent);
    }

    //이미 생성된 액티비티는 onCreate()가 아닌, onNewIntent()로 시작됨 *생명주기 참고
    @Override
    protected void onNewIntent(Intent intent) {
        processCommand(intent);
        super.onNewIntent(intent);
    }

    //intent의 내용을 editText에 지정하는 함수
    private void processCommand(Intent intent){
        if(intent != null){
            String sender = intent.getStringExtra("sender");
            String contents = intent.getStringExtra("contents");
            String receivedDate = intent.getStringExtra("receivedDate");

            editText1.setText(sender);
            editText2.setText(contents);
            editText3.setText(receivedDate);
        }
    }
}

[참조]

https://www.boostcourse.org/mo316/lecture/259038?isDesc=false

 

안드로이드 앱 프로그래밍

부스트코스 무료 강의

www.boostcourse.org