Android TV Recommendations: What's in it for my app or game?
Android TV is at the core of the new SHIELD Android TV device, delivering great new experiences on much bigger screens. This blog post introduces developers to Android TV recommendations: what they are, how to make the best use of them for your application or game, and the Android TV recommendation APIs themselves.
What are Recommendations?
The recommendation row is the first thing you see when you boot up Android TV (see Figure 1). It is the first exposure of the user to content and services available to them. A recommendation is an individual 'card' presented to the user; it is a 'deep link', taking the user directly to the recommended content inside your app. Recommendations make it easier for applications to be discoverable, and for users to find content that will likely interest them without having to go digging around in applications or search specific terms. Recommendations also help drive users back to your app by advertising individual items they might enjoy.
Google designed recommendations as an extension of an Android system most app developers already use: Android TV recommendations are actually just Android notifications! Clearly, one look at the Leanback Launcher makes it clear that recommendations are both more, and (in some ways) less, than 'normal' Android notifications. But they are created using the NotificationCompat.BigPictureStyle notification class, so they really are notifications with a bit of extra functionality. The key is that they are specially 'interpreted' by the Leanback Launcher as recommendation cards.
Figure 1. The Recommendation Row
Users see and interact with this class of notifications much differently than those on a phone or tablet, with the focus being more graphical in nature, and the only interaction being a 'click' -- you can't have extra buttons, you can't specialize the look further, and users can't 'dismiss' individual recommendations (but can completely disable those of a given app just like on any Android device).
The purpose of recommendations are in most cases different from notifications, designed around trying to guide you to content that you might enjoy based upon prior usage patterns, new content available, or recently used applications. Similar to notifications, recommendations can also be used for timely information, such as the "Now Playing" card.
Recommendations are constructed from the following pieces (see Figure 2):
- the 'large icon' (the main image/bitmap)
- a short title
- a slightly longer description
- a small icon (16x16 monochrome app icon)
- a color (the background color for text content area when focused)
- an optional background image (a temporary replacement desktop bitmap behind the launcher -- we will cover that in a future article).
Figure 2. Components of Recommendation Card
While the main recommendation image can be of any source size, the Leanback Launcher seems to automatically scale and crop images to fit the recommendation row. Final cards are 352px in height on a 1080P display. For width, we suggest keeping an overall aspect ratio between 4:3 (wide) and 2:3 (tall), as those seem the most natural fit for content and are simple for artists to work with.
To set the images, you will simply pass a Bitmap object into the notification builder -- so you can have something static from a file, a resource, or from the web, or create something dynamic on the fly (like the Weather image in Figure 2).
For best results, we recommend sticking using the thinnest imagery possible -- however, you obviously need to match your content. Wide aspect is good for video or TV, but square (cropped) can work well generally. Square aspect is obviously a perfect match for music cover art, but also often fits web/news content. Tall aspect is great for game box art or video cover art, but also works for non-media information like small bits of text or icons (as seen above).
Why do I care about Recommendations?
It depends, as different apps can use Recommendations differently to engage their users.
Media apps can leverage them to show recently added content or last watched items, or if known can recommend content related to other recently watched media (like YouTube). Similarly, if global tracking is an app feature, the app can suggest 'popular' content based on global usage/access data.
Informational cards can be used just like normal notifications ("You have a full set of lives, come on back!" for a game, or 'breaking news' transient cards for a news radio app), and removed once interacted with.
Data transfers are another great use-case. Data updates for games, image downloads/uploads for photo apps, video downloads for apps with offline viewing, etc. can use the standard notification progress indication API to show a "Percent Done" card, so the user can see there is progress, and knows when to come back to the app (the card can be updated to have a 'completed' message when finished, to make it clear to the user).
Media apps can also use the progress indicator to show progress in recent 'unfinished' media (usually video content, but long audio like a single-track concert would also apply).
What do I need to do to support Recommendations?
Google has a good overview which dives into the core implementation and supporting code in the Android TV training materials:
https://developer.android.com/training/tv/discovery/recommendations.html
The code covered in the guide can be downloaded in full as it is part of the Leanback sample:
https://github.com/googlesamples/androidtv-Leanback
This blog does not duplicate that work or try to cover every piece of code they touch in detail. Rather, we focus on the significant bits at a high level, touch upon the most interesting or missed bits at the low level, and most importantly tell you why you should care about recommendations and how to deliver a great experience.
The Leanback sample demonstrates a lot of new Android TV interface mechanisms, but one big component is its recommendation support. In the following sections, we show snippets and screenshots from both Google and NVIDIA samples, and we will try to call out where we've deviated or innovated from the Google sample code in our own work.
There are a number of elements to developing support for Recommendations, as covered in the following subsections.
Consider your data
This seems an obvious point, but think about what you want to show before you put the whole system in place. What exactly are the types of cards you want to put in front of the user? What images do you have already available, or do you need to go create? How many cards are useful for the user of your application (see Follow the Rules section below)?
IntentService to build and update cards
Building a custom IntentService is covered well by Google in the Leanback sample. You create a subclass of IntentService that implements onHandleIntent. That function is triggered to have the service create/update the app recommendations. In it, you can iterate over an interesting set of data from your application, and build a recommendation for each item, and have each item create a unique intent that will launch the correct activity/task with the right data to take the user directly to the item of interest.
One thing we found useful is to have a helper class to help manage the scheduling (or resetting) of the repeated intents required to update the cards. This is especially true in cases where your app's code may need to generate card information from multiple locations (e.g. reacting to different situations). Also, it is possible that your recommendation code might actually be managing more than just the recommendation intent itself. Your recommendation code might be managing other systems, such as services polling for internet data updates. You can use something like the following class to make things much simpler:
public class RepeatScheduler { public static void doSchedule(Context context, Class someClass, long msDelay, long msInterval) { Log.v("RepeatScheduler", "Scheduling repeat intent for class: "+someClass.getSimpleName()); AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent repeatIntent = new Intent(context, someClass); PendingIntent alarmIntent = PendingIntent.getService(context, 0, repeatIntent, 0); context.startService(repeatIntent); // start the service that will handle the intent. alarmManager.cancel(alarmIntent); // cancel any previous scheduled intent. alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, msDelay, msInterval, alarmIntent); } }
That's just an example that you can expand to fit your needs. Then to use the helper you would make a simple call like this:
RepeatScheduler.doSchedule(mContext, UpdateRecommendationsService.class, initialDelayMS, minutesBetweenUpdatesMS);
The first parameter would usually be your application context, the second is the service class you want to schedule to 'wake' with an alarm intent, the third is the delay before the first intent fires, and the fourth is the interval between subsequent intents.
Set fields appropriately
As you create each recommendation, you will want to set a unique ID for each so that you can refer to them later. This is especially important for recommendations that are 'live', such as download status, so you can point at the existing card to update.
For each card you will also set a priority level. Google's code shows a simple approach of reducing the priority of each card added, to ensure a 'spread' of the cards across the row with other apps' cards. However, there is a serious error to note in their sample code, which is they begin their priority level using the maximum number of recommendations as a base priority value. Instead, they clearly should have used the Notification class' priority constants instead. You should start your cards at PRIORITY_DEFAULT and reduce from there for multiple recommendations (unless you are posting a single 'alert style' recommendation in which case PRIORITY_HIGH or PRIORITY_MAX may be more reasonable). See the Follow the Rules section below for more on priorities.
Show some progress
Since a recommendation is a notification under the hood (built using NotificationCompat.Builder), some standard functionality spans the gap -- specifically, basic progress indication is supported. The result is the same as the "Now Playing" card, a thin bar between the card image and the content area.
As noted, this can be particularly useful for data up/downloads, showing progress in unfinished media playbacks, or visualizing the furthest point in reading a book or comic. Adding a progress indicator to a recommendation being built is as simple as adding an extra call to setProgress on the builder:
cardBuilder.setProgress(100, 50, false);
In this example, 100 is the maximum value, 50 is the current value (and the value to change each update), and the last parameter must always be false (as it is a flag indicating indeterminate progress, which isn't available for recommendation cards).
To update a card showing live progress, you simply make another builder or cache the original, make a new setProgress call with a different current value, call the build method on the notification, and then call NotificationManager.notify(cardID, recommendation) with the same cardID each time so the linked card is updated.
If you are doing live progress and want to keep the card around after the process is complete, you should do a final update with setProgress(0, 0, false) to remove the progress bar, and then can also call setContentText on the builder object to reflect status in the text ("Upload complete" or the like).
Refresh your cards properly
You shouldn't trigger recommendation updates too frequently, as large changes to the recommendation row content can be disconcerting to the user, with cards popping in and out. However, you should always refresh your recommendations when key events happen like finishing playing a piece of content, leaving the application (since you will want to refresh items then or show last left off state, etc.), or when a background data has new status (such as downloading large data, uploading photos, or new data on a news site).
Also, don't recreate your cards from scratch if you just need to update them, as that affects positioning and may affect Google heuristics for importance rankings. Rather, remove outdated cards (like media that has been played), update existing cards with changed data, then add new cards as needed.
Give the user some control
Until Android TV gets around to offering some kind of global 'knobs', if you are a media app you might consider letting the user choose how many cards they want from your app -- maybe they really only want one or two cards from your app. Further, whenever possible allow users to pick from categories, channels, or folders of content they feel are most important/applicable to deliver to them as recommendations.
Start it all in the Manifest
The initial launch point for supporting recommendations is to create a subclass of BroadcastReceiver that schedules your IntentService to run. In the manifest, you then register the receiver as handling the ACTION_BOOT_COMPLETED intent. This ensures that when the system first boots up, all applications that are interested get an initial 'ping' to generate their recommendations.
Follow the Rules
"What rules?" Many app developers are breaking what should be considered to be clear, common-sense, unwritten rules of etiquette and user experience when it comes to recommendations. NVIDIA is hitting these misuses so often, we feel it necessary to write down some good rules of thumb to follow in order to have only positive experiences with recommendations. Following the rules will lead to happier users!
Be Responsible With Recommendations ...
Do NOT crank up the priority level.
Many applications seem to be using higher priority values than specified by the Google API documentation. Unless you have only one card and you need the user to see it (an actual 'priority notification'), you should start with priority 0 (using the constant PRIORITY_DEFAULT). This ensures that actual high-priority recommendations are always visible and not 'crowded out' of the first screen of cards.
Do NOT fill the screen with your content.
It is a horrible user experience to see a newly-installed app consume the entire home screen with cards. Don't do it. There are a number of simple solutions to this.
- Use fewer cards
- Use thinner content
- Adjust the priority of each additional card
We have seen apps display as many as six or even eight cards! Generally stick to three or four cards, five at the most, regardless of size (in other words, don't use more cards just because you make them thinner -- see the next point).
Apps with wider (4:3) cards can very quickly dominate the entire screen. Try square or thin aspect instead if it works for your imagery. As noted in the prior point, don't start showing more cards simply because you saved space with a thinner image!
For your first card, start with PRIORITY_DEFAULT. Then for every additional card, drop the value by one, which helps to spread your cards out across the recommendations row, rather than them 'clustering' and blocking other app cards from showing equally.
Do NOT keep stale cards around
Make sure your content is still relevant, and remove old or stale cards. Further, don't instantiate 'already stale' cards, make sure you track to some degree what content has been shown (and visited) before.
If we take YouTube as an example, it currently gets this right and wrong. When a user clicks a card for a video that YouTube recommends to watch, the card is removed -- perfect! However, when the device goes to sleep for a while and the user wakes it back up, suddenly previously recommended videos that have been watched reappear, creating an immediate negative experience. The visited content could be easily tracked on device or in the cloud (and YouTube does keep history), and the content should have been refreshed at wakeup with something new.
... Or Face the Consequences
Obviously, it is your app and you call the shots. The 'rules' above are just guidelines we think will help your overall user experience. Follow them and users may not say a thing, they'll just inherently enjoy your app! However, if you don't follow them, in the best case the user will manually turn off your recommendations completely; in the worst case, the user will be so frustrated by 'recommendation overload' that they will go the faster route and simply uninstall your app, and potentially leave a negative rating in the store.
Personally, I've uninstalled many media and news apps because they flooded the recommendations row of my personal NVIDIA SHIELD Android TV. The 'overload' was so intense that I just wanted the apps gone from my device. Don't let that happen to your app.
In Conclusion
We hope this has given you a starting idea of how to use recommendations to bring users into your application, how to use recommendations to deliver information to users in ways you might not have considered, and how to use them responsibly and not cause recommendation overload.
Make sure to check out our other recent guides like our high-level Android TV Developer Guide, and our new Android Devcasts and SHIELD Android developer training materials, and be sure to go pick up a SHIELD Android TV today -- for development and enjoyment!