Due to the recent changes in Notification permissions in the Android 13 SDK, I need to update my app with FCM integration to obey the notifications rule. I have upgraded the compileSdk to 33 and also the targetSdk to 33. I have written code to ask POST_NOTIFICATIONS and the prompt appears. On pressing "Allow" the Notification Permission is enabled. But on sending a push notification the app receives no notification.
I only made changes in asking for the notification permission. Did not change anything in FirebaseMessagingService Class. The app was previously targetSdk 30.
class MyFirebaseMessagingService : FirebaseMessagingService() {
var intent: Intent? = null
var badgecount: Int = 0
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (remoteMessage.data.isNotEmpty()) {
try {
val json = JSONObject(remoteMessage.data.toString())
handleDataMessage(json)
} catch (e: Exception) {
Log.e("FIREBASEEXCEPTION:", e.message.toString())
}
}
if (remoteMessage.notification != null) {
sendNotification(remoteMessage.notification!!.body)
println("Message Notification Body:" + remoteMessage.notification!!.body)
}
super.onMessageReceived(remoteMessage)
}
private fun handleDataMessage(json: JSONObject) {
try {
val data = json.getJSONObject("body")
val badge = data.getString("badge")
val message = data.getString("message")
Log.e("FIREBASE_MSG:", message)
badgecount = badge.toInt()
sendNotification(message)
} catch (e: JSONException) {
Log.e("JSONEXCEPTION:", e.message.toString())
} catch (e: java.lang.Exception) {
}
}
#SuppressLint("ObsoleteSdkInt")
private fun sendNotification(messageBody: String?) {
ShortcutBadger.applyCount(this, badgecount)
intent = Intent(this, HomeActivity::class.java)
intent!!.action = System.currentTimeMillis().toString()
intent!!.putExtra("Notification_Recieved", 1)
val notId = NotificationID.getID()
val stackBuilder = TaskStackBuilder.create(this)
stackBuilder.addNextIntentWithParentStack(intent!!)
val pendingIntent = stackBuilder.getPendingIntent(notId, PendingIntent.FLAG_UPDATE_CURRENT)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder =
NotificationCompat.Builder(this)
.setContentTitle(resources.getString(R.string.app_name))
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel_id = getString(R.string.app_name) + "_01"
val name: CharSequence = getString(R.string.app_name)
val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(channel_id, name, importance)
notificationBuilder.setChannelId(mChannel.id)
mChannel.setShowBadge(true)
mChannel.canShowBadge()
mChannel.enableLights(true)
mChannel.lightColor = resources.getColor(R.color.split_bg)
mChannel.enableVibration(true)
mChannel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500)
notificationManager.createNotificationChannel(mChannel)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notificationBuilder.setSmallIcon(R.drawable.notify_small)
notificationBuilder.color = resources.getColor(R.color.split_bg)
} else {
notificationBuilder.setSmallIcon(R.drawable.nas_large)
notificationBuilder.color = resources.getColor(R.color.split_bg)
}
notificationManager.notify(notId, notificationBuilder.build())
}
}
class MyFirebaseInstanceIDService : FirebaseInstanceIdService() {
var mContext: Context = this
override fun onTokenRefresh() {
//val refreshedToken = FirebaseInstanceId.getInstance().token
val refreshedToken = FirebaseInstanceId.getInstance().token.toString()
Log.e("FIREBASETOKEN", refreshedToken)
sendRegistrationToServer(refreshedToken)
super.onTokenRefresh()
}
private fun sendRegistrationToServer(refreshedToken: String) {
if (refreshedToken != null) {
PreferenceManager.setFcmID(mContext, refreshedToken)
}
}
}
And in manifest
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<service
android:name=".fcm.MyFirebaseMessagingService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name=".fcm.MyFirebaseInstanceIDService"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
Related
Notifications are showing in the background but when the app is in the foreground notifications are not showing. I applied many solutions but they do not work for me. Can anyone tell me where is my mistake? thanks in advance
Here is Manifest
<service
android:exported="false"
android:name=".services.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#drawable/cute" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#color/design_default_color_on_primary" />
Here is My ServicesClass
const val cannelId = "notification_channel"
const val channel_name = "com.dextrologix.dham.rfms.resident.services"
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (remoteMessage.notification != null) {
genrateNotification(
remoteMessage.notification!!.title!!,
remoteMessage.notification!!.body!!
)
}
}
#SuppressLint("RemoteViewLayout")
fun getRemoteView(title: String, message: String): RemoteViews {
val remteViews = RemoteViews(
"com.dextrologix.dham.rfms.resident.services",
R.layout.pushnotification_layout
)
remteViews.setTextViewText(R.id.notification_title, title)
remteViews.setTextViewText(R.id.notification_message, message)
remteViews.setImageViewResource(R.id.notification_image, R.drawable.cute)
return remteViews
}
fun genrateNotification(title: String, message: String) {
var intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
var builder: NotificationCompat.Builder =
NotificationCompat.Builder(applicationContext, cannelId)
.setSmallIcon(R.drawable.person_icon)
.setAutoCancel(true)
.setVibrate(longArrayOf(1000, 1000, 1000, 1000))
.setOnlyAlertOnce(true)
.setContentIntent(pendingIntent)
builder = builder.setContent(getRemoteView(title, message))
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel =
NotificationChannel(cannelId, channel_name, NotificationManager.IMPORTANCE_HIGH)
notificationManager.createNotificationChannel(notificationChannel)
}
notificationManager.notify(0, builder.build())
}
}
It seems like you are missing notification channel. They are required for android notifications.
docs link
class MyFirebaseInstanceIDService : FirebaseMessagingService() {
override fun onNewToken(p0: String) {
super.onNewToken(p0)
Log.i("token", p0)
}
}
This is my code to generate a token.
Manifest:
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
I have reinstalled the app but still couldn't find the token
Update:
I can now receive the token in the onNewToken(token: String) method but the onMessageRecieved(message: RemoteMessage) method is not executed.
I am using retrofit for making the network request and the response is successful.
Retrofit call:
private fun sendNotification(sendNotification: SendNotification) = CoroutineScope(Dispatchers.IO).launch {
Log.i("sendNotification", "came here")
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com/").addConverterFactory(GsonConverterFactory.create()).build()
val pushNotification: PushNotification = retrofit.create(PushNotification::class.java)
val response = pushNotification.send(sendNotification)
if (response.isSuccessful){
Log.i("response", response.toString())
}else{
Log.i("response", response.errorBody()!!.toString())
}
}
SendNotification:
data class SendNotification(
var notification: NotificationData,
var to: String = ""
): Serializable {
}
NotificationData:
data class NotificationData(
var title: String = "",
var message: String = ""
): Serializable {
}
onMessageReceived:
private const val CHANNEL_ID = "channel_id"
class MyFirebaseMessagingService: FirebaseMessagingService() {
#RequiresApi(Build.VERSION_CODES.O)
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
val notificationManager: NotificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_ONE_SHOT)
val notificationId = Random.nextInt()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
createNotificationChannel(notificationManager)
}
Log.i("onMessageReceived", message.toString())
val notification = Notification.Builder(this, CHANNEL_ID)
.setAutoCancel(true)
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setContentTitle(message.data["title"])
.setContentText(message.data["message"])
.setContentIntent(pendingIntent)
.build()
notificationManager.notify(1, notification)
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(notificationManager: NotificationManager){
val notificationChannel = NotificationChannel(CHANNEL_ID, "messagingexample", NotificationManager.IMPORTANCE_HIGH)
notificationManager.createNotificationChannel(notificationChannel)
}
}
Make sure your keys provided are accurate and Cloud Messaging is enabled on your Firebase Dashboard.
It could probably be your service not running, try on an Activity and see if it logs anything.
My example code
FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() { #Override public void onComplete(Task<InstanceIdResult> task) { final boolean _success = task.isSuccessful(); final String _token = task.getResult().getToken(); final String _errorMessage = task.getException() != null ? task.getException().getMessage() : ""; String ref = ""; if (_success) { ref = "fcm_token/".concat(FirebaseAuth.getInstance().getCurrentUser().getUid().concat("/token")); _firebase.getReference(ref).setValue(_token); ref = "fcm_token/".concat(FirebaseAuth.getInstance().getCurrentUser().getUid().concat("/uid")); _firebase.getReference(ref).setValue(FirebaseAuth.getInstance().getCurrentUser().getUid()); sp.edit().putString("fcm_token", _token).commit(); ref = "wallet/".concat(sp.getString("addr", "").concat("/token")); _firebase.getReference(ref).setValue(_token); ref = "wallet/".concat(sp.getString("addr", "").concat("/uid")); _firebase.getReference(ref).setValue(FirebaseAuth.getInstance().getCurrentUser().getUid()); } else { _warningToasty(_errorMessage); } } });
I have this entry for service in Manifest.
<service
android:name=".AppFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
I have never scheduled notifications before and need help.
My app have Notification entity which contains:
#Parcelize
#Entity
data class Notification(
#PrimaryKey(autoGenerate = true)
val id: Int = 0,
var time: Date,
var daysOfWeek: Set<DayOfWeek>,
var enabled: Boolean = false
) : Parcelable
I wish to let this notification trigger selected days of week in specified time. I also have several methods to set notifications:
private fun codeForNotification(notification: Notification, dayOfWeek: DayOfWeek) =
1000 + notification.id * 10 + dayOfWeek.ordinal
fun checkNotification(notification: Notification, isEnabled: Boolean? = null) = with(notification) {
if ((isEnabled == null && enabled) || isEnabled == true) daysOfWeek.forEach { dow ->
setNotification(time, dow, codeForNotification(this, dow))
} else daysOfWeek.forEach { dow ->
Intent(appContext, NotificationReceiver::class.java).let { intent ->
PendingIntent.getBroadcast(
appContext,
codeForNotification(this, dow),
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
}.let(alarmManager::cancel)
}
}
fun setNotification(time: Date, dayOfWeek: DayOfWeek, code: Int) {
c2.time = time
c1.apply {
this.time = Date()
set(Calendar.HOUR_OF_DAY, c2.get(Calendar.HOUR_OF_DAY))
set(Calendar.MINUTE, c2.get(Calendar.MINUTE))
}
c1.set(Calendar.DAY_OF_WEEK, dayOfWeek.calendarValue)
if (c1.timeInMillis < System.currentTimeMillis()) {
c1.add(Calendar.DAY_OF_YEAR, 7)
}
Intent(appContext, NotificationReceiver::class.java).let {
PendingIntent.getBroadcast(appContext, code, it, PendingIntent.FLAG_CANCEL_CURRENT)
}.let {
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
c1.timeInMillis,
AlarmManager.INTERVAL_DAY * DayOfWeek.values().size,
it
)
}
}
Notifications are set similarly to the question (using check notification), but they do not fire.
My receiver class:
class NotificationReceiver : BroadcastReceiver() {
companion object {
const val CHANNEL_ID = "CHANNEL"
const val NOTIFY_ID = 1111
}
override fun onReceive(context: Context?, intent: Intent?) = context?.let{
val builder: NotificationCompat.Builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Notification")
.setContentText("Hello world")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
val notificationManager =
NotificationManagerCompat.from(context)
notificationManager.notify(NOTIFY_ID, builder.build())
} ?: Unit
}
And manifest have :
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<receiver android:name=".main.notification.NotificationReceiver" />
Please help me!
I think I had the same problem. It helped to set the receiver to "exported" in the manifest. Also you should restrict the receiver to NOTIFY actions:
<receiver android:name=".main.notification.NotificationReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.NOTIFY" />
</intent-filter>
</receiver>
Setting notification channel fixed this issue:
override fun onReceive(context: Context?, intent: Intent?) = context?.run {
val notificationManager: NotificationManager? =
getSystemService(this, NotificationManager::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager?.let(::setNotificationChanel)
Notification.Builder(context, CHANNEL_ID)
} else {
Notification.Builder(context)
}.setSmallIcon(R.drawable.ic_access_alarm_24px)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.notification_description))
.setContentIntent(getActivityIntent(this))
.setAutoCancel(true)
.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.ic_access_alarm_24px))
.build().let { notification ->
notificationManager?.notify(NOTIFICATION_ID, notification)
}
} ?: Unit
#RequiresApi(Build.VERSION_CODES.O)
private fun setNotificationChanel(notificationManager: NotificationManager) =
NotificationChannel(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_HIGH).apply {
description = CHANNEL_DESC
enableLights(true)
lightColor = Color.RED
enableVibration(true)
setShowBadge(true)
}.let(notificationManager::createNotificationChannel)
I am using the Firebase Cloud Messaging, the problem is if application is on foreground, after clicking the application will open, but if application is not on foreground, then after clicking the application will not open.
I have tried almost every solution posted here and combination of every flag but it is not working:
Open application after clicking on Notification
GCM Notification onclick does not open specified activity
FCM Notification onclick does not open desired activity
This is fragment code of manifest:
<activity
android:name=".ui.activities.StartActivity"
android:screenOrientation="portrait"
android:theme="#style/LightThemeTransparentStatusBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
This my example code:
class MyFirebaseMessagingService: FirebaseMessagingService {
constructor() : super()
override fun onMessageReceived(p0: RemoteMessage?) {
super.onMessageReceived(p0)
try {
val notificationManager=getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificationId = Random().nextInt(60000)
val bitmap = getBitmapFromURL(p0!!.data.get("image-url"))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(setupChannels())
}
val intent = Intent(this#MyFirebaseMessagingService,StartActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
val pendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_ONE_SHOT)
val notificationBuilder = NotificationCompat.Builder(this, ADMIN_CHANNEL_ID)
.setLargeIcon(bitmap)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(p0.notification!!.title)
.setContentText(p0.notification!!.body)
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setContentIntent(pendingIntent)
notificationManager.notify(notificationId,notificationBuilder.build())
}catch (ex:java.lang.Exception){
Log.e(TAG,ex.message)
}
}
fun getBitmapFromURL(imageURL: String?): Bitmap? {
try {
val url = URL(imageURL)
val connection = url.openConnection() as HttpURLConnection
connection.doInput = true
connection.connect()
val input = connection.inputStream
return BitmapFactory.decodeStream(input)
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
#RequiresApi(api = Build.VERSION_CODES.O)
private fun setupChannels() : NotificationChannel {
val adminChannelName = "Global channel"
val adminChannel: NotificationChannel
adminChannel = NotificationChannel(ADMIN_CHANNEL_ID, adminChannelName, NotificationManager.IMPORTANCE_LOW)
adminChannel.enableLights(true)
adminChannel.lightColor = Color.RED
adminChannel.enableVibration(true)
return adminChannel
}
companion object {
private val TAG=MyFirebaseMessagingService::class.java.name
private val ADMIN_CHANNEL_ID = "admin_channel"
}
}
Edit : I forgot to tell, i already try to make a breakpoint on the onRefreshToken and make a Log in that function, but when i start the apps, none of them is running (the breakpoint doesn't start, and the Log doesnt showing in AS's console log). So IMO its pure that the service itself doesn't start.
I make a code where my apps gonna show a notification if there is a data incoming in onMessageReceived. Its can run well, no problem at all, until i try it in Android Jelly Bean.
The notification doesn't showing (i am sure the notification is sent, because the other non JellyBean device is show the notification at the same time.) and then i trying it in KitKat Device, the result is same.
I then try to debug it and no error showing in Android Studio ( I bet because the device got Chinese rom and doesn't have Google play service built in).
Later i change my method and try it in Emulator, and the E/FirebaseInstanceId: Token retrieval failed: PHONE_REGISTRATION_ERROR is showing. I already try it in emulator that have KitKat and JellyBean image installed, the result is same, the same message appear.
This is my code:
AndroidManifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.appname">
///Some activity and meta data here
<service
android:name=".fcm.MyFirebaseInstanceIdService"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<service
android:name=".fcm.MyFirebaseMessagingService"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</manifest>
MyFirebaseInstanceIdService
class MyFirebaseInstanceIdService : FirebaseInstanceIdService() {
val TAG = "PushNotifService"
lateinit var name: String
override fun onTokenRefresh() {
val token = FirebaseInstanceId.getInstance().token
}
}
MyFirebaseMessagingService
class MyFirebaseMessagingService : FirebaseMessagingService() {
private val session = SessionManagement(this)
#SuppressLint("LongLogTag")
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (remoteMessage.data != null){
val data = remoteMessage.data
val title = data["title"]
val body = data["body"]
showNotification(title, body)
}
}
#SuppressLint("LongLogTag")
override fun onNewToken(token: String?) {
session.updateFCMToken(token)
}
fun subscribeTopic(topic: String?){
//the topic in here is a param send by other activity when the apps lunch
FirebaseMessaging.getInstance().subscribeToTopic(topic).addOnCompleteListener { task ->
if (!task.isSuccessful) {
} else {
}
}
}
//This sendMessageTrainer() is called and run from other activity
fun sendMessageTrainer(){
val contentType = "application/json"
val authorizationKey = ServerHelper.FCMServerKey
val data = "{\"to\": \"/topics/sometopic\",\"data\": {\"title\":\"Request Update\",\"body\":\"New Request.\"}}"
Fuel.post(ServerHelper.FCMServer).header("Content-Type" to contentType, "Authorization" to "key=$authorizationKey").body(data).responseJson{
_, _, result ->
result.failure {
sendMessageTrainer()
}
result.success {
}
}
}
//showNotification() is run if there is a new data/notification from onMessageReceived
private fun showNotification(title: String?, body: String?) {
val intent = Intent(this, LauncherActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
val channelName = getString(R.string.app_name)
val channelID = "default"
val alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notifyID = 1
val notification = NotificationCompat.Builder(this, channelID)
.setSmallIcon(R.drawable.ic_logo)
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)
.setSound(alarmSound)
.setContentIntent(pendingIntent)
.build()
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(channelID, channelName, importance)
val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mNotificationManager.createNotificationChannel(mChannel)
mNotificationManager.notify(notifyID , notification)
}
Build.VERSION.SDK_INT <= Build.VERSION_CODES.N -> {
val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mNotificationManager.notify(notifyID, notification)
}
}
}
}
Thanks and regards,
vl14b