Posts Tagged ‘WP7Dev’

This post is a continuation of the first part (here) on Updating the underlying data behind an OData service.

So, we continue from where we left off. We have a SQL Azure table called “Team” which has its data exposed as an OData service. This can obviously be read easily by various clients; but what about updates? In the last post, we talked about how we can host a WCF Service in Azure that allows us CRUD operations on the underlying SQL Azure data. This does work; but what if you did not want to add a Service Reference to your project? Isn’t the promise of OData to be able to make plain HTTP requests to perform CRUD on data?

Yes, you are right. In this post, we see how to insert records into the SQL Azure DB table through OData by simply doing native HTTP Posts at the correct URL. Now, I will do this from a Windows Phone app, that is, Silverlight. This brings in the asynchronous factor since we cannot lock up the UI thread. Essentially, we need to open a Request channel at the right URL, write some byte content into the request stream & then expect a response back. In Silverlight, this means jumping through two different threads before coming back to the main UI thread. Check out this wonderful post on why we need to do this (here).

Now, in addition to the thread-hopping, our HTTP Post request needs to be formatted correctly in header & content for the OData endpoint to honor it. Here’s some code using the native “HttpWebRequest” from a Windows Phone app. Essentially, I am making an HTTP Post request against my SQL Azure OData service to insert a new Team member to our DB table:


       private void SubmitPost()
        {
            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(new Uri("YourODataEndpoint"));
            myRequest.Method = "POST";
            myRequest.Accept = "application/atom+xml";
            myRequest.ContentType = "application/atom+xml;type=entry";
            myRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), myRequest);
        }

        private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
        {
            HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
            System.IO.Stream postStream = request.EndGetRequestStream(asynchronousResult);

            XNamespace ds = "http://schemas.microsoft.com/ado/2007/08/dataservices";
            XNamespace dsmd = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
            var content =
              new XElement(dsmd + "properties",
                new XElement(ds + "Name", "Test"),
                new XElement(ds + "TwitterHandle", "@Test")
              );

            XNamespace atom = "http://www.w3.org/2005/Atom";
            var entry =
              new XElement(atom + "entry",
                new XElement(atom + "title", "A new team member"),
                new XElement(atom + "id", string.Format("urn:uuid:{0}", Guid.NewGuid())),
                new XElement(atom + "updated", DateTime.Now),
                new XElement(atom + "author",
                new XElement(atom + "name", "Sam")),
                new XElement(atom + "content",
                  new XAttribute("type", "application/xml"),
                  content)
              );

            byte[] postContentBytes = Encoding.UTF8.GetBytes(entry.ToString());
            postStream.Write(postContentBytes, 0, postContentBytes.Length);
            postStream.Close();
            request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
        }

        private void GetResponseCallback(IAsyncResult asynchronousResult)
        {
            HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
            HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
            Stream streamResponse = response.GetResponseStream();
            StreamReader streamRead = new StreamReader(streamResponse);
            string responseString = streamRead.ReadToEnd();   
            // Update some UI if needed.         
            streamResponse.Close();
            streamRead.Close();           
            response.Close();
        }

In addition to the native “HttpWebRequest”, the “WebClient” class also seems to get the job done; but you need to set the “WebClient.Headers” property correctly to make the right HTTP request. Check out this post (here) on details about HTTP communication from the Silverlight world. Anyway, here’s the corresponding code using WebClient class:


       private void PostUsingWebClient()
        {
            WebClient client = new WebClient();
            client.UploadStringCompleted += new UploadStringCompletedEventHandler(client_UploadStringCompleted);

            XNamespace ds = "http://schemas.microsoft.com/ado/2007/08/dataservices";
            XNamespace dsmd = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
            var content =
              new XElement(dsmd + "properties",
                new XElement(ds + "Name", "Test"),
                new XElement(ds + "TwitterHandle", "@Test")
              );

            XNamespace atom = "http://www.w3.org/2005/Atom";
            var entry =
              new XElement(atom + "entry",
                new XElement(atom + "title", "A new team member"),
                new XElement(atom + "id", string.Format("urn:uuid:{0}", Guid.NewGuid())),
                new XElement(atom + "updated", DateTime.Now),
                new XElement(atom + "author",
                new XElement(atom + "name", "Sam")),
                new XElement(atom + "content",
                  new XAttribute("type", "application/xml"),
                  content)
              );

            client.UploadStringAsync(new Uri("YourODataEndpoint", UriKind.Absolute), entry.ToString());
        }
        
        void client_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                // Oops
            }
            else
            {
                // Success
            }
        }

So, that’s it. Performing CRUD operations against an OData service isn’t all that bad, is it?

Hope this was of some interest. Please drop comments.

Adios!

This will be a longish post .. you have been warned :)

I think OData is rather cool .. head over (here) to learn more, if you haven’t already. There is nothing proprietary  about it; just culmination of existing technologies of HTTP, AtomPub and Json to provide a platform independent way of sharing information. As I heard someone say “..think of it as RSS for real updateable data..” and in this new age of www as a collection of connected web services rather than collection of web pages, I think OData can play an important role. Why the restrictions against opening up our data to interoperability if we can put adequate security on it?

Now, I wanted to talk about OData from a Windows Phone standpoint (specially WP7.5 or Mango); but also something that applies equally & is just as easily doable on other mobile OS platforms. Most well-designed mobile solutions these days also have a web interface that allows for easier data entry than on a phone form factor; yet data has to stay in sync whenever the user switches between platforms. A centralized data repository with OData feeds seems perfect for a case like this .. so, that’s what we shall talk about in this post.

After looking around a little bit on OData & WP7, I found a lot of examples on how to set up an OData source & consume the feeds from a Windows Phone app. This is obviously the first step & rather important as we shall see; but I also wanted to see if I could set up CRUD operations from WP7 back to the OData source. In the meantime, check out these few posts on how to consume OData in the first place:

  • Surely, one of the best ways to play with OData in real world is the Netflix catalog. Netflix worked with MSFT to expose their entire data set through an OData service (here) and now it becomes easy for various applications to consume the vast information in their catalog. This series of posts (here) covers how to build a Windows Phone client to browse through the Netflix catalog.
  • Michael Crump did this brilliant series on how to set up an OData source & consume it in Silverlight/Windows Phone. You may find it (here).
  • Post (here) from your’s truly on how to consume OData feeds from SQL Azure in a Windows Phone app.

Now all this is cool; but what if you wanted to update the OData source from your Windows Phone app? Isn’t that the promise of OData? Sure, we can do CRUD in Windows Phone 7.0; it just gets much easier in Mango. And the beauty of it is that it can be done completely without adding a service reference (proxy) to the OData service; simply HTTP Post with the correct request at the right URL. And this works the same way in iOS, Android or any darn platform .. that is where lies the nicety of OData.

For our data repository, lemme again chose SQL Azure, which gives us a nice way to expose our data globally through a simple OData feed. Please see my previous post (here) on how to set up a simple web application to reach out to create/manipulate data in SQL Azure & have it consumed in a Windows Phone app. Now, we really want the Windows Phone version to be able to update data records back in Azure & have it picked up seamlessly in the web application or any other consumer .. wouldn’t that be nice!

So, we start with a simple table called “Team” which is hosted in SQL Azure & has the data schema as below:

SQL Azure Team Table

The data in the table is then exposed out as an OData service with these steps:
  • Heading over to https://www.sqlazurelabs.com/ and logging in to the OData service with admin credentials.
  • Then, we select the DB & Table and check the OData checkbox.
  • For authentication, we select Anonymous Access so that anybody may access the data without security sign-ons. In real world, this is obviously not good practice. In essence however, Anonymous Access really uses the “dbo” or your admin credentials for SQL Azure operations. So, it should not be too difficult to create users for SQL Azure access & giving them appropriate access rights. This way, the OData service will require submission of security tokens (through ACS or some other federated authentication source) for data access/updates. This post (here) describes the intricacies of the Azure OData service wonderfully. Make sure you copy the OData URL as exposed by your SQL Azure instance.

Next, we talk about 3 ways in performing updates to the underlying SQL Azure database through the OData service. First, through our own hosted service in Azure. Here are the steps:
  1. Make sure you have the Azure VS SDK (get is from here). This ensures that we can create projects with the Azure wrapper so that our service may be hosted easily in Azure.
  2. File — New Project — Cloud — Windows Azure Project. This creates the Azure wrapper project. Let’s add an ASP.NET Web Application project to be hosted as Web Role.
  3. In the ASP.NET project, Add a new item of type ADO.NET Data Entity. This is essentially the ORM mapper that reflects on DB schema and gives us .NET object to play with. In the setup wizard, we make it use an Existing DB & point to the OData Service URL as exposed by our SQL Azure table. You will be required to enter your SQL Azure Admin DB credentials or some other user access details. This should build the proxies out of the OData metadata, and thus expose the service offerings to our project. For our “Team” table in SQL Azure exposed through OData, the data model looks like this:
  4. ADO.NET Data Entity

     

     

     

     

     

     

     

     

     

     

     

  5. Next, we add a WCF Data Service to the project which should expose out the data provided by the ADO.NET data service. The configuration of the SVC.cs file should look something like this:
  6. 
    using System;
    using System.Collections.Generic;
    using System.Data.Services;
    using System.Data.Services.Common;
    using System.Linq;
    using System.ServiceModel.Web;
    using System.Web;
    
    namespace WebRole1
    {
        public class DemoService : DataService
        {
            // This method is called only once to initialize service-wide policies.
            public static void InitializeService(DataServiceConfiguration config)
            {
                config.SetEntitySetAccessRule("Teams", EntitySetRights.All);            
                config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
            }
        }
    }
    
    

  7. Notice how we ask for building a collection of data objects of the same entity type as exposed by our ADO.NET data model. Also, we allow this service to have access rights of “All ” so that we may perform CRUD against the SQL Azure table at will.
  8. If everything goes well, we should be able to right-click on the .SVC file & do a “View in Browser“. This should get us to the same OData view as in Azure; you should be able to drill in & see all your data through OData filtering on browser URL.
  9. Local WCF Service exposing OData


     

  10. So, by now we have our own service ready & pointed to the SQL Azure OData service with user credentials configured as needed. Now, this service is ready for primetime hosting in Azure.
  11. We right click on the solution & choose “Publish” and create the packages locally in our bin directory. This makes two files — a .cspkg application wrapper & a .cscfg configuration file.
  12. We simply create a new Hosted Service in Azure and push out these files & swap their IP to be Production. The end state looks something like this:

WCF Service in Azure

That’s it! We now have our own configured WCF Service hosted in Azure. We can try hitting the Production SVC URL to make sure we have access to the same OData stream.

Now, let’s switch focus to our Windows Phone app. We can obviously read data out of the Azure OData feed pretty easily, as evident (here). What we want to be trying to do is to add records into the SQL Azure table from the Windows Phone app. Accordingly, we add a little UI for allowing data entry; end goal is something like this:

Team in WP7

Added Team Member through WP7

The way we go about doing this is by adding a Reference to our own WCF Service hosted in Azure, as below. Please note, that this will work only in Windows Phone 7.1 Mango tooling:

Add WCF Service Ref.

With the service reference under our belt, we now have a direct context of the OData service in Azure. Accordingly, we can have access to “Team ” collection objects in code & be able to manipulate them at will. In our case, we already have Sam & Jeff in the SQL Azure table; we want the phone app to add a new member Mike. Here’s some code:


using Demo;
using System.Data.Services.Client;

namespace SQLAzureOData_Demo
{
    using DemoService;

    public partial class MainPage : PhoneApplicationPage
    {
       private static DemoEntities wcfContext = null;

       private void InsertDataThruService()
        {
            wcfContext = new DemoEntities(new Uri("http://YourAzureServiceNDS.cloudapp.net/DemoService.svc/"));

            var TeamMember = Team.CreateTeam(3);
            TeamMember.Name = "Mike";
            TeamMember.TwitterHandle = "@michaelcollier";

            var collection = new DataServiceCollection(wcfContext);
            collection.Add(TeamMember);
            wcfContext.BeginSaveChanges(new AsyncCallback(SaveChangesCB), null);
        }

        private void SaveChangesCB(IAsyncResult asynchronousResult)
        {
            // Warning: You are on a different thread!
            // Success or Error Handling.
            Dispatcher.BeginInvoke(() =>
                {
                    MessageBox.Show("All done!");
                    LoadTeam();
                });
        }
     }
}

Just a few points to mention. On hitting the “+” App icon, I have a little UI pop-up that accepts the new Team members Name & TwitterHandle. The code sample above hard-codes the user; but you get the point that the UI needs to fire the “InsertDataThruService” method. Once we have the “WCFContext”, you can see how easy it gets to manipulate the data through any CRUD operations. Another important point to keep in mind is that the “BeginSaveChanges” call is asynchronous, this being Silverlight. So, the “SaveChangesCB” callback delegate does not fire on the same UI thread. This is important to keep in mind if you start wondering why the message pop-up or the refresh of the Team member list in our case, does not fire.

That’s it, few steps; but now we have a fully functional data repository that exposes OData feeds and can be read/updated from multiple platforms. This Service Reference way is just one way to do CRUD on an OData service though. In the next post, we talk about how to do this natively.

Hope this was helpful.

Adios!

Recently I had to work on a cross-platform mobile app, meant for internal use in our company. The iOS & Android versions were ready to ship; Windows Phone 7 just could not lag behind. It was mostly static content (lots of it) to display and centralization of data was important for content updates. Even before the Windows Phone app was started, much of the content was centralized for mobile device consumption and the format was HTML. Since it was mostly textual data with bulleted sections, I guess it was decided to marry pure data with some display pieces.

So, now the challenge was to design a Windows Phone 7 app that had rich metro UI; but mostly rendered HTML! The control of choice to display all this data was the WebBrowser control, which takes remote or local HTML and renders content using the IE shell in a Windows Phone. Thankfully, the content only had HTML snippets of data and not a full HTML DOM that the WebBrowser renders; so there was an opportunity to style some of the content to match the theme of the Windows Phone app. So, essentially, right before we hand-off the local HTML to our WebBrowser control, we got to give it some header/metedata or styling information.

I cannot post screenshots of the WP7 app for confidentiality; but here’s some code in use:


    private void webBrowserControl_Loaded(object sender, RoutedEventArgs e)
    {
       webBrowserControl.NavigateToString(FormatHTML("Local_HTML_Filepath"));
    }

    private string FormatHTML(string filePath)
    {
       string result = string.Empty;
       var ResourceStream = Application.GetResourceStream(new Uri(filePath, UriKind.Relative));

       if (ResourceStream != null)
       {
          Stream myFileStream = ResourceStream.Stream;

          if (myFileStream.CanRead)
          {
              using (StreamReader myStreamReader = new StreamReader(myFileStream))
              {
                  StringBuilder htmlText = new StringBuilder();
                  htmlText.Append("<html><meta name='viewport' content='width=400,user-scalable=no'/>");
                  htmlText.Append("<body bgcolor='ThemeColor'>");
                  htmlText.Append("<font color='SomeFontColor' size='SomeSize'>");
                  htmlText.Append("<link rel='stylesheet' type='text/css' href='someURI'>");
                  htmlText.Append(myStreamReader.ReadToEnd());
                  htmlText.Append("</font>");
                  htmlText.Append("</body>");
                  htmlText.Append("</html>");
                  result = htmlText.ToString();
               }
          }
       }

       return result;
     }

Now, before you start thinking this is all too basic, there are couple of very important caveats. First, the HTML rendered by the webbrowser control cannot have a transparent background ; so your hopes of preserving the theme of the underlying XAML page are futile. Also, any URI references that you make towards styling the content cannot come from local application package !! For example, if you have an image that you package as a resource/content in your Windows Phone project, it cannot be used as a background image for your HTML content. Needless to say, I find this restrictive and had to fight my way around this. If someone knows a way to use local resources for styling, please please drop me a comment.

So, essentially we have two options for referencing media or CSS for styling our HTML for the WebBrowser to render. One — make the resource available on the internet; that is, through an unique absolute HTTP URL. Or Two — store the resource as a file in Isolated Storage on the phone and then use the relative filepath from isolated storage in your CSS. These are the only two ways I know how to reference an external resource while styling HTML. Like I said, I would love to hear how other’s are doing this or any other ideas.

Adios!

This post is a continuation of Part 1 (here) & Part 2 (here) of the series on setting up a cloud-enabled Weather app for Windows Phone.

By now, we have a fully working Windows Phone app which allows the user to pull up weather forecasts for any city of their choice. We also Push Enabled the app so that any number of cities can be pinned to the start screen using the Mango secondary Live Tiles. Now, the only thing left to do is to have some sort of a service running in the cloud or some server that is able to keep track of all these cities the user has chosen to pin &  for the service to be able to push out live weather feeds at pre-defined/customized intervals. Now, for hosting such a service, I chose to utilize a free subscription I had in Azure; this could just as well be done on any machine running IIS with open ports.

Let’s add my thoughts here on the utilization of Azure cloud infrastructure for supplementing a mobile solution. As Windows Phone & other mobile platforms have shown us, smartphones do a lot with their small batteries & any work we can offload from it’s small processing power, helps in increasing battery life & hence user experience. As such, most connected Mobile solutions could compromise of a client running on the phone & some cloud support which does the repetitive heavy-lifting for the phone, sparing it from polling. Now, Windows Azure with its vast infrastructure for scalability could provide the perfect hosting solution for cloud service support; but there are some considerations. If you have MSDN subscription, you get some Azure processing & storage time free. After some benefit upgrades earlier this year, this Azure subscription can come in very handy, as you could really keep a couple of services with storage & decent bandwidth requirements running free 24/7 all month long. However, if your MSDN is paid for by your employer, and you are using it to host a cloud service that will empower your mobile app through which you make money, you are in shady territory :) So, my advice would be to check subscription & legal details to make sure you are safe. Another option is to flat-out pay for Azure services if you know your mobile app is that good & you will need the cloud support. My experience says that you need to be able to justify a $30 cost per month to be able to break even on Azure costs; be it through a paid app or advertisements. The $30 number though could fluctuate a lot based on what the needs are for your service hosting; it is simply based on my experience on running a simple service in Azure on 2 small instances & using some table storage.

So, with my babbling out of the way, here is the Azure hosted cloud service that acts as the backend for our Weather app, as it appears in the Azure portal. Now, this is set up to feed the Windows Phone Live Tiles; however, please take note that it can easily be set up to support iOS & Android Push Notifications. The Azure Toolkit for WP7 (here) already support iOS notifications, with Android support coming soon.

Azure WeatherLite Backend Service

The highlighted URL is where we get to hit up Azure to access the hosted service. This is what we had used to add a service reference for building proxies in our Windows Phone solution. With the metadata exposed, the phone app could call into any methods supported by the service. So, let’s see how we set up the cloud service. Here’s the project setup for the backend VS Solution:

WeatherBackend Project

WeatherBackend Project

 

 

 

 

 

 

 

 

 

 

 

 

So, our cloud backend solution is created using the Azure cloud solution template in VS & is made of 3 projects:

  • WeatherBackendCloud:
    This is the Azure wrapper project that includes the deliverables from the other two projects & provides for deployment settings for our service in the cloud.
  • WeatherBackendCloudSite:
    This is sort-off the web front-end of our service; essentially the WCF service is hosted inside of IIS as a web application. This project provides the “svc” WCF endpoint for our service and a 404 for everything else. Check out this wonderful video tutorial by Aaron Skonnard on how to set up your WCF service to run in IIS (here).
  • WeatherLiteBackend:
    This is our core WCF service that exposes the methods that our Windows Phone app calls into. This project includes the core processing of fetching Weather from Yahoo, packaging it up in a Push Notification payload & sending it out in an HTTP Post to the phone’s unique Channel URI in MPNS.

Now, about some implementation. The interface in our WCF project defines a standard for some methods exposed to consuming applications. The “Register” call that our phone app makes to let the cloud service know about Channel URI, city of choice & Secondary Live Tile ID has been defined as such:


    [ServiceContract]
    public interface IRegistrationService
    {        
        [OperationContract, WebGet]        
        void Register(string channelURI, int WOEID, string liveTileURI);
    }

The actual implementation of our Registration service simply hangs on to the request parameters from the phone, performs core processing to fetch weather & builds a payload for submitting to MPNS. Since this is just for demo purposes, I am storing the subscriber’s Channel URIs in session memory, since the Registration service has been defined as a “Singleton” class. For any cloud services headed for Production, you would have to consider storing the URI & other needed subscriber details in Azure table storage or SQL Azure. Here’s what I am doing:


    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class RegistrationService : IRegistrationService
    {
        public void Register(string channelURI, int woeid, string liveTileURI)
        {
           // Add subscriber details to some list.

           WeatherLiteBackendCore coreEngine = new WeatherLiteBackendCore();
           coreEngine.PerformCoreProcessing();
        }
    }

    public class WeatherLiteBackendCore
    {
        public void PerformCoreProcessing()
        {
            // Fetch current list of Subscribers.
            SubscriberList = RegistrationService.GetSubscribers();

            if (SubscriberList == null || SubscriberList.Count == 0)
                return;

            foreach (Subscriber WP7Client in SubscriberList)
            {
                // Pick up the URIs.
                subscriberChannelUri = new Uri(WP7Client.ChannelUri);
                subscriberLiveTileUri = new Uri(WP7Client.LiveTileUri);

                // Fetch weather for registered location.
                // Send out Tile update payload to MPNS.                
            }
        }
    }

Our “WeatherBackendCloudSite” simply sets up the service endpoint to use the “RegistrationService”; here’s the config in “Registration.svc”:


     <%@ ServiceHost Language="C#" Debug="true" Service="WeatherLiteBackend.RegistrationService" %>

Now, the “PerformCoreProcessing()” method is also invoked once every hour (set at application level) to get a list of all WP7 subscribers & send out appropriate live weather feeds to all. Also, the “WeatherLiteTileNotificationSender” helper class builds the requisite payloads required to send out the Live Tile updates. Please see my earlier post (here) on exactly what the payloads need to look like for the Windows Phone OS to process the incoming data bits as Live Tiles or Toast notifications. Here’s the old-school way of building a secondary Live Tile payload & submitting to MPNS for delivery to subscribing phones:


        private static byte[] PrepareTilePayload()
        {
            MemoryStream stream = new MemoryStream();
            XmlWriterSettings settings = new XmlWriterSettings() { Indent = true, Encoding = Encoding.UTF8 };
            XmlWriter writer = XmlTextWriter.Create(stream, settings);
            writer.WriteStartDocument();
            writer.WriteStartElement("wp", "Notification", "WPNotification");
            writer.WriteStartElement("wp", "Tile", "WPNotification");            
            writer.WriteStartAttribute("ID");
            writer.WriteValue(appropriateLiveTileURI);
            writer.WriteEndAttribute();
            writer.WriteStartElement("wp", "BackgroundImage", "WPNotification");
            writer.WriteValue(appropriatebackgroundImageUri);
            writer.WriteEndElement();
            writer.WriteStartElement("wp", "Count", "WPNotification");
            writer.WriteValue(appropriateTemp.ToString());
            writer.WriteEndElement();
            writer.WriteStartElement("wp", "BackTitle", "WPNotification");
            writer.WriteValue(appropriateCity);
            writer.WriteEndElement();
            writer.WriteStartElement("wp", "BackContent", "WPNotification");
            writer.WriteValue(appropriateWeatherCondition);
            writer.WriteEndElement();
            writer.WriteEndDocument();
            writer.Close();

            byte[] payload = stream.ToArray();
            return payload;
        }

        private void SendMessage(Uri channelUri, byte[] payload, NotificationType notificationType, SendNotificationToMPNSCompleted callback)
        {  
            try
            {
                // Create and initialize the request object.
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(channelUri);
                request.Method = WebRequestMethods.Http.Post;
                request.ContentType = "text/xml; charset=utf-8";
                request.ContentLength = payload.Length;
                request.Headers[MESSAGE_ID_HEADER] = Guid.NewGuid().ToString();
                request.Headers[NOTIFICATION_CLASS_HEADER] = ((int)notificationType).ToString();

                if (notificationType == NotificationType.Toast)
                    request.Headers[WINDOWSPHONE_TARGET_HEADER] = "toast";
                else if (notificationType == NotificationType.Token)
                    request.Headers[WINDOWSPHONE_TARGET_HEADER] = "token";

                request.BeginGetRequestStream((ar) =>
                {                   
                    Stream requestStream = request.EndGetRequestStream(ar);
                   
                    requestStream.BeginWrite(payload, 0, payload.Length, (iar) =>
                    {                      
                        requestStream.EndWrite(iar);
                        requestStream.Close();
                        
                        request.BeginGetResponse((iarr) =>
                        {
                            using (WebResponse response = request.EndGetResponse(iarr))
                            {                               
                                // Give back response to callback method, if any.
                            }
                        },
                        null);
                    },
                    null);
                },
                null);
            }
            catch (WebException ex)
            {
                // Do something.
            }
        }

That’s about it with the code. Once everything compiles & works with locally hosted service, we simply right-click on the cloud wrapper project & allow it to create Azure packages locally. This generates two files in the Bin/Publish directory of the Azure solution — one .cscfg file containing configuration & one .cspkg package container containing the deployable deliverables of the project. Now, we simply create a new hosted solution in Azure & upload these two files into a Production environment to expose the cloud service endpoint for our Windows Phone application. We then have a service running in Azure that the phone application can reach out to if the user chooses to pin a city’s weather to start screen & one that pushes out live weather feeds to the secondary Live Tiles. Voila!

Hope this series was helpful. Please drop comments if you see something that could be done better or have any other thoughts.

Adios!

This post is a continuation of Part 1 of the series (here).

So, we now have a basic Weather app that allows the user to look up live weather & a little forecast for his/her selected city. But, what good is that, unless we can pin the app to the start screen & then get live Weather feeds for the selected city, without having to launch the app! Now, sure we can enable that for Windows Phone using regular techniques of Push Notifications & Live Tiles. However, every time I show off something like that, the next obvious question is — What if I live in City A & work in City B and I want live weather updates for both cities? Or, if you are like me, you always keep Hawaii weather pinned so you feel good & look forward to your vacation :) Now, there is something we could possibly work out using the Windows Phone 7.0 OS & toolsets. May be our cloud service could take in multiple cities & push out live weather for all cities in cycles? But, the weather app on the phone still has one pinned Live Tile and it is kinda cumbersome to display so much changing information in one Tile. Same problem crops up if you want to follow two live flights or numerous other situations. Hmmm…

Thankfully, Windows Phone Mango update has the near-perfect way of solving this issue. In addition to the pinned Application Tile (which is still just one & done manually by the user), we can now have multiple secondary Tiles for each app. These tiles are just like pinned Live Tiles on the start screen, but only better since they are created & controlled programmatically from the app itself! The windows phone team heard some feedback about the pain to stand up a service just to update a Live Tile when the app itself knows all the data; so now we can! And the best part — just as the Secondary Live Tile name suggests, each of these secondary Tiles behaves exactly like Application Tiles, have a unique URL identifier and most importantly, can be updated from the cloud using MPNS!! Voila .. now we can have multiple parts of the application pinning themselves to the start screen and receiving Live Tile updates through Push Notifications. This helps in situations like following weather of multiple cities, following multiple live flights or pinning various news sections for a news app. More detailed information about Application & Secondary Live Tiles can be found (here).

Apart from the programmatic control over secondary Live Tiles, the tiles themselves convey a little more information. In addition to the usual front of the Tile (with count, title & background image), the Tiles also flip to expose a back side of the tile. This has the potential for carrying some back-content text, title & another background image. So, information conveyed by the start screen Tiles can literally be doubled! And the OS makes sure the secondary tiles pinned from the same app don’t do their flip animations at the same time .. nice little trick.

So, back to our weather app. Now, each city the user cares about can be pinned to the start screen, providing live weather feeds that are easy to follow, each being separate. Our little implementation looks like this:

Secondary Live WX Tiles with Temp

Secondary Live WX Tiles with Temp

Secondary Live WX Tiles with Conditions

Secondary Live WX Tiles with Conditions

 

 

 

 

 

 

 

 

 

 

 

 

You will notice that with the extra real estate offered by the back of the tile, we were able to split up the weather information: current weather image & temperature in the tile front and current conditions & city name in the back of the tile. You can, off course, design your secondary live tiles to carry any information just the way you want. Here is some code to get the above working on clicking the “pin to start” menu as below:

Pin to Start Menu

Pin to Start Menu


   // Check the existence of pinned tile.
   var shellTile = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("WOEID=" + this.currentWOEID));

   if (shellTile != null)
   {
       return;
   }

   // Create the WX tile data with appropriate properties.
   var tileData = new StandardTileData
   {                
       BackgroundImage = this.currentWxImage,
       BackTitle = this.currentCity,
       Count = this.currentTemp,                
       BackContent = this.currentWxCondition
   };

   // Create the secondary tile on start screen.
   ShellTile.Create(new Uri("/MainPage.xaml?WOEID=" + this.currentWOEID, UriKind.Relative), tileData); 

With the above code firing, our app is immediately deactivated & the user is taken to the start screen to see the secondary Live Tile. Now, every city that the user cares to store & check weather for is represented by an unique WOEID (as explained in Part 1 here). Now, for every secondary Live Tile that is pinned, there has to be a unique way to identify the Tile; so why not simply use the city’s unique WOEID for the tile as well?

You will notice that as we created the ShellTile programmatically, we gave it a unique URL — “/MainPage.xaml?WOEID=something”. This serves a nice dual purpose. One, it identifies each secondary Live Tile uniquely. And since we can check the existence of secondary tiles programmatically, we get to tweak our app accordingly. For example, if we find that a city is already pinned to the start (proven by the existence of a secondary Live Tile with the same WOEID parameter), our menu option should possibly say “unpin from start” so the user may delete following that city.

Another awesome effect of uniquely identifying our Tiles with URL parameters is that it gives us a way to respond specifically when the user launches our app by hitting one of these Live Tiles. If the user has 2 cities pinned to the start screen, launching the app by hitting the Live Tile for City A should take the user directly into weather details for City A, right? Yep, we can do that because the URL parameters are passed directly into the app as we navigate back to the URL-linked page. This provides the developer with a way to respond to page events and may be take appropriate action based on the URL parameters coming in. For example, in our case, the MainPage.xaml is the weather UI for a given city. Much like the page_load events in the ASP.NET webforms world, we get to respond and take unique actions based on incoming parameters. Here’s some code:


        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            if (NavigationContext.QueryString.ContainsKey("WOEID"))
            {
                if ((App.Current as WeatherLiteForMango.App).LocationList.Count > 0)
                {
                    foreach (App.LocationStuff existingLoc in (App.Current as WeatherLiteForMango.App).LocationList)
                    {
                        if (existingLoc.WOEID == NavigationContext.QueryString["WOEID"])
                        {
                            locationNumber = (App.Current as WeatherLiteForMango.App).LocationList.IndexOf(existingLoc);
                            break;
                        }
                    }

                    // Fetch WX data & fill.
                    this.GetYahooWeatherAsync(Convert.ToInt32((App.Current as WeatherLiteForMango.App).LocationList[locationNumber].WOEID));
                }
            }
            else
            {
                if ((App.Current as WeatherLiteForMango.App).LocationList.Count > 0)
                {
                    // Load the last location in list.
                    locationNumber = (App.Current as WeatherLiteForMango.App).LocationList.Count - 1;

                    // Fetch WX data & fill.
                    this.GetYahooWeatherAsync(Convert.ToInt32((App.Current as WeatherLiteForMango.App).LocationList[locationNumber].WOEID));
                }
                else
                {
                    // Ask user to enter new locations.
                }
            }
        }

So, you see how we take the user directly to a city’s weather if coming from a secondary Live Tile or just load the last visited city’s weather if we have any. Pretty cool right? I can think of so many instances where such direct deep-linking is going to induce rich user experience.

Now, all these secondary Live Tiles will not be of much use, unless they relayed live weather updates from some cloud service. Thankfully, even in Mango, the way we subscribe to Live Tile notifications has not changed much. We use the same techniques to either find a unique MPNS channel or create/save one if needed. Here’s some code to handle channels for Push Notification subscriptions:


        // Try to find existing Channel from Isolated Storage or create new one.
        if (!this.TryFindChannel())
           this.DoConnect();

        private bool TryFindChannel()
        {
            bool gotcha = false;

            // Look for saved Channels in Isolated Storage.
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {                
                if (isf.FileExists(somePredefinedFileName))
                {                    
                    using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(somePredefinedFileName, FileMode.Open, isf))
                    {
                        using (StreamReader sr = new StreamReader(isfs))
                        {
                            string uri = sr.ReadLine();
                            httpChannel = HttpNotificationChannel.Find(somePredefinedChannelName);

                            if (null != httpChannel)
                            {
                                if (httpChannel.ChannelUri.ToString() == uri)
                                {
                                    // If Channel is found, make necessary Subscriptions
                                    this.SubscribeToChannelEvents();
                                    this.SubscribeToNotifications();
                                    this.SubscribeToService();
                                    gotcha = true;
                                }
                                sr.Close();
                            }
                        }
                    }
                }               
            }

            return gotcha;
        }

        private void DoConnect()
        {          
            // First, try to pick up existing channel.
            httpChannel = HttpNotificationChannel.Find(somePredefinedChannelName);

            if (null != httpChannel)
            {
                this.SubscribeToChannelEvents();
                this.SubscribeToService();
                this.SubscribeToNotifications();
            }
            else
            {
                // Create the new channel.
                httpChannel = new HttpNotificationChannel(somePredefinedChannelName, "WeatherLiteBackendService");

                this.SubscribeToChannelEvents();
                httpChannel.Open();
            }           
        }


        private void SaveChannelInfo()
        {
            // Save off Channel Info into Isolated Storage as a file.
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {            
                using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(somePredefinedFileName, FileMode.Create, isf))
                {
                    using (StreamWriter sw = new StreamWriter(isfs))
                    {                        
                        sw.WriteLine(httpChannel.ChannelUri.ToString());
                        sw.Close();                        
                    }
                }
            }
        }

And, if we did end up creating a new MPNS channel, we wait for the “httpChannel_ChannelUriUpdated” event so that we subscribe to a few things with the channel URI. Notice that the same API — BindToShellTile(), that we had used before Mango can now be utilized to subscribe all the secondary Live Tiles to receiving Push Notifications. This now essentially tells the OS — Hey, be ready to receive any Tile updates for this app! Here’s some code:


        private void SubscribeToChannelEvents()
        {
            // Register to UriUpdated event - occurs when channel successfully opens.
            httpChannel.ChannelUriUpdated += new EventHandler(httpChannel_ChannelUriUpdated);
        }   

        void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
        {
            // Once the client has successfully created a channel with MPNS, save & subscribe.
            Dispatcher.BeginInvoke(() => this.SaveChannelInfo());           
            this.SubscribeToService();
            this.SubscribeToNotifications();
        }     

        private void SubscribeToNotifications()
        {
            // Bind to Tile Notificaions.            
            try
            {
                if (httpChannel.IsShellTileBound != true)
                {
                    httpChannel.BindToShellTile();
                }
            }
            catch (Exception)
            {
                // Do stuff.
            }
        }

Now, I need to mention something we shall cover in the next post — our cloud service backend running in Azure. For now, let us just assume that we have a service running somewhere which is willing to accept requests from our app & push out live weather updates to our secondary Live Tiles. The cloud service is referenced within our phone client project so that we get the proxy made & can make method calls against this service. Here’s the simple set-up:

Weather Project

Weather Project

 

 

 

 

 

 

 

 

 

 

Having access to our cloud service’s methods, we can now easily let the service know the details it needs to push our live weather updates to MPNS, and eventually to the phone client as Live Tiles. One important thing to remember is that our cloud service needs to know exactly which Live Tile to update for each city! So, in addition the channel URI & the city’s WOEID, we also need to let our service know about the unique URL that identifies the secondary Live Tile we want the service to update.


        private void SubscribeToService()
        {
            // Reference to backend service.
            RegistrationServiceClient client = new RegistrationServiceClient();            
            client.RegisterAsync(httpChannel.ChannelUri.ToString(), this.currentWOEID, this.currentLiveTileURI.ToString());
        }

That’s it! Now, our secondary Live Tile can stay happily pinned to start screen and our cloud service should wake up from time to time (based on how you configure it) and send out Live Tile updates with latest weather. Not too difficult; just a few moving pieces. Hope this was helpful.

Adios!

June 2010. I decide that one of the first WP7 apps I should do is surely a weather app. And with the whole “It’s not Sunny & 73” jab against the iPhone weather app, one just had to have cloud support for “glance & go” weather, right? :) . Then, after Windows Phone launched, I took one look at the Weather Channel app. Oops! Off course, mine didn’t stand a chance against their rich UI! So, I never quite bothered submitting the weather app; but I still use it to demo Push Notifications from the cloud. So, that is what we shall do in this post; remember though, this was before we had the Push Notification Helpers & the Azure Toolkit. So, this will all be down to rather basic details; sometimes it may be worth knowing how stuff works just out of the box.

So, we shall strive for a really basic Weather app that shows current/forecast weather for a bunch of places that the user is interested in. There are off course several free sources of weather data; but few get as simple as the Yahoo API. So, let us head over (here) for the Yahoo Weather feed. It essentially a semi-RESTful API that we make a HTTP GET call against, passing in a WOEID & unit of measurement. The Where On Earth ID (WOEID) is simply a unique indicator of the Lat-Long of a place on earth. If you look up the weather of any city on Yahoo, the WOEID is in the URL or it can also be derived from geo-coding an address.

Here is a basic look at what we are striving to build:

Add City

Weather

 

 

 

 

 

 

 

 

 

 

 

 

So initially, we ask the user what city or town they want weather for. We fire off the user’s search criteria to Yahoo’s service to come back with a scrollable list of locations with State & Country information. We bind this return data to a Listbox and allow the user to pick the right one; not fail-proof, but you get the idea. Here is what we want to hang on to for each city the user cares about; and this is defined in our global App.xaml.cs file:


        public class LocationStuff
        {
            public string City { get; set; }
            public string State { get; set; }
            public string Country { get; set; }
            public string WOEID { get; set; }
        }

Armed with this, we can go ahead & request possible city matches from Yahoo’s GeoPlanet API & parse the response before binding to our XAML UI:


        // Registering for the API is needed. 
        string yahooGeoServiceURL = "http://where.yahooapis.com/v1/places.q('" + user-typed-city + "');start=0;count=5?appid='Your Developer ID'";

        var client = new WebClient();
        client.DownloadStringCompleted += ClientDownloadStringCompleted;
        client.DownloadStringAsync(new Uri(yahooGeoServiceURL, UriKind.Absolute));

        private void ClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            string possibleLocationsXML = e.Result;

            if (possibleLocationsXML != string.Empty)
            {
                // LINQ to XML.
                XDocument xmlDoc = XDocument.Parse(possibleLocationsXML);

                var locationInfo = from xElem in xmlDoc.Descendants(XName.Get("place", "http://where.yahooapis.com/v1/schema.rng"))
                                   select new App.LocationStuff
                                   {
                                       City = xElem.Element("{http://where.yahooapis.com/v1/schema.rng}name").Value,
                                       State = xElem.Element("{http://where.yahooapis.com/v1/schema.rng}admin1").Value,
                                       Country = xElem.Element("{http://where.yahooapis.com/v1/schema.rng}country").Value,
                                       WOEID = xElem.Element("{http://where.yahooapis.com/v1/schema.rng}woeid").Value
                                   };

                foreach (var stuff in locationInfo)
                {
                    App.LocationStuff newLocation = new App.LocationStuff();
                    newLocation.City = stuff.City;
                    newLocation.State = stuff.State;
                    newLocation.Country = stuff.Country;
                    newLocation.WOEID = stuff.WOEID;

                    SomeLocationListToBindTo.Add(newLocation);
                }
            }
         }

So, once the user selects the city they want weather for, we have it’s WOEID available right away to fetch latest weather from Yahoo. To pull off parsing for weather, we define two more little classes:


        public class StuffWeCareAbout
        {
            public string City { get; set; }
            public string State { get; set; }
            public string Condition { get; set; }
            public int ConditionCode { get; set; }
            public string Temp { get; set; }
            public string Description { get; set; }
            public string ImageURL { get; set; }
            public IEnumerable Forecasts { get; set; }
        }

        public class Forecast
        {
            public string Day { get; set; }
            public string High { get; set; }
            public string Low { get; set; }
            public int ConditionCode { get; set; }
        }

The actual GET call to fetch weather & it’s subsequent parsing is as follows. Please note that Yahoo returns weather icons as GIFs, which Silverlight cannot render out of the box; so I have a set of weather icons locally (from Weather.com) & simply map the appropriate icons based on weather condition.


        // WebClient request at this URL.
        string yahooWXServiceURL = "http://weather.yahooapis.com/forecastrss?w=" + currentWOEID;

        private void ClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            string weatherRSS = e.Result.Replace("yweather:", "yweather");
            
            if (weatherRSS != string.Empty)
            {                
                // LINQ to XML parsing.
                XDocument xmlDoc = XDocument.Parse(weatherRSS);

                // Notice the nested LINQ for repeating forecasts.
                var weatherInfo = from xElem in xmlDoc.Descendants("channel")
                                  select new StuffWeCareAbout
                                  {
                                      City = xElem.Element("yweatherlocation").Attribute("city").Value,
                                      State = xElem.Element("yweatherlocation").Attribute("region").Value,
                                      Condition = xElem.Element("item").Element("yweathercondition").Attribute("text").Value,
                                      ConditionCode = Convert.ToInt16(xElem.Element("item").Element("yweathercondition").Attribute("code").Value),
                                      Temp = xElem.Element("item").Element("yweathercondition").Attribute("temp").Value,
                                      Description = xElem.Element("item").Element("description").Value,
                                      Forecasts = from xSubElement in xElem.Element("item").Elements("yweatherforecast")
                                                  select new Forecast
                                                  {
                                                      Day = xSubElement.Attribute("day").Value,
                                                      High = xSubElement.Attribute("high").Value,
                                                      Low = xSubElement.Attribute("low").Value,
                                                      ConditionCode = Convert.ToInt16(xSubElement.Attribute("code").Value)
                                                  }
                                  };


                foreach (var stuff in weatherInfo)
                {
                    // UI binding & Weather Icon selection here.
                }
             }
          }

Also, as the user adds more cities that he/she wants weather for, the Application Bar navigation buttons are supposed to allow them to cycle through them by going left/right. However, a button is inconvenient, right? Why not just swipe to right/left for the next/previous city? Yup, that makes sense and needs our app to support flick gestures. There are a few ways to achieve this; following was just my way of doing things:


        // Reference the XNA library in the project.    
        using Microsoft.Xna.Framework.Input.Touch;

        // Wire up the top-most layout grid to watch out for Manipulation events.
        private void LayoutRoot_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
        {
            // Read gestures if available.
            while (TouchPanel.IsGestureAvailable)
            {
                // Read the current gesture.
                GestureSample gesture = TouchPanel.ReadGesture();

                // Only act on flicks. 
                if (gesture.GestureType == GestureType.Flick)
                {
                    float startX = gesture.Position.X;
                    float endX = gesture.Delta.X;

                    if (startX > endX)
                    {
                        // Flick to the left.
                        if (GlobalLocationList.Count > currentLocationNumber + 1)
                        {
                            // Load next city.
                            currentLocationNumber ++;
                        }
                    }
                    else if (startX < endX)
                        {
                            // Flick to the right.
                            // Load previous city.
                            currentLocationNumber --;
                        }
                    }
                }
             }
         }

That’s it with our phone app UI. I skipped showing some XAML since it can be entirely customized with how you want the weather UI to look like; so it is this easy to add some weather data to your WP7 apps. Hope this was interesting. Please stay tuned as we look at Live Tiles & Push Notifications from the cloud in upcoming posts.

Adios!