A story about a small project and a huge amount of inspiration

Mariusz Błaszczak
akra-polska
Published in
6 min readNov 5, 2018

Is it difficult for you to find time and willpower to do some side project you always dreamed of? Or maybe you have always wanted to start contributing to open source, but never could find enough time after work? I am that kind of person. In the past I dreamt to have something like Thoughtbot has in my job: fridays for investment time or something like that. When I joined AKRA Polska, I was told, that the company has something similar — in the time between projects. While you rotate from one project to another, you usually have some time without any commercial project. Then you can educate yourself on your own. You can read programming books but also experiment with some fancy technologies in greenfield projects.

So lastly the above happened to me and my coworker, Adrian. We were having a little bit longer break between our commercial projects. We decided to spend that time developing a simple application together where we could learn some new technologies and have fun :)

The very first task in our project was to find a problem to solve. We were thinking that it would be nice to facilitate some process in our company. I am a Ruby programmer and my friend is an Android developer. We wanted to combine those skills in one project.
We decided to make an app for notifications called openeiro. The main GUI of the app will be in an Android widget — backend will be in Ruby on Rails. It also has an integration with Slack, so it could be used by people who haven’t installed the widget on their smartphone.

The problem we wanted to solve:

In AKRA Polska, in one of our offices, we were relocating to the new place and temporarily we had to use a shared key that we were n’t taking home after work, but we're leaving it to the doorkeeper. So basically, the first person who comes to the office in the morning is going to the doorkeeper place, takes the key, opens the office and notifies all the employees on Slack that: “the office is already open”. Then every next person, on the way to the office checks if the office is already “open” on Slack and if it is, they don’t need to go the doorkeeper, so don’t waste their time, and go can go directly to the office.

We wanted to facilitate this a little bit, by creating the widget app for Android. The widget has a very simple user interface. Button to toggle the state of doors open/closed, an icon of a padlock, to inform a user about the current state of doors and timestamp of last doors event, to inform a user if the opening event isn’t outdated.

The integration with Slack is working accordingly:

  • When somebody changes the state of doors via the Android widget from closed to open, the backend API sends a Slack notification to our main Slack channel, so it can be read by non-Android users as well.
  • When somebody writes on a Slack channel that the office is open, the status in the Android widget updates in real-time.
The finished, Android widget — the office was open at 7:02AM today!

We need to go deeper!

First, I will describe a little bit the Ruby backend and all of the integrations I had to connect together and how I solved some of the problems. Then in the next part of this blog post, my friend Adrian will describe Android implementation details. At the end of the article, we will share some final thoughts of this whole, funny process.

The gory details of Ruby Backend:

The requirements of the server API that will be used by Android widget:

  • HTTP endpoint for toggling doors state (open/close doors)
  • HTTP endpoint for reading the current state of a door
  • HTTP endpoint to register Android user of the widget. Android will use that endpoint to register Google Firebase Cloud Messaging (FCM, https://firebase.google.com/docs/cloud-messaging/) client tokens. Then the Server API will use this client tokens to push updates to all subscribing widgets.
  • Integration with Firebase FCM, to accomplish real-time updates in all installed widgets
  • Integration with Slack webhooks to send a notification to Slack
  • HTTP endpoint to handle Slack events (posting message), to check if somebody wrote on Slack that the office is open,

Let’s investigate some of the more interesting parts of the implementation.

HTTP endpoint for toggling doors state (open/close doors)

HTTP endpoint for toggling doors state is very basic. It creates an event every time the endpoint is used.

HTTP endpoint for reading the current state of a door

The HTTP endpoint for reading the current state of the door is even easier. It just returns the last event of opening doors record from the database.

HTTP endpoint to register Android user of the widget.

To register android users we took the simplest possible solution. Android will just send device-specific uuid together with FCM client token and the backend server will store it using User Active Record model.

Integration with Firebase FCM

To implement pushing to FCM we used gem ‘fcm’:
https://github.com/spacialdb/fcm

The above code fetches all registration ids (fcm client tokens) and then it pushes to those devices newest door event.

Integration with Slack

The last what must be done is an integration with Slack
We used Slack incoming webhooks (https://api.slack.com/incoming-webhooks) and event subscriptions (https://api.slack.com/events-api) to avoid using OAuth and full-blown Slack API in this simple case.

Now let’s show some fancy stuff about Android implementation:

(this part of the article was written by https://medium.com/@adrian.tworkowski)

The android application provides a widget which minimizes the steps required to handle the current lock state. It displays the state and time of last lock event and a toggle button. Clicking the state gets the current lock state from the server API while clicking the button requests the API to toggle it. Both requests update the widget’s lock state. It also gets updated when the app is push-notified. The state itself is cached in app memory to reduce the need for frequent API requests.

The code base consists of 5 sections:

  • widget — handling widget behavior and UI,
  • api — responsible for requests to provided API endpoints,
  • push — registering to API pushes and handling received messages,
  • data — storing the current lock state,
  • util — providing utility methods used in app.

Widget:

To create a widget I extended AppWidgetProvider and registered the class in AndroidManifest.xml.

The appwidget_info file contains <appwidget-provider/>an element with widget configuration — not important for this sample.

LockWidgetProvider class shown below updates all added widgets according to the provided lock state in onUpdate method. Clicks on widget elements are handled by the onReceive method. Mentioned click intents are created by createPendingIntent method. Widget refreshes its state in onEnabled method called by the system when the first widget instance is added. Methods creating widget’s RemoteViews are skipped in this sample.

LockWidgetJobService handles both widget’s actions: toggle and refresh. It enqueues only supported intent actions. In this case, it requests API calls (skipped in this sample).

API:

API was handled by the Retrofit library with GSON converter in a singleton ApiClient class allowing access to the ApiService object from anywhere in the app. Created API classes are presented below.

Push:

App pushes were handled by the FCM (Firebase Cloud Messaging) library. After adding gradle dependencies and configuring Firebase project in the console I created a Service extending FirebaseMessagingService and registered it in AndroidManifest.xml.

AppMessagingService requests API to register the device for pushes on every token change. It also deserializes received messages containing lock state JSON and updates the widget.

Data:

The application currently stores lock state only in app process memory. It could use some kind of SharedPreferences or SQLite in the future. AppCache is a singleton class with lockState field. The field is only updated with newer data or when the current one is null.

Util:

Contains various utility methods and classes. UUIDProvider used for push registration is one of them.

Final thoughts

The special time dedicated for non-commercial projects at work is a really great idea. We learned new technologies, finally had the opportunity to work together on the same project, so we’ve got to know each other much better.
And of course, we’ve got a huge amount of inspiration and motivation so we can proceed with commercial projects with fresh, fully motivated and inspired mind.

Happy coding :)

--

--