일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- java
- 혼공챌린지
- select
- 안드로이드
- Til
- Kotlin
- 코틀린
- 프로그래머스
- groupby
- 안드로이드스튜디오
- Android
- 정보처리기사
- 티스토리챌린지
- 알고리즘
- 스터디
- 인프런
- 카카오코테
- 자바
- MySQL
- 혼공단
- 기술면접
- 오블완
- CS
- SQL
- join
- 자료구조
- 혼공파
- doitandroid
- 정처기
- 코테
- Today
- Total
Welcome! Everything is fine.
[Android] Retrofit2 이용해 FCM 댓글 푸시알림 구현하기 본문
이전에 Firebase를 이용하여 콘솔에서 테스트 메세지를 보내는 것까지는 성공했다.
그러나 나는 콘솔에서 보내지 않고 앱에서 알림을 송신하고 수신하도록 만들고 싶었다. 사실 검색해도 콘솔에서 보내는 방법이 제일 많이 나와서 꽤나 삽질을 많이 했는데, Retrofit2를 사용해 댓글 알림을 구현하였다. 잊어버리지 않기 위해 정리해놓으려고 한다.(틀리거나 좀 더 좋은 방법이 있다면 댓글로 알려주세요.) 먼저 결과는 다음과 같다.
📌 결과
댓글을 달면 다음 영상과 같이 포그라운드 상태에서 알림이 온다. 또한 백그라운드 상태에서 다른 기기로 댓글을 달았을 때 알림이 오는 것을 확인했다. 지금은 테스트를 하느라 내 게시물에 내가 댓글을 달아도 알림이 오지만, 다른 사용자의 게시물에 댓글을 달았을 때만 알림이 오도록 수정해야한다.
📌 FCM 알림 구현하기
Retrofit Instance
public class RetrofitInstance {
private static Retrofit retrofit;
public static Retrofit getClient() {
if(retrofit == null){
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
baseUrl, Converter를 설정하는 부분이다. baseUrl은 말 그대로 baseUrl, 즉 Url에서 기본이 되는(변하지 않는) 부분을 넣으면 된다. 기억해야할 점은 baseUrl은 반드시 /로 끝나야 한다는 점이다. 나는 미리 MyKey라는 상수 데이터 클래스를 만들어 놓고 작업하였다. 여기서 baseUrl은 https://fcm.googleapis.com/ 이 된다.
POJO 클래스
알림이 올 때 나타나는 알림 제목과 내용, 메세지를 수신할 사용자의 디바이크 토큰 값을 데이터 클래스로 만든다. 먼저NotificationData와 PushNotification 두 개의 파일을 만든다. NotificationData는 PushNotification에 들어가는 데이터 클래스로, title과 body, click_action으로 구성되어있다. title은 알림 제목, body는 알림 내용, click_action은 원래 알림 메세지를 누르면 넘어가는 액티비티를 넘겨주는 필드인 것 같지만, 나는 댓글이 달린 게시물의 Id를 넘겨주는데 사용했다.
public class NotificationData {
public String title;
public String body;
public String click_action;
public NotificationData(String title, String body, String click_action) {
this.title = title;
this.body = body;
this.click_action = click_action;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
PushNotification은 다음과 같이 notificationData와 토큰을 담는 to로 이루어져있다. 토큰 값을 알아야 제대로 전송할 수 있다.
public class PushNotification {
@SerializedName("notification")
public NotificationData notificationData;
@SerializedName("to")
public String to;
public PushNotification(NotificationData notificationData, String to) {
this.notificationData = notificationData;
this.to = to;
}
}
NotificationAPI
다음은 API 인터페이스를 만든다. 헤더에는 서버키와 콘텐츠 타입을 넣는다. SEVER_KEY와 CONTENT_TYPE 역시 상수 데이터 클래스에서 가져온 것이다. 서버키는 Firebase 콘솔에서 자신의 서버키를 복사해놓고 사용하면 된다.
public interface NotificationAPI {
@Headers({"Authorization: key=" + SERVER_KEY, "Content-Type:" + CONTENT_TYPE})
@POST("fcm/send")
Call<ResponseBody> sendNotification(@Body PushNotification pushNotification);
}
MyFirebaseMessagingService
Firebase 서버에서 온 메세지를 받는 부분이다. title은 알림 제목, body는 내용, click_action에는 피드 Id가 들어있다. 처음에는 여기서 피드 Id를 무작정 intent.putString()으로 보내려고 하니 자꾸 오류가 생겼다. 알고보니 푸시알림 클릭 시 일어나는 이벤트를 정의할 때는 PendingIntent를 이용한다고 한다. intent.putString()으로 보내되, 번들로 감싸 보내야한다고 한다. 이렇게 보내놓고 CommentActivity에서 번들을 받아 사용하면 된다.
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "FirebaseMsgService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
if (remoteMessage.getNotification() != null) {
Log.d(TAG, remoteMessage.getNotification().getClickAction());
sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody(),
remoteMessage.getNotification().getClickAction());
} else if (remoteMessage.getData().size() > 0) {
String title = remoteMessage.getData().get("title");
String body = remoteMessage.getData().get("body");
String click_action = remoteMessage.getNotification().getClickAction();
sendNotification(title, body, click_action);
}
}
public void sendNotification(String title, String body, String click_action) {
Intent intent = new Intent(this, CommentActivity.class);
Bundle bundle = new Bundle();
bundle.putString("POSTSDocumentId", click_action);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
String channelId = "test_channel";
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_background))
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setDefaults(Notification.DEFAULT_VIBRATE)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
}
CommentActivity에서 통신
댓글이 달리면 댓글이 달린 게시물의 작성자 id를 변수에 저장한다. 작성자의 id를 알아야 userToken도 구할 수 있기 때문이다. 여기서 토큰은 Firestore의 users 컬렉션에 저장해둔 상태이다. 댓글 업로드 버튼을 누르면 sendCommentToFCM()을 실행하도록 한다. 여기서 나중에 댓글의 길이, 댓글 작성자의 id와 게시글 작성자 id 비교, 알림 수신 동의 등을 고려해 메서드를 실행시킬지 말지 결정해야한다.
sendCommentToFCM()에서는 NotificationData와 PushNotification 객체를 만들고, 댓글 내용과 피드Id, 토큰을 넣어 SendNotification()에 전송한다.
private void sendCommentToFCM() {
final String comment = Et_Comment.getText().toString();
db.collection("users")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot documentSnapshot : task.getResult()) {
if (documentSnapshot.getId().equals(postPublisher)) { // user 테이블의 사용자 id가 댓글이 달린 게시글 작성자 id와 같다면
token = documentSnapshot.getData().get("userToken").toString(); // 해당 사용자의 토큰을 얻는다.
NotificationData data = new NotificationData("채곡채곡", "댓글이 달렸습니다 : " + comment, FeedId);
pushNotification = new PushNotification(data, token);
SendNotification(pushNotification);
}
}
} else {
Log.d("error", "Error getting documents", task.getException());
}
}
});
}
SendNotification()에서는 위에서 만들어진 pushNotification을 받아 retrofit 통신한다.
public void SendNotification(PushNotification pushNotification) {
NotificationAPI api = RetrofitInstance.getClient().create(NotificationAPI.class);
retrofit2.Call<ResponseBody> responseBodyCall = api.sendNotification(pushNotification);
responseBodyCall.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d("SendNotification","성공");
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("SendNotification","실패");
}
});
}
이렇게 해서 테스트 해보니 정상적으로 푸시알림이 오고, 알림 클릭 시에도 해당 댓글로 잘 이동하는 것을 볼 수 있었다. 이제 추가적으로 토큰값을 업데이트 하고, 알림메세지 수신동의에 따라 노티를 조절하기만 하면 구현하고자 했던 결과에 가깝게 갈 수 있을 것이다.
'Android' 카테고리의 다른 글
[Andorid] 사용자 차단 기능 구현(+Firebase Realtime DB) (0) | 2023.09.21 |
---|---|
[Android] 키보드가 두번 눌리는 오류(setOnKeyListener) (0) | 2023.09.14 |
[Android] 키보드 엔터키 클릭 시 중복된 값이 나올 때 (0) | 2023.08.19 |
[Android] FCM 이용하여 테스트 메시지 보내기 (0) | 2023.07.15 |
[Android] 네이버 맵 반경 1km 내 마커찍기 (0) | 2023.06.22 |