Posts from July 2017

Hootsuite’s Android team adopted Kotlin in the first half of 2016 as an alternative programming language to Java. We immediately loved it. Within a few months, one of our developers wrote a blog post raving about Kotlin idiomatic code. Now that Kotlin is an officially supported language for Android, we’d like to share our story of nurturing the growth of Kotlin in Hootsuite. Adopting a new language in a mature brownfield project requires a group of passionate individuals willing to take on a significant risk and be accountable everyday.

First Encounter and Case to Adopt

It was February 2016. Jetbrains had just released Kotlin v1.0 and one Android (and Scala) developer sent out Jake Wharton’s (1 year old) proposal for Project Kotlin for Android. The whole Android team met to discuss every point in the proposal, weighing null safety, higher order functions, and technical JVM details with alternative technologies along with the risks of adopting a new language. Surely, this proposal was all that was needed to convince everyone on the team? Nope, and rightly so. We already had RxJava and Retrolambda to bring streaming, concurrency and lambdas.

It was by another presentation from Jake Wharton, Advancing Development with Kotlin, that, in tandem with his first proposal, challenged us to Build a Better Way and #BSU. From here, a few of us on the team worked out a plan to get the ball rolling:

  1. Communicate and get buy-in from our Development Directors
  2. Schedule dedicated training sessions
  3. Dip a small toe (code) into the Kotlin waters
  4. Identifying and mitigating operational risks

Getting Buy-in

Most technologies that we adopt and use at Hootsuite originally start off as ideas from the development teams. The journey to successfully adopt Scala and Go required many groups in the organization to overcome varied challenges: deployment infrastructure, DevOps practices, build pipelines, developer tooling amongst others. Beyond developers, changes were made in hiring talent and training developers. Adopting Kotlin was just embarking on a new journey.

In our case of the Android platform in February 2016, the answer to “What does Google think?” was silence. The strongest individual backing of Kotlin came from JetBrains, the company behind the Kotlin language. Andrey Breslav, Lead Language Designer, made this statement on JetBrains’ usage of Kotlin :

“At JetBrains, we’ve not only been implementing the compiler and tooling but have also been using Kotlin in real-life projects on a rather extensive scale over the last two years.”

Andrey Breslav on Kotlin 1.0 Release (Feb 2016)

In the community, Hadi Hariri in the Fragmented Podcast mentioned how amazing it was that the Android community has adopted and supported Kotlin, with special thanks to the aforementioned essay by Jake Wharton. The community was vouching for its adoption.

I created a sample Android application and tested the major features, especially Java interop. If we could demonstrate a low risk for a sample brownfield project (Java, Dagger, Retrofit, RxJava, etc), there are high hopes we could bring it safely into our smaller projects. Our development director, took our proposal, considered the supporting information, asked critical questions regarding risks, and encouraged us to keep exploring.

Training Up in Kotlin

On Kotlinlang.org, Jetbrains encouraged new developers to either try Kotlin right in the browser, or work on exercises called Koans. At first, we played with the web interface to get a glimpse of the language syntax. As the end of February 2016 approached, we booked 1-hour study group sessions for all Android devs on Tuesday and Thursday after lunch. These sessions would focus specifically on working through each chapter in the Koans together with a partner to compare answers and approaches. After 8 study sessions (4 weeks), the team had gone through most of the exercises and was ready to try Kotlin in the app.

For any brand new users to Kotlin, I’d suggest playing with Kotlin from the browser first. When you get serious about developing in it, I would recommend to git clone the Koans, then work through the exercises using the IntelliJ IDE.

Kotlin Officially in Production

We wanted to introduce Kotlin in a small spot so we could isolate risks and validate development feasibility. In early 2016, our team was investing more into internal libraries to speed up development of features. It’s in these libraries that Kotlin would also make the most impact, by enabling consuming apps to benefit from Kotlin features. Kotlin in these repositories was a crucible; if developers could consume libraries written in Java and Kotlin, in a Java only project, then developers would benefit, otherwise Kotlin would not fit our development model.

So in April 2016, I chose to officially introduce Kotlin into our core library by converting one Java class to Kotlin and introducing a new enum. That’s It! Our main Hootsuite app consumed this library and was able to use the class and enum from Java classes, as if those Kotlin classes were Java. Tests passed and regression tests found no issue. The seed was planted.

The rest of the team in the same week began committing with a mix of Java and Kotlin code. With every commit, each developer introduced simple pieces of Kotlin. We saw the introductions of POKOs (Plain Old Kotlin Objects), extension functions, injectable classes, custom android views, presenters (from Model-View-Presenter) and unit tests. As Kotlin was being used more, the team started using Kotlin for portions of new features, Team Collaboration with Message Approvals for example.

In July 2016, the Android team released a feature fully written in Kotlin, called Compose Feedback. Originally, Kotlin played only a supporting role to Java, defining model classes, enums and interfaces. As the entire team was ramping up on Kotlin, the team felt that it was a safe opportunity to put Kotlin fully into production. The team converted Java code to Kotlin and refined the generated Kotlin to use Nullable types with Elvis operators, Kotlin standard library functions and Named Arguments.

When the Compose Feedback feature was released and demoed to developers, our team also announced to the broader Hootsuite developer community that we had been adopting Kotlin for development, and that this feature was our effort to prove Kotlin was ready for production.

Operational Risks

To adopt Kotlin and keep Kotlin running well in production, and eventually into prominence, we asked and were asked these questions:
  1. What is the learning curve of Kotlin and how do we lower the barrier of entry for others who want to contribute to our project?
  2. What would impact our ability to develop and deliver projects on a day-to-day?
  3. How will Kotlin impact our ability to hire talent for our team?
  4. How do we ensure our risk exposure doesn’t grow while we see how Google responds to Kotlin in general?

Learning Curve

Our opportunities to learn Kotlin were limited in early 2016; we only had videos and articles from early adopters. The Kotlin Koans, from JetBrains, was and continues to be our go-to place to learn Kotlin and its features. To lower the barrier of entry, we created an onboarding session for new developers and worked closely with each of them as they developed their first features in Kotlin.

Issues coming from Development and Building

Introducing Kotlin to the codebase also brings with it the Kotlin compilation step whenever a build to device or unit tests are done. It was during the build process that we ran into some issues.

When we first introduced Kotlin, the build time increases were minor and trivial. We had very few Kotlin files to compile. As more Java code was converted to Kotlin and new Kotlin files were created, the build times slowly creeped up to several minutes, like others. With incremental compilation added to Kotlin through Gradle in 1.0.2, we experienced some improvements. Sure, it would be nice to convert our project to 100% Kotlin to mitigate long build times, but we didn’t have that luxury. We were stuck with the compileDebugKotlin, compileDebugJavaWithJavac, compileDebugKotlinAfterJava gradle build steps.

At Hootsuite, we are proponents of the Library Oriented Programming approach to facilitate rapid development. As such, we adopted Kotlin into a number of our many libraries. Issues surfaced when different versions of Kotlin (1.0.2, 1.0.3-1, 1.0.3-5, etc) were distributed amongst our libraries. When built into our main app, we would often receive an error while compiling. The solution we employed after some failed attempts to try to understand the problem was to always align the Kotlin versions in all libraries.

One specific issue we were forced to work around on several occasions was in ProGuard. While Proguard had no problem evaluating literals, variables or exceptions in the null position of the Elvis operator (?:), it would fail when the null case was an expression with or without operations.

In the above Gist, Proguard would fail trying to evaluate ?: doubleup(a) portion. This issue can be found in project Anko, where they suggest the bug from Proguard either forces a change to Anko, or to not optimize in Proguard.

We’ve found other weird build issues in 2016 that we worked past. Usually, our team found a solution by talking to other Kotlin devs in slack and after a lot of trial and error. Sometimes, we chose to keep the code as Java and circle back later. It meant picking the right problems to fix based on their necessity in our product.

Kotlin a Bonus in Finding Talent

When it came to hiring Android developers, our job postings in 2016 didn’t mention Kotlin until later in the year. As development in Kotlin picked up on the team, we observed that developers new to the language were ready to code in Kotlin after 2 weeks, and after 1-2 months, were contributing code that effectively used Kotlin features. Developer happiness was high and each demonstrated that Kotlin wouldn’t be a barrier to write great code.

In the 2nd half of 2016, we started mentioning Kotlin in our job postings as a bonus. To this day, most candidates that we interview don’t know much about Kotlin. But whenever we mention that we are invested in using Kotlin, candidates show genuine interest in how positive it has been in our development and ask about the technical hurdles we had encountered.

Bringing Kotlin into Prominence

Our team at Hootsuite worked hard in the first six months to get the Kotlin ball rolling and build momentum. Within six months, the core Android team had fully adopted Kotlin as the primarily language. One year later, in 2017, all new features and code are written in Kotlin. Building momentum was more than just working through Koans and trying features; it required our consistent drive to push ourselves to learn and adopt more idiomatic Kotlin practices.

I highly recommend you come back for our next blog post from Neil Power on how the Android team grew Kotlin to be the primary language for development, and how we have kept the interest of Kotlin alive in Hootsuite and in our Vancouver dev community.

 

About the Author

Simon TseSimon Tse is an Development Manager on the mobile team at Hootsuite. He enjoys testing the limits of new technologies and ideas in his apps. Follow him on Twitter @simtse.

Last year, Paul Cowles and I authored a blog post on accelerating cross platform development with serverless microservices. Our experiment for our Query Builder feature, was using AWS Lambda as the serverless backend for our Android and iOS apps.

Presenting at AnDevCon

Last week, I had the great honor to present our experiment at AnDevCon in Washington, DC. This was my first speaking engagement and I was very happy to #workoutloud and share what we had been working on to a wider audience.

Unfortunately, the sessions were not recorded, but I have uploaded my slides to slideshare. The talk is very much a natural extension of the blog post Paul and I wrote.


Open Source Library

If you are interested in trying AWS Lambda as a serverless backend for your own cross platform experiment, we have also created an open source library which will walk you through the steps from creating an AWS account, to invoking your function through API Gateway. It can be found on Github: https://github.com/neil-power-hs/aws-lambda-sample.

Using this sample project, you will be able to automate the deployment up front, avoid some of the difficulties that we encountered, and get up and running quickly. We would love to hear feedback and hear any success stories, so if you do try it, reach out to me on Twitter @NeilPower or Paul Cowles @paulrc. While AWS Lambda and serverless microservices are not a silver bullet solution to cross platform development, we believe they are a valuable tool in the cross platform toolbelt.

Conference Highlights

Aside from my own presentation at the conference, I got to hear a number of interesting talks from other Android developers. Benjamin Scholtysik gave a great talk on Mobile DevOps, Continuous Integration and Deployment. Here at Hootsuite, we recently started using buddybuild for our mobile pipeline and refining our Mobile DevOps processes has been on the top of our minds recently.

Anyone who knows me, knows that I am a big fan of automation and tooling, and so I really enjoyed a look at the suite of tools fastlane is offering for Android developers. Todd Burner spoke to us on how we can save 10 hours a week with Fabric and Fastlane. We already use Fabric extensively but after Todd’s talk, we’re going to take a closer look at fastlane. Right now we’re manually maintaining our app’s screenshots on the Play Store, screengrab seems like it could save us a lot of time by automating that process.

Another talk that I found very engaging was Doug Stevenson’s talk on Firebase. He showed us their Test Lab, Crash Reporting, Remote Config, and Performance Monitoring tools. The new Performance Monitoring tool looks very interesting, I’m excited to try it.

On the final day of the conference there was a half day Kotlin tutorial which was standing room only, a lot of interest for this newly official Android development language!

AnDevCon was a great experience and an excellent way for me to work out loud, a cornerstone of our Hootsuite development culture. I really enjoyed having an opportunity to share what we’ve been working on with the community at large.

Why custom notifications?

Arriving at Hootsuite, my first project was to revamp our Android push notifications – from single lines of text to ones that contained the same information as our in-app notifications. This would allow customers to reply to mentions, check out new followers, publish Instagram posts and much more right from their home screen, reducing the friction of their workflow. The original plan was to switch from InboxStyle to BigPictureStyle or BigTextStyle, but it soon became clear that default styles were not enough. They forced us to choose between showing media or text when we wanted to display images with multiline text, formatted to be consistent with our in-app user interface. This article will cover creating completely custom rich notifications with actions using the DecoratedCustomViewStyle introduced in API level 24.

The original notifications, using InboxStyle. Each one was a line of text in a single Android notification, and users had to enter the app to get more value out of them:

The first iteration using BigTextStyle and BigPictureStyle. We have a lot more information and actions, but it’s a tradeoff between text and media, and we can’t format or style the Views:

The final iteration using DecoratedCustomViewStyle. The Views are completely custom and support rich media, including images, videos and GIFs:

So let’s dive into the world of rich notifications! We’ll be making a sample app that can send this notification:

You can find the complete code for it here.

Starting with the basics

We’ll use NotificationCompat.Builder, the builder class that makes it easy to specify the appearance and behavior of our NotificationCompat object. (NotificationCompat includes the features introduced to the Notification class after API level 4 while remaining backwards compatible with devices that are at a lower API level.) Custom notifications start out like any other notification:

All notifications require these three fields to be set, although you won’t actually see them once you add your custom views. We’ll add two more lines:

This ensures that the notification is automatically cancelled and MainActivity launches when the user taps it.

XML files

Now let’s make the layout files for our custom views, which we’ll eventually inflate into RemoteViews objects. (RemoteViews is a view hierarchy that can be displayed outside of your app’s process, and the only View subclass that NotificationCompat.Builder will accept as an argument for CustomContentView or CustomContentBigView.) You can style your XML layout files however you want, but keep in mind that RemoteViews only support FrameLayout, LinearLayout, RelativeLayout, and GridLayout, and a limited set of View subclasses. If you want to customize both the collapsed and the expanded view, you’ll need to create a layout file for each of them.

Here’s the layout for our collapsed notification…

and our expanded one.

Side note on styles

My sample app’s layouts look similar to the default Android notifications. If you want your UI to be consistent with the Android design as well, notifications, like any other widget, have built-in default styles that you can apply. For example, I used

to style the timestamp.

Adding the information

Notifications are most interesting when they contain relevant, dynamic information. In the sample app, we’ll make the notification display whatever the user types and also set the time stamp to be the current time. We’ll create new RemoteViews objects using our XML files.

There are numerous setter methods that you can call to programmatically populate the view – just pass in the viewId and whatever you want to put there. The ones I ended up using were setTextViewText(), setImageViewResource() and setOnClickPendingIntent(), since together they provide the features of a standard Android notification.

Adding the actions

For the demo app, we’ll just make the button trigger a Toast message helpfully telling you which button you clicked. We’ll set a PendingIntent for the button using setOnClickPendingIntent(), which allows a foreign application, such as the home screen, to execute code with your application’s permissions. We’ll make an IntentService and call PendingIntent.getService() to retrieve a PendingIntent that starts the service, since all we want to do is display a Toast.

You’ll have to set the action string whenever you have multiple actions, so that you can do a switch statement in onHandleIntent. Since the IntentService runs on a background thread that doesn’t display UI, we’ll have to make a Handler object on the main thread to make the Toast show up.

In the actual Hootsuite app, we made the Toast on the main thread using RxJava:

But that’s another story.

Don’t forget to register your IntentService in AndroidManifest.xml!

By the way, the other options for creating a PendingIntent are getActivity() (the PendingIntent begins an Activity), getActivities() and getBroadcast() (the PendingIntent performs a broadcast, and could be better choices depending on what your intended actions are.

Almost done!

We’re ready to turn the NotificationCompat.Builder into a real NotificationCompat – just call builder.build().

You’ll have to get the system level NotificationManager from the system service.

Finally, we’ll call notificationManager.notify() to send the notification to the system. We’re passing in 0 as the id parameter but if you want your app to display multiple notifications, you’ll need to pass in a unique int each time.

Here’s our complete MainActivity.java:

Final note on sizing

Sizing will vary from device to device, so it’s important to make sure our layouts will fit on every screen. The height measurements are 64 dp for a collapsed notification and 256dp for an expanded. I ended up adding maxLine attributes to TextViews and maxHeight attributes to ImageViews to prevent elements from getting pushed off the screen.

Results

There were two main problems that rich notifications were meant to address: our old notifications didn’t contain enough information to be very useful to users, and Instagram publishing had a complicated workflow. We were happy to see that Instagram publishing notifications were the most used, with the “publish” action making up 85% of all user notification actions.

However, one customer sent feedback that he didn’t like the update because he received hundreds of notifications every day. This was fine when he’d have a single InboxStyle notification informing him that he had a few hundred unviewed messages, but when they arrived individually, they became distracting. We realized that actions are ideal for users who have enabled the few notifications that they find really important. For those with many notifications enabled, grouping their summaries into a single notification would be more useful. So now, with a simple check of the notification count, we’re sending the first four notifications as rich ones for users to action on and combining them once again into an InboxStyle notification when the count reaches five. After users clear it, the count resets and they’ll receive rich notifications again. After this change, the number of notifications cleared dropped significantly, to a tenth of what it was before. We’ll continue to listen to customer feedback and tweak notifications to improve the experience as much as possible.