30 September 2012

Repairing the Windows Phone 7 behavior to show an image background for a search string

On the last day of 2011 ago I made DynamicBackgroundBehavior - a quite bizarre behavior which plays a supporting role in my latest Windows Phone application “What’s cooking”. It basically shows a picture from a search string as a background, using Bing Image search. Quite neat. Until Microsoft decided to change the Bing Search API a bit. Up until then, it had been a free unlimited ride – since about August you have to buy a package of searches (anything below 5000 searches per month is free). And some more stuff has been changed.

Microsoft provides a nice little C# client library that should solve all of this using an OData protocol. The sad thing is that for the life of it I could not get it to compile on Windows Phone. So I ended up analyzing the PHP (*shudder*) sample in this document – a Word document no less – to see what I needed to do (see page 27 and further).

From a coding perspective, the following details have been changed:

Okay. My cheese moved a little. First things first. I need base64 encoded text. Samples of this are on the internet a dime a dozen, I took this one:

/// <summary>
/// Converts string to base64
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private string Base64Encode(string data)
{
  try
  {
    var encData_byte = new byte[data.Length];
    encData_byte = System.Text.Encoding.UTF8.GetBytes(data);
    return Convert.ToBase64String(encData_byte);
  }
  catch (Exception e)
  {
    throw new Exception("Error in Base64Encode" + e.Message);
  }
}

Now the method that does the actual calling of the Bing Search API has changed a little, but not very much:

/// <summary>
/// Start the image request using Bing Serach
/// </summary>
/// <param name="searchString"></param>
protected void StartGetFirstImage(string searchString)
{
  if (!string.IsNullOrWhiteSpace(searchString))
  {
    var queryUri =
      string.Format(
        "https://api.datamarket.azure.com/Data.ashx/Bing/Search/v1/Image?Query=%27{0}%27&$top=1&$format=Atom",
         HttpUtility.UrlEncode(searchString));
    var request = WebRequest.Create(queryUri) as HttpWebRequest;
    request.Headers["Authorization"] = "Basic " + Base64Encode(string.Format("{0}:{0}", BingSearchKey));
    
    var response =
      Observable.FromAsyncPattern<WebResponse>(
        request.BeginGetResponse, request.EndGetResponse)();
    response.Subscribe(WebClientOpenReadCompleted, WebClientOpenReadError);
  }
}

Note the new url. It goes to a different location, the search term must be enclosed by %27 (i.e. the double quote “) and I have to specify a format (Atom). Really interesting is the thing I marked red, for a variety of reasons:

  • Windows Phone’s Response.Headers collection does not sport the Add method. That stopped me a little, until I realized you could poke directly a new key in the collection by using [“Autorization”] =.
  • Now the authentication is Basic, followed by a space, your account key, colon, your key again and that key:key sequence needs to base 64 encoded. Why this is implemented this way – no idea, but it gets your key across – and validated.

The final thing is that in the callback WebClientOpenReadCompleted I need to use “http://schemas.microsoft.com/ado/2007/08/dataservices” for a namespace,as I already stated.

And then my little Windows Phone behavior works again. The fixed version can be downloaded here. I will fix my #wp7nl library on codeplex ASAP.

2 comments:

My friends call me Poo said...

So does this somehow get around the 5000 query limit?

Joost van Schaik said...

@poo nope. After 5000 queries, I have to wait till the month is up. Or pay.