Unity

Unity 에서 Android Push Notification 구현

Dean83 2022. 3. 24. 23:05

1. 요약
    - Google firebase를 통해 FCM으로 메세지 전송 -> Unity 에서 메세지 수신시, Local Push Notification 발생 순으로 동작
    * 앱이 실행중 일 때에는 popup 알림이 오나, 앱이 실행중이 아니거나 background에 있을때에는 popup은 표시되지 않고, Status Bar에만 표시된다.
       - 유니티로 개발시 백그라운드 서비스로 동작하지 않기 때문에 발생하는 문제
       - 이를 해결하기 위해서는 안드로이드로 fcm 수신 aar 등을 만든 후, 유니티에서 해당 서비스를 실행하는 방법으로 구현해야 한다. 이 경우, 아이폰도 마찬가지일 가능성이 높음.  

2. Google Firebase 세팅
    - 참조 : https://firebase.google.com/docs/cloud-messaging/unity/client?hl=ko
    - Firebase 프로젝트 생성 (위의 링크 참조) 및 앱 추가에 Unity를 선택
    - Android의 패키지 이름과 ios의 번들 id 세팅 
    - GoogleService-Info.plist (ios용) 과 google-services.json 다운로드 후 Unity 프로젝트 -> Assets 폴더에 붙여넣기

3. Firebase Unity SDK 추가
    
 - https://firebase.google.com/download/unity?hl=ko 에서 패키지 다운로드
     - Unity 에서 패키지 import를 하는데, FirebaseAnalytics.unitypackage와 FirebaseMessaging.unitypackage를 import (.net4를 주로 쓰므로 해당 패키지 import)
     - 각종 설정을 물어 보는데, 보통 enable로 두면, 패키지 재구성을 하게 됨. 이는 반드시 필요함. (필요한 파일들 자동으로 다운로드 및 manifest 파일 수정)      

4. Firebase 연동 스크립트 작성 (foreground 시에만 헤드업 팝업 발생)
    - 스크립트 생성 하여, 다음의 코드 참조 하여 개발
    - 단, 개인에게 보내는 FCM의 경우 백앤드 서버의 별도 개발이 필요함
    - 개인에게 보내는 메세지의 경우, Firebase.Messaging.MessageReceivedEventArgs 에 Notification이 아닌 data 항목에서 title 과 body를 추출해 내야 함

public void Start() {

  //android의 경우 token 이벤트만 추가 하면 된다. (ios의 경우 테스트 필요)
  //fcm 메세지 수신은 자바 aar 에서 수행한다.
  Firebase.Messaging.FirebaseMessaging.TokenReceived += OnTokenReceived;
  //Firebase.Messaging.FirebaseMessaging.MessageReceived += OnMessageReceived;


}

public void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token) {
  UnityEngine.Debug.Log("Received Registration Token: " + token.Token);
}

public void OnMessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e) {
  UnityEngine.Debug.Log("Received a new message from: " + e.Message.From);
}

5. Android Local Notification 발생
    - FCM을 통해 메세지 수신시, 알람이 적용되지 않음. 단순 메세지 수신만 하므로, 이후 해당 메세지 내용을 알림창에 내 보내는 Local Notification을 발생 해야함. 
    - Unity -> 상단 메뉴 중 Window -> Package Manager -> Mobile Notification 설치
    - Android 26버전 이상일 경우, 채널 id 를 세팅해주어야만 함. 

6. Android Local Notification 코드
    
- 참조 : https://docs.unity3d.com/Packages/com.unity.mobile.notifications@1.3/manual/Android.html
    - channel 초기화 코드
    - Importance를 High로 설정해야 상단 팝업이 발생한다

//유니티에서 메세지 발생치 않고 안드로이드의 경우 aar에서 처리한다. 

//var channel = new AndroidNotificationChannel()
//        {
//            Id = "채널아이디",
//            Name = "채널명",
//            Importance = Importance.High,
//            Description = "설명",
//        };

//        AndroidNotificationCenter.RegisterNotificationChannel(channel);

      - Notification 발생 코드
 

//안드로이드의 경우 aar 에서 처리한다.

private void FirebaseMessaging_MessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e)
    {
        //특정 대상을 위한 notification등 백앤드에서 개발시, e.Message.Notification
        //이 아닌 e.Message.Data 에서 title과 body를 추출해야 함. (예 : e.Message.data["title"])
        //notification.Title = e.Data["Title"];
        //notification.Text = e.Data["Body"];
        //notification.FireTime = System.DateTime.Now;
        //AndroidNotificationCenter.SendNotification(notification, "위에서 작성한 채널id");
    }

7. background 혹은 기기 슬립일때 Push 메세지 생성
    - Unity Package를 사용한 FCM 구현시에는 foreground 일때에만 Push 메세지를 표시할 수 있다. (수신은 기본적으로 됨) (유니티로 만든 앱이 서비스가 아니기 때문)
    - 따라서, 일반적으로 앱 실행중이 아닐때나, 기기가 슬립중일때 헤드업 알림을 표시하기 위해선 서비스 aar을 만들어 동작해야 한다. 

    7.1. 안드로이드 스튜디오에서 aar 생성 
           - app 하단에 있는 build.gradle 에서, plugins { id 'com.android.application' } 를 plugins { id 'com.android.library' } 로 변경

    7.2 Push 코드 추가 (슬립을 깨우는 코드도 포함됨)
          -  sendNotification 함수에 있는 Intent 생성시 사용할 class를 위해, 빈 클래스를 프로젝트에 추가 하여 인자값으로 전달

package net.mintpot.unitypushnotification;

import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.PowerManager;
import android.util.Log;

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

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

public class PushNotificationService extends FirebaseMessagingService {

    private static PowerManager.WakeLock m_wakeLock;

    @NonNull
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        if(remoteMessage.getNotification()!= null)
        {
            sendNotification(remoteMessage.getNotification().getTitle(),remoteMessage.getNotification().getBody());
        }else if(remoteMessage.getData().size() > 0)
        {
            sendNotification(remoteMessage.getData().get("title"),remoteMessage.getData().get("body"));
        }
    }

    private void sendNotification(String messageTitle, String body)
    {
        Log.d("haha","title : " + messageTitle + " body : " + body);
        ReleaseWakeLock();
        Intent intent = new Intent(this, UnityPushNotification.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_ONE_SHOT);

        String channelID = "mintpush";
        String channelName = "mintpush";
        //Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        //Uri defaultSound = Uri.parse("android.resource://"+ getPackageName() + "/raw/fifteenmin");
        NotificationCompat.Builder notiBuilder = new NotificationCompat.Builder(this,channelID).setSmallIcon(R.drawable.notify_icon_small)
                .setContentTitle(messageTitle).setContentText(body).setAutoCancel(true).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)
    {
        GetWakeLock();
        Log.d("haha","token : " + s);
        super.onNewToken(s);
    }

    @SuppressLint("InvalidWakeLockTag")
    private void GetWakeLock()
    {
        PowerManager manager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
        m_wakeLock = manager.newWakeLock(PowerManager.FULL_WAKE_LOCK |
                                         PowerManager.ACQUIRE_CAUSES_WAKEUP |
                                         PowerManager.ON_AFTER_RELEASE, "haha");

        //m_wakeLock.acquire();
    }

    private void ReleaseWakeLock()
    {
        if(m_wakeLock == null)
            GetWakeLock();

        if(m_wakeLock != null)
        {
            Log.d("haha","wakeup call");
            m_wakeLock.acquire();
            m_wakeLock.release();
        }

    }
}

    7.3 슬립을 깨우는 코드
          - 위의 코드에서, 하단부 GetWakeLock 과 ReleaseWakeLock 부분에 해당함. 
          - GetWakeLock은 전원관리 매니저를 가져오는 부분으로, 서비스 시작시 가져오고 (여기서는 onNewToken에 해당), 호출시 마다 가져오면 됨
          - ReleaseWakeLock은 슬립모드를 해제하는 함수로, 메세지 수신시 마다 호출 해줌 (sendNotification 함수에서 호출) 

    7.4  Manifest.xml 수정
           - Unity android 및 aar 프로젝트 매니페스트 파일 둘 다 수정 (혹시 몰라서...둘다 설정)
           - <application> 태그 안에 다음의 태그 추가

<service
            android:name="aar 패키지명 + 서비스클래스명" 
            (예 : net.mintpot.unitypushnotification.PushNotificationService)
            android:stopWithTask="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

            - <application> 태그 위에 permission 추가
 

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




8. 아이콘 추가
    - unity -> 상단 메뉴 중 Edit -> Project Setting -> Mobile Notifications 선택 후 하단에 아이콘 추가
    - https://docs.unity3d.com/Packages/com.unity.mobile.notifications@1.3/manual/Android.html 의 Set Icon 부분 참조