Feb 18 | 8:25 AM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Kai H. | has entered the room |
Mark M. |
hello, Kai!
|
Mark M. |
how can I help you today?
|
Kai H. |
Hello Mark.
|
Kai H. |
I thougth I had some questions, but I have none written down atm.
|
Kai H. |
So I'll be lurking for the time being.
|
Feb 18 | 8:30 AM |
Mark M. |
ok
|
Feb 18 | 8:35 AM |
Grigore C. | has entered the room |
Mark M. |
hello, Grigore! sorry, I didn't notice your arrival!
|
Mark M. |
how can I help you today?
|
Grigore C. |
Hello, Mark! No problem.
|
Grigore C. |
Well I'm facing some problems with FCM and notifications
|
Mark M. |
I do not work with FCM much, but I can still try to help
|
Grigore C. |
And I got some questions and I'd appreciate some direction on what to read or tips about notifications
|
Grigore C. |
Sooo first one :D
|
Grigore C. |
Notification channels are immutable? You cannot change them after they are created? An example, I set a default sound for a channel and the change to a custom sound. When I reinstall the app the channel still has a default sound. I guess this type of changes requires a notification channel "manager"? Like recreate the channel or so
|
Grigore C. |
Immutable as you cannot change all of the properties, only some of them
|
Mark M. |
"You cannot change them after they are created?... Immutable as you cannot change all of the properties, only some of them" -- correct
|
Feb 18 | 8:40 AM |
Grigore C. |
So if I want to change the sound I have to delete and create the channel?
|
Mark M. |
yes, or just let the user change the sound themselves via the Settings app
|
Mark M. |
that's really part of the point here: you state what you would like, and from there, the user takes over
|
Grigore C. |
but can I use the same channel properties? like delete channel 1 with no custom sound and create it again with a custom sound
|
Mark M. |
I think there may be some restrictions around reusing channel names -- I haven't dealt with this in a few years so I forget that level of detail
|
Mark M. |
Google would really prefer that you not try to manage the notification sound in-app
|
Grigore C. |
hmm, that's interesting
|
Grigore C. |
Some of the phones work ok, but some of them don't "find" the custom sound
|
Grigore C. |
So it just might be better to guide the user
|
Mark M. |
in modern Android, users can choose, per channel, whether notifications play a sound and what that sound is
|
Mark M. |
(in addition to blocking a channel's notifications outright)
|
Grigore C. |
Ok, back to FCM :D did you use any other tool for notifications?
|
Feb 18 | 8:45 AM |
Grigore C. |
I assume that FCM might be the best choice because is made by Google...
|
Mark M. |
here, if by "notifications" you mean "push messages", then no -- if I needed those to occur all the time, I have only needed FCM
|
Mark M. |
however, I have not had that requirement all that much
|
Grigore C. |
push notifications, yes
|
Grigore C. |
It seems like my phone won't be triggered by high priority notifications
|
Grigore C. |
while it's in doze mode...
|
Mark M. |
in newer versions of Android, there are throttles as to how frequently a high-priority message will wake up the device
|
Grigore C. |
yes, the buckets, right?
|
Mark M. |
right
|
Grigore C. |
but active bucket I think it doesn't have restrictions
|
Mark M. |
correct, at least based on https://developer.android.com/topic/performance...
|
Grigore C. |
but Huawei, Samsung, etc might override that...
|
Mark M. |
yes, though in Huawei's case that would be limited to Play Store ecosystem devices -- their other ones lack FCM
|
Grigore C. |
I don't really understand this one. Phones will not get notified...
|
Mark M. |
for an active app, that is a case where I would be using something else (e.g., websocket) -- I have only used FCM for cases where we have no idea if the app is active or not
|
Grigore C. |
Hmm, I think that's my case, "no idea if the app is active or not"
|
Feb 18 | 8:50 AM |
Grigore C. |
but the user should be notified as soon as possible
|
Feb 18 | 8:50 AM |
Mark M. |
however, in the "no idea if the app is active or not" case, you may not be in the active bucket
|
Mark M. |
(BTW, Kai: if you come up with a question, let me know!)
|
Grigore C. |
Any idea on how a chat app always pop-up notifications? I want to achieve chat-app like notification consistency
|
Grigore C. |
I've studied the Singal app source code and I think it forces a WakeLock
|
Mark M. |
that would depend a bit on what app you are using as a basis for comparison
|
Grigore C. |
using an Alarm
|
Grigore C. |
notify the user with a 1-5 minute error
|
Mark M. |
Signal definitely has no abilities beyond what you or I could do -- WhatsApp, on the other hand, might use FCM but might also use $$$ to get manufacturers to not mess with their messages
|
Mark M. |
if I had to implement something Signal-ish, I would look at having a durable websocket connection to the server with a foreground service, and use FCM only when that websocket is gone for X minutes
|
Mark M. |
and even there, the objective of the FCM message is less "show something to the user" and more "get our process running again so we get the websocket up"
|
Grigore C. |
so when the foreground service is kinda dead
|
Grigore C. |
That makes sense
|
Feb 18 | 8:55 AM |
Grigore C. |
I've read that FCM might deliver notifications using Google Play Services
|
Grigore C. |
Hopefully, I am not wrong
|
Mark M. |
it definitely does -- FCM is part of the Play Services framework
|
Mark M. |
which is why FCM can do things that you and I cannot
|
Grigore C. |
But if the app is killed I don't think it will be able to show notifications
|
Mark M. |
that will depend a fair bit on what happens when your app "is killed"
|
Mark M. |
your UI process probably is terminated
|
Mark M. |
separate processes (e.g., for a foreground service) may or may not survive
|
Mark M. |
and, on some devices, "killing" the app has the same effect as does the "Force Stop" button in Settings, and that has fairly drastic effects
|
Grigore C. |
but what if I don't have a foreground/background service
|
Grigore C. |
can I prevent the app from being killed?
|
Mark M. |
not without custom firmware
|
Mark M. |
basically, if you build your own Android OS version, you can do whatever you want
|
Grigore C. |
uff :(
|
Mark M. |
however, even in the "Force Stop" scenario, FCM messages should still get to your app, as FCM will use an explicit Intent to talk to your app, which will move your app out of the "stopped" state and allow processes to run again
|
Grigore C. |
seems impossible to make it work most of the times
|
Grigore C. |
I'll try to force stop it and see what happens
|
Grigore C. |
do you think using a wakelock when a notification is consumed would improve anything?
|
Feb 18 | 9:00 AM |
Grigore C. |
or it will cause the app to be "blacklisted" even faster
|
Mark M. |
I can't speak to manufacturer-specific blacklist sorts of behavior
|
Grigore C. |
yes, of course, just your opinion
|
Mark M. |
and I have not tried maintaining a long-lived websocket like this -- about an hour is all I have needed to deal with
|
Grigore C. |
and how did you do it? like described before?
|
Mark M. |
well, my current client uses websockets, but it is mostly while the app is in use
|
Grigore C. |
I understand
|
Mark M. |
the "about an hour" scenario is a case where the app is running and probably in the foreground, but the device is set aside and the screen turns off
|
Mark M. |
we use a partial wakelock and a WifiLock to keep the websocket going (because we know that we are on WiFi)
|
Mark M. |
as part of a foreground service, to help ensure Android doesn't terminate the process
|
Grigore C. |
and when do you require that wakelock? using the foreground service I assume
|
Mark M. |
yes -- there is a particular user action that triggers the foreground service
|
Mark M. |
we keep that service going for the duration of a particular event (5-60 minutes)
|
Grigore C. |
and the wakelock duration is set to 5-60 minutes?
|
Mark M. |
and stop that service when the event ends (either automatically at the end of the time or if the user manually cancels it)
|
Mark M. |
I forget the actual duration value that we use -- we mostly focus on manually releasing it when the service stops
|
Grigore C. |
yes, I mean the wakelock is released after the service stops
|
Grigore C. |
like you said
|
Feb 18 | 9:05 AM |
Mark M. |
yes -- we hold the WakeLock and WifiLock for the duration of this "session", then release them
|
Mark M. |
our results are not 100% reliable, though how much of that is tied to our app and how much is tied to other things is unclear
|
Grigore C. |
I don't understand a thing
|
Grigore C. |
"we keep that service going for the duration of a particular event (5-60 minutes)"
|
Grigore C. |
how?
|
Mark M. |
it's a foreground service
|
Mark M. |
we start the service at the start of the "session"
|
Mark M. |
we stop the service at the end of the "session"
|
Mark M. |
we know when the session starts based on user interaction
|
Grigore C. |
and you have a counter inside it or something that will stop it after x minutes?
|
Mark M. |
effectively, yes -- the story is a bit more complicated and I cannot discuss it
|
Grigore C. |
sure, no problem, I thought Foreground Service has an option
|
Grigore C. |
a timer like one
|
Mark M. |
I do not recall there being one -- we know when the session ends either due to elapsed time or user interaction (basically, clicking a stop button)
|
Grigore C. |
yes, I got it now, thank you :)
|
Grigore C. |
I've ran into this today https://dontkillmyapp.com/
|
Grigore C. |
looks like things are getting worse and worse
|
Mark M. |
yes, the "War on Background Processing" is something I have been writing about since Android 6.0
|
Mark M. |
writing a Signal-style messaging app is not going to be fun
|
Feb 18 | 9:10 AM |
sudokai | has entered the room |
Grigore C. |
where did you write about it? in your books?
|
Mark M. |
I have used that phrase in blog posts and Stack Overflow answers, more so than in books, IIRC
|
Mark M. |
let me take a question from sudokai, and I will come back to you in a bit
|
Mark M. |
sudokai: hi! how can I help you today?
|
Grigore C. |
sure
|
sudokai |
Hi
|
sudokai |
How do you deep link into a screen
|
sudokai |
And make sure the back button works
|
sudokai |
By taking you to the parent screen
|
sudokai |
Instead of the main screen
|
sudokai |
I know there's TaskStackBuilder for activities
|
sudokai |
But what do you use for fragments
|
Mark M. |
I cannot really answer that question, as it depends mostly on what you are doing for navigation
|
Mark M. |
so, for example, if you are using Jetpack's Navigation component, it has built in support for deep links
|
sudokai |
Nothing
|
sudokai |
I do it manually
|
sudokai |
With FragmentTransactions
|
sudokai |
And I called add and addToBackStack
|
sudokai |
The problem is that I don't want my intermediate screens to show
|
sudokai |
I just want to go to the final destination
|
sudokai |
But still have the back button work
|
Feb 18 | 9:15 AM |
sudokai |
So I go like this
|
sudokai |
A -> B -> C -> D
|
sudokai |
The problem is that when I open the "deep link"
|
sudokai |
I see for a brief moment
|
sudokai |
A, B and C
|
sudokai |
I would like to go to D directly
|
Mark M. |
if you are going to do all of this manually, you would need to manually handle back navigation, recognize that you are in this deep link scenario, and run the correct fragment transaction(s)
|
sudokai |
But if I just do a single transaction, when I hit back from D I go to A, not C
|
Mark M. |
only if you let fragments handle back navigation, or you use popBackStack()
|
Mark M. |
if *you* register your *own* back navigation listener and handle it all manually, you can do whatever you want, including a replace() to show C
|
sudokai |
So you are saying that the trick is just displaying D and listen to the back navigation
|
Mark M. |
yes
|
sudokai |
And when the back navigation is clicked, then create C
|
Mark M. |
yes
|
sudokai |
Wow thanks
|
Mark M. |
if you are lucky, you might be able to only deal with your deep link scenario manually and defer to the existing implementation for non-deep-link scenarios
|
sudokai |
Can you pass data up?
|
sudokai |
What I mean is:
|
sudokai |
Sometimes I need
|
sudokai |
A-B-C-D
|
sudokai |
But other times it's
|
sudokai |
A-X-C-D
|
Feb 18 | 9:20 AM |
sudokai |
It depends on the data
|
Mark M. |
um, when you create your C instance you can supply data via the arguments Bundle
|
sudokai |
So C needs to know if it needs to go back to B or X
|
sudokai |
Okay, the standard arguments way
|
Mark M. |
which could include details on what C should be showing next (B or X) when you are manually handling back navigation
|
sudokai |
Thanks
|
Mark M. |
right
|
sudokai |
Wow thanks
|
Mark M. |
OK, we are running out of chat time -- if anyone has any questions, go ahead!
|
Grigore C. |
I'm good, thank you for your help!
|
sudokai |
It seems an awful lot of work for such a simple use case
|
Mark M. |
IMHO, it is not that simple of a use case
|
Feb 18 | 9:25 AM |
Mark M. |
but, that is why they created Jetpack Navigation -- to package up common navigation/routing patterns
|
sudokai |
Well, the whole app just uses the first approach I told you
|
sudokai |
It's awful
|
Feb 18 | 9:30 AM |
Mark M. |
OK, that is all for today's chat
|
Mark M. |
the transcript will be posted to https://commonsware.com/office-hours/ shortly
|
sudokai |
Thanks!
|
Mark M. |
the next chat is Saturday in the 4pm US Eastern time slot
|
Mark M. |
have a pleasant day!
|
sudokai |
You too
|
Kai H. | has left the room |
Grigore C. | has left the room |
sudokai | has left the room |
Mark M. | turned off guest access |