Welcome back. In this video,
we're going to observe the changes to the device configuration
to allow our app to respond when the locale changes.
When something like this happens,
the system will broadcast a message to any broadcast receivers
that have registered to be notified.
Setting up a BroadcastReceiver
follows the same pattern as setting up a ContentObserver.
We have to create an object that will respond to the messages then register it.
When we no longer need it, we unregister it.
One difference is how we specify which broadcasts we're interested in.
When using a ContentObserver,
we tell it which URI to observe.
With a BroadcastReceiver, we use an IntentFilter instead.
Let's see some code.
I'll start by creating a BroadcastReceiver object
in our DurationsViewModel.
We've got an error because we need to implement the OnReceive function.
I'll use control I to get android studio to generate the stub.
The OnReceive function is an abstract function
in the BroadcastReceiver class,
which is also abstract.
That means we have to provide the implementation ourselves.
An abstract class is similar to an interface in many ways.
There are some important differences.
And if you don't already know about abstract classes,
it's worth Googling for something like "JavaAbstract class
versus interface" to learn more about them.
An abstract class can provide some implementation itself,
which an interface couldn't
but that distinction has faded a bit recently
because interfaces can now provide some implementation
in later java versions.
All right. What do we want to do
when we receive a notification that the locale has changed.
We've already discussed that in fact.
We need to create a new Gregorian calendar instance,
so that the calendar is using the settings for the new locale.
The user may have changed the calendar's date
and will probably expect to still see that date.
That means we should store the date then set a new calendar instance
to use it.
There's no way to change the locale of a calendar instance,
which is why we create a new one.
That means we have to declare our calendar as var
rather than val.
That's fixed the error.
After creating a new calendar instance, we call the ApplyFilter function,
that re-queries the database and make sure the report reflects
what the user expects.
There's something else we should do,
and that's described in the docs string for the BroadcastReceivers
OnReceive method.
Control click on BroadcastReceiver on line 44
to see the source code,
that takes us to the class constructor
and the docs string below that relates to the OnReceive method.
This is java code remember which is why I'm saying method
instead of function.
It's worth reading the docs strings
or the google documentation if you prefer
for any classes and methods that you use in your apps.
You'll often find unexpected details that can save you getting into difficulties.
I'm interested in the last paragraph here,
just above the parameter descriptions.
But I'm not going to read it just yet.
It'll make a bit more sense once we've finished setting up the BroadcastReceiver.
Back in our DurationsViewModel class, we need to register the receiver,
that involves creating an IntentFilter
for the messages we want to receive.
I'll add the code in the init block.
The IntentFilter tells android which broadcast messages
we want to receive.
Here, we're going to create a new calendar instance
if the user changes the locale
because that changes things like which day the week starts on.
We also create a new calendar if they change time zones
that's obviously something our calendar needs to know about.
You can add as many IntentFilters as you need
by calling the AddAction method
and providing the action you're interested in.
We finish by registering our server
by calling the RegisterReceiver method of the application object.
In the documentation that you google for,
you'll find a discussion about the context of your receiver.
If we were doing this in an activity,
we'd call the activities RegisterReceiver method
that would allow the activity to receive the broadcasts
until the activity gets destroyed.
We're using the application to register our receiver,
which means it will remain registered as long as the app is running.
Of course, we're going to do things properly
and unregister the receiver when our ViewModel gets destroyed.
We do that in the uncleared function.
Before we test it, let's have another look at the code in our init block.
We've created an intent filter that specifies
action time zone changed and
action locale changed.
That means we should only receive messages when those events are broadcast.
I'm going to add some more code to the OnReceive function,
then we'll have a look why.
Okay. Why have I done that.
If we're only going to get called when one of those actions is broadcast,
why bother checking?
The reason's described in the last paragraph of that doc string.
Let's have another look at it.
Ctrl click on BroadcastReceiver to go back to the source for the class.
It says that the Intent filters are not guaranteed to be exclusive,
they're hints to the android framework but we may get other messages,
that's why we should check that the actions are only the ones that we are interested in.
Now in this particular app,
it's not crucial if we create a new calendar
when some other event occurs,
it's inefficient but it won't cause serious problems.
But there may be situations where it is important.
So make sure you always check the intent action
that causes your receiver to be called.
All right. That code's finished.
In the next video, we'll test the app to make sure that we've fixed the bug.
See you there.