Jetpack Compose Navigation Deep Link causes duplicate activity - android

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)
}

Related

Set up a special activity PendingIntent

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
}

Reschedule notifications

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?

Firebase not sending notifications after account change/replacing firebase account

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

React native with foreground service - handleWindowVisibility: no activity for token android.os.BinderProxy

I create app in react native, that needs to run foreground service containing native code. The service itself works well but after killing main application process (by pressing square button and swiping the app away) - it sometimes returns without any complications on launch from icon or notification but it oftens displays blank screen (without any activity launched) and prints handleWindowVisibility: no activity for token android.os.BinderProxy in logcat. I've been googling it for a few hours but there is little feedback on that matter and nothing seems to work for me.
The class I use to communicate through RCT Bridge
#ReactMethod
fun init() {
val service = Intent(context, MyService::class.java)
service.action = SERVICE_ACTION_EXECUTE
if(!isMyServiceRunning(MyService::class.java)) {
Log.i("MyService", "Starting new service")
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(service)
} else {
context.startService(service)
}
}
val serviceConnection = object: ServiceConnection {
override fun onServiceConnected(p0: ComponentName?, binder: IBinder?) {
myService = (binder as MyService.LocalBinder).getService()
myService?.bindClient(this#Integrator)
}
override fun onServiceDisconnected(p0: ComponentName?) {
myService?.unbindClient()
}
}
context.bindService(service, serviceConnection, Context.BIND_AUTO_CREATE)
}
private fun isMyServiceRunning(serviceClass: Class<*>): Boolean {
val manager: ActivityManager? =
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager?
for (service in manager?.getRunningServices(Int.MAX_VALUE)!!) {
if (serviceClass.name == service.service.className) {
return true
}
}
return false
}
My foreground service:
#RequiresApi(api = Build.VERSION_CODES.O)
private fun createNotificationChannel() {
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val name: CharSequence =
getString(R.string.app_name)
val channel =
NotificationChannel(NOTIFICATION_CHANNEL_ID, name, NotificationManager.IMPORTANCE_LOW)
notificationManager.createNotificationChannel(channel)
}
private fun buildNotification(): Notification {
val intent = Intent(applicationContext, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(applicationContext, 0, intent, 0)
if(notificationBuilder == null) {
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
notificationBuilder = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(pendingIntent)
.setCategory(Notification.CATEGORY_SERVICE)
.setOngoing(true)
}
// Set Exit button action
val exitIntent =
Intent(applicationContext, MyService::class.java).setAction(SERVICE_ACTION_STOP)
notificationBuilder!!.addAction(
android.R.drawable.ic_delete,
getString(R.string.close),
PendingIntent.getService(applicationContext, 0, exitIntent, 0)
)
return notificationBuilder!!.build()
}
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
createNotificationChannel()
val notification = buildNotification()
startForeground(NOTIFICATION_FOREGROUND_SERVICE_ID, notification)
val action = intent?.action
if(action != null){
when(action) {
SERVICE_ACTION_EXECUTE -> {
Log.d(TAG, "Service executed")
executor.execute(IncomingIntentRouter(intent))
}
SERVICE_ACTION_STOP -> {
Log.d(TAG, "Service stopped")
stopService()
}
}
} else {
Log.d(TAG, "Got null onStartCommand() action")
}
return START_STICKY
}
Android manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.app">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:name=".MainApplication"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:roundIcon="#mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="#style/AppTheme"
android:networkSecurityConfig="#xml/network_security_config"
android:extractNativeLibs="true">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyService" />
</application>

Android - Launching Activity from Notification

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()

Categories

Resources