기본 콘텐츠로 건너뛰기

안드로이드 앱 만들기 Firebase FCM 으로 메시지 전송하기


원본출처: 티스토리 바로가기

Fcm 으로 메시지를 수신하는 예제들은 많이 찾아 볼 수 있으나, 보내는 건 ? 그것도 안드로이드 앱으로 그런 예제는 없는 것 같아서 정리를 해 보겠다. 다만, 전체를 다 정리하는 것이 아니라 꼭 필요한 부분만...

 

  1. MainActivity 에 아래 함수를 넣고 앱이 실행 되는 동안에 처리를 하자.   

   - 목적은 allDevices 라는 것은 나중에 메시지 전송을 할 때 사용할 Topic 이다.  

     subscribeToTopoc 을 이용해서 내가 구동하는 메시지중에서 해당 Topic 으로 전송되는 것을 구독(?)할 수 있도록 등록을 해 두는 것이다. 

   - 두번째 목적은 getToken 함수를 이용해서 특정앱에게만 메시지를 보내고자 할 떄 token 값으로 구분 하여 메시지 수신자를 지정하기 위함이다.

    public void onRegistryToken() {          FirebaseMessaging.getInstance().subscribeToTopic("allDevices")                 .addOnCompleteListener(new OnCompleteListener<Void>() {                     @Override                     public void onComplete(@NonNull Task<Void> task) {                         Log.e(TAG, "allDevices subscribed ...");                     }                 })                 .addOnFailureListener(new OnFailureListener() {                     @Override                     public void onFailure(@NonNull Exception e) {                         Toast.makeText(getApplicationContext(), getString(R.string.nonSyncTopic), Toast.LENGTH_LONG).show();                     }                 });          FirebaseMessaging.getInstance().getToken()                 .addOnCompleteListener(new OnCompleteListener<String>() {                     @Override                     public void onComplete(@NonNull Task<String> task) {                         if (!task.isSuccessful()) {                             Log.w(TAG, "Fetching FCM registration token failed", task.getException());                             return;                         }                         // Get new FCM registration token                         String token = task.getResult();                         getRegistryPhoneNumber(token);                     }                 });     }

 

2. 다음은 Message 전송을 위한 함수 코드을 만들어 두는 것이다. 

   - 아래는 전체 코드이고 package 이름만 숨김했다.

package com.bill..............tils;  import android.content.Context; import android.content.SharedPreferences; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.util.Log;  import com.google.auth.oauth2.GoogleCredentials; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject;  import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import java.util.Arrays; import java.util.Scanner;  /**  Firebase 클라우드 메시징(FCM)을 사용하여 iOS, Android 및 웹의 클라이언트에 메시지를 보낼 수 있습니다.   이 샘플은 FCM을 사용하여 `news`를 구독하는 클라이언트에 두 가지 유형의 메시지를 보냅니다.  주제. 메시지의 한 유형은 단순 알림 메시지(디스플레이 메시지)입니다. 다른 하나는  플랫폼별 사용자 정의가 포함된 알림 메시지(알림 표시), 예를 들어,  iOS 장치로 전송되는 메시지에 배지가 추가됩니다.  */ public class Messaging {      /**      * project_id 는 firebase 에 등록한 나의 project ID      */     private static final String PROJECT_ID = "my-application-f80fb";     private static final String BASE_URL = "https://fcm.googleapis.com";     private static final String FCM_SEND_ENDPOINT = "/v1/projects/" + PROJECT_ID + "/messages:send";      private static final String MESSAGING_SCOPE = "https://www.googleapis.com/auth/firebase.messaging";     private static final String[] SCOPES = { MESSAGING_SCOPE };     private static final String TAG = "FCM Messaging";      public static String TITLE = "FCM Notification";     public static String BODY = "Notification from FCM";     public static String URL = "https://billcorea.tistory.com";     public static String IMAGEURL = "";     public static final String MESSAGE_KEY = "message";      public static String tokenExam = "fIkVvZbATHu6Ilv1PNhp_8:APA91bG2q8MmfQvNqeP5afzKZRuLsTu1Mu6MVuYXuGlBhhgXZ3QGvP5EjoUBhVmmaBsT3CTKwKpUy5odRhzp5N46NB01txhxOFTUZnP8-LwnUovCx3hMGqNZtYW9jZ7FT-kCV37m7V2r";      Context context ;     SharedPreferences sp ;      public Messaging(Context context) {         sp = context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE) ;         this.context = context ;     }      /**      FCM REST에 대한 요청을 승인하는 데 사용할 수 있는 유효한 액세스 토큰을 검색합니다.      API.      *      *  https://console.firebase.google.com/u/1/project/fcm.......40/settings/serviceaccounts/adminsdk      *  이 url 에서 비공개생성키 을 클릭 해서 생성된 파일은 assets 폴더에 넣는다.      *     파일 이름은 소문자로만 적용한다.  (파일 이름이 너무 길면 줄이는 것이 좋다)      *      * @return Access token.      * @throws IOException      */     // [START retrieve_access_token]     public String getAccessToken(Context context) throws IOException {          AssetManager assetManager = context.getAssets();         AssetFileDescriptor fileDescriptor = assetManager.openFd("fcmdemo.json");         FileInputStream fileInputStream = fileDescriptor.createInputStream();          GoogleCredentials googleCredentials = GoogleCredentials                 .fromStream(fileInputStream)                 .createScoped(Arrays.asList(SCOPES));         return googleCredentials.refreshAccessToken().getTokenValue();     }     // [END retrieve_access_token]      /**      * 검색 및 게시 모두에 사용할 수 있는 HttpURLConnection을 만듭니다.      *      * @return Base HttpURLConnection.      * @throws IOException      */     public HttpURLConnection getConnection(Context context) throws IOException {         // [START use_access_token]         URL url = new URL(BASE_URL + FCM_SEND_ENDPOINT);         HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();         httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken(context));         httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");         return httpURLConnection;         // [END use_access_token]     }      /**      HTTP를 사용하여 FCM 메시지에 요청을 보냅니다.      UTF-8로 인코딩되고 특수 문자를 지원합니다.      *      * @param fcmMessage Body of the HTTP request.      * @throws IOException      */     public void sendMessage(JsonObject fcmMessage, Context context) throws IOException {         HttpURLConnection connection = getConnection(context);         connection.setDoOutput(true);         OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");         writer.write(fcmMessage.toString());         writer.flush();         writer.close();          int responseCode = connection.getResponseCode();         if (responseCode == 200) {             String response = inputstreamToString(connection.getInputStream());             Log.e(TAG, "Message sent to Firebase for delivery, response:");             Log.e(TAG, response);         } else {             Log.e(TAG, "Unable to send message to Firebase:");             String response = inputstreamToString(connection.getErrorStream());             Log.e(TAG, response);         }     }      /**      공통 FCM 필드를 사용하여 모든 사용자에게 알림 메시지를 보내는 메시지를 보냅니다.      플랫폼. 또한 플랫폼별 재정의는 메시지가 표시되는 방식을 사용자 지정하는 데 사용됩니다.      안드로이드와 iOS에서 받았습니다.      *      * @throws IOException      */     public void sendOverrideMessage() throws IOException {         JsonObject overrideMessage = buildOverrideMessage();         Log.e(TAG, "FCM request body for override message:");         prettyPrint(overrideMessage);         sendMessage(overrideMessage, context);     }      /**      FCM 요청의 본문을 작성합니다. 이 본문은 공통 알림 개체를 정의합니다.      android 및 apns 개체를 사용하는 플랫폼별 사용자 정의도 가능합니다.      *      * @return JSON representation of the FCM request body.      */     public JsonObject buildOverrideMessage() {         JsonObject jNotificationMessage = buildNotificationMessage();          JsonObject messagePayload = jNotificationMessage.get(MESSAGE_KEY).getAsJsonObject();         messagePayload.add("android", buildAndroidOverridePayload());          jNotificationMessage.add(MESSAGE_KEY, messagePayload);          return jNotificationMessage;     }      /**      * Android에서 메시지가 수신되는 방식을 사용자 지정하는 Android 페이로드를 빌드합니다.      *      * @return android payload of an FCM request.      */     public JsonObject buildAndroidOverridePayload() {         /**          * 이부분은 notify 알림에 표시될 제목, 내용, 보여줄 이미지의 URL 경로          */         JsonObject androidNotification = new JsonObject();         androidNotification.addProperty("body", BODY);         androidNotification.addProperty("title", TITLE);         androidNotification.addProperty("image", IMAGEURL);          /**          * 사용자 viewAcitivy 로 전달할 내용 과 URL          *          * FcmReceiveService 에서 URL, BODY 을 읽어서 ViewActivity 로 전달함.          */         JsonObject data = new JsonObject();         data.addProperty("URL", URL);         data.addProperty("BODY", BODY);          JsonObject androidNotificationPayload = new JsonObject();         androidNotificationPayload.add("notification", androidNotification);         androidNotificationPayload.add("data", data);          return androidNotificationPayload;     }      /**      * iOS에서 메시지가 수신되는 방식을 사용자 지정하는 apns 페이로드를 빌드합니다.      *      * @return apns payload of an FCM request.      */     public JsonObject buildApnsHeadersOverridePayload() {         JsonObject apnsHeaders = new JsonObject();         apnsHeaders.addProperty("apns-priority", "10");          return apnsHeaders;     }      /**      전송되는 메시지에 배지 필드를 추가할 앱 페이로드를 빌드합니다.      iOS 기기.      *      * @return JSON object with aps payload defined.      */     public JsonObject buildApsOverridePayload() {         JsonObject badgePayload = new JsonObject();         badgePayload.addProperty("badge", 1);          JsonObject apsPayload = new JsonObject();         apsPayload.add("aps", badgePayload);          return apsPayload;     }      /**      * 등록된 장치에 전달하기 위해 FCM에 알림 메시지를 보냅니다.      *      * @throws IOException      */     public void sendCommonMessage(Context context) throws IOException {         JsonObject notificationMessage = buildNotificationMessage();         Log.e(TAG, "FCM request body for message using common notification object:");         prettyPrint(notificationMessage);         sendMessage(notificationMessage, context);     }      /**      * 알림 메시지 요청의 본문을 구성합니다.      *      * @return JSON of notification message.      */     public JsonObject buildNotificationMessage() {         /**          * notify 알림에 사용할 제목과 내용          */         JsonObject jNotification = new JsonObject();         jNotification.addProperty("title", TITLE);         jNotification.addProperty("body", BODY);          JsonObject jMessage = new JsonObject();         if (sp.getBoolean("pushOne", false)) {             jMessage.addProperty("token", tokenExam);         } else {             jMessage.addProperty("topic", "allDevices");         }         jMessage.add("notification", jNotification);          JsonObject jFcm = new JsonObject();         jFcm.add(MESSAGE_KEY, jMessage);          return jFcm;     }      /**      * InputStream의 내용을 String으로 읽습니다.      *      * @param inputStream InputStream to read.      * @return String containing contents of InputStream.      * @throws IOException      */     public String inputstreamToString(InputStream inputStream) throws IOException {         StringBuilder stringBuilder = new StringBuilder();         Scanner scanner = new Scanner(inputStream);         while (scanner.hasNext()) {             stringBuilder.append(scanner.nextLine());         }         return stringBuilder.toString();     }      /**      * JsonObject를 예쁘게 인쇄하십시오.      *      * @param jsonObject JsonObject to pretty print.      */     public void prettyPrint(JsonObject jsonObject) {         Gson gson = new GsonBuilder().setPrettyPrinting().create();         Log.e(TAG, gson.toJson(jsonObject) + "\n");     }  }

 

이 소스는 사실 구글링을 통해서 github 에서 퍼온 자료인데, 한국어 번역만 구글 번역을 통해서 해 보았다.

 

 

이 소스에서 봐야할 부분은 

 

- 서버인증을 위한 부분 인데... 여기서 중요한 것은 assetManager 의 자원을 가져오는 것이다.  fcmdemo.json 이라고 적었는데, 그것은 firebase 의 프로젝트 설정에서 얻어온다.   이것을 얻어와서 저장을 하는 것은 FCM 전송을 위한 서버 구현을 하기 위해서는 FCM 에 인증을 받아야 하는데,  개발가이드를 보면 서버에 보내고 받고 하는 과정이 있어야 한다고 되어 있기도 하지만, 아래 함수를 이용해서 token 을 받아오면 서버 인증절차가 한번에 해소가 되는 것을 확인하였다.

    public String getAccessToken(Context context) throws IOException {          AssetManager assetManager = context.getAssets();         AssetFileDescriptor fileDescriptor = assetManager.openFd("fcmdemo.json");         FileInputStream fileInputStream = fileDescriptor.createInputStream();          GoogleCredentials googleCredentials = GoogleCredentials                 .fromStream(fileInputStream)                 .createScoped(Arrays.asList(SCOPES));         return googleCredentials.refreshAccessToken().getTokenValue();     }

내 앱을 사용하기 위해서 구성한 firebase 의 프로젝트 설정에 보면 서비스 계정 이라고 있는데,  그곳에 보면 새 비공개키 생성이라고 버튼이 있다. 그 버튼을 클릭하면 기~인 이름의 json 파일 하나를 만들어 준다.   그럼 그것을 이름을 줄여서 저장하고. 

{"originWidth":1590,"originHeight":834,"style":"floatLeft","width":860,"height":451,"caption":"firebase 프로젝트 설정"}
{"originWidth":197,"originHeight":258,"style":"floatLeft","width":163}

 

내 앱 프로젝트에 assets 으로 넣어준다.   그런데, 여기서 하나 걸리는 부분이 그냥 넣어주고 빌드해 실행해 보면  오류가 발생한다.

 

 

 

 

 

 

 

{"originWidth":1815,"originHeight":341,"style":"alignCenter","caption":"실행시 오류"}

json 파일이 들어 있는데, 찾을 수 없다는 것이다. 왜 ?   빌드를 하면 압축(?)을 하나 보다. 그래서 json 파일을 제대로 읽어서 처리를 할 수 없는 것이다.    그래서 앱의 gradle 설정에 다음과 같이 추가 하였다.

{"originWidth":704,"originHeight":226,"style":"alignCenter","caption":"gradle (module)"}

resources 는 noCompress 'json' 이라고... 압축을 하지 말라는 옵션이러니... 

 

- 그 다음은 상수로 선언된 project-id : 이것은 나의 프로젝트 일반에 있는 것을 가져다 적는다.

    /**      * project_id 는 firebase 에 등록한 나의 project ID      */     private static final String PROJECT_ID = "my-application-f80fb";

 

{"originWidth":783,"originHeight":322,"style":"alignCenter","caption":"프로젝트 일반"}

 

그리고 인증을 하기 위해서는 HttpURLConnection 을 이용하고 있기 때문이기도 하지만, manifest 에 user-permission 을 설정해 주는 것도 있지는 말아야겠다.

 

{"originWidth":698,"originHeight":69,"style":"alignCenter","caption":"internet permission 설정"}

 

다음은 메세지를 보내는 함수 부분인데, 난 android 로만 메시지를 전송할 것이라서 buildAndroidOverridePayload함수만 사용한다.  그리고 그안에서 전달하고자 하는 파라미터등을 설정한다. body, title, image, URL, BODY 을 key로 설정해서 전달하고자 하는 값을 넣어 준다.  값을 global 변수를 이용해서 넣을 수 도 있고... 뭐 암튼...

    /**      FCM 요청의 본문을 작성합니다. 이 본문은 공통 알림 개체를 정의합니다.      android 및 apns 개체를 사용하는 플랫폼별 사용자 정의도 가능합니다.      *      * @return JSON representation of the FCM request body.      */     public JsonObject buildOverrideMessage() {         JsonObject jNotificationMessage = buildNotificationMessage();          JsonObject messagePayload = jNotificationMessage.get(MESSAGE_KEY).getAsJsonObject();         messagePayload.add("android", buildAndroidOverridePayload());          jNotificationMessage.add(MESSAGE_KEY, messagePayload);          return jNotificationMessage;     }     /**      * Android에서 메시지가 수신되는 방식을 사용자 지정하는 Android 페이로드를 빌드합니다.      *      * @return android payload of an FCM request.      */     public JsonObject buildAndroidOverridePayload() {         /**          * 이부분은 notify 알림에 표시될 제목, 내용, 보여줄 이미지의 URL 경로          */         JsonObject androidNotification = new JsonObject();         androidNotification.addProperty("body", BODY);         androidNotification.addProperty("title", TITLE);         androidNotification.addProperty("image", IMAGEURL);          /**          * 사용자 viewAcitivy 로 전달할 내용 과 URL          *          * FcmReceiveService 에서 URL, BODY 을 읽어서 ViewActivity 로 전달함.          */         JsonObject data = new JsonObject();         data.addProperty("URL", URL);         data.addProperty("BODY", BODY);          JsonObject androidNotificationPayload = new JsonObject();         androidNotificationPayload.add("notification", androidNotification);         androidNotificationPayload.add("data", data);          return androidNotificationPayload;     }

 

그럼 메시지 수신을 하는 FcmReceiveService 을 잠깐 살펴 보자.  onMessageReceived 부분을 보면 payload 에서 들어간 내용 URL, BODY 이런 것들이 키로 수신이 되는 것을 볼 수 있다.  그래서 그것을 저정하거나 해서 잘 활용하면 

수신된 메시지를 나의 앱으로 전달하는 방법으로 처리를 할 수 있을 것이다.

package com.bil.......................tils;  import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Color; import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.util.Log;  import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat;  import com.billcoreatech.msgfcm1020.MainActivity; import com.billcoreatech.msgfcm1020.R; import com.billcoreatech.msgfcm1020.ViewActivity; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage;  import java.util.Map;  public class FcmReceiveService extends FirebaseMessagingService {      private static final String TAG = "FcmReceiveService";     SharedPreferences sp ;     SharedPreferences.Editor editor ;      @Override     public void onMessageReceived(RemoteMessage remoteMessage) {          // TODO(developer): Handle FCM messages here.         // 여기에 메시지가 수신되지 않습니까? 이것이 가능한 이유를 참조하십시오 : https://goo.gl/39bRNJ         Log.e(TAG, "From: " + remoteMessage.getFrom());          // 메시지에 데이터 페이로드가 포함되어 있는지 확인하십시오.         if (remoteMessage.getData().size() > 0) {             Log.e(TAG, "Message data payload: " + remoteMessage.getData());              sp = getSharedPreferences(getPackageName(), MODE_PRIVATE);             editor = sp.edit() ;              Map<String, String> strMap = remoteMessage.getData();             Log.e(TAG, "URL=" + strMap.get("URL"));             Log.e(TAG, "BODY=" + strMap.get("BODY"));              editor.putString("URL", strMap.get("URL").contains("http") ? strMap.get("URL") : "https://" + strMap.get("URL"));             editor.putString("BODY", strMap.get("BODY"));             editor.putBoolean("FcmTy", true) ;             editor.commit();              handleNow();          }          // 메시지에 알림 페이로드가 포함되어 있는지 확인합니다.         if (remoteMessage.getNotification() != null) {             Log.e(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());             sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody()) ;         }          onDeletedMessages();     }     // [END receive_message]       @Override     public void onNewToken(String token) {         Log.e(TAG, "Refreshed token: " + token);          // If you want to send messages to this application instance or         // manage this apps subscriptions on the server side, send the         // FCM registration token to your app server.         sendRegistrationToServer(token);     }     // [END on_new_token]      /**      * BroadcastReceivers에 할당된 시간을 처리합니다.      */     private void handleNow() {          Log.d(TAG, "Short lived task is done.");         Intent intent = new Intent(FcmReceiveService.this, ViewActivity.class);         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);         startActivity(intent);      }      /**      타사 서버에 토큰을 유지합니다.       이 방법을 수정하여 사용자의 FCM 등록 토큰을 임의의      애플리케이션에서 유지 관리하는 서버 측 계정.      *      * @param token The new token.      */     private void sendRegistrationToServer(String token) {         // TODO: Implement this method to send token to your app server.     }      /**      * 수신된 FCM 메시지가 포함된 간단한 알림을 만들고 표시합니다.      *      * @param messageBody FCM message body received.      */     private void sendNotification(String msgTitle, String messageBody) {          sp = getSharedPreferences(getPackageName(), MODE_PRIVATE);         editor = sp.edit() ;         editor.putString("BODY", messageBody);         editor.putBoolean("FcmTy", true) ;         editor.commit();          Log.e(TAG, "---------------------------------------------------------------------");         Bundle bundle = new Bundle();         bundle.putString("TITLE", msgTitle);         bundle.putString("BODY", messageBody);          Intent notifyIntent = new Intent(this, MainActivity.class);         notifyIntent.putExtras(bundle);         // Set the Activity to start in a new, empty task         notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK                 | Intent.FLAG_ACTIVITY_CLEAR_TASK);         PendingIntent notifyPendingIntent = PendingIntent.getActivity(                 this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT         );         String channelId = getString(R.string.default_notification_channel_id);         CharSequence channelName = getString(R.string.default_notification_channel_name);         Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);          NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId);         builder.setContentIntent(notifyPendingIntent)                 .setSmallIcon(R.drawable.ic_launcher_foreground)                 .setContentTitle(getString(R.string.fcm_message))                 .setContentText(messageBody)                 .setAutoCancel(true)                 .setSound(defaultSoundUri);          NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {             NotificationChannel channel = new NotificationChannel(channelId,                     "Channel human readable title",                     NotificationManager.IMPORTANCE_HIGH);             channel.enableLights(true);             channel.enableVibration(true);             channel.setLightColor(Color.BLUE);             channel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});             notificationManager.createNotificationChannel(channel);         }          notificationManager.notify(Integer.parseInt(getString(R.string.default_notification_channel_id)), builder.build());      }  }

 

수신된 알림이 notification 으로 저장이 되는 경우, 사용자는 알림창에 나와 있는 알림을 클릭했을 때 내 앱이 실행 되면서 그 내용을 확인할 수 있을 것이다.  알려야 하는 내용을 전달하는 방법은 메시지만 전달하거나, 미리 전달할 내용을 realtimedatabase 등에 저장해 두었다가, 확인해 볼 수 있도록 앱을 구성해 볼 수 있을 것 같다. 

 

알림 수신을 위한 FcmReceiveService는 manifest 에 service 로 등록해 주면 된다. 이런 부분들은 구글링을 해 보면 많이 나오고 해서 추가 설명은 생략해 보겠다.

        <meta-data             android:name="com.google.firebase.messaging.default_notification_icon"             android:resource="@drawable/ic_launcher_foreground" />         <meta-data             android:name="com.google.firebase.messaging.default_notification_color"             android:resource="@color/purple_500" />         <meta-data             android:name="com.google.firebase.messaging.default_notification_channel_id"             android:value="@string/default_notification_channel_id" />          <service             android:name=".FcmUtils.FcmReceiveService"             android:exported="true">             <intent-filter>                 <action android:name="com.google.firebase.MESSAGING_EVENT" />             </intent-filter>         </service>

 

3.  MainActivity 에서 메시지 전송을 구현해 보자...

    아래 소스 처럼 필요한 부분만 살펴 보도록 하겠다.

package co.......................020;  import android.Manifest; import android.annotation.SuppressLint; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.CompoundButton; import android.widget.Toast;  import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; ........   import java.io.IOException; import java.util.ArrayList; import java.util.Set;  public class MainActivity extends AppCompatActivity {      private static final String TAG = "MainActivity";          ......               String strFCM = "" ;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         binding = ActivityMainBinding.inflate(getLayoutInflater());         setContentView(binding.getRoot());          Intent intent = getIntent();         Bundle extras = intent.getExtras();         # FcmreceiveService 에서 extra 로 전달하면 그 값을 확인하기 위해서 추가         if (extras != null) {             Log.e(TAG, "===" + extras.getString("BODY"));              .....         }          # 안드로이드 버전이후 에서는 channel ID 을 설정해 주어야 한다.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {             // Create channel to show notifications.             String channelId  = getString(R.string.default_notification_channel_id);             String channelName = getString(R.string.default_notification_channel_name);             NotificationManager notificationManager =                     getSystemService(NotificationManager.class);             notificationManager.createNotificationChannel(new NotificationChannel(channelId,                     channelName, NotificationManager.IMPORTANCE_LOW));         }           }      @Override     protected void onStart() {         super.onStart();          ...          binding.btnSend.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View view) {                 if (sp.getBoolean("pushOne", false) && getChkCnt(userInfos) < 1) {                     Toast.makeText(getApplicationContext(), getString(R.string.msgCheckOnlyOne), Toast.LENGTH_LONG).show();                     return;                 }                 pushBinding = PushentryBinding.inflate(getLayoutInflater());                 pushBinding.edURL.setText("billcorea.tistory.com");                 pushBinding.edMesg.setText("test send messages ");                 AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.myDialog);                 builder.setTitle(getString(R.string.pushTitle))                         .setMessage(getString(R.string.msgPushMessages))                         .setView(pushBinding.getRoot())                         .setPositiveButton(getString(R.string.OK), new DialogInterface.OnClickListener() {                             @Override                             public void onClick(DialogInterface dialog, int which) {                                                              # 메시지 전송을 위한 호출                                  SendNotify(pushBinding.edMesg.getText().toString(),                                         pushBinding.edURL.getText().toString()) ;                             }                         })                         .setNegativeButton(getString(R.string.CANCEL), null);                 AlertDialog dialog = builder.create();                 dialog.show();             }         });     }      /**      * 전송할 메시지와 url 을 입력 받아옴.  fragment 에서 개인/전체 발송 구분은 선택해서 sp 에 저장 되어 있다고 봄      * @param msg      * @param url      */     private void SendNotify(String msg, String url) {          # 쓰레드을 사용하는 건 http 전송을 하기 때문에...         new Thread(() -> {              Messaging messaging = new Messaging(getApplicationContext());              # 전송하고 싶은 값들을 여기서 선언해 준다.              # 추가가 필요하면 Message class 에 선언해 주면서 추가 하면됨.             messaging.TITLE = getString(R.string.app_name) ;             messaging.BODY = msg ;             messaging.URL = url ;              Log.e(TAG, "Send Messages " + msg);             Log.e(TAG, "pushOne=" + sp.getBoolean("pushOne", false));             Log.e(TAG, "pushAll=" + sp.getBoolean("pushAll", false));             Log.e(TAG, "userInfo=" + userInfo.getUserToken());              if (sp.getBoolean("pushOne", false)) {                 if (userInfo == null) {                     Toast.makeText(getApplicationContext(), getString(R.string.nonSelectUser), Toast.LENGTH_LONG).show();                     return ;                 }                  if (!userInfo.isUseTy()) {                     Toast.makeText(getApplicationContext(), getString(R.string.nonSelectUser), Toast.LENGTH_LONG).show();                     return ;                 }                  /**                  * 개인에게 만 전송할 떄 : 미리 수집된 token 값으로 전달하고                  */                 messaging.tokenExam = userInfo.getUserToken() ;                 try {                     messaging.sendOverrideMessage();                 } catch (IOException e) {                     e.printStackTrace();                 }             } else {                  /**                  * 전체에게 전송할 떄 : 앱 실행시 구독설정한 topic 으로 전송을 하게 됨                  */                 try {                     messaging.sendOverrideMessage();                 } catch (IOException e) {                     e.printStackTrace();                 }             }          }).start();      }       # 토근 얻어오기 및 구독 설정 하기      public void onRegistryToken() {          FirebaseMessaging.getInstance().subscribeToTopic("allDevices")                 .addOnCompleteListener(new OnCompleteListener<Void>() {                     @Override                     public void onComplete(@NonNull Task<Void> task) {                         Log.e(TAG, "allDevices subscribed ...");                     }                 })                 .addOnFailureListener(new OnFailureListener() {                     @Override                     public void onFailure(@NonNull Exception e) {                         Toast.makeText(getApplicationContext(), getString(R.string.nonSyncTopic), Toast.LENGTH_LONG).show();                     }                 });          FirebaseMessaging.getInstance().getToken()                 .addOnCompleteListener(new OnCompleteListener<String>() {                     @Override                     public void onComplete(@NonNull Task<String> task) {                         if (!task.isSuccessful()) {                             Log.w(TAG, "Fetching FCM registration token failed", task.getException());                             return;                         }                         // Get new FCM registration token                         String token = task.getResult();                         getRegistryPhoneNumber(token);                     }                 });     }  }

처음에 보았던 client 토큰 값을 이용해서 개별에게 전달을 하거나,  구독 설정한 topic 을 통해서 전체 전달을 할 수 있다. 

 

여기서 중요한 것은 개별 전송을 위한 token 을 메시지를 보내는 쪽에서 알아야 한다는 것인데,  그걸 해소하기 위해서는 추천하는 방법으로는 realtime database 에 저장을 해서 공유하는 방법이 실시간으로 데이터를 공유할 수 있지 않을 까 생각이 든다. 관리자는 모든 사용자의 token 값을 알고 있어야 하고, 서로 모르는 상대방에게 메시지를 보낸다면 특정인의 token을 알 수 있는 방법이 있어야 하기 떄문이다 .

 

 

p.s : 2021.11.15 오늘 공개할 python 이야기도 잠시 참고해 보시길.

 

https://billcoreapython.tistory.com/29

 

댓글

이 블로그의 인기 게시물

개인정보처리방침 안내

 billcoreaTech('https://billcoreatech.blogspot.com/'이하 'https://billcoreatech.blogspot')은(는) 「개인정보 보호법」 제30조에 따라 정보주체의 개인정보를 보호하고 이와 관련한 고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이 개인정보 처리방침을 수립·공개합니다. ○ 이 개인정보처리방침은 2021년 8월 26부터 적용됩니다. 제1조(개인정보의 처리 목적) billcoreaTech('https://billcoreatech.blogspot.com/'이하 'https://billcoreatech.blogspot')은(는) 다음의 목적을 위하여 개인정보를 처리합니다. 처리하고 있는 개인정보는 다음의 목적 이외의 용도로는 이용되지 않으며 이용 목적이 변경되는 경우에는 「개인정보 보호법」 제18조에 따라 별도의 동의를 받는 등 필요한 조치를 이행할 예정입니다. 1. 서비스 제공 맞춤서비스 제공을 목적으로 개인정보를 처리합니다. 제2조(개인정보의 처리 및 보유 기간) ① billcoreaTech은(는) 법령에 따른 개인정보 보유·이용기간 또는 정보주체로부터 개인정보를 수집 시에 동의받은 개인정보 보유·이용기간 내에서 개인정보를 처리·보유합니다. ② 각각의 개인정보 처리 및 보유 기간은 다음과 같습니다. 1.<서비스 제공> <서비스 제공>와 관련한 개인정보는 수집.이용에 관한 동의일로부터<사용자의 설정시간>까지 위 이용목적을 위하여 보유.이용됩니다. 보유근거 : 앱의 기본기능 활용에 필요한 위치정보 제3조(개인정보의 제3자 제공) ① billcoreaTech은(는) 개인정보를 제1조(개인정보의 처리 목적)에서 명시한 범위 내에서만 처리하며, 정보주체의 동의, 법률의 특별한 규정 등 「개인정보 보호법」 제17조 및 제18조에 해당하는 경우에만 개인정보를 제3자에게 제공합니다. ② billcoreaTech

안드로이드 앱 만들기 : jetpack compose URL 에서 image 받아와서 보여 주기 (feat coil)

원본출처: 티스토리 바로가기 샘플 이미지 오늘은 앱을 만들면서 이미지를 보여 주어야 하는 경우 중에  URL에서 이미지를 가져와 보는 것에 대해서 기술해 보겠습니다.  URL에서 image를 가져온다는 것은 서버에 저장된 image 일수도 있고, SNS profile의 image 정보일수도 있을 것입니다.    구글에서 찾아보면 다른 것들도 많이 있기는 합니다.  그 중에서 coil 라이브러리를 이용해서 한번 만들어 보도록 하겠습니다.    gradle 설정 그래들 설정은 아래 한 줄입니다. 현재 시점에서 최신 버전인 것으로 보입니다.  이 글을 보시는 시점에 최신 버전이 아니라면 아마도 android studio는 추천을 해 줍니다. // image load from url implementation("io.coil-kt:coil-compose:2.2.2")   manaifest  설정 당연한 이야기 겠지만, url에서 이미지를 받아 와야 하기 때문에 권한 설정을 해야 합니다. 또한 인터넷에서 자료를 받아 오는 것은 지연이 발생할 수 있기 때문에 application에서도 다음 문구의 추가가 필요합니다.  <uses-permission android:name="android.permission.INTERNET" /> <application ... android:networkSecurityConfig="@xml/network_config" ... /> 이 설정을 하게 되면 xml 파일을 하나 추가해 주면 됩니다.  network_config.xml 이라는 이름으로 말입니다.  <?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextT