Windows Phone 8 Development Internals: Phone and Media Services

  • 6/15/2013

Search extensibility

Another way that your app can communicate with standard phone services is by extending the Bing search experience with custom behavior, integrating your app seamlessly with the search results. There are two ways by which you can extend the Bing search behavior in your apps: App Connect and App Instant Answer. With both features, you can set up your app so that it shows up in the Bing search results when the user taps the hardware Search button. Table 5-2 summarizes the differences.

Table 5-2. Bing search extensibility

Requirement

App Connect

App Instant Answer

WMAppManifest.xml

Requires Extensions entries for each Bing category that you want to extend.

No specific changes required.

Extras.xml

Required. Specifies captions to be used in the search results apps pivot item.

Not used.

UriMapper

Recommended. Allows you to re-route to a specific page on app startup.

Not required.

Target page

You can re-route to multiple different pages, depending on the search item, if you want.

No option. Your app is launched as normal, with its default startup page.

Query string

You should parse the incoming query string for categories and item names for which you want to provide extensions.

You should parse the incoming query string for the bing_query value.

Search connection

Bing includes your app in the search results when the user’s search matches the categories for which you registered extensions.

Bing includes your app in the search results, based on whether it thinks the query is relevant to your app.

In both cases, your app is launched with a particular query string, and you are responsible for parsing that query string to get the search context. It is then up to your app to decide what behavior to execute, based on this search context. Both approaches are discussed in more detail in the following sections.

App Connect

The App Connect approach is the more complex of the two extensibility models. You register your app in a way that gives you more fine-grained control over the criteria that Bing will use to identify it as a suitable extension. You should only register your app for the search categories—or search “extensions”—for which you believe your app has relevance.

If and when the user chooses to start your app from the search results list, your app is given a richer query string with which you can fine-tune its subsequent behavior. The overall model for App Connect is illustrated in Figure 5-3. In summary, the user initiates a Bing search and then taps one of the items in the search results. This navigates to a system-provided Quick Card, which is a pivot page that offers an “about” pivot that provides basic information, a “reviews” pivot, and an “apps” pivot. Your app can be listed in the apps pivot.

Figure 5-3

Figure 5-3 The App Connect extensibility model.

The following steps walk through how to create an App Connect search extension. A completed example is in the SimplestAppConnect solution in the sample code. Figure 5-4 (left) shows the search results page for a search on the string “coffee.” Figure 5-4 (center) shows the Quick Card for one of the selected items from the search results, and Figure 5-4 (right) shows the apps pivot for that Quick Card, in which the sample app’s title string and caption strings are displayed alongside the app’s icon.

Figure 5-4

Figure 5-4 Search results, Quick Card, and apps list for a search on the term “coffee.”

First, create a new Windows Phone app project. Add a second page to the project, named MyTargetPage. This will be the target page to which to navigate when the app is launched via App Connect. In the XAML for this page, add a simple TextBlock; the app will eventually set the text for this dynamically using the item information in the Bing search results.

<TextBlock x:Name="Target" TextWrapping="Wrap" Margin="{StaticResource PhoneHorizontalMargin}"/>

Then, add an Extensions section to your WMAppManifest.xml, within the App element, after the Tokens section. The Extension entry must specify one of the defined extension identifiers, as listed in the Search Registration and Launch Reference for Windows Phone, which you can find at http://msdn.microsoft.com/en-us/library/hh202958(VS.92).aspx. In this example, the app registers for just one extension: Bing_Products_Gourmet_Food_and_Chocolate. The ConsumerID is always the same: {5B04B775-356B-4AA0-AAF8-6491FFEA5661}. This specifies that the app is an extension to Bing search. The TaskID is always “_default”, and the ExtraFile must be a relative path to the Extras.xml file in your project.

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

Now that you’ve referred to the Extras.xml file, you should actually create it. To do so, make a new folder in your project named Extensions, add a new XML file to this folder, and then name it Extras.xml. You should set the Build Action for this to Content—in Microsoft Visual Studio 2012, this will be set by default. The Extras.xml must be in the Extensions folder, but the path you specify in the ExtraFile attribute can omit the Extensions root path and just specify “Extras.xml.” Either will work, so long as the file itself is in the right place. This file is where you specify the strings to be used in the Bing search results list for your app.

<?xml version="1.0" encoding="utf-8" ?>
<ExtrasInfo>
  <AppTitle>
    <default>My Search Extension</default>
  </AppTitle>
  <Consumer ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}">
    <ExtensionInfo>
      <Extensions>
        <ExtensionName>Bing_Products_Gourmet_Food_and_Chocolate</ExtensionName>
      </Extensions>
      <CaptionString>
        <default>All you need to know about coffee</default>
      </CaptionString>
    </ExtensionInfo>
  </Consumer>
</ExtrasInfo>

In Extras.xml, you can supply an AppTitle with at least one string for the default language. In Windows Phone 7.1, the AppTitle element is used to specify the title of the app as you would like it to appear in the apps pivot page of the Quick Card; it is therefore a required field. In Windows Phone 8, however, the system uses the app title from the phone’s installed Apps list, instead; so, on this platform, the AppTitle is not required. In the Consumer section, the ConsumerID is again the same Bing search ID. Under this, you list all the Extensions that you want to register for Bing search. Again, you must provide at least one default CaptionString. The app’s icon (the 62×62-pixel image) for an extension app should not use transparency—and, as always, you should test this to ensure that it displays correctly in both light and dark themes.

The next item you need is a custom URI mapper. You need this in order to map the navigation query string that Bing search passes to your app to the correct target page within your app. For this piece, add a new class file to your project and then change the code to derive your class from UriMapperBase. For example, map this incoming URI

/SearchExtras?ProductName=coffee&Category=Bing_Products_Gourmet_Food_and_Chocolate

to this target page, including the original query string parameters:

/MyTargetPage.xaml?ProductName=coffee&Category=Bing_Products_Gourmet_Food_and_Chocolate

In addition to determining the correct target page, you’re free to do whatever other processing you want, including modifying the parameter list according to your requirements before passing it on, if you need to. When the app has been launched via Bing search App Connect, the URI will include the “/SearchExtras” substring. So, if you examine the URI and find that it does not include this substring, you should immediately return because this means that the app has been launched normally, not via Bing search.

public class MyUriMapper : UriMapperBase
{
    public override Uri MapUri(Uri uri)
    {
        String inputUri = uri.ToString();
        if (inputUri.Contains("/SearchExtras"))
        {
            if (inputUri.Contains("Bing_Products_Gourmet_Food_and_Chocolate"))
            {
                String outputUri = inputUri.Replace(
                    "/SearchExtras", "/MyTargetPage.xaml");
                return new Uri(outputUri, UriKind.Relative);
            }
        }
        return uri;
    }
}

A custom URI mapper must override the one and only virtual method, named MapUri. This takes in the search URI, as supplied by Bing. In your implementation, you typically parse the URI, and look first for the “/SearchExtras” substring. If this is found, you can then go on to look for the Products category. In this example, the only category of interest is “Bing_Products_Gourmet_Food_and_Chocolate.” This app provides only one target page for all search requests. In a sophisticated app, you might have multiple pages; if this is the case, you would need to implement a more complex decision tree to determine which page to return from the URI mapper. You would also typically search for more than one category and one product.

To use the URI mapper in your app, you create an object of this type and then set it as the value of the UriMapper property in the RootFrame. The best place to do this is at the end of the InitializePhoneApplication method in the App class.

RootFrame.UriMapper = new MyUriMapper();

This causes the system to load the specified target page and pass in all incoming URIs to your UriMapper so that they can be manipulated before navigation takes place. These incoming URIs include the query string as part of the NavigationContext that comes in to the page in the form of a dictionary of key-value pairs.

In the target page, you should override the OnNavigatedTo method so that you can examine the incoming query string. Look for an incoming ProductName. Having found the corresponding value for the key-value pair—in this case, “coffee”—you can then make a decision as to whether you know anything about this specific product. If so, you can then go on to do whatever domain logic you want based on this value. In this example, the app simply indicates that this is a known product by setting the TextBlock.Text value. The value of this string will be the full product name of the product that the user selected. Using the example in Figure 5-4, this would be “Blue Mountain Coffee Beans.”

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    String product;
    if (NavigationContext.QueryString.TryGetValue("ProductName", out product))
    {
        if (product.ToLowerInvariant().Contains("coffee"))
        {
            Target.Text = String.Format("We know about {0}.", product);
        }
        else
        {
            Target.Text = String.Format("We don't know about {0}.", product);
        }
    }
}

With the code complete, you can test this either in the emulator or on a physical device. Tap the Bing search button and then enter coffee. In the primary search results, swipe over to the shopping pivot item, if it’s not already selected, and then scroll down to find the products list. If necessary, tap the See More Products link to get to a coffee product that contains the string “coffee.” Tap any such item; this takes you to the Quick Card for that item. In the Quick Card, swipe over to the apps pivot item. Your app should be listed there. When you tap the app to launch it, the URI mapper is invoked, and the app navigates to the target page and updates the text with the Bing search information, as shown in Figure 5-5.

Figure 5-5

Figure 5-5 When the user taps your app in the apps pivot of the Quick Card, it navigates to your target page.

To test the functionality of the app in a more deterministic way, you can provide a fake launch query string in the WMAppManifest.xml file. For example, replace the DefaultTask entry with an entry that specifies a SearchExtras query string (without a leading slash). You can also test the negative case by providing a query string that should not result in listing your app in the search results.

<Tasks>
  <!--<DefaultTask  Name ="_default" NavigationPage="MainPage.xaml"/>-->
  <DefaultTask Name="_default" NavigationPage="SearchExtras?ProductName=coffee&amp;
      Category=Bing_Products_Gourmet_Food_and_Chocolate"/>
  <!--<DefaultTask Name="_default" NavigationPage="SearchExtras?ProductName=bananas&amp;
      Category=Bing_Products_Gourmet_Food_and_Chocolate"/>-->
</Tasks>

Suppose that you want to support more than one extension category and perhaps provide different caption strings for some or all of these. Or, suppose that you want to map the launch URI to one of several different target pages, according to some part of the query string. All of these behaviors are possible (see the SimpleAppConnect solution in the sample code). First, consider the requirement to support multiple extension categories. To do this, simply add each additional category in the Extensions section in your WMAppManifest.xml file. In the following listing, the app supports one of each of the three major categories, Products, Places, and Movies:

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

Be aware that there’s a difference between Bing_Movies and Bing_Products_Movies—the latter will include results for movies that are not found to be showing in theatres; for example, if the user is searching for movies to buy on DVD. In the Extras.xml, you could group multiple ExtensionName entries to share the same caption strings. You can also divide your supported categories into groups, each with its own caption strings.

<ExtensionInfo>
  <Extensions>
    <ExtensionName>Bing_Products_Gourmet_Food_and_Chocolate</ExtensionName>
    <ExtensionName>Bing_Places_Food_and_Dining</ExtensionName>
  </Extensions>
  <CaptionString>
    <default>All you need to know about coffee</default>
  </CaptionString>
</ExtensionInfo>
<ExtensionInfo>
  <Extensions>
    <ExtensionName>Bing_Movies</ExtensionName>
  </Extensions>
  <CaptionString>
    <default>Coffee in movies</default>
  </CaptionString>
</ExtensionInfo>

You could also enhance your URI mapper to target different pages for the different categories.

public override Uri MapUri(Uri uri)
{
    String inputUri = uri.ToString();
    if (inputUri.Contains("/SearchExtras"))
    {
        String targetPageName = "/MainPage.xaml";
        if (inputUri.Contains("Bing_Products"))
        {
            targetPageName = "/ProductTargetPage.xaml";
        }
        else if (inputUri.Contains("Bing_Places"))
        {
            targetPageName = "/PlaceTargetPage.xaml";
        }
        else if (inputUri.Contains("Bing_Movies"))
        {
            targetPageName = "/MovieTargetPage.xaml";
        }
        String outputUri = inputUri.Replace("/SearchExtras", targetPageName);
        return new Uri(outputUri, UriKind.Relative);
    }
    return uri;
}

Clearly, you could take this a step further by pivoting your decisions off any of the elements of the query string. To ensure robustness, you should also decode the incoming URI (typically, by using HttpUtility.UrlDecode) before processing it and then re-encode it (by using HttpUtility.UrlEncode) before returning from your MapUri method.

App Instant Answer

The second search extensibility model is simpler. It requires no special manifest entries, no Extras.xml, and no URI mapper. You have no choice about which page to launch based on the search results, and Bing will always launch your app using its default page. The way Bing identifies your app as being suitable for listing in the search results is internal to Bing. The overall model for App Instant Answer is summarized in Figure 5-6.

Figure 5-6

Figure 5-6 The App Instant Answer extensibility model.

To create an App Instant Answer app, create a Windows Phone app as normal. An example of this is in the SimpleAppInstantAnswer solution in the sample code. When the user performs a Bing search, it might include apps in the web pivot. For example, if the user searches for “banana,” and your app name is “Banana Instant Answer,” this will match, and Bing will potentially add your app to the results list. On the other hand, if your app name is “Banoffee Instant Answer,” the match will fail. To set your app name, you set the Title attribute of the App element in your WMAppManifest.xml. Typically you set this in the project properties page in Visual Studio, although you can also edit the manifest manually, if you prefer, as follows:

Title="Banana Instant Answer"

There are two Title entries in the manifest: one is an attribute of the app element, the other is a subelement of the Tokens element. The app element’s Title attribute is the one that you want here. Also note that even if your app name exactly matches the user’s search term, there’s no guarantee that your app will be listed in the search results.

If you want to allow for the possibility that you’ll be included in search results for App Instant Answers, you should test for the bing_query parameter in the navigation query string.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    String query;
    if (NavigationContext.QueryString.TryGetValue("bing_query", out query))
    {
        // Do something useful with this information.
    }
}

As with App Connect, you can test an App Instant Answer app by providing a fake launch URI in your WMAppManifest.xml file. But remember, just as before, this must be removed before submitting your app to the Windows Phone Store.

<Tasks>
  <!--<DefaultTask  Name ="_default" NavigationPage="MainPage.xaml"/>-->
  <DefaultTask Name="_default" NavigationPage="MainPage.xaml?bing_query=Banana" />
</Tasks>

Finally, be aware that this is the only way that you can test your app, because even on the emulator, Bing only includes App Instant Answer apps from the published catalog in the Windows Phone Store. If it finds an app in the Windows Phone Store that is already installed on the phone, it will change the link from a Store download link to an installed app link, but it will not include an installed app unless it first finds a match for the app in the Windows Phone Store.