일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 막내의막무가내 코틀린
- 안드로이드 Sunflower 스터디
- 막내의막무가내
- 부스트코스
- 프로그래머스 알고리즘
- 안드로이드
- 막무가내
- 막내의막무가내 플러터
- 막내의 막무가내
- 막내의막무가내 프로그래밍
- 막내의막무가내 안드로이드 에러 해결
- 주택가 잠실새내
- Fragment
- 프래그먼트
- 막내의막무가내 SQL
- 막내의막무가내 목표 및 회고
- 부스트코스에이스
- 막내의 막무가내 알고리즘
- 막내의막무가내 알고리즘
- 막내의막무가내 안드로이드 코틀린
- 주엽역 생활맥주
- 2022년 6월 일상
- 막내의막무가내 코볼 COBOL
- 막내의막무가내 플러터 flutter
- flutter network call
- 막내의막무가내 일상
- 막내의막무가내 안드로이드
- 막내의막무가내 코틀린 안드로이드
- 막내의막무가내 rxjava
- 안드로이드 sunflower
- Today
- Total
막내의 막무가내 프로그래밍 & 일상
[안드로이드] 노티피케이션 FCM 정리 ( 누르면 해당 액티비티와 내용 불러올 수 있도록) 본문
단순 알림을 주고 알림을 누르면 런처액티비티로 이동하는 것은 예전에 해봤으나 알림을 누르면 채팅방이나 게시물로 이동하고 해당 내용들을 보여주게 하는 것은 이번에 처음 해봤다.
그에 대해 다시는 까먹지 않도록 코드와 간단한 설명을 기록하기위해 포스팅한다.
먼저 위 문서를 보면 Data는 포어그라운드와 백그라운드일 때 둘다 onMessageReceived를 통해 전달이 되나 Notification은 백그라운드에서는 onMessageReceived로 받을 수 없음을 알 수 있다.
그래서 두가지 경우에 대해 해봤다.
먼저 노티피케이션으로 감싸보냈을 때와 Data로 감싸보넀을 때 중 전자를 먼저 작성해보겠다.
예를들어 전자는 이러한 페이로드로
{
from:,
to:,
notification: {
title:,
body:
},
data: {
type:,
index:
}
}
후자는 이렇게 보내는 거다.
{
from:,
to:,
data: {
title:,
body:,
type:,
index:
}
}
전자 :
public class SendNotification {
final static String TAG = "SendNotificationT";
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public static void sendNotification(String regToken, String title, String messsage, Object object){
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... parms) {
try {
Log.d(TAG, "상대방토큰 : " + regToken);
Log.d(TAG, "제목 : " + title);
Log.d(TAG, "메세지 : " + messsage);
String type = "";
OkHttpClient client = new OkHttpClient();
JSONObject json = new JSONObject();
//데이터 필드 담기
JSONObject dataJson = new JSONObject();
Gson gson =new Gson();
String data = gson.toJson(object); //객체JSON으로 변형해서 전달할거
dataJson.put("data", data);
//type담을 예정
if(object instanceof Meeting){
type = "meeting";
dataJson.put("type",type );
}else if(object instanceof CommunityPost){
type = "communityPost";
dataJson.put("type",type );
}
json.put("data", dataJson); //이건 데이터필드니깐 키필드값을 data로 해주어야한다.
Log.d(TAG, "노티 데이터 페이로드===> " + data);
Log.d(TAG, "노티 데이터타입 페이로드===> " + type);
//노티 필드 담기
JSONObject notiJson = new JSONObject();
notiJson.put("smallIcon", R.drawable.ic_rose_pink);
notiJson.put("body", messsage);
notiJson.put("title", title);
json.put("notification", notiJson);
//토큰 필드 담기
json.put("to", regToken);
RequestBody body = RequestBody.create(JSON, json.toString());
Request request = new Request.Builder()
.header("Authorization", "key=" + "서버키")
.url("https://fcm.googleapis.com/fcm/send")
.post(body)
.build();
Response response = client.newCall(request).execute();
String finalResponse = response.body().string();
Log.d("TAG", finalResponse);
}catch (Exception e){
Log.d("error", e+"");
}
return null;
}
}.execute();
}
}
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
//putExtra
final static String EXTRA_MEETING = "EXTRA_MEETING"; //미팅
final String EXTRA_COMMUNITY_POST = "EXTRA_COMMUNITY_POST"; //커뮤니티
/**
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
// if(!isAppRunning(getApplicationContext())) { //백그라운드일떄만 보낸다.
//json 데이터 페이로드
String dataJson = remoteMessage.getData().get("data");
String type = remoteMessage.getData().get("type"); //type필드 없으면 null반환됨
Gson gson = new Gson();
Log.d(TAG, "노티 타입 => "+type);
if (type.equals("meeting")) {
Meeting meeting = gson.fromJson(dataJson, Meeting.class);
sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody(), meeting);
}else if(type.equals("communityPost")){
CommunityPost communityPost = gson.fromJson(dataJson , CommunityPost.class);
sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody(), communityPost);
}else{
}
// }
}
// [END receive_message]
// [START on_new_token]
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID token
* is initially generated so this is where you would retrieve the token.
*/
@Override
public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token);
}
// [END on_new_token]
/**
* Create and show a simple notification containing the received FCM message.
*
* @param messageBody FCM message body received.
*/
private void sendNotification(String title, String messageBody, Object object) {
PendingIntent pendingIntent = null;
if (object instanceof Meeting) {
Log.d(TAG, " 미팅 노티 전송");
Intent intent = new Intent(this, MeetingCardActivity.class);
Meeting meeting = (Meeting) object;
intent.putExtra(EXTRA_MEETING, meeting);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
}else if(object instanceof CommunityPost){
Log.d(TAG, " 커뮤니티 노티 전송");
Intent intent = new Intent(this, CommunityFullActivity.class);
CommunityPost communityPost = (CommunityPost) object;
intent.putExtra(EXTRA_COMMUNITY_POST, communityPost);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
}
/* Intent intent = new Intent(this, Splash.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 *//* Request code *//*, intent,
PendingIntent.FLAG_ONE_SHOT);*/
String channelId = getString(R.string.default_notification_channel_id);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_rose_pink)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("fcm_default_channel",
"fcm_default_channel",
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
//백그라운드일떄만 푸시메세지 보내고싶을 때 사용
private boolean isAppRunning(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> procInfos = activityManager.getRunningAppProcesses();
for (int i = 0; i < procInfos.size(); i++) {
if (procInfos.get(i).processName.equals(context.getPackageName())) {
return true;
}
}
return false;
}
}
후자 : (데이터페이로드로 감싼다) , 백그라운드에서도 getIntent()로 받고 이동 할 수 있다. 위에서와 달리 노티피케이션 페이로드는 없에고 데이터페이로드에 다함께 담는다고 생각하면 될 것 같다. 백그라운드에서 알림을 누르면 해당액티비티로 이동을 하고 내용을 인텐트에서 받아와서 불러줄 수 가 있다. 근데 문제는 거기서 뒤로가기를 하면 액티비티스택에 액티비티가 없기 떄문에 앱이 꺼진다. 그러므로 처음 나오는 화면이나 중심화면인 메인액티비티로 가게끔 pendingIntent를 해준다음에 메인액티비티에서 노티인 경우를 판단하여 해당 액티비티를 띄워주는 로직을 하면 좋을 것 같다.
public class SendNotification {
final static String TAG = "SendNotificationT";
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public static void sendNotification(String regToken, String title, String messsage, Object object){
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... parms) {
try {
Log.d(TAG, "상대방토큰 : " + regToken);
Log.d(TAG, "제목 : " + title);
Log.d(TAG, "메세지 : " + messsage);
String type = "";
OkHttpClient client = new OkHttpClient();
JSONObject json = new JSONObject();
//데이터 필드 담기
JSONObject dataJson = new JSONObject();
Gson gson =new Gson();
String data = gson.toJson(object); //객체JSON으로 변형해서 전달할거
dataJson.put("data", data);
//type담을 예정
if(object instanceof Meeting){
type = "meeting";
dataJson.put("type",type );
}else if(object instanceof CommunityPost){
type = "communityPost";
dataJson.put("type",type );
}
//title, body 담을 예정
dataJson.put("title", title);
dataJson.put("body", messsage);
//json에 puy
json.put("data", dataJson); //이건 데이터필드니깐 키필드값을 data로 해주어야한다.
Log.d(TAG, "노티 데이터 페이로드===> " + data);
Log.d(TAG, "노티 데이터타입 페이로드===> " + type);
Log.d("JSON" , json.toString());
//토큰 필드 담기
json.put("to", regToken);
RequestBody body = RequestBody.create(JSON, json.toString());
Request request = new Request.Builder()
.header("Authorization", "key=" + "AAAAZrRF5os:APA91bHb-iU8RFQJQSJvCMP79nzh4eeRmWvZDMQQg6-335WWN8aK1I9KeGEp74x-1bq-K_toStweUQ1L2hVjkK_IIbLg17X8Ft0vuo2aHgZ6eyr5ZIA_CKChFZ56IcsgLzDL6H_Lh5X5")
.url("https://fcm.googleapis.com/fcm/send")
.post(body)
.build();
Response response = client.newCall(request).execute();
String finalResponse = response.body().string();
Log.d("TAG", finalResponse);
}catch (Exception e){
Log.d("error", e+"");
}
return null;
}
}.execute();
}
}
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
//putExtra
final static String EXTRA_MEETING = "EXTRA_MEETING"; //미팅
final String EXTRA_COMMUNITY_POST = "EXTRA_COMMUNITY_POST"; //커뮤니티
/**
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
// if(!isAppRunning(getApplicationContext())) { //백그라운드일떄만 보낸다.
//json 데이터 페이로드
String title = remoteMessage.getData().get("title");
String message = remoteMessage.getData().get("body");
String dataJson = remoteMessage.getData().get("data");
String type = remoteMessage.getData().get("type"); //type필드 없으면 null반환됨
Log.d(TAG, title);
Log.d(TAG, message);
Gson gson = new Gson();
Log.d(TAG, "노티 타입 => "+type);
if (type.equals("meeting")) {
Meeting meeting = gson.fromJson(dataJson, Meeting.class);
sendNotification(title, message, meeting);
}else if(type.equals("communityPost")){
CommunityPost communityPost = gson.fromJson(dataJson , CommunityPost.class);
sendNotification(title,message, communityPost);
}else{
}
// }
}
// [END receive_message]
// [START on_new_token]
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID token
* is initially generated so this is where you would retrieve the token.
*/
@Override
public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token);
}
// [END on_new_token]
/**
* Create and show a simple notification containing the received FCM message.
*
* @param messageBody FCM message body received.
*/
private void sendNotification(String title, String messageBody, Object object) {
int notifyId = 0; //노티피케이션 아이디 값이 서로 다르면 각각 구분되어 알림이뜬다. (ex 댓글알림을 1로 설정한 경우 댓글알림이 여러개오면 하나의알림이 새로고침되는형식이고, 커뮤니티게시물은 1인 경우 댓글알림과 커뮤니티게시물 알림은 구분되어 보여진다.)
PendingIntent pendingIntent = null;
if (object instanceof Meeting) {
Log.d(TAG, " 미팅 노티 전송");
Intent intent = new Intent(this, MeetingCardActivity.class);
Meeting meeting = (Meeting) object;
intent.putExtra(EXTRA_MEETING, meeting);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
notifyId = 1;
}else if(object instanceof CommunityPost){
Log.d(TAG, " 커뮤니티 노티 전송");
Intent intent = new Intent(this, CommunityFullActivity.class);
CommunityPost communityPost = (CommunityPost) object;
intent.putExtra(EXTRA_COMMUNITY_POST, communityPost);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
notifyId = 2;
}
String channelId = getString(R.string.default_notification_channel_id);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_rose_pink)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("fcm_default_channel",
"fcm_default_channel",
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(notifyId /* ID of notification */, notificationBuilder.build());
}
//백그라운드일떄만 푸시메세지 보내고싶을 때 사용
private boolean isAppRunning(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> procInfos = activityManager.getRunningAppProcesses();
for (int i = 0; i < procInfos.size(); i++) {
if (procInfos.get(i).processName.equals(context.getPackageName())) {
return true;
}
}
return false;
}
}
출처 및 참고:
https://developer.android.com/guide/topics/ui/notifiers/notifications?hl=ko
https://firebase.google.com/docs/cloud-messaging/android/receive#handling_messages
https://youngest-programming.tistory.com/393
추가 2020 업데이트 글 남깁니다.
도움이 되셨다면 공감과 댓글 구독 부탁드립니다.!!
'안드로이드 > 자바 & Previous' 카테고리의 다른 글
[안드로이드] 리사이클러뷰 , 스와이프리프레쉬아웃 함께 사용 ( recyclerview, SwipeRefreshLayout) (2) | 2019.08.10 |
---|---|
[안드로이드] 서비스(Service) (0) | 2019.08.06 |
[안드로이드] EditeText 글자 힌트 및 크기 변경 (0) | 2019.07.30 |
[안드로이드] 인플레이션 이해하기 (0) | 2019.07.22 |
[안드로이드] 카카오톡 로그인 (54) | 2019.07.19 |