Tropos For Android

A long time ago, in a galaxy far, far away we released Tropos for iOS : a simple weather conditions app. Today, from this galaxy, I am thrilled to announce that we have released Tropos for Android.

Backstory & Design

Tropos was designed with a single question in mind:

“What does it feel like outside?

Tropos answers this by relating the current conditions to the conditions at the same time yesterday. It turns the weather into information you can act on.

Code

A few months after we released Tropos for iOS we open sourced the code for the app. And since anything iOS developers can do, Android developers can do better(πŸ’ͺ), we are making the source code available on GitHub today!

For those of you who didn’t immediately click on the link to the code and are still reading this, hello! πŸ‘‹ Thanks for sticking around! As a consolation prize, here are some of the technical specs of the app that might make you want to go back and click that link!

Language

We decided to write the entire app (including all the tests) in Kotlin. While have used Kotlin in a few of our client apps, we were excited to try it out with such a design and custom view heavy app. Custom views turned out to be a fantastic learning tool and an interesting way to test out the merits and interoperability of a new language. Unlike model objects, or generic logic classes where the developer has total freedom when it comes to how to write and design their code, custom view classes come with stricter rules enforced by the API and design of the framework.

Architecture

This app is relatively small in terms of backend logic (there are only two network requests) and number of screens (there’s only one). So we decided to have a little fun with our architecture and not strictly adhere to any single pattern. We knew we wanted optimize for testability, so we decided to take the best of both MVP and MVVM and mix them in with some of Kotlin’s fancy language features like, Delegated Properties.

For example, the main activity of the app, aptly named MainActivity, passes all of it’s business logic off to a presenter class, MainPresenter. As with any classic MVP app, the presenter tells the view (in Android’s case, the Activity), what to do via a view interface. But rather than have several methods for each individual view update (i.e. setTitle(), setSubtitle()) we have only a single variable in our MainView interface - viewState.

viewState is of the type ViewState which is a sealed class that has subclasses for each of the different states our main screen could be in - loading, showing the weather, and an error state. Sealed classes are Java enums on steroids πŸ’‰, but if steroids compiled on the JVM. They allow each case to have their own associated values which can be passed through at run time - very similar to how Swift enums work. So for us, that meant that each ViewState could pass along their own ToolbarViewModel which we could build at runtime. For example, the Weather case is instantiated with a WeatherToolbarViewModel which uses the users current location as the title.

This approach also made our unit tests more flexible. The MainPresenterTest now only has to check that our view is in the expected state:

verify(view).viewState = isA<ViewState.Weather>()

While the WeatherToolbarViewModelTest handles all the testing for what the expected values should be:

@Test
fun testSubtitle() {
  val viewModel = WeatherToolbarViewModel(context, mockCondition)
  val expected = "Updated at 4:16 PM"
  val actual = viewModel.subtitle()

  assertEquals(expected, actual)
}

This means that down the line if we added more views to a particular state, i.e. we added a fancy icon to the toolbar, the presenter test remains the same and only the view model test has to be updated.

Dependencies

In case all these patterns and approaches weren’t enough, when it came time to design our networking layer, we decided to add one more paradigm - Rx (reactive programming). Because the entire app stems from a single piece of information - the weather - the reactive paradigm felt like a good fit. To that end, we used RxJava2 in conjunction with Retrofit as our networking client.

Custom Views and Layouts

As aforementioned, we really wanted to use this app as a way to explore all the features of Kotlin and test it against as much of the Android framework as possible, so when it came time to the views and interactions in the app we wanted to make as much of it from scratch as possible. The hardest challenge was the custom PullToRefreshLayout . It has several (literal) moving pieces - from the content on the screen that actually shifts down, to the animating striped background to the progress wheel that animates based on how much the user is dragging their finger.

But there were also a few smaller components that we also wanted to get just right. At the bottom of the screen we show the current wind conditions along with a quick overview of the day’s high, low, and current temperatures. While the standard Android TextView does allow you to set an image within the TextView widget it doesn’t allow for exact placement or alignment. While we could have used a simple LinearLayout to display the icons along side the text, we wanted to keep the layout hierarchy as flat as possible, because #Perfmatters πŸŽπŸš— so we made DrawableTextLabel to allow us control over the vertical alignment and size of the icon.

Download

Download or check out Tropos today!