Android

Android Push Notification 구현 (커스텀 알림음 포함)

Dean83 2022. 3. 24. 22:58

참조 사이트 : https://firebase.google.com/docs/cloud-messaging/android/client?hl=ko
실제 구현 예제 : https://medium.com/2kyung19/firebase-android-%EC%97%B0%EB%8F%99-%EB%B0%8F-push-%EC%95%8C%EB%A6%BC%EB%B3%B4%EB%82%B4%EA%B8%B0-ec6c57b2eb68
                       (조금 예전버전으로, 더이상 사용하지 않는 FirebaseInstanceIdService를 사용함. 이는 FirebaseMessagingService 내부로 통합되었음)
                      https://herojoon-dev.tistory.com/23?category=797826

** Flow 설명
     - Firebase 에서 FCM 메세지 발생시, 안드로이드 앱에 구현한 서비스에서 이를 수신 -> 수신받은 메세지를 내부 notification으로 noti 발생.
        - 따라서, Firebase에서 메세지를 전달해줄 앱 등록 필요.
        - 앱에서는 Firebase 연동 sdk 및 코드 세팅 필요
        - 메세지를 수신할 서비스 등록 및 해당 클래스 구현 필요
        - 클래스에서 메세지 수신시 내부 noti를 통해 noti 발생
        - 앱을 한번은 실행을 해주어야 메세지 수신이 가능함.

1. Google Firebase 세팅
     - https://console.firebase.google.com/ 으로 접속하여 프로젝트 생성 및 android 앱의 패키지 네임 등록
     - google-services.json 을 다운로드 하여, Android Studio 의 프로젝트에 추가 (Project 탭에서 앱 명칭 -> app 폴더 하위에 추가. (gradle과 같은 위치)
     - 2개의 gradle (app 에 있는 gradle과 전역 gradle) 에 각각 세팅 맟  sdk 사용 추가 (https://firebase.google.com/docs/cloud-messaging/android/client?hl=ko#kotlin+ktx_1 참조)
     

2. Android Studio 에서 세팅
    - 메뉴 중 Tools -> FireBase 클릭 -> 우측에서 1, Connect your app to Firebase 및 Add FCM to your app 에서 클릭을 통해 설정

3. Manifest 수정
    - 인터넷 접근권한 추가 
       - <application> 태그 위에 <uses-permission android:name="android.permission.INTERNET " /> 추가
       - uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 추가

    - </activity> 하단에 다음의 코드 추가. 서비스는 현재 구현되어 있지 않으므로 오류가 날 것임.  
    - FCM으로 부터 메세지를 수신받고 기기로 알림을 보내는 클래스 파일을 service android:name에 작성한 동일한 이름으로 만들어 줘야 한다.  

<service android:name=".MyFirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>

4. 서비스 추가
    - Android Studio 에서 클래스 추가 -> 클래스명은 Manifest의 Service에 등록한 MyFirebaseMessagingService 로 생성.
    - 26버전 미만, 이상의 처리가 다르다.
    - 최신버전에서는 channel id와 name 설정을 추가로 해야한다.

package com.example.pushtest;
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.media.AudioAttributes;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.view.textclassifier.ConversationActions;

import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;

import com.google.firebase.FirebaseApp;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class MyFirebaseMessagingService extends FirebaseMessagingService {
    @NonNull
    @Override
   public void onMessageReceived(RemoteMessage remoteMessage) {
        if(remoteMessage.getNotification()!= null)
        {
            sendNotification(remoteMessage.getNotification().getTitle(),remoteMessage.getNotification().getBody());
        }
    }

    private void sendNotification(String messageTitle, String body)
    {
        Intent intent = new Intent(this,MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_ONE_SHOT);

        String channelID = "sajuid";
        String channelName = "sajuname";

        //실제 사용할 효과음을 res -> raw에 등록 후 사용.
        Uri defaultSound = Uri.parse("android.resource://"+ getPackageName() + "/raw/cancel");
        NotificationCompat.Builder notiBuilder = new NotificationCompat.Builder(this,channelID).setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(messageTitle).setContentText(body).setAutoCancel(true).setSound(defaultSound).setContentIntent(pendingIntent);

        NotificationManager notimanager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
            NotificationChannel chan = new NotificationChannel(channelID,channelName,NotificationManager.IMPORTANCE_HIGH);
            chan.setSound(defaultSound, new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());
            notimanager.createNotificationChannel(chan);
        }

        notimanager.notify(0,notiBuilder.build());
    }

    @NonNull
    @Override
    public void onNewToken(String s)
    {
        super.onNewToken(s);
        sendTokenRegist(s);
    }

    @NonNull


    private void sendTokenRegist(String token)
    {
        //구현된 백엔드 서버에 전송 필요
    }
}

    4. 1. MainActivity
            - Push Notification을 수신하기 위한 토큰값을 검색해야함. 
            - onCreate에 구현

package com.example.pushtest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Debug;
import android.util.Log;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.messaging.FirebaseMessaging;

public class MainActivity extends AppCompatActivity {

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

        FirebaseMessaging.getInstance().getToken().addOnCompleteListener(new OnCompleteListener<String>() {
            @Override
            public void onComplete(@NonNull Task<String> task) {
                if(!task.isSuccessful())
                {
                    return;
                }

                String token = task.getResult();
                Log.d("haha",token);
            }
        });
    }
}



5. 메세지 전송
    - firebase 콘솔에 접속 (https://console.firebase.google.com/)
    - 프로젝트 선택 후 Cloud Messaging 선택
    - 새 알림 클릭하여 내용 작성 후 전달

6. 특정 사용자에게만 메세지 전달하기
    6. 1. firebase 콘솔에 세팅이 필요함. 

            - firebase 콘솔 접속 -> 톱니바퀴 모양의 설정 클릭 -> 서비스계정 클릭 -> 새 비공개키 생성 클릭



    6. 2. 특정 기기에만 push 전송 (서버)
            - 특정 사용자에게만 메세지 전달하는 방법
            - 서버에서 구현 필요 (참조 : https://firebase.google.com/docs/cloud-messaging/send-message?hl=ko#python)
            - firebase -> 프로젝트 -> 설정 -> 서비스계정 에서 firebase admin SDK 에서 비공개키 생성을 통한 json 필요
            - 참조 2 : https://herojoon-dev.tistory.com/23?category=797826
            - 구현 상세
               - 클라이언트에서 token값을 검색 -> 로그인시 회원 아이디와 토큰값을 서버에 전달
               - 서버에서 push 알림 생성시, 저장된 토큰값을 이용하여 FCM 서버에 메세지 전달
               - FCM에서 해당 토큰을 사용중인 클라이언트에게 push 전달

7. 커스텀 알림음
     - 사용할 알림음을 project -> app -> res -> raw에 저장
     - raw 폴더가 없을경우 생성
     - 파일 확장자는 영문자 소문자 및 _ 만 가능함.
     - 안드로이드 최신버전의 경우, NotificationChannel 을 사용하게 되는데, 해당 변수에서 setSound를 통해 커스텀 알림음을 등록해야 한다.
     - background 이거나 앱실행중이 아닐경우 : https://mintpot.synology.me:30000/boards/50/topics/516

// cancel 이라는 파일명의 음성파일이 res -> raw에 있어야 함.
Uri defaultSound = Uri.parse("android.resource://"+ getPackageName() + "/raw/cancel");



if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
            NotificationChannel chan = new NotificationChannel(channelID,channelName,NotificationManager.IMPORTANCE_HIGH);
            chan.setSound(defaultSound, new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());
            notimanager.createNotificationChannel(chan);
        }

        notimanager.notify(0,notiBuilder.build());