기본 콘텐츠로 건너뛰기

안드로이드 앱 만들기 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 티스토리 ## 로또 분석 및 예상 번호 추천 (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 *...

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

Good Lock !!! 참조 site 티스토리 ## 다음 회차 로또 645 예상 번호 5가지 (1164회차 기준) 지난 10회차 (1154회차 ~ 1163회차) 로또 결과를 분석하여, 다음 회차에 당첨될 가능성이 높은 6개의 숫자 조합 5가지를 제시합니다. 분석은 제시된 4가지 기준 (자주 출현하는 조합, 연속적으로 증가하는 숫자 조합, 홀짝 숫자 개수가 같은 조합, 무작위 조합)을 고려하여 이루어졌습니다. 이미 당첨된 조합은 제외하였습니다. **분석 기준:** 지난 10회차 데이터를 토대로 각 기준에 부합하는 숫자들을 선정하고, 중복을 최소화하여 5개의 조합을 도출했습니다. 특히, 최근 회차의 추세를 반영하여 자주 등장하는 숫자와 최근 빈도가 낮은 구간의 숫자들을 균형있게 배치하는 것을 목표로 하였습니다. **예상 번호:** **Lucky Number #1 [7, 14, 21, 28, 35, 42]:** 7의 배수로 구성된 조합입니다. 로또 번호에서 특정 배수가 연속적으로 나오는 경우는 드물지만, 장기간 분석 시 특정 배수의 출현 빈도를 파악하는 것이 도움이 될 수 있습니다. 이 조합은 홀수와 짝수의 비율이 3:3으로 균형을 이루고 있습니다. (분석방법 1,3 부분적 적용) **Lucky Number #2 [5, 12, 20, 27, 34, 41]:** 이 조합은 최근 10회차에서 덜 등장한 구간 (1~10, 20~30, 40~45)의 숫자들을 포함하여 균형을 맞추었습니다. 이는 최근 고정 번호의 출현 빈도가 높았기에 덜 나온 구간의 숫자들이 등장할 가능성을 고려한 것입니다. (분석방법 4 적용) **Lucky Number #3 [2, 9, 16, 23, 30, 37]:** 7의 배수를 기준으로 7, 14, 21, 28, 35에 비해 각각 5를 뺀 값들을 선정하여 구성했습니다. 이는 Lucky Number #1의 보완적인 조합입니다. (분석방법 1, 3 부분적 적용 및 1번 조합의 변형) **Lucky ...