[wpdev] Tips and tricks about updating live tiles in Mango
Cet article est aussi disponible en francais.
In the last published applications I've worked on, like Foursquare, Flickr or TuneIn (and more are coming), all of them have live tiles, in both the Pull and locally generated tiles forms. But there are a few things to know to have a great experience with it, and you'll find it out by reading this article.
This is a very powerful feature, letting the user choose how to customize its own very personal experience, with no-one forcing the user into having a tile he does not want. This is the very same reasoning behind the absence of API to add items in Windows 7 task bar.
Live Tiles in Pull mode
In the foursquare app there is the main tile, updated via the "pull" model, every hour or so (and the "or so" has a very strong meaning).
That tile that displays the Leaderboard is built in an Azure cloud service using a WPF offscreen rendering, based on the requests of the tile Pull Engine. This tile was built this way because of the limited capabilities of NoDo, where background agents were not available to render it locally on the device.
With Windows Phone Nodo, many users were complaining about the main tile not updating, and quite frankly, this has been a mystery up to the end. It seems like tiles would update on some devices, but not on others, but would only update if the battery power was more 50%.
Also, these tiles seemed to not update if the device is in standby, but only when the user sees the home screen, and when the suggested refresh delay has expired. I say "seem" because it seems like the rules behind this tile update were either not clear, or broken in some way.
This has changed with mango though. The Pull tiles are not updating almost all the time, but the 50% battery rule still seems to apply.
Also there's the rule of the 80KB file size JPEG, that if you go over, your tile won't be displayed.
Programmatic Live Tiles
In Foursquare, the user may choose to pin a secondary "Tile" a specific place to its main screen for easy access.
Updating these tiles can be acheived with the ShellTile API, and with that you can set four thing:
- A title for the front and back
- An image URL on the front and back
- A four lines text on the back
- A number on the front
- (and you forget about the animated tiles like the people hub)
While all these features are interesting, only one of them is actually very useful: Images URL.
All the other properties are not stylable, they only follow the system's colors, and do not fit very well with user generated content. In the case of Foursquare, Flickr and TuneIn, the displayed images is user provided content, and having a white on white text is not very useful.
On the subject of image URLs, setting an external URL sets the image of the tile, but as long as the device does not reboot. If the device is rebooted, the tile looses its content. A pretty strange behavior, if you ask me.
Using the new isostore uri schema
Fortunately, it is now possible to store the image locally in a special folder in the isolated storage named /Shared/ShellContent, and use the new URI prefix "isostore", like this "isostore:/Shared/ShellContent/MyTile.jpg".
This means that you can download the image to display to the isolated storage, and use it from there.
But there's a big problem with using this technique : You do not control the size of the downloaded image. So if it is bigger than 80KB, you're stuck with the accent color background.
On a side note, I'd be curious to know the story behind this isostore prefix, because there are only two places that can use it, SQL CE Databases and Live Tiles. This prefix cannot be used as a Source property for Image controls, even though it would be very useful. But I digress.
Generating Live Tiles
Hopefully, it's very much possible to generate a complete tile's content, using the WriteableBitmap.Render method. This method allows the offscreen rendering of any UIElement, then save it using the SaveJpeg extension method to persist it.
The tiles for Foursquare, Flickr and TuneIn are generated this way, using a user control that a real designer person created. This gives great looking tiles, and the layout and style can be updated depending on the dynamic content.
Here are a few things to generate tiles :
- The "new" (kinda) Silverlight 4 ViewBox control is very handy to resize text to fit the 173x173 layout,
- You can use an Image control in your render source, but you need to wait for the BitmapImage (not the Image) to raise the ImageLoaded event, (The Reactive Extensions can be very handy for that)
- You'll also need to set the CreateOptions to None on your BitmapImage to make sure the image is downloaded immediately,
- If you download images, make sure you have a local fallback image underneath, just in case the remote image cannot be downloaded,
- Before rendering the content, make sure to call Measure and Arrange methods to force the layout to the 173x173 size required by the tiles.
- You may need to call Measure and Arrange multiple times, because for some obscure reason, the control to be rendered may not honor these commands. Check for the ActualHeight and ActualWidth properties values to see if they are correct.
- Make sure to render your tile before pinning it to the home screen ! The app is basically halted when you call the pin command, and the user may not come back to your app for you to finish the image rendering.
- Don't take too long to render your tile though, if you wait too much, the user experience if pretty bad. That can definitely be a challenge when downloading content to be displayed on the tile.
But then, you may only refresh your tiles when the application is running, unless you use the new Background Agents mango feature.
Updating the tiles with Background Agents
Background agents are Microsoft's way of letting third party apps run code in the background, but with some big restrictions, like memory (4MB), schedule (30 minutes) or duration limits (15 Seconds) for Periodic Tasks.
Here are a few tricks about background agents :
- Periodic Agents run at a 30 minutes interval, and that is not configurable. So be gentle, you may want to add logic to avoid doing work too often, like not refreshing tiles during the night, and actually update the tile every 3 to 6 hours.
- Don't wait too much to generate the tile, 15 seconds is very short. And your task may get killed by the OS before that.
- Don't rely solely on the agent to run to update your tiles, the user may disable your agent using the Settings / Applications / Background Agent page. And the OS may prevent it from running, if it needs to.
- Abuse of the ScheduledActionService.LaunchForTest, to test your background agent,
- A background agent runs your code in a different process than your application, meaning that both your app and the agent can run at the same time. Watch out for shared resources, like a SQL CE database or an isolated storage file.
- If your are updating your tiles in both your application and your background agent, you may need to add some IPC using an old fashion named-Mutex (ahh, the good old days) and synchronize access to your resources.
- Avoid referencing too many assemblies in your background agent, there are a lot of Unsupported API that may make your app fail certification. You can validate your app using the Marketplace Test Kit automated tests.
About the first point, while I understand the power consumption concerns on running below 30 minutes, I still don't get why that interval cannot be set higher, to avoid that very same power consumption issue. There also must be a story behind this...
Then about the last point, during the Beta Phase of the Mango SDK, the StandardTileData class was considered an unsupported API, making the automatic background update of tiles impossible. Hopefully, this changed since the RC of the SDK and it is now possible to update tiles from background agents.
That's it for now. Have fun with the tiles !