My internship at Flow Pilots: making the app Call’y, by Bram Ravijts May 13, 2020
For my internship, I was tasked to make Call’y, a to-do list for calls you have to make. This was my first experience working with Xamarin and the more specific parts of Android like notifications, permissions and interaction with other apps. I also had to work with a framework called MvvmCross, which I had never heard of before. In this post I’ll show which problems and tasks I tackled when making the Call’y app.
Learning the language
I spent the entire first day watching introduction courses of both Xamarin and MvvmCross. The next day, I did further research and followed along with some examples to get the hang of writing this new language. Afterwards, I decided to dive straight in to making the app and learning along the way. I asked a colleague for help setting up the files correctly to their standard. At the end of the second day, I started to apply what I had learned in the exercises to my actual project.
Before I can actually start explaining what I coded and applied to make Call’y, we need to know what we are dealing with. Let’s start with the basic building blocks of working with Xamarin Android: The ViewModel, View and Model. One of the core functionalities of Xamarin is cross-platform code sharing. This is only possible by splitting the sharable code from the platform-specific code. This is the difference between the ViewModel and the View.
The ViewModel is all your sharable code. When you add a new functionality, you always try to place it in the ViewModel first. This has all your standard logic, such as database interactions, navigation, etc. ViewModels don’t include platform-specific code or advanced UI interactions.
These things are done in View. One View binds to one ViewModel. In the View you place your layout file and you declare to which ViewModel this View is bound. Examples of code you put in the View are click events, permissions, resource usage (such as images), interaction with hardware, etc.
Last but not least, there is the Model. Models are definitions of objects. Here you declare what attributes a certain object can have. An example is a call in my to-do app. A call contains an Id, number, name and a whole bunch of other stuff. This is all declared in the Call Model, which I then use in the ViewModel to bind to different input or text fields in the layout and save to the database.
This interaction between a Model instance in the ViewModel and the layout is also something which I want to explain, because I will use this a lot below. Binding means that when the property changes in the layout, the data in the Model instance updates as well and vice versa. To do this easily, I use a functionality in MvvmCross called MvxBind. You use it like this code your xml layout file:
local:MvxBind=”Text Name” />
This binds the text property of that EditText, which means the text it will show in the input field, to the property Name in the ViewModel. When you edit the text in there, the Name property its value will also change to what you entered. Also, when the value of Name changes and the ViewModel alerts the View of this change, the text in the UI will also change.
When I started the project, I didn’t have a design yet. So I made basics layouts of all the pages as I was working on them. The most important part of a to-do app is of course the to-do list itself. The basics of making a list of an indefinite amount of items is with a RecyclerView. When working in standard Android, RecyclerViews are rather annoying to work with, as you have a bunch of elements you have to create and link to each other for it to work. You have to make an adapter and link it to your RecyclerView to make interactions with the items possible, a Viewholder to show the items in and a layout file to design the items in your RecyclerView. Making a RecyclerView with MvvmCross right from the start showed me how efficient it can be. You can make a whole RecyclerView with this in your xml file:
local:MvxBind=”ItemsSource Calls; ItemClick CommandCallClick; ItemLongClick CommandCallLongClick”/>
With this code, you just have to create your layout for the item, which in this case is ‘item_call_list_item’ and you are set. In the ViewModel you set the data you want to display to the variable or property Calls and that’s it! The CommandCallClick command is a variable command with which you can execute a method when an item is clicked, CommandCallLongClick does the same for holding a click on an item.
After setting up the overview for the calls, I made the layout for adding a call manually and the detail screen. At first, the edit functionality was implemented in the detail screen, but this was moved to a separate screen in the final design. After the layouts were made, I started to implement the functionality in these screens. To be able to do that, I first needed a working database. Because it’s a small project, and calls didn’t need to be shared across devices at the moment, I decided on a local SQLite database. This was simply added after making a database path and setting up a service with CRUD methods. I didn’t need to worry about tables or columns, because I could use the Model of Call I made in the beginning, as my database layout. This way I can just save the Call instance in the ViewModel straight into the database. Afterwards, I got to work on the add screen functionality. This was just the basics, so you could add a call manually by filling in all the input fields on the add screen. Then I started on the detail & edit screen. These are a simple text and input fields for each bit of info. When you’re in detail mode, it showed the text fields. In edit mode, it showed the input fields. At first, there was also a button to save the call to your contacts, but this was removed later on.
A to-do call app in which you can’t add contacts, or more importantly can’t call with, is not very useful. So the next step was to make it possible to interact with the contact book and make calls. This required me to research permissions first, as you need permission from the user at runtime to read contacts and make phone calls. The interesting thing I learned here was that you can’t just check if the permission has been granted. You have to also check if it has been denied with ‘don’t show again’. If this is the case, you can’t just show the permission popup again, but you have to show a temporary message to remind the user that the app won’t work as expected without these permissions. You add a button to this message to allow the user to bring the permission popup back to accept them. When this worked completely, I added the option to add a contact from your contact book in the add call screen and made it possible to make a call from the overview and from the detail screen.
An overview of calls on which you can only click and has a few buttons is not very user friendly. There are 2 things that are really important for a RecyclerView: Swiping and Drag & Drop. To implement this, I used a ItemTouchHelper. This is a class in Android that gets called when you touch an item in a list. Exactly what you do when you want to move an item or swipe. This class has the 2 methods we need: OnMove and OnSwiped. The OnMove method is called after an item is moved from its position in the list. It also sends along the target position. If you use these to update all the items’s positions in the database, you can always sort them by this position and have a manually ordered list. The problem I had here was that I had a specific button inside the item layout that enables the drag. This is because there is no binding in the MvxBind library for touch, only for click. So this is when I learned that you could write these bindings yourself. So I created a binding with a OnTouchListener, then I bound this to a method that would send the position of the currently clicked item from the ViewModel of the item to the ViewModel of the overview itself. From there I launched a Touch Event to the View which calls the itemTouchHelper class and we’re off. Quite a bit of work, but satisfying when it does! The swipes are called on the item instead of inside it, so I didn’t have this problem here. The difficulty was showing an icon when swiping. You have to make the canvas itself as well as draw the icon, so there is some code involved to calculate the size of the icon and then make the canvas in that size, draw the icon in there so that it doesn’t get stretched and finally calculate when to show the icon so that it doesn’t overlap with the item you are swiping.
Automatically add missed calls
When you miss a call, most of the time you have to call them back. This is also a to-do, so they should be added to the list. But manually adding every call you have missed is very inefficient and not user friendly. So I decided to make the app recognise when a call is missed add it to the list automatically. This is done by making a BroadcastReceiver. A BroadcastReceiver is a class in Android that reacts when a broadcast with the same intent as the one you register in your receiver is called. Broadcasts are sent out by the Android OS when certain actions happen. The intent we listen to is PhoneStateChanged. This broadcast is sent when the call state of the phone changes, for example from idle to ringing for an incoming call or from ringing to off hook when you answer the call. When the state goes from ringing to idle, the call was not answered, which means it is a missed (or rejected) call and should be added to the Call’y list. Because you only get the number of the caller, you can then search for this number in your contact book. If the contact exists, you can add the name and company to the info of the call. Otherwise you save just the phone number in the to-do list.
When the app is closed, it is difficult for the user to know if a call has been saved to the list. To solve that, I implemented notifications to alert the user when a call has been added to their Call’y list. Sending a notification is not that hard. First you have to set up the channel through which you want to send your message. You can make different channels for different types of messages. Each channel has its own title and description, but most of all, a priority setting. This decides in which your notification needs to be shown. After that, you can make your message and send it. To make a message, you need to pass along the id of the channel, a unique id for the message, title, description and a whole bunch of other optional things such as an icon, what to do when the message is clicked and if the notification should disappear after clicking it.
When the core functionalities were in place, it was time to start upping the quality of the app for user convenience. The first thing that I implemented for that was a translate plugin from Flow Pilots. You make a file for each language you want to implement. In these files you place all your translatable texts and give each of them a key. When the app starts, it looks at the language of your device and then shows the texts in the file corresponding to that language. Because you assign variable names to these texts, you just need to send the variable name and the app will show that text in the correct language.
Other things that were added were a splash screen, updates to the permissions and a bunch of changes to the structure. This can happen quite often, so be prepared to rework parts you have worked a lot on, because the design changed later on. For example, I spent 3 days trying to figure out how to make a good working tab layout with the current to-do list and the archive, only to have the tab layout scratched right after I got it working.
A lot of time was also spent on rewriting code. Every time a functionality is finished, it gets code reviewed to make sure it is written correctly and efficiently. Because I didn’t have this kind of checkup on my code before, I had to do a lot of reworking in the beginning. This can be a little frustrating, but most of all I learned so much from it.
Finally, after the design got approved, I had to implement it. This meant that I had to rework a lot of what I had already made, because the structure was different than what I had made in my temporary layout. This can also take quite a bit of time, but seeing your app take form, changing from this blank set of input fields and texts to this pretty and full fledged app was something I really enjoyed. A good design makes you appreciate what you have made, because it not only works, but it is enjoyable and comfortable to use as well.
Making an app from scratch, in a language you haven’t used before, is a lot of work. You are going to bump into a lot of walls and spend ages trying to solve the smallest of things. But it is incredibly satisfying. Learning something new, putting that knowledge to use, twisting that knowledge to your specific case, building on what you have learned to make increasingly more difficult parts and adding all those parts together into a complete application. If you then look back on what you started with, you get a feeling of both pride and satisfaction for your accomplishment. And that is something I believe we all strive for.