Android: Clearing notification results in new, blank notification
This has been an issue for a while (I think since Android 15 first launched? Maybe Android 14?), but I've never actually seen anybody mention it:
When I clear a BlueBubbles notification, a new notification pops up immediately afterward, but with nothing in it. It has a minimized look to it, but expanding it has no data, and there are no actions on it. This will happen in all of the following scenarios:
- I tap "Mark as Read" in the notification quick actions
- I swipe away the notification without opening it
- I read the message somewhere else, and BB removes the notification when it syncs up with the server
This new notification does NOT appear if I tap on the original to open BlueBubbles and go to the chat itself. I.e. if I get a message and tap the notification, it'll open the app, go to the chat, but the notification will fully clear and nothing new will show up.
I can swipe this away and it doesn't reappear, but while it's there it does make it appear like I have a new notification even when I don't. I'm also unsure of what happens if I get another message while the empty notification is there; I believe that it would replace it with the new notifications, but I'm not positive and I would need to do some further research on that.
I've only had one other app that acts like this, and that's Tiktok. It's exactly the same behavior, but I've never been able to figure out what connects the two.
I can't be the only one who has this issue, but I've only found a couple of mentions of this problem on the web, and no solutions to it.
Notification:
After swiping:
This is a known issue. Happens to me too. Unknown why it happens. We've tried fixing it a few times, but as you can see, none of the attempts worked fully. I believe it's due to some sort of race case, but it's been hard to identify.
I'm starting to wonder if this is a firebase problem: https://github.com/firebase/flutterfire/issues/12667
https://github.com/firebase/flutterfire/issues/3331
Then there's this one: https://github.com/Pushwoosh/pushwoosh-flutter/issues/82
That one actually has a very similar set of steps to replicate the problem, but I don't know if they use Firebase or not. But that mention in the comments about setMultiNotificationMode makes me wonder if BB has something similar (I assume so if you got messages from different chats, it would show up as a grouped and nested notification?) that might have something to do with it?
I mean obviously, for all I know these are totally unrelated, but I'm really just grasping at straws to find out why this is not only apparently such a rare problem that there are so few reports of it online, but what could tie similar-sounding instances of it happening in different apps together.
Is Firebase the only method BB has for sending push notifications?
The only reason i wouldn't say that it's a firebase issue is because our notifications aren't being delivered through firebase like those apps might be. We utilize FCM (Firebase Cloud Messaging) to send notification to your device. Those notifications get passed to the app, processed, then shown. They aren't through the native Firebase Notifications system.
So it's just likely how we are building the notification, and removing the notification group once all sub-notifications are removed. I get this issue too. However, I've been unable to find a pattern for when it happens.
If you can identify a pattern for when it happens vs. doesn't, and we can replicate it more easily, that would be helpful
The only reason i wouldn't say that it's a firebase issue is because our notifications aren't being delivered through firebase like those apps might be. We utilize FCM (Firebase Cloud Messaging) to send notification to your device. Those notifications get passed to the app, processed, then shown. They aren't through the native Firebase Notifications system.
Womp womp.
Is this what you were trying to fix with this commit here? https://github.com/BlueBubblesApp/bluebubbles-app/commit/75aa8f9c39b9c1213a008d17b39521c96f4aabe6
I really don't know this code at all, but I'll look through the procedures there and see if anything sticks out to my completely amateur eye, though for obvious reasons I doubt it will.
If you can identify a pattern for when it happens vs. doesn't, and we can replicate it more easily, that would be helpful
The thing that jumps out at me off the top of my head is that if I tap a notification to open the app, the notification will ALWAYS go away, but if I tap Mark as Read, it ALWAYS comes back. What I couldn't tell you, though, is what happens if there are multiple new messages in the notification group and I open one of them. I assume it does go away.
I'm guessing that the notification returning is the parent/group one, just with no children in it?
The only reason i wouldn't say that it's a firebase issue is because our notifications aren't being delivered through firebase like those apps might be. We utilize FCM (Firebase Cloud Messaging) to send notification to your device. Those notifications get passed to the app, processed, then shown. They aren't through the native Firebase Notifications system.
Womp womp.
Is this what you were trying to fix with this commit here? 75aa8f9
I really don't know this code at all, but I'll look through the procedures there and see if anything sticks out to my completely amateur eye, though for obvious reasons I doubt it will.
Haha, yup... Clearly it didn't work...
I hate to be the "vibe" coder, but due to time constraints and how inconsistent the issue is, I asked an AI, and found exactly what I was guessing... a race condition. Here is what it said:
Analysis of the Bug
The issue is in the Android notification deletion logic in DeleteNotificationHandler.kt. Here's what's happening:
When a user taps "Mark as Read" on a notification, the InternalIntentReceiver calls DeleteNotificationHandler().deleteNotification() The deletion handler is supposed to:
- Cancel the specific notification
- Check if there are any remaining notifications with the same tag
- If only the summary notification remains (ID = 0), cancel it too
The Bug: There's a race condition in the notification deletion logic. Here's the problematic code:
The issue is that when notificationManager.cancel() is called, it doesn't immediately remove the notification from activeNotifications. The notification might still appear in the activeNotifications list when we check for leftover notifications, causing the condition to sometimes fail.
The Race Condition Scenario
- User has multiple notifications for the same chat group
- User taps "Mark as Read" on one notification
- The specific notification gets cancelled, but might still appear in activeNotifications briefly
- The filter counts more than 1 notification (including the just-cancelled one that hasn't been removed yet)
- The summary notification doesn't get cancelled
Result: Empty notification group header remains visible
Proposed Fix
The fix addresses the race condition by:
- Explicitly excluding the just-cancelled notification from the leftover count by filtering out notifications with the same ID (it.id != notificationId)
- Improved logic for summary notification cleanup by specifically checking for non-summary notifications (those with ID != 0) rather than relying on the total count
- Better logging to help debug future issues
More robust condition checking that accounts for the asynchronous nature of notification cancellation
Why This Fixes the Issue
The original logic was flawed because:
- It relied on the assumption that notificationManager.cancel() would immediately remove the notification from activeNotifications
- It didn't properly distinguish between the summary notification (ID = 0) and actual message notifications
The new logic:
- Explicitly excludes the notification we just cancelled, preventing race conditions
- Only looks at non-summary notifications to determine if the summary should be removed
- Is more deterministic and doesn't rely on timing
This should resolve the issue where notification group headers sometimes remain visible after all child notifications have been cleared via the "Mark as Read" button.
So... definitely worth a look into. I can make a branch with the proposed changes, and if you'd like you could theoretically make a build using it and test it out. Otherwise, I can do the same and test it out myself.
Here is the branch/PR: https://github.com/BlueBubblesApp/bluebubbles-app/pull/2940
Okay, so based on what Copilot here is saying, the problem is that you go to dismiss the message notification, and because it was the last one, it also clears the group, but the group goes away first, then the message, and the group comes back because the message one is still there for a split second? That makes sense to me.
I'll see if I can get the build tools installed and try out a test. (I've never wished for more friends who text me using iMessage before...)
Okay, so based on what Copilot here is saying, the problem is that you go to dismiss the message notification, and because it was the last one, it also clears the group, but the group goes away first, then the message, and the group comes back because the message one is still there for a split second? That makes sense to me.
I'll see if I can get the build tools installed and try out a test. (I've never wished for more friends who text me using iMessage before...)
So there are 2 trains of thought:
- The issue is that when we call ".cancel()", it's not immediate, so when we check to see if the notification group still has active notifications, it checks too quickly after calling cancel that the notification is still technically active, so the group doesn't get removed. The fix will exclude the cancelled notification by ID from the check, so even if it still exists, it'll be considered "inactive" and won't factor into the size calculation.
- What I've seen sometimes is that the notification group will clear and disappear, but then half a second later, just the group header pops back up. To me, this implies that maybe the 1st train of thought may not solve it. However, maybe it's just a visual thing with Android, and the change will solve it.
Side note about testing yourself. Make sure to checkout the development branch and not master. Then you can pull the PR or just pull the branch the PR is based on (then pull development). Then you should be able to build using the normal flutter build commands. Make sure to align your Flutter version with what's listed as supported in the pubspec.yaml
So there are 2 trains of thought:
The issue is that when we call ".cancel()", it's not immediate, so when we check to see if the notification group still has active notifications, it checks too quickly after calling cancel that the notification is still technically active, so the group doesn't get removed. The fix will exclude the cancelled notification by ID from the check, so even if it still exists, it'll be considered "inactive" and won't factor into the size calculation.
What I've seen sometimes is that the notification group will clear and disappear, but then half a second later, just the group header pops back up. To me, this implies that maybe the 1st train of thought may not solve it. However, maybe it's just a visual thing with Android, and the change will solve it.
This may be a silly question, but we have the ID of the notification that we're trying to dismiss, right? Is there a reason we don't just run a wait condition until that notification is gone so we can be sure it's been dismissed? (Maybe with a timeout so there isn't a hang if it doesn't go away for some reason.) The potential issue I see with just not factoring the dismissed notification's ID when calculating is that you're still just assuming that it's going to go away, rather than verifying that it has, right? Or am I misunderstanding that? If it hasn't been dismissed after the timeout period, you can assume that it didn't get deleted properly, leave the group (and message) notification in place, and throw to a handler.
...or am I overcomplicating it?
Yeah, we are assuming the notification will go away... but that's after we call "cancel", so it should go away. Unless there is a reason that calling "cancel()" would fail. I do think it's overcomplicating things a bit if we have a timer to recheck until a given timeout
Make sure to align your Flutter version with what's listed as supported in the pubspec.yaml
Forgive me, but I'm having trouble seeing which version of flutter is specified in that file. I'm assuming based on your comment here that the latest stable release (3.32.5) is NOT what I want to install? Is it the line that references SDK (i.e. greater than 3.1.3, less than 3.29.0)?
sdk: '>=3.1.3 <3.29.0'
use a flutter version that's < 3.29.0 for now. The latest release may have changes that may break dependencies or other parts of the code.
It just took me a couple extra seconds after I posted that to realize that flutter was defined as the sdk, and it's the sdk that was being version-specified 😅
Took me quite a while, but I was able to get a build put into place with your change, and after some wrestling, received a notification in the emulator that allowed me to click Mark as Read. The notification went away for good. Ironically, it marked it as read on my phone, and the ghost notification is still there...
Tried to grab a debug log from the app so I could see what was going on, but it created an empty zip file...?
At any rate, I was able to test these actions in the emulator:
- Clicking Mark as Read
- Swiping away the notification
- Marking as read on a different device
All three resulted in a deleted notification with nothing coming back. This may just have done it.
Took me quite a while, but I was able to get a build put into place with your change, and after some wrestling, received a notification in the emulator that allowed me to click Mark as Read. The notification went away for good. Ironically, it marked it as read on my phone, and the ghost notification is still there...
Tried to grab a debug log from the app so I could see what was going on, but it created an empty zip file...?
At any rate, I was able to test these actions in the emulator:
- Clicking Mark as Read
- Swiping away the notification
- Marking as read on a different device
All three resulted in a deleted notification with nothing coming back. This may just have done it.
So in the emulator (ignoring your main phone), everything worked as expected? Do you think you could possibly create a build/apk that you install on your main phone to use? If not, that's fine. I just have had instances where "it works on the emulator", but then in real world situations it doesn't. Ultimately, getting an official build going is probably best, but if you can test that, it saves me time later.
So in the emulator (ignoring your main phone), everything worked as expected? Do you think you could possibly create a build/apk that you install on your main phone to use? If not, that's fine. I just have had instances where "it works on the emulator", but then in real world situations it doesn't. Ultimately, getting an official build going is probably best, but if you can test that, it saves me time later.
Is it just a matter of copying over the apk I built and installing it to my phone (I assume the answer to this is yes)? If I do that, will it overwrite the existing app? If it doesn't, would I end up with conflicts between the existing install and the dev build? I'm happy to give it a go.
So in the emulator (ignoring your main phone), everything worked as expected? Do you think you could possibly create a build/apk that you install on your main phone to use? If not, that's fine. I just have had instances where "it works on the emulator", but then in real world situations it doesn't. Ultimately, getting an official build going is probably best, but if you can test that, it saves me time later.
Is it just a matter of copying over the apk I built and installing it to my phone (I assume the answer to this is yes)? If I do that, will it overwrite the existing app? If it doesn't, would I end up with conflicts between the existing install and the dev build? I'm happy to give it a go.
You can just run flutter build apk --flavor prod to build a production APK. However, that's meant to overwrite the existing app. So you can just change the --flavor flag to beta to get an APK that can be installed and setup alongside your existing app from the play store
Okay, I've got it running on my phone now. Oddly, I didn't consider that this makes it easier to see the differences between them: since I have both apps running with notifications, I can mark as read on one and see what happens with the other.
I fired off a test message, it showed up as two notifications (one in prod, one in beta), and tapped Mark as Read on the prod notification. It opened a ghost, but the other one disappeared immediately. So, we're good there, it seems. I'll leave these running side by side for a while and see how well it works.
Is there anything I can provide for you to help give you any data (logs etc)?
Okay, I've got it running on my phone now. Oddly, I didn't consider that this makes it easier to see the differences between them: since I have both apps running with notifications, I can mark as read on one and see what happens with the other.
I fired off a test message, it showed up as two notifications (one in prod, one in beta), and tapped Mark as Read on the prod notification. It opened a ghost, but the other one disappeared immediately. So, we're good there, it seems. I'll leave these running side by side for a while and see how well it works.
Is there anything I can provide for you to help give you any data (logs etc)?
Nope, this is good. Just keep an eye on it and see if you can keep replicating it (on the main app) and then make sure it's not happening on the beta. I'll assume it's fixed, but just follow up here after a bit to let me know.
After a few days' worth of testing various situations with both versions of the app install, I think it's safe to say that the notifications are all properly going away when they should. I would definitely recommend implementing this change.
Oh, huh, one odd thing though: It appears that if I read the message on the BB Windows client, it's not clearing out the notification on the beta version, but is on the prod one (well, it's marking the notification read, but the parent notification is still there, as we've already seen on that version). If I read it in the prod client on my phone, it does clear out the beta notification. Not sure what's going on from the Windows client, or why with just the change you made it would affect it, so it could just be a full-on fluke, because I can't see how it would prevent this from working just on the beta.
Oh, huh, one odd thing though: It appears that if I read the message on the BB Windows client, it's not clearing out the notification on the beta version, but is on the prod one (well, it's marking the notification read, but the parent notification is still there, as we've already seen on that version). If I read it in the prod client on my phone, it does clear out the beta notification. Not sure what's going on from the Windows client, or why with just the change you made it would affect it, so it could just be a full-on fluke, because I can't see how it would prevent this from working just on the beta.
Might be a fluke. See if it keeps happening. The mechanism to mark as read shouldnt matter if it's coming from the desktop client or another type of client.
Might be a fluke. See if it keeps happening. The mechanism to mark as read shouldnt matter if it's coming from the desktop client or another type of client.
Yeah, it looks like it was a fluke, I just received a bunch of messages and the Windows client is clearing out the notifications after all. This change definitely looks like it's ready to add in. 👍🏽
Awesome, i'll have to merge it in and do a release. Apologies if it happens slowly...
I mean there's nothing to really stop me from using the version I've built in the meantime (though I'm not sure if this build would support Android Auto, now that I think about it), but it's enough to know that it's fixed and that I helped do that :)
Awesome, i'll have to merge it in and do a release. Apologies if it happens slowly...
I have been experiencing this issue for a long time now. Has it been fixed in a recent build?
I have been experiencing this issue for a long time now. Has it been fixed in a recent build?
Not yet. This is still waiting on prod release.