I'm writing a time tracker app that starts an unbound foreground service to keep the user informed about the elapsed time.
The service runs smoothly and everything works like a charm... EXCEPT for one thing!
When the user clicks on the notification the apps main activity should start.
According to Androids documentation (https://developer.android.com/training/notify-user/navigation) this code should work, but it just starts the Android Settings Activity for the app.
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "Started")
isRunning = true
val channelID = createNotificationChannel()
val pendingIntent: PendingIntent = Intent(this, MainActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(this, 0, notificationIntent, FLAG_UPDATE_CURRENT)
}
val notification: Notification = NotificationCompat.Builder(this, channelID)
.setContentTitle(CHANNEL_NAME)
.setContentText("My wonderful Text")
.setPriority(PRIORITY_LOW)
.setContentIntent(pendingIntent)
.build()
startForeground(FOREGROUND_ID, notification)
timer.scheduleAtFixedRate(TimedTask(), 0, 1000)
return super.onStartCommand(intent, flags, startId)
}
private fun createNotificationChannel(): String{
val chan = NotificationChannel(CHANNEL_ID,
CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return CHANNEL_ID
}
The manifest.xml looks like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.maybe.tima">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<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>
<service
android:name=".TimerService"
android:label="#string/app_name"
/>
</application>
</manifest>
Any ideas why this happens? Your help is very appreciated :)
The only change I've done to make your code work - is to add method "setSmallIcon" to your notificationBulider (but I couldn't find in official documentation any mention about such kind of affection of this method):
val notification: Notification = NotificationCompat.Builder(this, channelID)
.setContentTitle(CHANNEL_NAME)
.setContentText("My wonderful Text")
.setPriority(PRIORITY_LOW)
.setSmallIcon(R.drawable.ic_launcher_background) // line added
.setContentIntent(pendingIntent)
.build()
Related
I want open special activity when the app is closed and user tap on notification. But the special didnt open. When user will click to notification i want open TestActivity but instead of this MainActivity opening all time.
In the manifest i have this code:
<service
android:name=".services.firebase.notification.AppFirebaseMessaging"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
.
.
.
<activity
android:name=".MainActivity"
android:exported="true"
android:screenOrientation="portrait"
tools:ignore="LockedOrientationActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
.
.
.
<activity android:name=".TestActivity"
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true"/>
this is my AppFirebaseMessaging
class AppFirebaseMessaging : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// here parsing RemoteMessage object,
sendNotification("title", "description")
}
private fun sendNotification(title: String, description: String) {
val random = System.currentTimeMillis().toInt()
val notificationBuilder: NotificationCompat.Builder = notificationBuilder(title, description)
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as? NotificationManager
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager?.createNotificationChannel(getChannel(title))
}
notificationManager?.notify(random, notificationBuilder.build())
}
private fun notificationBuilder(title: String, description: String): NotificationCompat.Builder {
val random = System.currentTimeMillis().toInt()
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notifyIntent = Intent(this, TestActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val notifyPendingIntent = PendingIntent.getActivity(
this, 0, notifyIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val notificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(this, random.toString())
.setSmallIcon(R.drawable.icon_notification)
.setContentTitle(title)
.setContentText(description)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.apply {
setContentIntent(notifyPendingIntent)
}
return notificationBuilder
}
}
When user will click to notification, MainActivity will be open, dont understand why
Tapping on Notification will always open the activity that has the following intent filter attributes.
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
If you can't make the 'TestActivity' the Main activity, you could try as follows.
Keep the MainActivity as the Main.
There set the launch mode to singleTop in MainActivity.
<activity
android:name=".MainActivity"
android:launchMode="singleTop"/>
Then Override the 'onNewIntent' inside MainActivity,
#Override
protected void onNewIntent(#NonNull Intent intent) {
super.onNewIntent(intent);
// here send intent to open TestActivity
}
I'm trying to show a notification that when clicked will open ChatScreen. It does open ChatScreen but started as a different activity, so there's 2 MainActivity in the back stack.
I use Compose Destinations, a wrapper library for Compose Navigations.
I have tried singleTop, singleTask, singleInstance launch mode, nothing works.
ChatScreen:
#Destination(deepLinks = [DeepLink(uriPattern = "https://mantools/chats")])
#Composable
fun ChatScreen(
navigator: DestinationsNavigator,
viewModel: ChatViewModel = hiltViewModel()
) {
//contents
}
Manifest
<application
android:name=".MainApplication"
android:allowBackup="true"
android:dataExtractionRules="#xml/data_extraction_rules"
android:fullBackupContent="#xml/backup_rules"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/Theme.Mantools"
android:hardwareAccelerated="true"
android:usesCleartextTraffic="${usesCleartextTraffic}"
tools:targetApi="31">
<activity
android:name=".features.MainActivity"
android:launchMode="singleInstance"
android:exported="true"
android:theme="#style/Theme.Mantools.Splash">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<data android:scheme="https" android:host="mantools"/>
</intent-filter>
</activity>
</application>
Notification
private fun showChatNotification(message: RemoteMessage) {
val data = message.data
showNotification(
data["title"] ?: "",
data["text"] ?: "",
TAG_CHAT,
Intent(Intent.ACTION_MAIN, "https://mantools/chats".toUri(), this.applicationContext, MainActivity::class.java)
)
}
fun showNotification(
title: String,
message: String,
tag: String,
intent: Intent
) {
val pendingIntent = getPendingIntent(intent)
val builder = NotificationCompat.Builder(context, GENERAL_CHANNEL_ID)
.setLargeIcon(context.getDrawable(R.mipmap.ic_launcher)?.toBitmap())
.setSmallIcon(R.drawable.app_logo)
.setColor(ContextCompat.getColor(context, R.color.primary))
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
notificationManager.notify(tag, System.currentTimeMillis().toInt(), builder.build())
}
private fun getPendingIntent(intent: Intent) = TaskStackBuilder.create(context).run {
addNextIntentWithParentStack(intent)
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
}
I have the following structure:
Main Class
private fun scheduleNotification()
{
val intent = Intent(applicationContext, Notification::class.java)
val title = binding.titleET.text.toString()
val message = binding.messageET.text.toString()
intent.putExtra(titleExtra, title)
intent.putExtra(messageExtra, message)
val pendingIntent = PendingIntent.getBroadcast(
applicationContext,
1 + (Random.nextInt(1, 100)),
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val time = getTime()
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
time,
pendingIntent
)
showAlert(time, title, message)
}
Notification Class
class Notification : BroadcastReceiver(){
override fun onReceive(context: Context, intent: Intent)
{
val notification = NotificationCompat.Builder(context, channelID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(intent.getStringExtra(titleExtra))
.setContentText(intent.getStringExtra(messageExtra))
.build()
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.notify(notificationID, notification)
}
}
Android Manifest
<receiver
android:name=".Notification"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
With this I am able to SCHEDULE a NOTIFICATION with DATE and TIME. But when the device is restarted all scheduled notifications are erased.
I tried to solve it by adding:
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Indeed it "works" but I get an empty notification and outside the scheduled time and I understand that it is an Android behavior, but is there any solution?
I'm working on building an alarm clock app. The end goal is to add a specific feature to it, but right now I'm working on making it function as a regular alarm clock.
One problem I'm coming across is that when I run it on my Pixel 4a, the notification to dismiss the alarm comes later than it should. The BroadcastReceiver that launches the Service is called on time, and the ringtone and vibration are activated as soon as the broadcast is received, but I have noticed a delay of up to 15 seconds from when the broadcast is received to the time the alarm notification appears. Another strange thing is that if I set another alarm for a short time later, the notification may appear instantly like it should.
On Android Studio's emulator, the notification appears on time every time EDIT: on API 30. On API 33, the same thing happens as on my phone.
Here is the code for the service:
class AlarmService : Service() {
private lateinit var ringtone: Ringtone
private lateinit var vibrator: Vibrator
override fun onCreate() {
super.onCreate()
vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager =
getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator
} else {
// Backwards compatibility for API < 31 requires using deprecated service
#Suppress("DEPRECATION")
getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val pendingIntent = getPendingIntent(intent)
val alarmLabel = intent?.getStringExtra(LABEL)
val shouldVibrate = intent?.getBooleanExtra(VIBRATE, false) ?: false
val ringtoneUri = getRingtoneUri(intent)
ringtone = getRingtone(ringtoneUri)
val notification = getNotification(pendingIntent, alarmLabel)
ringAndVibrate(shouldVibrate)
startForeground(1, notification)
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
ringtone.stop()
vibrator.cancel()
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
#SuppressLint("UnspecifiedImmutableFlag") // In android < S, mutability flags not compatible
private fun getPendingIntent(intent: Intent?): PendingIntent {
val notificationIntent = Intent(this, RingActivity::class.java)
moveExtras(intent, notificationIntent)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getActivity(
this,
0,
notificationIntent,
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
} else {
PendingIntent.getActivity(
this,
0,
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
}
}
private fun getRingtoneUri(intent: Intent?): Uri =
intent?.getParcelableExtra(RINGTONE_URI) ?: RingtoneManager.getDefaultUri(
RingtoneManager.TYPE_ALARM
)
private fun getRingtone(ringtoneUri: Uri): Ringtone {
val ringtone = RingtoneManager.getRingtone(this, ringtoneUri)
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ALARM)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
ringtone.audioAttributes = audioAttributes
ringtone.isLooping = true
return ringtone
}
private fun getNotification(pendingIntent: PendingIntent, alarmLabel: String?): Notification =
NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(alarmLabel ?: getString(R.string.notification_default_alarm_label))
.setSmallIcon(R.drawable.ic_baseline_alarm_24)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setFullScreenIntent(pendingIntent, true)
.build()
private fun ringAndVibrate(vibrate: Boolean) {
ringtone.play()
if (vibrate) {
val vibEffect = VibrationEffect.createWaveform(longArrayOf(0, 100, 1000), 0)
vibrator.vibrate(vibEffect)
}
}
}
EDIT: Here is the Android manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="[REDACTED]">
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".app.AlarmApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="[REDACTED]">
<activity
android:name=".features.alarmring.RingActivity"
android:exported="false" />
<activity
android:name=".features.mainscreen.MainActivity"
android:exported="true"
android:showOnLockScreen="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".features.alarmscheduling.AlarmBroadcastReceiver"
android:exported="false">
</receiver>
<service android:name=".features.alarmring.AlarmService" />
</application>
</manifest>
I am trying to implement how to push notifications from firebase. I have added the dependencies and made a project in my firebase account and connected my app. It initially worked fine but I changed the firebase account that is associated with the app to another(2nd account) then I saw that I did not receive any notification so I deleted my project on the 2nd account and replaced my json files with the first one and reconnected to it again but I am not receiving any notification which was working previously. Here is my reference https://www.geeksforgeeks.org/how-to-push-notification-in-android-using-firebase-cloud-messaging/
Here is my code for notification service
package com.crazy.pushnotifications
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.os.Build
import android.widget.RemoteViews
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class FirestoreNotification : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (remoteMessage.getNotification() != null) {
// Since the notification is received directly from
// FCM, the title and the body can be fetched
// directly as below.
showNotification(
remoteMessage.notification!!.title!!,
remoteMessage.notification!!.body!!
)
}
}
private fun getCustomDesign(title: String, message: String): RemoteViews {
val remoteViews = RemoteViews(
applicationContext.packageName,
R.layout.layout_notification
)
remoteViews.setTextViewText(R.id.title, title);
remoteViews.setTextViewText(R.id.message, message);
remoteViews.setImageViewResource(
R.id.icon,
R.drawable.ic_baseline_fingerprint_24
);
return remoteViews;
}
private fun showNotification(title: String, body: String) {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val channelId = "notification_channel"
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
var builder = Notification.Builder(applicationContext, channelId)
.setSmallIcon(R.drawable.ic_baseline_fingerprint_24)
.setAutoCancel(true)
.setOnlyAlertOnce(true)
.setContentIntent(pendingIntent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
builder = builder.setContent(
getCustomDesign(title, body)
);
} else {
builder = builder.setContentTitle(title)
.setContentText(body)
.setSmallIcon(R.drawable.ic_baseline_fingerprint_24)
}
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// Check if the Android Version is greater than Oreo
// Check if the Android Version is greater than Oreo
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(
channelId, "CTQ",
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(notificationChannel)
notificationManager.notify(0, builder.build())
}
}
}
and main activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.crazy.pushnotifications">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="#xml/data_extraction_rules"
android:fullBackupContent="#xml/backup_rules"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.Pushnotifications"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".FirestoreNotification"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
I want to know if I made any mistake cause i didnot get any error but I didnot get any notification either.
NOTE: I have checked my gradle files and I am not missing any dependencies