skip to Main Content

Main Activity

package com.example.pone

import android.content.ContentValues.TAG
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

const val TOPIC = "/topics/myTopic2"


class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)



    FirebaseService.sharedPref = getSharedPreferences("sharedPref", Context.MODE_PRIVATE)
    FirebaseMessaging.getInstance().token
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                if (task.result != null && !TextUtils.isEmpty(task.result)) {
                    val token: String = task.result!!
                    etToken.setText(token.toString())
                }
            }
        }
    FirebaseMessaging.getInstance().subscribeToTopic(TOPIC)

    btnSend.setOnClickListener {
        val title = etTitle.text.toString()
        val message = etMessage.text.toString()
        val recipientToken = etToken.text.toString()
        if(title.isNotEmpty() && message.isNotEmpty() && recipientToken.isNotEmpty()) {
            PushNotification(
                NotificationData(title, message),
                recipientToken
            ).also {
                sendNotification(it)
            }
        }
    }
}

private fun sendNotification(notification: PushNotification) = CoroutineScope(Dispatchers.IO).launch {
    try {
        val response = RetrofitInstance.api.postNotification(notification)
        if(response.isSuccessful) {
            Log.d(TAG, "Response: ${Gson().toJson(response)}")
        } else {
            Log.e(TAG, response.errorBody().toString())
        }
    } catch(e: Exception) {
        Log.e(TAG, e.toString())
    }
}
}

Firebase Service Class

package com.example.pone

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_ONE_SHOT
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Color
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import kotlin.random.Random

private const val CHANNEL_ID = "my_channel"

class FirebaseService : FirebaseMessagingService() {

companion object {
    var sharedPref: SharedPreferences? = null

    var token: String?
        get() {
            return sharedPref?.getString("token", "")
        }
        set(value) {
            sharedPref?.edit()?.putString("token", value)?.apply()
        }
}

override fun onNewToken(newToken: String) {
    super.onNewToken(newToken)
    token = newToken
}

override fun onMessageReceived(message: RemoteMessage) {
    super.onMessageReceived(message)

    val intent = Intent(this, MainActivity::class.java)
    val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    val notificationID = Random.nextInt()

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        createNotificationChannel(notificationManager)
    }

    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
    val pendingIntent = PendingIntent.getActivity(this, 0, intent, FLAG_ONE_SHOT)
    val notification = NotificationCompat.Builder(this, CHANNEL_ID)
        .setContentTitle(message.data["title"])
        .setContentText(message.data["message"])
        .setSmallIcon(R.drawable.common_google_signin_btn_icon_dark)
        .setAutoCancel(true)
        .setContentIntent(pendingIntent)
        .build()

    notificationManager.notify(notificationID, notification)
}

@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(notificationManager: NotificationManager) {
    val channelName = "channelName"
    val channel = NotificationChannel(CHANNEL_ID, channelName, IMPORTANCE_HIGH).apply {
        description = "My channel description"
        enableLights(true)
        lightColor = Color.GREEN
    }
    notificationManager.createNotificationChannel(channel)
}

}

PushNotification Data Class

data class PushNotification(
    val data: NotificationData,
    val to: String
)

NotificationAPI interface

import com.example.pone.Constants.Companion.CONTENT_TYPE
import com.example.pone.Constants.Companion.SERVER_KEY
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST

interface NotificationAPI {

    @Headers("Authorization: key=$SERVER_KEY", "Content-Type:$CONTENT_TYPE")
    @POST("fcm/send")
    suspend fun postNotification(
        @Body notification: PushNotification
    ): Response<ResponseBody>
}

Notification Data Class

data class NotificationData(
    val title: String,
    val message: String
)

RetroFitIntance class

import com.example.pone.Constants.Companion.BASE_URL
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

class RetrofitInstance {

    companion object {
        private val retrofit by lazy {
            Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
        }

        val api by lazy {
            retrofit.create(NotificationAPI::class.java)
        }
    }
}

Constants

class Constants {

    companion object {
        const val BASE_URL = "https://fcm.googleapis.com"
        const val SERVER_KEY = "*****"
        const val CONTENT_TYPE = "application/json"
    }

}

I am receiving messages on emulator but not real device. I am able to send messages to emulator from real device but not receiving messages from emulator to real device.

Update

When I try to send message to real device itself, this error happens

2022-09-18 00:54:34.087 8325-8423/com.example.pone E/AndroidRuntime: FATAL EXCEPTION: Firebase-Messaging-Intent-Handle
    Process: com.example.pone, PID: 8325
    java.lang.IllegalArgumentException: com.example.pone: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
    Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.
        at android.app.PendingIntent.checkFlags(PendingIntent.java:375)
        at android.app.PendingIntent.getActivityAsUser(PendingIntent.java:458)
        at android.app.PendingIntent.getActivity(PendingIntent.java:444)
        at android.app.PendingIntent.getActivity(PendingIntent.java:408)
        at com.example.pone.FirebaseService.onMessageReceived(FirebaseService.kt:52)
        at com.google.firebase.messaging.FirebaseMessagingService.dispatchMessage(FirebaseMessagingService.java:235)
        at com.google.firebase.messaging.FirebaseMessagingService.passMessageIntentToSdk(FirebaseMessagingService.java:185)
        at com.google.firebase.messaging.FirebaseMessagingService.handleMessageIntent(FirebaseMessagingService.java:172)
        at com.google.firebase.messaging.FirebaseMessagingService.handleIntent(FirebaseMessagingService.java:161)
        at com.google.firebase.messaging.EnhancedIntentService.lambda$processIntent$0$EnhancedIntentService(EnhancedIntentService.java:78)
        at com.google.firebase.messaging.EnhancedIntentService$$ExternalSyntheticLambda1.run(Unknown Source:6)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at com.google.android.gms.common.util.concurrent.zza.run(com.google.android.gms:play-services-basement@@18.1.0:2)
        at java.lang.Thread.run(Thread.java:920)

2

Answers


  1. Chosen as BEST ANSWER

    Update

    Fixed

    Inside FirebaseServiceClass replace FLAG_ONE_SHOT with FLAG_MUTABLE


  2. This is kind of a bug of firebase. try after sometime, you will receive the fcm message on real device too.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search