Devember - Road Trip Tracker (Android Application)

Day 0 I’m late…

Hi,

This is my blog for my #devember2k19 project Road Trip Tracker.

During the summer I started to learn Kotlin for Android development as it’s now the new official language for native Android development and a new buzz word for hiring managers. As it was the summer and I was making the most of the good Irish weather (the rain was warmer than usual) I decided a good way to learn Kotlin while keeping motivated would be to write an application I’d have a use for, so I wrote a simple application to track my cycling trips called “Road Trip Tracker”.

Since then I’ve learned a bit more Kotlin but more importantly I’ve been refreshing my knowledge of all the new (or not so new) Android frameworks and architectural components recommended by Google for new Android App.

So to that end I’ve decided to call my initial efforts a proof of concept/prototype and to re-write Road Trip Tracker from scratch and to motivate me to actually do some work on the project I’d like to tackle it as my #devember2k19 project.

This is the original application I intend to re-create (with less horrible code)

I am no designer but I hope to make it a little more modern looking and to embrace Googles beloved Material Design guidelines (Yep, that’s gonna end in tears), functionally it should be pretty much the same as the prototype with the following main features: -

  • A home/dash screen showing a summary of the latest recorded trip and navigation controls to the new tracking and tracking history screens
  • A screen to start and monitor a new tracking session
  • A list showing previously tracking sessions with the option to view the details or delete sessions.

Anyway, I have created a new repository for my project on my GitHub account which can be found here qnd as per the #devember guidlines here is my signed pledge.

“I, Shecks, will participate to the next Devember. My Devember will be to re-write my Road Trip Tracker Android application . I promise I will program for my Devember for at least an hour, every day of the next December. I will also write a daily public devlog and will make the produced code publicly available on the internet. No matter what, I will keep my promise.”

Shecks :nerd_face:

6 Likes

Day 1 I am still late.

Did some sleeping and then decided to do a bit of work on my #devember2k18 project “Screep Studio” … I wonder if Level1Techs will be holding a Devember this year? Hmmm…

Shecks

2 Likes

Day 2 I was doing something this time last year…

Found the #devember2k19 thread on the Level1Techs forum … hmmm… :thinking:
Not sure if I will take part this year… need to think about it.

Shecks

2 Likes

You can do it! We believe in you :tada:

2 Likes

Day 3 Playing catch up

Since I am a couple of days late to the party I did a little more than 1 hours work today and achieved the following:-

  • Setup my GitHub repository for Road Trip Tracker
  • Created a new Android project for the application and committed the initial skeleton code to GitHub
  • Setup the initial UI framework for the application with main activity, two fragments and navigation graph
  • Started work on the data layer for the application by setting up the Room database, entities and DAOs
  • Created as TrackingRepository class in preparation for accessing the database from the applications view models.

Here’s my lovely UI so far:


Tomorrow I need to figure out how to get GitHub integration to work correctly with Android Studio (I can’t figure out how to get it to read the login credentials I added when pushing commits so have to do the push from the command line)

The next step will be to add some test data to my Room database so that I can work on the route summary list for the history fragment.

Shecks

4 Likes

Day 4 It’s all gone blue!

Lots of “fun” was had today.

The plan was to do some quick tests on the data layer code I wrote last night then move on to working on the History fragment and get some data displayed in RecyclerView. Unfortunately this took a lot longer that planned. I ran into an issue I hadn’t seen before. It turns out that when you write data to a Room database (SQLiteDatabase) it is cached in an in-memory database until it reaches a certain size and only then is it flushed to disk.

So, when I copied the database from my phone to check it with DB Browser, it was empty, the schema hadn’t even been created. It was only after a lot of swearing, checking code, re-writing code, running database inserts in transactions, out of transactions, on the applications disk io thread pool, on the network thread, on the UI thread etc that I changed my test to insert 200 records into the database. Only at that point did Android decided it wasn’t going to cache the data in memory anymore and finally wrote it to disk … Arrrrrrgggggg!

At that point I had already spend well over the #Devember 1 hour per day quota but I couldn’t leave it without making progress so I had a big mug of tea and a snack then got back to it.

I now have the bulk of the code written for the route history fragment, and can display the test data I added to database in a paged RecyclerView (Using a ViewModel, LiveData, PagedList, DataSource.Factory, into a PagedListAdapter as per Mr. Googles recommendation so it’s fully “Buzzword Compliant” ™️)

You might also notice that the application is blue again :neutral_face:

UI design is not my area of expertise. I’ve been reading the Material Design guidelines but I don’t think I have the artistic skills to appreciate all the subtleties involved in making an application look professional … so it’s in danger of ending up looking just like the prototype.

If there are any UI design experts reading I’d really appreciate your input on this. I’ve been using the Material Design Color Tool to generate the palette for the app which makes things a bit easier but I sill don’t know what makes a good material palette.

If anyone want’s to have a go at picking a better palette that would be cool. The tool can export the color palette in a resource format that can be dropped into the app. This what I am currently using: -

    <color name="primaryColor">#0288d1</color>
    <color name="primaryLightColor">#5eb8ff</color>
    <color name="primaryDarkColor">#005b9f</color>
    <color name="secondaryColor">#b0bec5</color>
    <color name="secondaryLightColor">#e2f1f8</color>
    <color name="secondaryDarkColor">#808e95</color>
    <color name="primaryTextColor">#fafafa</color>
    <color name="secondaryTextColor">#808e95</color>

Anyway, I think that’s all I can manage for today, I’ve pushed all the changed to GitHub if anyone’s interested.

Shecks

4 Likes

You may find this useful for choosing a good color scheme to go with.

4 Likes

Day 5 Tricky toolbars

Not much of an update today. I did some more work on the route history list fragment. Reworked my code to handle item selection to use the RecyclerView.OnItemTouchListener interface rather than my own dodgy implementation using onItemClicked() directly on ViewHolders in the RecyclerView.Adapter (I’ve been using that techniques for ages and thought it was about time I looked into how to do it correctly)

Once this was done I added code to allow list items to be selected and planned to have a ActionMode menu display on the Toolbar to allow the user to choose an option to be performed on the selected items (in this case ‘delete’).

But I ran into issues, for some reason the ActionMode menu is being positioned above the Toolbar

Which is not what I want. I think it might be to do with the fact that I am using a CoordinatorLayout with the HideBottomViewOnScrollBehavior mode that hides the Toolbar and BottomNavigationView when I scroll the list and the ActionMode menu isn’t playing well with it :neutral_face:

I like the way the toolbar and navigation bar slide off the screen when the list is scrolled so I don’t really want to remove it.

Anyway, more research is required. I don’t think I will be pushing any code tonight though as once I figure out the problem I will need to clean up all the test code I’ve added to try to resolve the issue.

Shecks

4 Likes

Thanks @Engle that will come in handy. I am starting to figure out where all the different palette colours are used in the material themes so once I have that sorted I’ll be able to pick and nice palette and the UI should look better.

Shecks

2 Likes

Still Day 5 I just couldn’t let it go…

Woohooo :nerd_face:

Turns out it was a theme setting (windowActionModeOverlay) and was off by default…

<item name="windowActionModeOverlay">true</item>
<item name="actionModeBackground">@color/primaryColor</item>

Shecks

3 Likes

Day 6 Elusive buttons

It’s been another one of those days, not a whole lot of progress as I got caught up trying to chase down an issue with the layout of the applications main activity.

I started work on laying out the applications home fragment. The plan is for it to display a Google Map in the top 60% or so of the screen that shows the latest route in the database and in the bottom 40% of the screen there will be some summary information either about the last route or calendar of showing the days routes were recorded for over the last month.

But I am having some trouble getting the layout to work well with the BottomNavigationView and collapsing toolbar. The hosted fragments seem to be extending off the bottom of the screen instead of just to the top of the BottomNavigationView so the buttons and other views are being hidden.

When what I really want is something like this

More research is required …

On a more positive note, I did manage to register a new Google Maps API key for the application (I hate doing that, Googles Cloud Platform Console is a pain to navigate) so at least the maps are loading.

Unfortunately I won’t be checking my API key into GitHub so if anyone does want to try out the app they will need to supply their own key in order for it to build. (There’s a note in AndroidManifest.xmlexplaining where the API key should be)

Shecks

4 Likes

Day 7 Edge dodging

If I never see another XML layout file again it’ll be too soon :neutral_face:

Today I spent far too much time trying to figure out the the cause of the issue with the BottomNavigationView obscuring the bottom of the home screen fragment. I’ve lost count of how many times I’ve deleted everything from the layout XML and started over.

From what I found out, it seems to be a bug that a lot of other Android developers have been struggling with and there doesn’t seem to be a definitive solution for it. There are a couple of work arounds but they all have side effects. It has been logged with Google but there’s no fix yet.

Anyway, I managed to come up with a layout that works for me so I was able to get a little work done.

Because I spent so much time finding a solution I ran out of steam and don’t have the motivation to spend anymore time trying to finish the layout for the bottom summary area, so this will do as a placeholder for now.

I think tomorrow I am going to work on the route tracking or route detail activities because I need to get back to writing some code, this design/pixel pushing is killing me.

Shecks

4 Likes

Day 8 XML Hell

Yesterday I said that I needed to get back to writing code and had planned on doing work on the TrackingActivity and RouteDetailActivity today but I ended up doing more styling and XML layout tweaking.

I much prefer writing code than trying to make UIs look good (or in my case … a level just above hideous) by tweaking XML but it’s an area I need to improve on and so part of the reason I chose this project for #devember2k19.

The original application is about 90% functional anyway so my main goal is to get better at Android UI work (especially Material Design) so I have been spending time making sure I’m doing this the “material way” and trying to use best practices when it comes to styles and themes.

Anyway, today I did more work on the layout for the HomeFragment and added some new layouts for landscape mode. This is something that usually ends up as an after thought for me.

The MainActivity is currently the only activity in the application and, as per Mr. Googles Guidelines, it acts as a host for the application content (child Fragments) providing the chrome around them. In portrait orientation it provides a collapsing toolbar and a BottomNavigationView for to allow the user to navigate around the app but in landscape it doesn’t make much sense (and takes up too much screen real-estate), so this had to change.

So I decided to use a DrawerLayout to host the navigation controls when the device is rotated.

I also decided not to show the planned ‘Route Summary’ fragment when the HomeFragment is in landscape orientation.

I don’t think the HistoryFragment needs a specific layout for landscape, perhaps the list items could make better use of the space available, but it will do for the moment.

It’s not the progress I wanted to make today but it’s something I would have had to address at some point. Anyway, maybe I’ll get some code done tomorrow.

Shecks

4 Likes

Day 9 Re-inventing the wheel

Today I discovered a round thing that rolls along the ground that can be attached to stuff to make moving around easier :neutral_face:

I added a new fragment to the app planning to use it to show a list of “favourite” routes. Since this fragment was pretty much going to be a list of routes with a lot of common between it and the HistoryFragment (e.g multi-item selection) I decided to extract the code I wrote for RecyclerView multi selection into a new class RouteSummaryRecyclerView

Which I did, and I was very happy with it. Now all I need to do to handle multi selection is to implement a couple of callbacks in each fragment: -

routeSummaryList.setOnItemClickListener(object: RouteSummaryRecyclerView.ItemClickListener {
    override fun onItemClicked(item: RouteSummary) {
        HistoryFragmentDirections.actionDestinationHistoryToRouteDetail(item.id!!).also { action ->
            Navigation.findNavController(routeSummaryList).navigate(action)
        }
    }

    override fun onItemLongClicked(item: RouteSummary) {
    }

})

routeSummaryList.setOnItemSelectionListener(object: RouteSummaryRecyclerView.ItemSelectionChangedListener{
    override fun onBeginMultiSelect() {
        actionMode = startActionMode(this@HistoryFragment)
    }

    override fun onEndMultiSelect() {
        Log.i(TAG, "onEndMultiSelect()")
    }

    override fun onItemSelectionChanged(selectedItems: Set<RouteSummary>) {
        actionMode?.let {
            it.title = resources.getQuantityString(R.plurals.route_list_selection_action_title, selectedItems.size, selectedItems.size)
        }
    }
})

Pretty handy, until I thought … “Doesn’t Android Jetpack provide something like this … if it doesn’t then it should!”

And sure enough it does … RecyclerView.Selection and SelectionTracker … arse!!!

It wouldn’t be so bad if I had never heard about them before but I had. I remember reading about SelectionTracker.Builder and thinking “That’s pretty cool, I should use that next time I need to handle RecyclerView selections”

Oh well, I’ll be spending my next #devember2k19 session reading about SelectionTracker and re-writing code.

In happier news, I actually got back to writing code today (even if most of it needs to be re-written).

  • Added FavouritesFragment so favourite routes can be tracked (I definitely didn’t add this only because the Material Guidelines say you should only use BottomNavigationView if you have between 3 and 5 top-level navigation destinations and I only had 2 :cowboy_hat_face:)

  • Moved the SettingsFragment out of the top-level navigation into the AppBar

  • Added a stub for the RouteDetailFragment and hooked it up to my item selection code in HistoryFragment (code which I am now going to replace)

Anyway, I am off to study the Androidx SelectionTracker

Shecks

3 Likes

XML Hell

This was my professional life for a while :joy:

Hang in there! Looks like you’re making progress

2 Likes

Day 10 Do-over day

Today I spent time reading up on the Android selection library and the SelectionTracker component so that I could replace my own RecyclerView multi-selection implementation with it.

It’s not the easiest of components to use and to get simple multi-selection working with it requires quite a bit of boiler plate code, because of this I decided to keep my RouteSummaryRecyclerView implementation so I could hide all the details from the UI (I need the re-usability anyway since both HistoryFragment and FavouritesFragment will require a list of Route Summaries with multi-selection support)

The result is I still have a nice clean interface for my UI to implement and it doesn’t need to know anything about the horrible selection library code.

Incidentally, just using the SelectionTracker component required as much code as it took to implemented my own multi-selection solution.

Also, from some reason the API devs at Google thought it was a good idea to introduce a component with a circular dependency. To use the SelectionTracker component your implementation of the RecyclerView.Adapter needs to hold a reference to the SelectionTracker (fair enough, the adapter needs to know when items are selected) however, to create an instance of the SelectionTracker (via SelectionTracker.Builder) you need to pass a reference to the RecyclerView you intend to track item selections for (also fair enough).

BUT … there is a sanity check (or in this case an insanity check) inside the SelectionTracker.Builder.build() method that checks to make sure that the RecyclerView reference passed has a non-null RecyclerView.Adapter reference … and if not it throws an exception.

My OCD didn’t like that one so it was fun trying to come up with a clean way to get around that little gem.

Anyway, it’s all working now … now I just need to clean up all the code required to handle the ActionMode menu and the edge cases around showing/hiding/restoring etc when the device is rotated, time to read up on LifeCycleOwner

Shecks

5 Likes

Day 11 Just about…

Didn’t get much time to work on my #devember2k19 project today but I did get to watch my little nephew play Joseph in his schools Christmas nativity play (complete with his awesome painted on beard! … hahaha)

So when I got home I decided to just do some more preparation work on the UI (it has to be done anyway)

  • Added a tabbed layout and two child fragments, RouteDetailStatsTabFragment and RouteDetailMapTabFragment to the RouteDetailFragment that will be used to display the recorded statistics and route map for a saved route (when selected from the route history list)

  • Decided that trying to get the collapsing AppBarLayout and auto hiding BottomNavigationView widgets to play nicely together wasn’t worth it. The first item on the route history list was being obscured by the toolbar and the tabs on the new tab layout in the RouteDetailFragment was also hidden behind the toolbar so I reverted back to static chrome for the MainActivity layout (I might come back to it when I have the application functional)

More TODO placeholders …

Tomorrow I will work on some actual functionality, I’ll get the saved route detail data into the RouteViewModel and at least get some of the stats displayed on the stats tab of the route detail fragment.

Shecks

3 Likes

Day 12 Let there be code

Finally made some good progress today.

  • Added functionality to my RouteViewModel to provide RouteDetail objects from the database to the UI for display.

  • Put together a layout for the stats tab of the route details fragment (Still needs some tweaking but it’s good enough for now)

  • Added code for formatting times, distances and speeds. Each profile (e.g Walking, Running, Driving etc) can be configured with preferred distance and speed units so that the data can be formatted correctly in the context of the profile (e.g. Speed for boating is “knots” where as the driving and motorcycling profiles could be “mph” or “kph” etc)

  • Added code to retrieve the list of favourite routes from the database and display them in the route summary list in the favourites fragment.

  • Updated the route summary lists to use the new unit formatting utilities.

The most tedious part of writing Android apps for me is putting together layouts, I spend most of my dev time today trying to come up with a reasonably usable layout for the route details stats tab:

Fun with ConstratintLayouts and CardViews

And here’s what it looks like with test data:-

And the History and Favourites lists:-

I can’t really do any work on the route maps yet because I don’t have any GPS data to plot the routes. I was going to write an application where I can point and click on a map and save the locations but I think I will just get the tracking service written then go for a cycle :laughing:

Anyway, that will be the next step, get a stub together for the RouteTrackingActivity and start work on the tracking service so I can get some real GPS data.

Shecks

6 Likes

Looks like real progress.

2 Likes

that’s why I despise doing front-end stuff e.e

the layout is pretty good also, good job!

3 Likes