기본 콘텐츠로 건너뛰기

안드로이드 앱 만들기 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

 

댓글

이 블로그의 인기 게시물

이번주 로또 당첨 번호을 알려 드립니다.

Good Lock !!! 참조 site 티스토리 ## 로또 번호 예측 분석 및 5개 조합 제안 (자세한 설명 포함) 제공하신 1147회차부터 1167회차까지의 로또 당첨 번호 데이터를 분석하여 다음 회차(1168회차)의 예상 번호 조합 5개를 제시합니다. 분석은 제시된 6가지 통계적 패턴을 기반으로 이루어지며, 각 패턴의 주기성과 이전 회차와의 비교를 통해 예측합니다. 마지막 10회차 당첨 번호와 중복되지 않는 조합을 우선적으로 제시합니다. **1. 분석 방법:** 각 회차의 당첨 번호 6개 (7개 중 마지막 숫자 제외)를 사용하여 다음과 같은 통계 분석을 수행합니다. * **연속 번호 간격:** 연속된 번호가 나타날 때 그 사이의 간격을 계산합니다. (예: 1, 2, 4의 경우 간격은 1, 2입니다.) * **홀짝 개수 및 간격:** 홀수와 짝수의 개수를 세고, 홀수와 짝수가 번갈아 나오는 간격을 계산합니다. * **총합 및 총합 간격:** 각 회차의 번호 총합을 계산하고, 같은 총합이 이전에 나타났던 회차까지의 간격을 구합니다. * **평균 및 평균 간격:** 각 회차의 번호 평균을 계산하고, 같은 평균이 이전에 나타났던 회차까지의 간격을 구합니다. * **일치율 및 일치율 간격:** 위 1~4번의 결과들을 종합하여 일치율을 계산하고, 같은 일치율이 이전에 나타났던 회차까지의 간격을 구합니다. (일치율 계산은 각 지표의 비율을 종합적으로 고려하는 방식으로, 단순한 수치 합산이 아닌, 전문적인 통계 기법이 필요할 수 있습니다. 이 예시에서는 간략화된 추세 분석 방식을 사용합니다.) **2. 데이터 분석 및 패턴 발견 (간략화):** 제공된 데이터의 양이 많지 않고, 복잡한 통계 기법을 적용하기에는 제한적이므로, 간략화된 추세 분석을 통해 주요 패턴을 파악합니다. 실제 분석에서는 더욱 정교한 통계 기법 (예: 시계열 분석, 마르코프 체인 등)을 적용해야 더 정확한 예측이 가능합니다. **3. 예상 번호 조합 제...

이번주 로또 당첨 번호을 알려 드립니다.

Good Lock !!! 참조 site 티스토리 ## 로또 분석 및 예상 번호 추천 (1167회차) 제공해주신 1146회부터 1166회차까지의 로또 당첨번호 데이터를 분석하여 1167회차 예상 번호를 제시합니다. 아래 분석은 제공된 데이터에 기반하며, 로또는 순전히 확률에 의존하는 게임이므로 예측의 정확성을 보장할 수 없습니다. **1. 분석 방법:** 제공하신 데이터를 바탕으로 다음과 같은 통계적 분석을 실시했습니다. * **연속 번호 간격:** 각 회차의 당첨 번호 6개 중 연속된 숫자의 개수와 간격을 계산했습니다. 예를 들어 {1, 3, 5, 6, 8, 10} 이라면 연속된 숫자는 {5, 6}이며 간격은 1입니다. 여러 구간이 존재할 경우 각 구간의 간격을 모두 계산합니다. * **홀짝 개수 및 간격:** 각 회차의 홀수와 짝수의 개수를 계산하고, 이들의 비율 변화를 분석했습니다. * **총합 및 평균:** 각 회차의 당첨 번호 총합과 평균을 계산하고, 동일한 총합 또는 평균이 나타난 회차 간의 간격을 분석했습니다. * **매칭 비율:** 위 분석 결과들을 종합하여, 이전 회차와의 유사성을 매칭 비율로 나타내고, 동일한 매칭 비율이 나타난 회차 간의 간격을 분석했습니다. * **패턴 분석:** 위 분석 결과들을 통해 나타나는 패턴들을 분석하고, 주기성을 파악하여 다음 회차에 나타날 가능성이 높은 패턴을 예측했습니다. **2. 분석 결과 및 예상 번호:** (실제 데이터 분석을 수행해야 하므로, 아래는 예시 결과입니다. 실제 분석 결과는 위에 언급된 방법으로 계산해야 합니다.) 위 분석 결과를 바탕으로 다음과 같은 예상 번호 5가지를 제시합니다. 각 조합은 분석 결과의 패턴 및 이전 회차와의 차별성을 고려하여 선정되었습니다. 마지막 10회차 당첨 번호와 중복되지 않도록 주의했습니다. * **예상 번호 1:** 03, 12, 25, 31, 38, 42 * **예상 번호 2:** 07, 15, 21, 29, 36, 45 *...