국비교육(22-23)

85일차(1)/Android App(49) : Notification(2)

서리/Seori 2023. 2. 9. 18:04

85일차(1)/Android App(49) : Notification(2)

 

- 알림Notification 띄우기 / 자동,수동 삭제 방법

 

 

 

- 이전 예제에서 만든 자동으로 사라지는 알림 창 코드 복습!

 

- 알림 창을 클릭하면 어떤 액티비티가 활성화되면서 알림이 사라진다.

 (DetailActivity 로 이동하도록 함)

 

[ 알림을 띄우기 위해서 필요한 작업 ]
1. 알림 채널을 만들어야 한다.
2. 사용자가 직접 알림을 허용하도록 유도해야 한다.
3. AndroidManifest.xml에 알림 pemission 설정이 있어야 한다.

 

- 알림 띄우기 버튼을 클릭하면, 알림 권한이 켜져있는지 꺼져있는지 확인해서 꺼져있으면 켜도록 요청했다.

 

 

- 채널은 최초에 한번만 만들면 된다.

- 이전에 만든 코드로는 알림을 띄울때마다 매번 채널을 생성하게 되므로,

 onCreate() 메소드 if문 안으로 위치를 옮겨서 확인 후 채널을 생성하게 한다.

 

- 채널이 동작하는지 확인하려면?

- 가상기기에서 앱을 삭제하고 새로 install하면 알림권한 확인 창부터 다시 뜬다.

notification

 

- MainActivity 에서 알림 띄우기 버튼을 눌러서 알림을 띄우고,

 알림센터에서 해당 알림을 클릭하면 DetailActivity가 활성화된다.

 

- 그럼 MainActivity는 지금 onStop 되어있을까?

→ 아니다. 만약 그렇다면 백버튼을 누르면 돌아가야하는데 지금은 백버튼을 누르면 기본화면으로 간다.

 

 

- 이 intent에다가 이런 설정을 넣었기 때문에,

 DetailActivity가 기존에 떠 있는 Activity 위에 쌓인 것이 아니고, 새로운 태스크에서 실행되었다.

 

 

- 알림을 실행했을때 실행할 액티비티 정보를 담고있는 인텐트를 PendingIntent 객체에 담고,

 알림을 띄울 때 이 PendingIntent 를 같이 전달하는 구조이다.

 


 

** 수동 취소되는 알림 만들기

 

MainActivity

package com.example.step21notification;

import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
/*
    알림을 띄우기 위해서 필요한 작업

    1. 알림 채널을 만들어야 한다.
    2. 사용자가 직접 알림을 허용하도록 유도해야 한다.
    3. AndroidManifest.xml에 알림 pemission 설정이 있어야 한다.
 */
public class MainActivity extends AppCompatActivity {
    //알림 체널의 이름 정하기
    public static final String CHANNEL_NAME = "com.example.step21notification.MY_CHANNEL";
    //필요한 필드
    EditText inputMsg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        inputMsg = findViewById(R.id.inputMsg);
        //Auto Cancel 버튼을 눌렀을때 동작
        Button notiBtn = findViewById(R.id.notiBtn);
        notiBtn.setOnClickListener(v -> {
            makeAutoCancelNoti();
        });
        //수동 취소 버튼을 눌렀을때 동작
        Button notiBtn2=findViewById(R.id.notiBtn2);
        notiBtn2.setOnClickListener(v -> {
            makeManualCancelNoti();
        });

        //만일 알림이 가능하지 않다면(알림 채널이 만들어져 있지 않다면)
        if(!NotificationManagerCompat.from(this).areNotificationsEnabled()){
            //알림 채널을 만든다.
            createNotificationChannel();
        }
    }
    //수동으로 취소하는 알림을 띄우는 메소드
    public void makeManualCancelNoti(){
        if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
            //권한이 필요한 목록을 배열에 담는다.
            String[] permissions={Manifest.permission.POST_NOTIFICATIONS};
            //배열을 전달해서 해당 권한을 부여하도록 요청한다.
            ActivityCompat.requestPermissions(this,
                    permissions,
                    0); //요청의 아이디
            return;
        }
        //입력한 문자열을 읽어온다.
        String msg = inputMsg.getText().toString();
        //createNotificationChannel();
        //알림을 클릭했을때 활성화시킬 액티비티 정보를 담고 있는 Intent 객체
        Intent intent = new Intent(this, DetailActivity.class);
        //액티비티를 실행하는데 새로운 태스크에서 실행되도록 한다(기존에 onStop() 에 머물러 있다면 제거하고 새로 시작)
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
        //intent에 msg라는 키값으로 String type을 담는다. 새로 시작된 액티비티에서 읽어낼 수 있다.
        intent.putExtra("msg", msg);

        //알림의 아이디 얻어내기 (값이 겹치지않게)
        int currentId = (int) (System.currentTimeMillis() / 1000);
        intent.putExtra("notiId", currentId);

        //인텐트 전달자 객체
        PendingIntent pendingIntent = PendingIntent.getActivity(this, currentId, intent, PendingIntent.FLAG_MUTABLE);

        //띄울 알림을 구성하기
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_NAME)
                .setSmallIcon(android.R.drawable.star_on) //알림의 아이콘
                .setContentTitle("알림을 취소해 주세요!") //알림의 내용
                .setContentText(msg) //알림의 내용
                .setPriority(NotificationCompat.PRIORITY_DEFAULT) //알림의 우선순위
                .setContentIntent(pendingIntent) //인텐트 전달
                .setAutoCancel(false); //자동 취소되는 알림인지 여부

        //알림 만들기
        Notification noti=builder.build();

        //알림 매니저를 이용해서 알림을 띄운다.
        NotificationManagerCompat.from(this).notify(currentId, noti);
    }

    //자동으로 취소하는 알림을 띄우는 메소드
    public void makeAutoCancelNoti(){
        if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
            //권한이 필요한 목록을 배열에 담는다.
            String[] permissions={Manifest.permission.POST_NOTIFICATIONS};
            //배열을 전달해서 해당 권한을 부여하도록 요청한다.
            ActivityCompat.requestPermissions(this,
                    permissions,
                    0); //요청의 아이디
            return;
        }
        //입력한 문자열을 읽어온다.
        String msg = inputMsg.getText().toString();
        //createNotificationChannel();
        //알림을 클릭했을때 활성화시킬 액티비티 정보를 담고 있는 Intent 객체
        Intent intent = new Intent(this, DetailActivity.class);
        //액티비티를 실행하는데 새로운 태스크에서 실행되도록 한다(기존에 onStop() 에 머물러 있다면 제거하고 새로 시작)
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
        //intent에 msg라는 키값으로 String type을 담는다. 새로 시작된 액티비티에서 읽어낼 수 있다.
        intent.putExtra("msg", msg);

        //알림의 아이디 얻어내기 (값이 겹치지않게)
        int currentId = (int) (System.currentTimeMillis() / 1000);

        //인텐트 전달자 객체
        PendingIntent pendingIntent = PendingIntent.getActivity(this, currentId, intent, PendingIntent.FLAG_MUTABLE);

        //띄울 알림을 구성하기
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_NAME)
                .setSmallIcon(android.R.drawable.ic_btn_speak_now) //알림의 아이콘
                .setContentTitle("얘들아 나야~") //알림의 내용
                .setContentText(msg) //알림의 내용
                .setPriority(NotificationCompat.PRIORITY_DEFAULT) //알림의 우선순위
                .setContentIntent(pendingIntent) //인텐트 전달
                .setAutoCancel(true); //자동 취소되는 알림인지 여부

        //알림 만들기
        Notification noti=builder.build();

        //알림 매니저를 이용해서 알림을 띄운다.
        NotificationManagerCompat.from(this).notify(currentId, noti);
    }


    //앱의 사용자가 알림을 직접 관리 할수 있도록 알림 채널을 만들어야한다.
    public void createNotificationChannel(){
        //알림 채널을 지원하는 기기인지 확인해서
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //알림 채널을 만들기

            //셈플 데이터
            String name="삼성카드";
            String text="테스트!";
            //알림 채널 객체를 얻어내서
            NotificationChannel channel=
                    new NotificationChannel(CHANNEL_NAME, name, NotificationManager.IMPORTANCE_DEFAULT);
            //채널의 설명을 적고
            channel.setDescription(text);
            //알림 매니저 객체를 얻어내서
            NotificationManager notiManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
            //알림 채널을 만든다.
            notiManager.createNotificationChannel(channel);
        }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode){
            case 0:
                //권한을 부여 했다면
                if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    //자동 취소 알림 띄우기
                    makeAutoCancelNoti();
                }else{//권한을 부여 하지 않았다면
                    Toast.makeText(this, "알림을 띄울 권한이 필요합니다.",
                            Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

 

- 버튼에 onClickListener를 지정하고, 

 수동으로 취소하는 알림을 띄울 makeManualCancelNoti() 메소드 생성

 

- 자동 취소 메소드를 복사해와서 일부 수정해주기!

 

 

- 알림의 아이디를 얻어와서 전달해주기

 이 아이디를 DetailActivity에 전달한다. putExtra() 로 intent에 담으면 된다.

 

- 자동취소되는 알림인지 여부를 false로 바꾸기

 

 

- setSmallIcon() 메소드에서 알림 아이콘을 다른 것으로 바꾸어 볼 수 있다.

 

 

 

- 알림을 클릭해서 Detail Activity로 이동했음에도 불구하고, 알림창에 알림이 여전히 남아 있다.

 

- 블루투스로 음악을 들을 때 음악 어플리케이션을 꺼도 알림창에는 여전히 음악을 제어할 수 있는 기능이 남아있다.

- 알림창에 그런 제어 기능을 가지고있는 알림들은 항상 띄워놓고 필요할 때마다 들어가봐야 할 수 있다.

ex) 알람 남은시간 확인 등이 알림에서 줄어들고 있는 것 등등...

 

- 이런 알림의 경우 사용자가 일부러 취소(제거)하지 않는 이상 남아있는다.

 


 

- DetailActivity에 수동으로 알림을 제거하는 버튼 추가

 

activity_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.step21notification.DetailActivity"
    android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:id="@+id/textView"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="알림 삭제하기"
        android:id="@+id/cancelBtn"/>
</LinearLayout>

 

DetailActivity

package com.example.step21notification;

import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationManagerCompat;

public class DetailActivity extends AppCompatActivity {

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

        //전달된 intent 객체
        Intent intent = getIntent();
        //intent 객체에 "msg"라는 키값으로 전달된 문자열 얻어내기
        String msg=intent.getStringExtra("msg");
        //TextView에 출력해보기
        TextView textView=findViewById(R.id.textView);
        textView.setText(msg);

        Button cancelBtn=findViewById(R.id.cancelBtn);
        cancelBtn.setOnClickListener(v -> {
            //Intent 객체에 담긴 알림의 아이디 얻어내기
            int notiId=intent.getIntExtra("notiId", 0);
            //알림의 아이디를 이용해서 알림 취소하기
            NotificationManagerCompat.from(this).cancel(notiId);
        });
    }
}

 

- getStringExtra() 로 MainActivity에서 intent 객체에 넣었던 문자열을 읽어온다.

 

- getIntExtra() : 메소드에 디폴트 값이 없을 경우, 처음에 들어갈 디폴트값을 넣어주어야 한다.(에러 방지용)

- notiId를 이용해서 해당 아이디를 가지고 있는 알림을 취소한다!

 

- 수동 알림을 띄우고,

 

- 들어가서 알림 삭제하기 버튼을 누르면 알림의 별 아이콘이 사라진다.