This app will extend the Lambda Message Board to allow users to subscribe to boards and show notifications when a new message is posted to that board. However, since this project is so large already, we won't be storing a list of subscribed boards.
The first thing we'll need to do is create our basic service and make sure it starts.
- In your project window on the left, right click on
app
then go to new and then service at the bottom. SelectService
. - Name the service
SubscriptionMonitorService
, uncheck the exported box and finish. - Override the
onCreate
,onStartCommand
, andonDestroy
methods - Write a log statement in each of these methods to indicate that each method has been entered
- Remove the
throw
statement from theonBind
method. replace it with a statement to return null
We'll make sure we can start our service from the Boards activity
- Go to the method where you populate your board item data. Either in your
MessageBoardActivity
or theListAdapter
if you used theRecyclerView
- Add a
LongClickListener
to the topView
for your element
This is done the same way as a regular click listener, except you return a boolean if the click was consumed, generally, this will be true
- Write an intent to start the service. This is done just like starting an activity, except you call
startService
instead ofstartActivity
- Test your app to make sure that you see the
onCreate
andonStartCommand
when long pressing an item - Try long pressing two items in a row and watch your log, what do you see?
- In
onStartCommand
add thestopSelf
method call to make sure the service closes since we don't have it doing anything - Go back to your LongClickListener and add a
String
value for the identifier of the board in question to your intent with a key indicating it is to add to a subscription list. Then retreive and log it in your service
Notice that the
onStartCommand
method accepts anIntent
as a parameter, pull the extra from there
- Test your app
- Add your new files to git and commit them
We'll need to write a loop to search for new messages. There are a few things we'll need to do this.
- track the last time the boards were checked
- retreive the board data with our DAO
- look for a subscribed board and check if there have been new messages since the last subscription
- show a notification if a message is found
-
When testing your app so far, you saw that
onCreate
was only called once even thoughonStartCommand
was called multiple times with multiple button presses. We can useonCreate
to initialize values once. -
Create a new data member for this class of type
Long
calledlastCheckTime
-
In
onCreate
assign it the value ofSystem.currentTimeMillis() / 1000
this will get us the unix epoch time in seconds. More on epoch time (here)[https://www.epochconverter.com/] -
Create a
String
data member calledsubscription
, assign it the value of the empty string""
inonCreate
-
In
onStartCommand
store the passed identifier in yoursubscription
data member. -
Create an start a new thread after updating
subscription
. -
In the new thread write a
while
loop that will loop whilesubscription
isn't an empty string -
Move your
stopSelf
call into the thread but outside of the loop -
Inside of the loop you'll need to call your
MessageBoardDao.getMessageBoards
method and store the result. -
Loop through the result of that call and search for a board with the identifier which matches your
subscription
variable. If it does, check to see if the last message in that board has a timestamp greater than yourlastCheckTime
data member. If so, set a flag.It may be helpful to add a method to your
MessageBoard
class to retrieve the last message in the list of messages -
After leaving the loop, update your
lastCheckTime
variable -
If your flag is set, post a notification stating that there is a new message available on a subscribed board.
To use the package name in the channel name, you'll need to create a context data member and assign it in
onCreate
-
At the end of the while loop, call
Thread.sleep(CHECK_PERIOD);
where CHECK_PERIOD is the amount of time in milliseconds that you want to wait between checks -
Test your app
You can test the app by subscribing to a board and then posting a new message either using your app or Postman. I find postman easier as you can quickly repost the same message, however, in Postman, you'll have to manually add and update the timestamp
-
Commit changes to git
Finally, let's add support for users to subscribe to multiple subscriptions.
-
First, add something to your
LongClick
method to indicate that the board has been subscribed to, I changed the element background colorRemember, without persistence or
LiveData
, this will reset with each resume, but the subscription will still stay in the service since it is independent -
In your service, add an
ArrayList
data member to store a list of subscriptions and initialize it in theonCreate
method -
In
onStartCommand
, append the new subscription to the list -
In your thread's loop, create a separate copy of your list using the copy constructor (
new ArrayList<>(oldList)
)This ensures that any changes made to the list outside of your thread are only updated the next time your thread loops and don't affect the current loop. This works because
String
objects in java are immutable -
You'll have to loop through this new list for each board, this is a nested loop and looks something like this
for (MessageBoard board : messageBoards) { for (String subscription : localSubscriptionCopy) { if (board.getIdentifier().equals(subscription)) { // check for a new message } } } }
-
Test your app
-
Commit changes to git
Since we don't have persistence in this app, subscriptions won't work perfectly, but we can add the ability to unsubscribe to boards while you're still in the app.
- Add a boolean data member to your
MessageBoard
class which indicates if it is subscribed to or not. Give it a default value in your constuctors and a getter and setter. - In
LongClick
check if the board is subscribed to. If it is, add the identifier to the intent with a key that indicates it is to be removed. - In the service's
onStartCommand
check both keys for values, then check if the returned values are null, if not, add/remove the values from the list as necessary
ArrayList
has an overloaded version ofremove
which accepts an object and removes the first instance of it in the list rather than accepting an index. Change your while loop to exit if theArrayList
is empty (size == 0)
- Test your app
- Commit changes to git
- Push the changes
Add persistence to the subscription list so that the user can always know which lists they're subscribed to