Unity 에서 Android Push Notification 구현
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 부분 참조