Posts Tagged

android

Android – Wait for two networks requests to finish with Kotlin

In Android, UI operations happen in the main thread while longer running operations such as network requests and database transactions happen in the background thread.

When building mobile apps against public APIs, there are times where you have to make a network request to multiple endpoints in a single screen, but only display the entire screen once all of the network requests have completed. As mentioned at the beginning of this post, network requests are done in a separate thread, which is usually managed by whatever networking libraries you are using (it’s probably Retrofit), RxJava, or if you’re doing it manually in an AsyncTask.

Threads are hard. I don’t like threads. I have a difficult time understanding it, working with it, and managing it. Slight mismanagement of it can lead to all kinds of issues like race condition where it can become annoying enough to debug where you’ll want to flip a table. As mentioned above, when you have to build an a screen in a mobile app where you have to make a network request to more than one endpoint at a time, and only commence an operation (like displaying data on a screen) once all of the network requests have finished, you’ll have to figure out how to listen for when different requests, essentially threads, have finished loading.

There are multiple ways of going about this in Android. Some people will say look into CountDownLatch while some people will say to learn RxJava. While both suggestions will work, CountDownLatch looks too complicated for me to bother understanding and RxJava has too high of a learning curve for it to be a viable option if you need to learn how to do this quickly. If you use Kotlin, you can also look into coroutines but that’s apparently an experimental feature according to JetBrains.

While trying to figure out how to do this I found a Kotlin library called Kovenant that makes asynchronous operations super easy on Kotlin. If you’re unfamiliar with promises and asynchronous calls, the TLDR is that you run some code asynchronously and promises allow you to commence the next sequence of operation once that initial asynchronous operation has finished running. Kovenant also makes it easy to combine multiple promises, which is what we’ll be using to wait until two network operations are finished before commencing our UI operations. Let’s look at some code sample from the redesign I’m working on for MaterialReads.

In this redesign, I need to have data from two different endpoints: updates and reviews. Unfortunately, GoodReads do not provide an endpoint that reads both datasets in one API call, thus I have to make two. I want to be able to display a loading spinner while data from both endpoints are being loaded and then hide the loading spinner only if the two network requests have finished running its full course. This would be difficult to do manually but Kovenant makes it easy. As displayed in the code sample, we split the both network requests into two separate methods, and then create a deferred object. We only resolve the deferred object if the request has been successful in each network request.

In our refresh method, we can combine the two promises, which will allow us to wait until two network requests have finished loading and load the updates and reviews that have been retrieved from the API. In this example, we’re only combining two promises, but Kovenant allows you to combine up to 20 promises at a time.

If you Google how to wait and detect if multiple asynchronous operations have finished, you’ll get slews of answers that range from very manual (which also can mean error prone) to something like “just learn RxJava and reactive programming”. I found that the using something like Kovenant and its easy to use promises is the best way to solve this problem of asynchronous callbacks.

Goodreads OAuth in Android

For my solo project, I’m building a Goodraeds client. Mostly because I like to use Goodreads to track the books I’m reading and I don’t enjoy the default Android app provided by Goodreads.

Thus, I’m building my own alternative using their API. However, it has been a tricky process from the beginning since

  1. Goodreads API isn’t very well documented.
  2. Logging in via Goodreads means that I need to do OAuth authentication with their API, which as mentioned in point 1, is not very well documented, and also is OAuth 1.0.

It took me some time to figure out how to perform OAuth authentication in Android development and I’ll go over the steps on how to do this in the hopes that it’ll save other people a bunch of time.

Just as a note, since this is Android development, the code samples that I’ll be showing will work in any standard Java application or any language that can easily interpolate with Java and targets the JVM. Also, since I’m writing this app in Kotlin, do note that the code samples will be in Kotlin with the exception of one small portion that relies on a library. But if you need to use Java, you can always look at the code sample and figure out what’s going on pretty easily.

Things that we will need

We’ll need several components that come together to make OAuth authentication work. They are:

  • Goodreads developer key and secrets
  • Your own server that will act as a callback url for the Goodreads OAuth API
  • ScribeJava library for making OAuth easy

As far as setting up your own server that will act as a callback url for the Goodreads OAuth API, you’re on your own. What you’ll have to do is set up a simple API that will receive the callback url that includes the OAuth token and whether the user authenticated your app from accessing his/her data or not from Goodreads API with a route that you’ll set in the Goodreads API Developer dashboard.

1 – Register for the Goodreads developer keys

Just Google something like Goodreads API developer keys and you’ll find a link in Goodreads that will allow you to register for the developer keys so that you can access their APIs. Once you have registered yourself as a developer, you’ll be redirected to a page that looks like this that will provide you with your developer key, secret, and form with inputs where you can enter in your app’s name, domain, and a callback url. Fill these out appropriately with the exception of the callback url.

2 – Set up your own API to act as a callback url

I won’t go into the details of how to set up your own API to act as a callback url. It shouldn’t be too difficult if you’re somewhat familiar with backend development. All this server has to do is take in two parameters, oauth_token and authorize which will be coming from the Goodreads API to your callback server, and then render the results in a json format. I like to user Ruby on Rails for backend development purposes and for the sake of providing a simple example, this is what my controller looks like.

And my routes file looks like

Having my Rails controllers and routes like this will give me a url that goes something like https://myurl.com/goodreads_oauth_callback.

Once you have deployed your server, go back to the Goodreads page where your developer keys are listed, and fill out the Callback URL form. Just as an example, mine looks like this (sensitive data blurred out).

3 – Install ScribeJava

Depending on what you’re developing, you can do this several ways. In the context of Android development specifically, simply add this line in your app’s build.gradle file.

Notice here that I’m using an older version of ScribeJava. As of this writing, the latest version of ScribeJava is at 5.3.0. For whatever reason, I couldn’t get Goodreads OAuth to work with the latest version of ScribeJava as I kept running into an exception that I couldn’t find a solution to. Thus, rather than trying to figure out how to make the latest version of ScribeJava work, I decided to simply use a version that worked out of the box. If you want to try to get the latest version of ScribeJava to work, give it a try. If you want the easy way and get Goodreads OAuth authentication to work following my code samples, stick with version 4.2.0.

4 – Set up a Goodreads API adapter for ScribeJava

ScribeJava comes with a lot of different adapters to help make it easier for the user to work with various popular OAuth authentication services like Twitter, Facebook, and etc. However, it does not come with an adapter for Goodreads. Thus, we’ll have to write one. Don’t worry, it’s fairly simple. We’ll write this one in Java instead of Kotlin. Also, since Kotlin interpolates with Java, feel free to write this one in Kotlin if you desire.

5 – Authenticate the user via OAuth

This is the part where we’ll put everything together so that users can use Goodreads OAuth to authenticate with your app. One thing about the Goodreads OAuth is that user authentication happens via their login page. Thus, whatever you’re building, you’ll have to redirect your users to a webview of some sort to display the login form provided by Goodreads. The code sample below is for Android development, thus we’ll be using the standard Android WebView. If you’re not doing Android development, adjust your code accordingly.

While the above code should be relatively easy to follow and understand, I’ll explain the most important part where we retrieve the authenticated user’s access tokens and secrets that we’ll be using to make requests to the Goodreads API.

You’ll see that I override onPageFinished method in my GoodreadsAuthView webview. In this overridden method, I check to see if the url in the WebView contains the callback url that we defined in the Goodreads developer page. If it does, we will grab the query parameters in that url to retrieve the oauth token and whether the user has authorized your app to access his/her data. If the user has authorized your app to access his/her Goodreads data, the query parameter authorize will return a String value of 1.

From here, we check to see if the query parameter authorize has returned 1, and then retrieve the access token and the access token secret utilizing the ScribeJava library. We then use these tokens and the ScribeJava library to make special OAuth request to the https://www.goodreads.com/api/auth_user endpoint so that we can parse out the authenticated user’s user_id. The /api/auth_user endpoint is a regular endpoint defined in the Goodreads API documentation, but for whatever reason, I found it close to impossible to try to retrieve this with regular curl requests (or using Retrofit in Android). However, if I use ScribeJava’s built in functions, retrieving the user_id of the authenticated user is very simple. To be transparent, this part where we try to retrieve the user_id of the authenticated users is the part where most developers seemed to be having trouble based on the questions posted in the Goodreads developer forums. ScribeJava makes this process simple as one line of code.

Note that once we sign the request using the access token and ScribeJava’s built in functionality, we need to parse out the user_id from the XML that gets returned from the Goodreads API. Once we have retrieved the user_id, we can go ahead and save the user_id, access_token, access_token_secret, and then oauth_token in a SharedPreferences so that we can use them throughout the rest of the codebase.

Conclusion

This actually took me around 2 days to figure out how to do. Yes, it seems long to figure out how to do a simple OAuth authentication, but it’s a lot more difficult than it should be due to the lack of support and documentation from Goodreads. I’m hoping that this blog post helps anyone who’s trying to work with the Goodreads API. If you have any questions, feel free to leave a comment below or email me directly. Thanks for reading.

Navigating with BottomNavigationViews

You know those BottomNavigationView’s that are more commonplace in Android apps these days?

Recently, I’ve been building a small sample app for a PluralSight course I’m working on that has one of these BottomNavigationView’s and it wasn’t apparently obvious to me how I should be switching the actual views that are on screen as the user taps through various items in the bottom menu. A simple Google search didn’t answer my question either since most blog posts and StackOverflow answers on the subject matter did one of the following:

  1. Simply introduced the BottomNavigationView and left it at that
  2. Switched the views using a ViewPager but when I downloaded the sample app, the effect seemed a bit weird
  3. Recommended starting up an entire Activity every time a user tapped on a new menu item, but I didn’t like that since that meant you had to re-inflate the bottom navigation view every time. Also, when I implemented it that way, there seemed to be a bit of a lag every time I tapped on a new item.

Thus, I decided to implement the switching of BottomNavigationView menu items by inflating appropriate Fragments into the view. And now… let’s go over the code on how to implement this 🙂

First, our XML layout

And our content_main.xml file

And the bottom_navigation_menu file

These two layout files and the menu item xml file will give us a screen that looks something like this.

Now, what we want to implement is the actual switching of the screen in display. The screen in-display is actually the fragment that is inflated from the MainActivity. Also, when the user taps on a bottom menu item, we want the appropriate fragment to take place of the previous fragment. Here’s how we do it.

In the code, you can see that I inflate a new HomeFragment, which is the default fragment in my code sample automatically, which I store into a mCurrentFragment variable. And based on what the user taps on the BottomNavigationView (which I have a listener set on), I store the appropriate newly instantiated fragment to the mCurrentFragment variable and display that fragment instead.

So far, this is the simplest and most modular way I could think of to get this working. Google docs didn’t seem to have recommended approaches, and blog posts online didn’t have solutions that I liked. The solution that seemed to give the most fluidity that didn’t use fragments like I do involved simply hiding and then displaying certain elements in the layout file depending on which menu item was active. I didn’t like this since that meant unnecessary code will have to be run all at once, potentially causing memory problems.

Standardizing Android development

When I first started working with Android development, I was confused to no end.

I wasn’t confused not necessarily due to the Android framework, writing Java, and whatnot, but mostly due to lack of documentation on the standard practices when it comes to building Android apps. This might have a little bit to do with the fact that I was coming from the Ruby on Rails ecosystem, where the “convention over configuration” paradigm reigns supreme.

For example, in Ruby on Rails, if I want to query the database, there’s ActiveRecord that comes with the framework. If I wanted to send mails, there’s ActionMailer. If I want to run background jobs, there’s ActiveJob. If I want to <insert whatever a typical web application should do here>, there’s… you get my point. The thing is, Ruby on Rails came with standard ways of implementing the majority of what your typical web application should be able to do out of the box.

In Android, there was no such ecosystem built into the framework. The course I took on Android development had the students manually making HTTP requests to request data from the API, writing raw SQL queries to cache and query the local SQLite database, and so forth. However, as we all know that in the real world, no one develops applications this way. Rather than writing raw queries, we use ORMs to make our lives easier. And rather than coming up with our own ways to manage network requests, we tend to use libraries to set up our API calls simple to make.

In an ideal world (or at least in my ideal world), all these features would be baked into the Android framework. Anyone new to Android development will have the ORM, API mapping libraries, and other components needed to build apps out of the box. Instead, what we have to do today is utilize third party libraries to fill in the holes that the Android SDK doesn’t.

Third party libraries are great. I believe that having a plethora of third party open source libraries is part of what makes a great community. Ruby on Rails for example has open source gems written by individual contributors (or companies) for just about anything you can think of. This is not to say that Android doesn’t either, but the problem in the Android world is that because a lot of these functionality has to be provided via third party libraries instead of by Google, lots of new developers have to spend time picking which libraries they should use.

For example, what do you use if you need an ORM? Do you go with GreenDAO, SugarORM, or Realm? If you need an easy way to display images, do you use Glide, Picasso, or some other library? Thankfully, some of these choices become obvious quickly because the Android community have decided on which libraries to use in most projects. For example, Retrofit is the default go-to for setting up API calls. However, for types of libraries like ORMs, the choice isn’t as clear.

This problem isn’t just unique to the lack of default libraries provided by Google. It applies even in how Android apps are structured. In many full stack web application frameworks, when you start a new project, you’re given a default folder structure for where to put your models, controllers, views, and etc. In Android development, you get nothing. Literally nothing, with the exception of your assets. Every Android project that I’ve worked on had different folder structures, and it always took me some time to adjust to the codebase’s organization and become productive. Contrast this to something like Ruby on Rails, where it’s reasonable for a new developer to start committing new code within the first hour or two of project setup thanks to its convention over configuration paradigm.

When it comes to the folder organization/structure of the codebase, the Android community have tried to alleviate this problem via promoting certain architectural patterns like MVP (Model-View-Presenter). The problem with this is that everyone has a slight different way of implementing these patterns, thus leading to further fragmentation.

Some people might say that this is a good thing. That developers are given the freedom to pick and choose what libraries they want to use and how to go about structuring their codebase. While this argument has some merit, I have to disagree in that it unnecessarily raises the barrier to entry for new aspiring Android developers. Frankly, I think it also decreases overall productivity for the entire Android community. Every time I jump into a new codebase and I see some new library or architectural pattern, folder structure, and etc that I’m not familiar with, I go, “Ugh, now I have to spend time and mental energy figuring out this developer’s brain thought process rather than solving the actual problem on hand”.

One could argue that the amount of mental energy needed to absorb how a new codebase is structured or even choosing which library to use is miniscule. Perhaps they are insignificant individually, especially for simple things like choosing a library to display circular images, but all these are decisions that you have to make. And the energy spent making these tiny decisions eventually add up over time. This is mental energy that could have spent actually building the app or fixing bugs.

There have been recent efforts by the Android team to standardize this by releasing Android components. There have been many blog posts about Android components like Room, LiveData, and etc. In fact, I was listening to an episode of the Android Developers Backstage podcast (episode link http://pca.st/K7uy) and the hosts talk about this exact problem where there’s a lack of standardization and conventions in the Android development world and Android components is the Android team’s effort and addressing this very issue.

These component libraries by Google are still in their early stages and I believe that it’ll take some time for a lot of older Android apps to start integrating these into their codebase. As for me, I’m excited by the notion that one day, I’ll never have to think about problems that aren’t directly related to domain problem of the app that I’m working on. Just think about it. Being able to jump into a project and immediately knowing where everything is based on the community’s conventions and being productive right away. Sounds great to me.

Obfuscate builds before releasing apps

Obfuscating builds is one of those things that I didn’t pay attention to (and wasn’t even aware of) when I first started learning how to develop Android apps. I thought all I had to do was upload a release build of my app to the Google Play Developer Console and I was done.

Obfuscating builds is a straightforward process, but there are a few things that I learned during my client projects that weren’t obvious to me.

Obfuscate your app early in development cycle

Most of my client work involves regularly releasing builds incrementally as an alpha release to the Google Play Store. Some people say that you can obfuscate your builds before you release to production, but it’s better if you do this early in the development cycle.

This is because it can be difficult to figure out what didn’t obfuscate properly later in the development cycle when the codebase is larger. Doing this early allows you to catch any issues with obfuscation when the codebase is smaller and incrementally as the codebase grows.

Obfuscating builds is really easy. Just set minifyEnabled to true and map your proguard files with appropriate rules.

Configuring proguard rules is another subject so I’ll skip that in this blog post.

Upload your mapping files along with your obfuscated builds

When you upload your apk to the Google Play Developer Console, upload your mapping file after you release your build. Also, if you’re using something like Firebase Crash Reporting, you should also upload your mapping file there as well. This will allow you to see where in the code the app crashed if it does happen.

The mapping file can be found at app/build/outputs/mapping/{build_name}/mapping.txt.

Set SerializedName to your POJO attributes

If your app is backed by an API and you’re using Gson (default by Retrofit, which you’re probably using) to map your API JSON responses to your POJOs, you want to define @SerializedName in your class.

Something like the above will suffice. The reason for this is when you turn on obfuscation, those field names will be renamed to something like “a”, and then Gson’s Java to Json mapping will fail. Thus, you need to provide @SerializedName on every field you use for Json serialization with the name of the Json property which needs to map to your POJOs.

Returning to the parent activity

I recently had to resolve a bug in one of my projects where if the user pressed the back button in the Toolbar to go back to the previous screen, the app would crash.

And the app would crash because the previous screen was supposed to be displaying a data that was passed from the screen before that, and the activity would for some reason lose reference to the variable that was supposed to represent that data. To illustrate what I was experiencing, the flow went something like this.

Screen A -> Screen B -> Screen C

Let’s say the app is a cupcake ordering app. Screen A displays a list of cupcakes. User clicks on one cupcake and we display the single cupcake in Screen B. User clicks “Order Cupcake” which takes the user to Screen C. The user decides he/she does not want the cupcake and presses the back icon in the Toolbar.

Note: the bug does not occur if the user presses the Android’s native back button, the one on the bottom of the phone that’s native to the OS.

Once the user clicks the back icon in the Toolbar, the variable that represents the cupcake in Screen B will become null and it would cause the dreaded NullPointerException.

Clicking that in Screen C would crash the app in Screen B with the NullPointerException

And the code in Screen B looked something like this.

And in that code, mCupcake would become null and cause all sorts of NullPointerExceptions.

Now, why was this happening? It turns out that it’s due to the way I declared my activities in my AndroidManifest. In my AndroidManifest, I had declared my three activities like this.

It looks “correct” but it turns out the reason I was having the crashes with NullPointerException‘s was due to missing the android:launchMode="singleTop" in each of the activity declaration.

For example, each declaration should have looked like

Why is the launchMode option so important? It turns out that the standard launchMode (meaning if you don’t put one in), causes Android to always recreates the activity (calling onCreate) even if the task stack (parentActivityName declaration) is handled correctly. To prevent this, you need to add the launchMode="singleTop" declaration to your activities.

The link to the documentation is

https://developer.android.com/guide/topics/manifest/activity-element.html#lmode

For some reason, I think the default configuration should be to have the launchMode set to singleTop when the user presses back into the previous activity, rather than recreating the activity entirely. I can’t think of a single app that I use where I expect pressing the back button on the Toolbar to behave differently than when I press the Android OS’s back button.

Keep your Android Monitor logs when app crashes

My apps crash all the time during development (thanks to plethora of those pesky NullPointerExceptions).

Okay, probably not the best way to market myself BUT I do fix those bugs before the build is released to production (or alpha, or beta, whatever stage the app is currently in development). One thing I wanted to cover in this post is a neat little trick that I learned recently on how to keep crash logs during development.

Most of the time, when apps crash while running in Android Studio, you can see those little red error messages in the Android Monitor.

These logs tell you exactly what happened and where so that you can go and fix it. Pretty easy right?

But this is only applicable if you app is in a crashed state. If your app restarts automatically thanks to the Android OS trying to save face, these logs will be reset and your error messages will be gone, leaving you unable to figure out what went wrong in your codebase.

The way I used to figure out where my app went wrong was like playing a stealth video game like the old Metal Gear Solid games on the original PlayStation where you had to sneak up on your enemies to kill them. I would literally have my screenshot tool ready, open the app in my physical device with the Android Monitor open to full screen, replicate the steps necessary to crash the app, and as soon as the app crashed, try to time my screenshot so that I can capture the error messages before Android Studio erases them from my view. Yes, super primitive and a silly way to go about it.

I can’t remember why (I read it somewhere awhile ago)… but apparently the reason Android Studio wipes the logs when the app restarts is actually a feature that’s supposed to make your development life easier. But if you’re in a situation where you want to keep your logs for whatever reason (i.e. being able to see your error logs), you can create a custom filter configuration in your Android Monitor that won’t erase your previous logs even if your app restarts.

In the top right section of your Android Monitor, click the dropdown and create a new Logcat Filter.

Name your Filter whatever you want, and simply select that filter when trying to replicate the crash. You’ll see that your logs won’t be erased even when the app doesn’t restart.

 

Unnecessary use of CoordinatorLayouts

It feels like Google is seriously promoting the use of CoordinatorLayouts these days. They even released (unfortunately, I can’t seem to locate the link for it) an entire blog/website dedicated to teach little tips and tricks to properly using the CoordinatorLayout.

CoordinatorLayouts are great. I like them. When you start a new Android project and create a new empty Activity, Android Studio will even autogenerate a new XML layout file with a CoordinatorLayout. They make implementing certain Material design easy to implement and given that you actually know how to use the layout properly, makes laying out elements on your screen very easy.

However, I’ve seen some developers (which includes the past me) use CoordinatorLayout as container of some sort, kind of like a FrameLayout. While this “works”, it isn’t optimal.

CoordinatorLayout is computationally more expensive than any other layout in Android, thus including it in your layouts when you’re not using its specific features will unnecessarily increase CPU load in return for no benefit. Thus, if you’re not using any of CoordinatorLayout‘s features, it’s always better to replace with a FrameLayout or a RelativeLayout to lessen the CPU load.

This is a relatively short blog post, but an important one for me as I’ve been unnecessarily using CoordinatorLayouts when using other layouts would have been a better choice.

Use of Secure SharedPreferences

Many apps that have a user authentication component sends a user’s access token to have access to the API and that user’s information.

Many posts on StackOverflow suggest storing the user’s access token in SharedPreferences to have easy access to it. In fact, this is what I’ve been doing too as it is easy and convenient. Also, based on what I’ve been reading on how to store tokens on the client side, this seems to be the de-facto way most people approach storing tokens on client side.

One recent code review I’ve gotten suggested that I use a third party Secure SharedPreferences library to encrypt the user’s access token. This is because a user who may have root access to their phone will be able to view the values stored in SharedPreferences. Is this being a bit too paranoid? I think so. I mean, the last time I rooted my phone was back in the original Droid days (500 mhz phone…) so that I can overclock it and install custom ROMs. These days, phones are so fast that I’m too lazy to root it. But is Secured SharedPreferences easy enough to implement to warrant not doing it? Nope, it’s super easy that it makes no sense not to use it to encrypt potentially sensitive user data when saving data to SharedPreferences.

There are numerous libraries available to do this. I decided to go with this one since it was the one suggested by the code reviewer.

https://github.com/scottyab/secure-preferences

Code reviews like this made me wonder whether I should get a proper job where I can work on Android development on a team with more people who are more experienced in Android development than I am. It should speed up my learning process (as it did with web development a few years ago). Also, I do miss working on a proper software development team.

Build Custom Info Windows in Android

In this blog post, we’ll go over how to build out custom Info Windows in Android.

What are Info Windows? In applications that display a list of locations in a map with pins, you can often tap on the pins to display information about that specific location. One example is the maps feature in the Yelp app where the app will display a map with various pins of locations of interest on the map. You can click on the pins and it’ll display a little pop up with the various data about that location. Sample screenshot is below.

That little pop up that displays with the label “El Gran Pollo”, rating table, and etc is the Info Window. The Android SDK comes with a standard InfoWindow that allows you to display a title and description. If that’s enough and the design of the standard InfoWindow meets your requirements, then you can stop here. If you need to customize your InfoWindow, continue reading.

Prerequisites

This tutorial is going to assume that you know how to integrate Google Maps into your Android application and that you know how to obtain location information using the LocationListener and GoogleApiClient.

Set those up first and prepare a list of data that you would like to display.

Sample data that we’ll be working with

We’ll be displaying different restaurants on the map. The model will look like

The name and description fields are straightforward. photoUrl field will return a url with an image file. We’ll be using Picasso to insert photos into the ImageView‘s.

Pin the markers

Assuming that your Google Map on your Activity (or Fragment) is set up and ready to go, we’ll start pinning the markers. We’ll have a list of Restaurants in an ArrayList and go from there.

The code above is simple but there are two non-obvious things. The first is the mRestaurantMap where we keep reference to the restaurants themselves with the markers as keys and the second is the last two lines of code where I call showInfoWindow and hideInfoWindow methods on the markers.

In the RestaurantInfoAdapter that we’ll be coding up next, we’ll need to be able to retrieve the correct restaurants that belong to a specific marker that the user taps on the screen. the mRestaurantMap HashMap is an easy way for us to store and retrieve the correct restaurants.

As for the showInfoWindow and hideInfoWindow methods, this is a workaround in order to display any images that you want to show on your Info Windows. As noted in the comments in the code snippet above, you do not need these two lines of code if you do not have any images in your custom info windows.

Write the InfoAdapter

Let’s write our Custom Info Adapter and the accompanying XML layout file.

First create your custom layout file. I’ll be naming the file info_window_restaurant. The code for the layout is below.

In this layout, I’m using a CardView in order to easily provide rounding border radius. If that’s not a requirement for you, then you can use whatever layout (RelativeLayout, LinearLayout, etc.) that you want. I also have an ImageView in there to display the photo of the restaurants that will come in later.

Next up, let’s build our Info Adapter. Back in your Activity or Fragment file where you had that pinMarker method, add this class.

Here, you can see the mRestaurantMap coming into play, where we take the marker that is passed in to retrieve the correct Restaurant from mRestaurantMap. Then we grab that and set the texts and images for the TextView‘s and the ImageView.

If you are observant, you may have noticed the new MarkerCallback(marker) line in the Picasso part of the getInfoWindow method. This is a workaround that we have to do specifically when trying to load images in custom info windows due to the race condition that occur between Picasso and Google Maps. This StackOverflow post’s accepted answer explains it pretty well.

https://stackoverflow.com/questions/39551774/picasso-image-not-loading-in-custom-infowindow-why/39551998#39551998

Basically, you’ll need to build a custom callback into Picasso or your image loading in your custom info windows will be inconsistent at best and non-functional at worst. Also, I found the code solution in that StackOverflow post to not work for me, so without further ado, below is the solution that have worked for me.

And yep, the solution is pretty much the same, except that I call hideInfoWindow before showInfoWindow. I found that just calling showInfoWindow doesn’t help reload the page.

Also, remember the two lines of code in the pinMarkers method where I call hideInfoWindow and showInfoWindow during the pin marking process? The reason I do that is because if I don’t, the image in the first pin that the user taps on, the image will never load. I’m not sure why, but hiding and showing the info windows quickly (so that the user doesn’t notice) when you’re pinning the markers initially is a workaround that I found to work well.

You can also further optimize this solution by adding a boolean variable in your RestaurantInfoAdapter to determine whether Picasso has already loaded and displayed the image or not. Since Picasso automatically caches images for you, if the marker has been tapped and image already loaded and displayed, we do not have to add the MarkerCallback into our Picasso calls. This is simple to do so I won’t display that here.

I hope this post helps anyone who needs to build custom info windows in their Android apps.