Saturday, July 16, 2011

Windows Phone 7.5 (Mango) dynamic live tiles

In Mango, an application can have several tiles associated wih it : the primary or “application'” tile, and as many secondary tiles as desired. Secondary tiles have the capability of taking the user to a specific area of the application through an associated deep link, as opposed to the main application page as with the primary tile. This is one example of the new deep linking capability Mango brings.

As in WP7, tiles are pinned or unpinned to the phone’s start menu by the user. All tiles have the ability to display information dynamically, such as a current game score or flight status information. Your application updates the displayed information by setting the tile’s title and count properties, and/or the tile’s background image.

In Mango, tiles are now 2-sided, which means they flip over at regular intervals when pinned. We can now also use the BackTitle, BackContent and BackBackgroundImage properties to display on the flip side.

To create a tile in your app, you first create a StandardTileData object to set the desired properties :
StandardTileData NewTileData = new StandardTileData
{
BackgroundImage = new Uri("/Images/Red.jpg", UriKind.Relative),
Title = "Mary & John",
Count = 12,
BackTitle = "Just married",
BackContent = "Current score : 123",
BackBackgroundImage = new Uri("/Images/Blue.jpg", UriKind.Relative)
};

Then you call the static Create() method on the ShellTile class :

ShellTile.Create(new Uri("/NewlyWeds.xaml?TileID=2&date=Mary And John", UriKind.Relative), NewTileData);
Notice the argument passed to the Create method : When creating a secondary tile, you can specify the launch Uri that the app will receive when started from the tile. You can point the Uri to a specific page deep into the app, or simply use it to pass query string parameters into the app.

Before creating a tile, or if you want to update an existing tile, you may need to check wether the tile already exists and if so, retrieve the existing tile in order to update it. You can use the ActiveTiles collection property of the ShellTile class to do that. ActiveTiles always contains the application’s primary tile as well as any secondary tiles created by the application. To retrieve the application tile, just use FirstOrDefault() :

var apptile = ShellTile.ActiveTiles.FirstOrDefault();


To retrieve a specific secondary tile, you need to use a unique identifier for that tile. You can use the deep link Uri that was passed into the constructor when the tile was created :

DirectUri = "/View/Kite.xaml?id=fuel2011";
ShellTile.Create(new Uri(DirectUri, UriKind.Relative), tileData);

Now to retrieve the tile, use a LINQ query on the ActiveTiles property to test whether NavigationUri contains the unique ID :

var tile = ShellTile.ActiveTiles.SingleOrDefault(
t => t.NavigationUri.ToString().Contains("id=" + ParamId));

Note that I’m not using NavigationUri.Contains(DirectUri). The reason is that the NavigationUri property returns a URL encoded version of the deep link Uri that was passed in the constructor. If you set a breakpoint you’ll see that NavigationUri returns something like ‘file:///View/Kite.xaml%3Fid=fuel2011’. So NavigationUri.Contains(DirectUri) would return false for the tile we’re trying to retrieve.

Once you get ahold of the existing tile, you can easily update its properties :


var data = new StandardTileData
{
Title = "New Fuel 2011!",
Count = 2,
BackgroundImage = new Uri(picpath, UriKind.Relative),
BackContent = "This is the great new updated Fuel!",
BackTitle = "Check it out!",
BackBackgroundImage = new Uri("/Images/tilekiteback.png", UriKind.Relative)
};
 
tile.Update(data);

You can also remote a tile by calling the Delete() method on the tile.

So using ShellTile and StandardTileData, it’s very easy to create and manipulate both primary and secondary tiles in our app. In a later post I’ll discuss updating tiles on a schedule, using both the ShellTileSchedule APIs and background agents.

Happy tiling!

Friday, July 1, 2011

Windows Phone AppConnect Bing search extensibility (Apps aka Extras)

 
A couple of days ago the Mango Beta 2 SDK, aka Windows Phone 7.1, was released, along with a pre-release test version of the device OS. There were some great additions to the Beta 1 feature set, which I’ll be discussing over my next posts – stay tuned.
Today I want to focus on Bing integration in Windows Phone, also known as search extensibility, search extras, and officially now, AppConnect. What is that anyway ? AppConnect lets your app appear with relevant Bing search results on the phone.
Some Bing searches return what’s called “Quick Cards” in additional to traditional web results. There are Product, Place, and Movie Quick Cards, which contain detailed information about the item, such as descriptions, images, reviews, and prices. Quick Cards also contain an additional ‘Apps’ section that lists marketplace Windows Phone apps that are related to the card’s product, place or movie.
So if the user is doing a search on the phone for a specific product, place or movie, and a Quick Card comes up in the search results, and the quick card is in one of the Bing categories that you’ve associated with your app, then your app will appear listed in the Apps section of the Quick Card (formerly called the ‘Extras’ section, hence the extras term above). Of course, your application needs to be in the marketplace for this to happen.
If the user taps on your app’s title, if the app is already installed on the phone s/he will get taken to the specific page you configured in your app, and the Bing search parameters will be passed to the app – more on that later. If the app is not installed, the user will be able to download it from the marketplace.
The next question is, how do we set up our application so it’ll show up in relevant Bing searches ? The process is fairly simple :
1) Add some ‘extension’ elements to your app’s WMAppManifest.xml file to specify relevant Bing categories
2) Create an Extras.xml file that maps Bing categories to titles and descriptions that you want to be displayed in the Apps tab of the Bing quick card
3) Optionally, create a landing page in your app to retrieve the query string parameters passed from the Bing quik card when the user launches your app from that card
Configuring extension elements
You add an <extensions> element under the <app> element of the WMAppManifest file, and within it, as many <extension> elements as you want to specify the Bing categories your app should be associated with in searches, such as :
<Extension ExtensionName="Bing_Products_Sports_and_Outdoors" ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}" TaskID="_default" ExtraFile="Extensions\\Extras.xml" />

The list of possible extension names can be found here :
http://msdn.microsoft.com/en-us/library/hh202958(VS.92).aspx
The value of ConsumerID is fixed and indicates this is a Bing search extension.
ExtraFile specifies the location of the Extras.xml file.

In the beta release, to be able to test your app in the emulator, you should also add the beta extensions,  in this case :

<Extension ExtensionName="Products" 
ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}" 
TaskID="_default" 
ExtraFile="Extensions\\Extras.xml" />




This won’t be required in the release version of Windows Phone 7.5.

Create the extras file

The extras.xml file contains mapping between Bing categories or groups of categories, and strings you want to appear in the quick card to describe your app :

<ExtrasInfo>

<AppTitle>
<default>Kiteland</default>
</AppTitle>

<Consumer ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}">

<ExtensionInfo>
<Extensions>
<ExtensionName>Products</ExtensionName>
<ExtensionName>Bing_Products_Sports_and_Outdoors</ExtensionName>
</Extensions>
<CaptionString>
<default>Extensive kitesurfing equipment info</default>
</CaptionString>
</ExtensionInfo>

<ExtensionInfo>
<Extensions>
<ExtensionName>Bing_Products_Travel</ExtensionName>
</Extensions>
<CaptionString>
<default>All you need for your kite travels</default>
</CaptionString>
</ExtensionInfo>

</Consumer>
</ExtrasInfo>


In addition to specifying the app title that should appear in the quick card, you add one or more ExtensionInfo elements that each specify a caption to display for if the associated Bing categories are hit. For example, you may want the quick card to display “Kiteboarding equipment” if the search result is in the Bing Products Sports & Outoors category, and “Kiteboarding travel” if in the Bing Products Travel category.

Create a landing page

For a better user experience you may want the user to be taken to a specific page if s/he taps on your app title in the Apps tab of the quick card. To do that, create a landing page and retrieve and parse the Bing parameters which are automatically passed to the page when launched from the Bing quick card. These parameters are ProductName and Category for a product card, MovieName and Category for a movie card, and PlaceName, PlaceLatitude, PlaceLongitude, PlaceAddress and Category for a place card :


var prodName = NavigationContext.QueryString.TryGetValue("ProductName");
var bingCategory = NavigationContext.QueryString.TryGetValue("Category");

Your application can then perform some logic based on these passed parameters, such as retrieving the product, movie or place from a database and displaying some data.

Testing the app

Now I’ve found that testing these features can sometimes be tricky as it involves working with Bing searches. For this example, I’ll assume you’ve included the Bing_Products_Sports_and_Outdoors category in your list of extensions. If you use the emulator, remember to include the ‘Products’ beta extension as well.

First, deploy and run your app to the emulator or device, and then press the Search button to start a Bing search.  Now type “sporting goods” as a search phrase. At the top of the search results, you should see at least one Product card being displayed, above the Web heading.

image

Click on that card to enter the quik card. If everything is configured correctly, the card should have an Extras pivot with your app title displayed in it. Tapping on the title, should launch your app and take you to the landing page.

image   image  image



To test movies and places, you need to either have location enabled on your device, or if not, to specify a location in your search phrase, such as ‘movies Pittsburgh’ or ‘Starbucks New Orleans’. Remember, searches for places and movies are ‘local’ searches.

This example assumes you’ve included the ‘Places’ and ‘Movies’ beta extensions in you manifest file as well as ‘Products’ - you’d also need to add production Bing categories before submitting your app to the marketplace.

To test places, once the search results are displayed, bring up the Local pivot, and then select one of the places listed below the map. That will bring up the place quick card, and you can then flip to the App pivot of the card to see your app.

image  image

Tapping the app name will launch your app, but in this version, that will have the effect of detaching from the debugger.

Again, make sure both the beta extensions and relevant Bing extensions are specified in your manifest. Also make sure the extensions are mapped in the extras.xml file.

To search for a movie, you can search for something like “movies Philadelphia” (unless you’re using a device with location enabled, and you’re located somewhere in the US for example, in which case you can just type ‘movies’ or a movie name). Then click on the ‘Movies near Philadelphia PA’ section above the Web heading, and you’ll see a list of movies. Selecting a movie will open a movie card, and you can then flip to the App pivot.

image  image

  image  image

Again, make sure you list and map both the ‘Movies’ beta extension and the ‘Bing_Movies’ single production extension for movies.



So to recap, using AppConnect you can easily make your app stand out by making it more discoverable through Bing searches, thus providing the user with a richer experience on the phone.

Happy AppConnecting

Friday, June 17, 2011

Background transfers in Mango

This is a third article in a series on Mango background agents. Background transfers lets your application initiate file uploads and/or downloads which continue in the background even after the application is closed.
For transfers, there is no need to create a separate library as the BackgroundTransferService will handle the work for us. We simply create a background transfer request and queue it up with the service. Here’s an example for downloading a file from a remote Url :
   1: var request = new BackgroundTransferRequest(new Uri(downloadUrl, UriKind.Absolute));
   2: request.Method = "GET";
   3: request.DownloadLocation = new Uri("/transfers/" + filename, UriKind.RelativeOrAbsolute);
   4: BackgroundTransferService.Add(request);

Notice the “/transfers/” path in the DownloadLocation property value. The OS always downloads files (or uploads them from) the “transfers” directory  at the root of isolated storage. The directory is created at app installation, and shouldn’t be deleted.

In a real-world app, you should wrap your Add() call in a try-catch.

So you can see it’s very easy to initiate a background transfer. However, after launching a transfer, you’ll typically need to do a few common thing, such as :

- Monitor progress and update the UI accordingly
- Cancel a transfer
- Do something with a completed transfer
- Reconnect to ongoing transfers, if any, after closing then relaunching the app

To monitor progress, you handle the TransferProgressChanged event of the BackgroundTransferRequest. In the handler, you can retrieve the number of bytes received (or sent) and the total number of bytes to receive (or send) from the Request property of the eventargs, and based on these numbers, you can update the visual state of a progress bar for example.


   1: progbar.Maximum = e.Request.TotalBytesToReceive;
   2: progbar.Value = e.Request.BytesReceived;

Note that if you’re displaying a list of transfers in the UI with a progress bar for each transfer, you’ll need to find a way to tie each progress bar with its associated request. One way is to use the RequestId property of the request as a marker, for example you can set the Tag property of each progress bar to the request ID for its associated request.

To cancel a request, you first retrieve that request using the Find method of the BackgroundTransferService using the request ID, and then you call Remove() :


   1: var req = BackgroundTransferService.Find(requestId);
   2: if (req != null)
   3: {
   4:     try
   5:     {
   6:         BackgroundTransferService.Remove(req);
   7:     }
   8:     catch (Exception ex)
   9:     {
  10:         MessageBox.Show("Error cancelling request : " + ex.Message);
  11:     }
  12: }

To process a completed request, you can handle the TransferStatusChanged event of the BackgroundTransferRequest :


   1: request.TransferStatusChanged += request_TransferStatusChanged;

If the transfer is complete and no transfer error occured, you should remove the completed request from the queue, and then perform any custom work :


   1: void request_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
   2: {
   3:     if (e.Request.TransferStatus == TransferStatus.Completed)
   4:     {
   5:         if (e.Request.TransferError != null) 
   6:         {
   7:             //Handle transfer error scenario
   8:         }
   9:         else
  10:         {
  11:             //So some work, e.g. open downloaded file
  12:         }
  13:     }
  14: }

Finally, to reconnect our app to background transfers after the app has been closed and relaunched, we can use the OnNavigatedTo method. Here we retrieve the queued transfers using the BackgroundTransferService’s Requests property, and we rebind our UI to the requests. If we’re using custom objects instead of binding directly to the requests, we first need to re-attach our reconstructed objects to the requests, typically by examining the requestId property of the requests. We also need to reattach the event handlers (TransferProgressChanged and TransferStatusChanged) in the method.

To wrap up, the background transfer API provides convenient abstractions that we easily use for typical scenarios. Combined with data binding, the API makes it a breaze to allow downloads and uploads in our application.

Happy transfering!

Thursday, June 16, 2011

Generic background agents in Mango

In Mango, one of the multitasking mechanisms available is the ability to schedule custom code to run in the background at a regular time interval, even when the application is closed. The custom code that you need to run in the background is contained in a class library separate from the foreground application, and runs in a separate process.
In Visual Studio, you use the scheduled task agent template to create the agent library in the phone application solution, and you reference the agent library from the phone app. As a result, the WMAppManifest file gets updated with a new element that references the agent assembly, so the main app knows where to find the code to run.
For example, you can create an agent that will log some memory usage data for the phone to a local file in isolated storage. You add the code to carry out this task in the OnInvoke() override in the agent class, which the system invokes each time the agent gets run – and that in turn depends on how you schedule it (see below).
The agent code may look like this :
   1: protected override void OnInvoke(ScheduledTask task)
   2: {
   3:     string logFilename = "applog.txt";
   4:     var store = IsolatedStorageFile.GetUserStoreForApplication();
   5:  
   6:     using (var stream = new IsolatedStorageFileStream(
   7:         logFilename, FileMode.Append, store))
   8:     {
   9:         using (var sw = new StreamWriter(stream))
  10:         {
  11:             sw.WriteLine("Task type : " + task.GetType().ToString());
  12:             sw.WriteLine("Date time : " + DateTime.Now);
  13:             sw.WriteLine("Memory usage : " + DeviceStatus.ApplicationCurrentMemoryUsage);
  14:             sw.WriteLine("Peak mem usage : " + DeviceStatus.ApplicationPeakMemoryUsage);
  15:             sw.WriteLine("********************");
  16:         }
  17:     }
  18:  
  19:     NotifyComplete();
  20: }

Now that you’ve defined the code you want the agent to run in the background, you need to schedule the agent. You do that in the foreground phone application, your main app. For this, you create and register a scheduled task. It may be a periodic task, which the system will roughly run every 30 minutes for 15 seconds (some restrictions apply). Or a resource intensive task, which will only run in the phone is plugged in and charged and wifi is available, but will run for up to 15 minutes so, you can use it to do some serious work, like syncing large data.

Note that you may schedule both a periodic AND a resource intensive task.

You use the ScheduledActionService to register the task( s) with the system. Here’s how you do it :


   1: var task = new PeriodicTask(agentName);
   2: task.Description = "Logs memory performance for the device";
   3: ScheduledActionService.Add(task);

And/or :


   1: var riTask = new ResourceIntensiveTask(riAgentName);

Once the task is scheduled, your custom code in the agent will run on a regular basis in the background. In the main app you can check the log file in isolated storage to see the results :


   1: IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
   2:  
   3: try
   4: {
   5:     using (var isoFileStream = new IsolatedStorageFileStream(
   6:         logFileName, FileMode.Open, store))
   7:     {
   8:         using (var isoFileReader = new StreamReader(isoFileStream))
   9:         {
  10:             data = isoFileReader.ReadToEnd();
  11:             myTextBlock.Text = data; 
  12:         }
  13:     }
  14:  
  15: }
  16: catch
  17: {
  18:     MessageBox.Show("Log file not found.");
  19: }

In your app, you probably want to add mechanisms for the user deschedule and reschedule the task, and for the app to automatically reconnect to the scheduler service when resuming from closed or tombstoned state, since the service continues to run in the background.


var task = ScheduledActionService.Find(agentName);
var riTask = ScheduledActionService.Find(riAgentName);
 
if (task != null | riTask != null)
{
...

A final note : the user can view which apps have agents, and whether they’re currently scheduled, in the new Background Services section of the phone settings (‘Applications’ tab) :

image      image

To recap, generic agents let you run your own code at regular system-defined time intervals. This is Mango’s way of letting us multitask, and it’s designed to fully preserve system health when a bunch of crazy apps try to take over the background by running all sorts of resource-consuming things.

Cheers!

Monday, June 13, 2011

Windows Phone Mango background audio streaming

Mango provides two different agent classes that your application can use to play audio in the background : AudioPlayerAgent and AudioStreamingAgent. Based on their names It’s easy to believe the first is used to play local files while the second is used to stream audio from the web.
In reality, these class names are somewhat misleading. AudioPlayerAgent is actually designed to play URI-based audio in the background, whether locally or remote. What that means is that you can supply either a local path pointing to a media file in isolated storage, or the URL of a remote media file. The catch is, the media file must have a supported audio format, which is either MP3 or WAV.
So in your foreground application or in the agent, you can create an audio track like so :
   1: var track = new AudioTrack(
   2:                 new Uri("/mySong.mp3", UriKind.Relative),
   3:                 "myTitle", "myArtist", "myAlbum", null);

OR :




   1: var track = new AudioTrack(
   2:     new Uri("http://www.techfox.net/songs/Back_to_shore.mp3",  UriKind.Absolute),
   3:     "myTitle", "myArtist", "myAlbum", null);

Then you can load and play the media file regardless of location :





   1: BackgroundAudioPlayer.Instance.Track = localTrack;
   2: BackgroundAudioPlayer.Instance.Play();
You can actually mix and match between local files in isolated storage, and remote files in a same playlist :




   1: List<AudioTrack> playList = new List<AudioTrack> 
   2: { localTrack, remoteTrack };
The background player will play all these files in the same manner.

For local files, make sure the audio files added to your project are copied into isolated storage before trying to set the track on the background player. MSDN provides an example for doing that, which can be called at application startup. I’ve tweaked it so it reads the playlist songs from a xml file, and copies only the local meia files to isolated storage :



   1: void CopySongsToIsoStorage(string projectDirName)
   2: {
   3:     XDocument data = XDocument.Load("Songs/Playlist.xml");
   4:     var songFiles = data.Descendants("song").Select(s => s.Attribute("source").Value);
   5:  
   6:     using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
   7:     {
   8:         foreach (var song in songFiles)
   9:         {
  10:             if (song.StartsWith("http"))
  11:                 continue;
  12:  
  13:             if (!storage.FileExists(song))
  14:             {
  15:                 string _filePath = projectDirName +  "/" + song;
  16:                 StreamResourceInfo resource = Application.GetResourceStream(new Uri(_filePath, UriKind.Relative));
  17:  
  18:                 using (IsolatedStorageFileStream file = storage.CreateFile(song))
  19:                 {
  20:                     int chunkSize = 4096;
  21:                     byte[] bytes = new byte[chunkSize];
  22:                     int byteCount;
  23:  
  24:                     while ((byteCount = resource.Stream.Read(bytes, 0, chunkSize)) > 0)
  25:                     {
  26:                         file.Write(bytes, 0, byteCount);
  27:                     }
  28:                 }
  29:             }
  30:         }
  31:     }
  32: }

Here’s the sample xml playlist : 


   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <playlist>
   3:   <song source="Back_to_shore.mp3"
   4:         title="Back to shore"
   5:         artist ="Sound of Tarifa"
   6:         album="Exquisite" />
   7:   <song source="http://www.techfox.net/songs/Better_believe.mp3"
   8:         title="Better believe"
   9:         artist ="Sound of Tarifa"
  10:         album="Exquisite" />
  11:   <song source="Wind_riders.mp3"
  12:         title="Wind riders"
  13:         artist ="Sound of Tarifa"
  14:         album="Exquisite" />
  15: </playlist>



So to recap, the AudioPlayerAgent has more capabilities than can be guessed from the name. Use it in your apps for common scenarios of playing mp3 or WMA local or remote audio files in the background.



Cheers!

Saturday, May 21, 2011

Windows Azure Toolkit for Windows Phone - authenticating from the emulator

This post is about the VS template project in the Windows Azure Toolkit for WP.
 I succeeded in authenticating into the cloud service from the phone emulator using a self signed certificate. Here's the process :

CREATE SELF-SIGNED CERTIFICATE


- Open the VS prompt in admin mode and create a new self-signed certificate using the makecert command :
makecert -r -pe -n "CN=[yourservicename].cloudnet.app" -b 01/01/2000 -e 01/01/2036 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localMachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12
Notice the [yourservicename].cloudnet.app certificate name : it's the same as your production cloud service host name. This is the only way I've found so far for the cert name to match the service host, which seems to be a requirement. That means, you need to deploy the service in production instead of staging, since in staging the host name will be generated at deploy time using a GUID which you don't know ahead of time and so you can't configure it into your service endpoints (as shown in the next steps).
- Open the MMC by typing mmc in the start menu. Then, click File -> Add/remove snap-ins -> Certificates -> Add -> OK
Look in the Personal/Certificates directory and find the [yourservicename].cloudnet.app.

EXPORT & UPLOAD THE CERT

- Copy-paste the cert to 'Trusted Root Certification Authorities'/Certificates (so HTTP agents can trust the cert)
- Right-click the certificate and choose All Tasks -> Export to start the export wizard. Select "yes, export the private key", keep the default export file format, and provide a file name and path to export to.
- Log into the Azure Management Portal. Under your hosted service node, select "Certificates", click "Add certificate" to open the "Upload an X.509 certificate" dialog, select the exported file on your local machine and click OK to upload the cert to the cloud and associate it with your service role.

CONFIGURE AND PUBLISH CLOUD PROJECT

- In the Visual Studio WP7 cloud application template, open the properties page for the web role. Go to the Endpoints tab, and for the https endpoint, select the newly created certificate using the '...' list (give it a friendly name).
- Right click the cloud project and select 'Publish' to publish the service to the cloud

INSTALL CERT ON PHONE EMULATOR

In a new Visual Studio instance, open the W7CertInstaller project (if you haven't, download it from http://wp7certinstaller.codeplex.com)* Configure the WP7CertInstaller web app to run in IIS instead of the dev server
* Make the WP7CertInstallerExample phone app the startup project
* In MainPage.xaml.cs, change the CertificateUrl string to https://localhost/WP7CertInstaller/Certificate.p7b?findBy=FindByThumbprint&findValue=[your cert thumbprint]
(you get your cert thumbprint from the MMC or from the management portal by selecting the uploaded certificate)
- Without closing the emulator (otherwise the cert will be uninstalled), you can now run your WP7 cloud app from Visual Studio, and login or register from the phone emulator.
Note that instead of opening the wp7certinstaller project separately each, you can just host the wp7certinstaller web app locally once and for all and import the TrustedRootCertificateInstaller.cs class into your wp7 cloud app project so you can easily install the certificate in the emulator each time you need to.




Happy authenticating!