본문 바로가기

아옳옳의 코딩공부/아옳옳의 안드로이드스튜디오

2021-03-25안드로이드 스튜디오(브로드캐스트3 , 위험 권한)

반응형

위험 권한은 앱이 실행되는 시점에 수락을 해야 동작을 한다 라고 알고 있자 ! 

 

어제 배운 내용들을 사용하여 오늘 위험권한에 대해서 살펴 볼것이다. 

어제 작성한 코드를 보면 sms를 받으면 그 정보들을 받아와 브로드캐스트에서 받아서 나의 앱에 던저주는 것까지 했다

 

그런데 sms와 같이 아에 외부에서 들어오는 글로벌 이벤트 말고도 핸드폰에서 보내는 시스템 브로드캐스트라는 것도 있다. 전화로 예를 들면 전화가 왔을때 , 전화를 걸었을때, 배터리가 없을때 , 배터리를 충전할때  등과 같이 시스템에서 보내는 정보들도 브로드캐스트로 받아줄 수 있다.  하지만 나의 앱에서 이러한 정보들을 사용할려면 먼저 사용자에게 동의를 얻어야 사용할 수 있다. 그게 위험권한인것이다.

 

말로 설명하는 것보다 코드를 보면서 이해를 하도록 하자  제일 중요한 매니패스트 부터 보자 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.systembroadcastreceiver_ex">

    <!-- 유저 허용 권한 이거 설정해놓으면  -->
    <!-- 이 앱이 해당 기능을 사용하게 허용할 것인지 묻는다-->
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
    <uses-permission android:name="android.permission.READ_CALL_LOG" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Chap11">
        <!--리시버 만듬 -->
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>  <!--리시버 속에 인텐트 필터로 이런 정보들만 받을꺼임-->
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>
        <!--전화가 왔을때 보여줄 Dialog액티비티  Theme 으로 작게 만들어줌 -->
        <activity 
            android:name=".DialogActivity"
            android:label="전화가 호롤롤로" 
            android:theme="@style/Base.Theme.AppCompat.Light.Dialog" /> 
        
        <activity android:name=".MainActivity"><!--메인 액티비티 -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

맨위는 위험권한을 추가한 부분이고 리시버 만들어주고 리시버에 인텐트 필터로 내가 원하는 값만 수신한다 

자 그럼 브로드캐스트 코드 보자 

public class MyReceiver extends BroadcastReceiver {

    @Override// 전화 왔을때 정보들을 받아서 리시버가 정리해서 다이얼로그 액티비티에 넘겨준다
    public void onReceive(Context context, Intent intent) {


        // 넘어온 정보 action에 액션값 담아주기
        String action = intent.getAction();
        //action 과 내가 인텐트 필터에 설정해준 값과 같은지 비교
        if(action.equals("android.intent.action.NEW_OUTGOING_CALL")){
            //값이 같다면 인텐트에서 EXTRA_PHONE_NUMBER 해당 값 가져와 폰넘버에 넣어줌
            String phonenumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            //새로운 인텐트 만들어서 다이얼로그 액티비티로 보내주기
            Intent intent2 = new Intent(context,DialogActivity.class);
            intent2.putExtra("number" , phonenumber); // 인텐트에 폰넘버 던져준다
            intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 플래그 설정
            context.startActivity(intent2);
            //위랑 같은 내용이다. (상태 확인하기 )
        }else if (action.equals("android.intent.action.PHONE_STATE")){
            Bundle bundle = intent.getExtras();
            String state = bundle.getString(TelephonyManager.EXTRA_STATE); // 상태값 빼오기
            String phonenumber = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
            // 상태값이 벨이 울리는 상황이라면 실행
            if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)&&phonenumber != null){
                // 벨이 울리고 있고 전화번호를 넘겨 받았기 때문에 참 이므로 인텐트로 던져줌
                Intent intent2 = new Intent(context , DialogActivity.class);
                intent2.putExtra("number" , phonenumber);
                intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent2);
            }
        }

    }
}

자세한 내용들은 주석으로 처리 중요한 내용은 전화가 오면 방송을 수신할 것이고 그 수신된 방송은 인텐트로 날아와 

그 인텐트를 가지고 작업을 해준것이다. 

다음은 이렇게 처리한 작업을 실제로 받는 다이얼로그액티비티로 가자  

ImageView imageView;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog);
        textView = findViewById(R.id.lab1_phone_number);
        imageView = findViewById(R.id.lab1_remove_icon);

        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        // 리시버에서 받은것으로 셋팅을 해준것이다.
        Intent intent = getIntent();
        String number = intent.getStringExtra("number");
        textView.setText(number);

    }
}

이 액티비티는 아까 매니패스트에서 Theme을 다이얼로그 형식으로 설정을 해놓은 상태이다.  

이러한 형태의 다이얼 로그이고 지금으 안보이지만 가운데에는 택스트뷰가 들어가 있어서 전화가 오면 전화번호를 찍어줄 것이다. 

이제 제일 중요한 메인 액티비티이다.

public class MainActivity extends AppCompatActivity {
        
     ListView listView; //리스트뷰에 설정할것인데 리스트뷰는어탭터가 필요함
     ArrayList<String> dates; // 실질적인 데이터를 담은 어레이 리스트
     ArrayAdapter<String> adapter; // 그래서 어탭터도 만들어줌

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

         // 위험권한을 허용한것인지 확인                                                                     // 이부분 패키지 매니저 이거 모르겠음
         if (ContextCompat.checkSelfPermission(this, Manifest.permission.PROCESS_OUTGOING_CALLS) != PackageManager.PERMISSION_GRANTED ||
                 ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED ||
                 ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
             //권한부여 요청하는 코드이다. 
             ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.PROCESS_OUTGOING_CALLS,
                     Manifest.permission.READ_CALL_LOG, Manifest.permission.READ_PHONE_STATE}, 100);
         }
        // 리스트뷰 추가
         listView = findViewById(R.id.list);
         
         // 데이터를 담아줄 그릇 생성
         dates = new ArrayList<>(); 
         
         // 어탭터 생성 데이터 넣어줌                                     //이거 뭐지? 
         adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, dates);
         //리스트 뷰에 어탭터 연결 
         listView.setAdapter(adapter); 
         // batteryStatus메소드 호출해서 배터리 확인 
         batteryStatus(); 
     }
     
     //배터리 관련된것 받아서 사용 하기 (이건 시스템에서 보내는 정보이다)
     public void batteryStatus() { //코드로 필터링 해주는거 같은데
         
         IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
         Intent batteryStatus = registerReceiver(null, filter);
         
         int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
         //전원 공급되고 있는지 파악 
         boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING;
         if (isCharging) {
             //전원 공급유형 파악 
             int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
             boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
             boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
             if (usbCharge) {
                 addList("Battery is USB Charging");
             } else {
                 addList("Battery is AC Charging");
             }
         }
         //몇퍼센트 충전된 상황인지 파악 
         int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
         int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
         float pct = (level / (float) scale) * 100;
         
         //아래 데이터 넣고 리셋해주는 메소드 호출 
         addList("current Battery :" + pct + "%"); 
         
         //코드에 작성하여 받아주는 것 이다 ! 첫장에 나왔다
         registerReceiver(brOn, new IntentFilter(Intent.ACTION_SCREEN_ON));
         registerReceiver(brOff, new IntentFilter(Intent.ACTION_SCREEN_OFF));
         registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_POWER_CONNECTED));
         registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_POWER_DISCONNECTED));

     }
        //데이터에 받은 정보 넣어주고 , 어댑터 리셋해주는 메소드 
     public void addList(String msg) {
         dates.add(msg);
         // 데이터 넣어주는데 데이터만 넣어주면 어댑터 변경이 안됨
         // 그래서 아래 notifyDataSetChanged이걸로 어탭터 리셋해준다
         adapter.notifyDataSetChanged();
     }

     //방송을 받으면 자동실행 되는 콜백메소드 !!!
     BroadcastReceiver brOn = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             //aad list 메소드로 정보를 전달해주면 addList에서 처리
             addList("Screen On");
         }
     };
     //방송을 받으면 자동실행 되는 콜백메소드 !!!
     BroadcastReceiver brOff = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             addList("Screen Off");
         }
     };
     //방송을 받으면 자동실행 되는 콜백메소드 !!
     BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (action.equals(Intent.ACTION_POWER_CONNECTED)) {
                 addList("ON Connected");
                 batteryStatus();
             } else if (action.equals(Intent.ACTION_POWER_DISCONNECTED)) {
                 addList("ON DisConnected");
                 batteryStatus();
             }
         }
     };


     @Override // 이부분 아직 이해 못했다... 이것도 물어보자 어제 배운거 같은데 낼 다시 물어보자 
     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
         if (requestCode == 100 && grantResults.length > 0) {
             if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED || grantResults[3] != PackageManager.PERMISSION_GRANTED) {
                 Toast.makeText(getApplicationContext(), "No Permission", Toast.LENGTH_LONG).show();
             } else {
                 Toast.makeText(getApplicationContext(), "Permission On", Toast.LENGTH_LONG).show();
             }
         }
     }

     @Override // 끝날때 해지시키는것 ( 등록시켰던것들 메모리에서 해지시키는 것 )
     protected void onDestroy() {
         super.onDestroy();
         unregisterReceiver(brOn);
         unregisterReceiver(brOff);
         unregisterReceiver(batteryReceiver);

     }
 }

주석을 달다 보니 코드가 많이 길어졌넹 .. .ㅋ

주석을 보면 이번 파트에서 중요한 부분은 코드에서 방송을 받아주고 처리하는 부분들을 잘 보면 될것같다 

 

이렇게 작성한것을 실행하게 되면 결과는 이렇게 나온다 

이렇게 전화를 걸면 전화가 걸려오늘걸 볼 수있다. 

두개가 뜨는데 위는 시스템에서 제공하는 것이고 , 가운데 파란색이 내가 다이얼로그 액티비티로 만들어 본 화면이다. 

자옆에 보면 리스트뷰에 배터리의 충전상태, 배터리의 현재 남은 용량, 화면을 on off 상태가 출력되는것을 볼 수있다.

 

이렇게 각종 정보들을 받아와서 나의 어플리케이션에 사용할때 브로드캐스트를 사용하며,

또 이렇게 각종 정보들을 받을 때 필요한 위험권한에 대해서 배워 보았다~ 

 

오늘은 여기까지~ 아옳!
 

반응형