Ramp Up Your Mobile Development Skills

Ramp Up Your Mobile Development Skills

Looking for a quick way to get the basics of Windows Mobile development? Try the RampUp “Develop Windows Mobile 6 Applications” e-learning.  Whilst this focuses on Windows Mobile 6 using VS2008, a lot of the points covered actually apply to all versions of the Windows Mobile platform.  Topics include the following:

Level 1: Mobile Development Introduction
Level 2: Device Emulators
Level 3: Mobile Windows Forms Development
Level 4: Advanced Mobile Windows Forms Development
Level 5: SQL Server CE Introduction
Level 6: Security and Deployment
Level 7: Mobile Web Development

Office Developer Conference

Office Developer Conference

In case you hadn’t heard or had somehow forgotten, let me remind you that the Office Dev Con is on this weekend in Brisbane. Thanks to Graham and everyone else involved in making this happen – I feel honoured to be amongst the presenters.  Full information on sessions and the presenters is available at http://www.officedevcon.com.au. Here’s my two sessions:

Title: Where’s my runtime? A tale of a mobile database engine.

One of the challenges of building rich client applications is often storing data. Whether this be caching data to improve look up performance or data entered by the user that needs to be persisted until a data connection is available, the dilemma is always what technology should you use? With only a small amount of data the answer is often a simple XML file but as the quantity of data, or in fact the complexity of the data, grows it becomes inevitable that you will want some sort of database in which to store your data.

In this session you’ll learn all about SQL Server Compact Edition (CE), a client side database technology that you can ship alongside your application. Unlike other database technologies SQL Server Compact doesn’t need to be installed and thus doesn’t require elevated privileges, making it ideal for both Office and ClickOnce applications. The session will give a brief overview of the technology, before diving into how you can use, configure, deploy and manage your databases.

Title: Document creation in the cloud with Windows Azure and the OpenXML SDK.

Since the release of Office 2007 there has been considerable work put into the Open XML SDK. Unlike previous versions of Word that relied on interop assemblies to do automation, with the new XML-based file formats you can automate document creation as and when you need to. In fact, with the Open XML SDK there is no reliance on Word, Excel or any other Office component. This makes it particularly useful for building services on the Windows Azure platform. This session will provide an overview of Windows Azure and the Open XML SDK, as well as showing how you can easily combine the two to build high volume, scalable document automation services hosted in the cloud.

Good and Bad news about WCF for Windows Mobile

Good and Bad news about WCF for Windows Mobile

Given this post primarily contains a rant about WCF support within the .NET Compact Framework (ie Windows Mobile), I’ll start with the good points.

  • Further to a previous post on WCF for Windows Mobile the team at Microsoft have released an updated version of NetCFSvcUtil.exe that fixes the issue running under Vista SP2 and Windows 7.  You can download it directly from their blog post on the NetCFSvcUtil QFE (great way to ship updates – an obscure blog that no-on.
  • There is a great whitepaper put together by a couple of MVPs talking about using WCF from the .NET Compact Framework: http://wcfguidanceformobile.codeplex.com/.

Ok, so now for the bad news.

  • If you’re thinking about using Basic Authentication, you can forget WCF.  There is no support for nearly any practical scenario for using WCF unless you want to roll out X509 certificates.  Honestly, whoever didn’t think that Basic Auth over SSL would be good to support…..
  • If you want to share types between client and server, think again. It appears that the /reference and /excludeType flags don’t work as they do with svcutil.exe which means a lot of messing around if you want to share types.
  • No built in support within Visual Studio for doing “Add Service Reference”

If you’re like me and spend most of your time in Visual Studio the last point is a real kick in the head – you mean I have to run NetCFSvcUtil from the commandline, Urgh!  At this point I went “forget it – too hard, too broken, not worth the pain” and went back to “Add Web Reference” (which btw works for WCF endpoints that are configured with basicHttpBinding).  This works well with both basic authentication and SSL so suits my scenario.

Now the issue I have with “Add Web Reference” is that it does silly things with certain types.  For example Guids get converted to string.  It also means that if you have two services (eg ServiceA.svc and ServiceB.svc that use the same type (eg Customer) you will end up with multiple types created on the client (eg ServiceA.Customer and ServiceB.Customer). The solution to this lies within the References.cs file that gets generated – this file contains all the types that get created as part of creating the proxy.  If you remove all the additional generated classes (eg ServiceA.Customer).  This will of course cause compile errors because the referenced types can’t be resolve.  To fix this, add a reference to the assembly that contains the type and then add a using statement to the reference.cs to import the missing types.

One last point – In order for this to work you will probably have to annotate your shared types with xml attributes. For example:

[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://myschemas/DataTransferObjects")]
public class Customer
{

I do wish that Microsoft would do something about the dire state of affair around WCF from .NET Compact Framework, but in the meantime I’d still with Add Web Reference.

Integrating Twitter into your Windows Mobile Widget

Integrating Twitter into your Windows Mobile Widget

I guess the first question that comes to mind is why would you want to integrate twitter into your widget. Until recently I would have agreed that it seems somewhat superfluous as there is an almost infinite number of Twitter clients out there, all claiming that they are better than all that have gone before them. However, I was recently trying to add a mechanism for commenting on items within a widget I was writing.  Now I could have easily built yet another commenting system, involving the usual candidates of sql server backend, wrapped in a simple REST or SOAP service blah blah blah, but I came to the conclusion this would simply add to the overhead of managing the application, particularly since up until now the widget didn’t rely on any backend services. I figured since a lot of users would probably have Twitter accounts I could just let them use their account in order to make comments. The fact that their comments would be seen in their Twitter feed would just add some free advertising for my widget.


So, having chosen Twitter to handle comments I then had to work out the best way to integrate them. If you’ve used any of the current generation of Twitter clients out there you will be familiar with having to enter your username and password into the application – this is bad as you don’t really have any idea what else that application may be doing with your credentials. Of course, they would argue that they need to prompt for this information so that they can post on your behalf. Up until recently they would have been correct. However, Twitter now supports OAuth and in particular a permutation specifically designed to support desktop (or rich client) applications. In a nutshell this involves routing the user to Twitter, getting them to sign in there and then routing back to the calling application with a suitable authorisation token.  For the desktop permutation there is an additional step which involves copying a one-time pin from Twitter into the calling application – this is because there is no way for Twitter to redirect back to your desktop application. This process can be used for integrating Twitter into your widget.


Now for the technical bits:


Step 1: Registration



  • You need to register your application with Twitter.  This can be done at http://twitter.com/oauth_clients.  Once registered you will be given a Consumer Key and Consumer Secret that you will need to take note of for use within your widget.

Step 2: Request Token



  • The first step in OAuth is to get a request token.  This is done by issuing a POST to http://twitter.com/oauth/request_token, supplying the consumer key you have been allocated, a timestamp and nonce value. The request also needs to contain a SHA1 signature.

  • The return values (if successful) is a token and a token secret.

Step 3: Direct User to Twitter to Sign In



  • Once your widget has the request token and associated secret, you need to direct the user to Twitter.com so that they can log in and authorise your widget.  To do this, direct them to “http://twitter.com/oauth/authorize?oauth_token= + Token + & oauth_callback=oob”

  • The user will log in and then be given an authorisation pin.  They need to copy this and paste it (or type it) into your widget – you’ll need to give them a mechanism for doing this!

Step 4: Access Token



  • After the user has entered the authorisation pin you need to use it, coupled with the consumer key and request token to issue a POST to http://twitter.com/oauth/access_token.

  • This will return an access token that you can then use to post updates to Twitter.

Step 5: Post Updates



Acknowledgments to John Kristian who contributed a Javascript Library for OAuth.  Other OAuth libraries are available for a variety of languages at http://oauth.net/code.


Full TwitterAPI Listing


Feel free to use this code.  Note however that there is almost no error handling so that it’s easier to follow what’s going on.



var TwitterAPI = function() {
    return {
        ConsumerSecret: “<enter Consumer Secret from Twitter app registration>”,
        ConsumerKey: “<enter Consumer Key from Twitter app registration>”,
        SignatureMethod: “HMAC-SHA1”,


        RegisterUser: function(callback, errorcallback) {


            var accessor = { consumerSecret: TwitterAPI.ConsumerSecret
                           , tokenSecret: “”
            };
            var message = { action: “
http://twitter.com/oauth/request_token”
                  , method: “POST”
                  , parameters: []
            };
            message.parameters.push([“oauth_consumer_key”, TwitterAPI.ConsumerKey]);
            message.parameters.push([“oauth_signature_method”, TwitterAPI.SignatureMethod]);
            message.parameters.push([“oauth_timestamp”, “”]);
            message.parameters.push([“oauth_nonce”, “”]);
            message.parameters.push([“oauth_signature”, “”]);
            OAuth.setTimestampAndNonce(message);
            OAuth.SignatureMethod.sign(message, accessor);
            var parameterMap = OAuth.getParameterMap(message.parameters);


            var params = TwitterAPI.buildParameters(parameterMap);


            var request = WidgetAPI.createXmlHttpRequest();
            request.open(message.method, message.action, true);
            request.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”);
            request.setRequestHeader(“Content-length”, params.length);
            request.setRequestHeader(“Connection”, “close”);


            // Set the callback
            request.onreadystatechange = function() {
                if (request.readyState == 4) {


                    var params2 = request.responseText.split(“&”);
                    for (var i = 0; i < params2.length; i++) {
                        var bits = params2[ i ].split(“=”);
                        if (bits.length == 2 && bits[0] == “oauth_token”) {
                            TwitterAPI.Token = bits[1];
                        }
                        if (bits.length == 2 && bits[0] == “oauth_token_secret”) {
                            TwitterAPI.TokenSecret = bits[1];
                        }
                    }


                    if (!TwitterAPI.Token || TwitterAPI.Token == null) {
                        if (errorcallback) {
                            errorcallback();
                        }
                        return;
                    }


                    callback();


                    var url = “http://twitter.com/oauth/authorize?oauth_token=” + TwitterAPI.Token + “&oauth_callback=oob”;
                    window.open(url);
                    request = null;
                }
            };


            request.send(params);
        },
        AuthenticateUser: function(pin, callback, errorcallback) {
            TwitterAPI.Pin = pin;
            var accessor = { consumerSecret: TwitterAPI.ConsumerSecret
                           , tokenSecret: TwitterAPI.TokenSecret
            };
            var message = { action: “
http://twitter.com/oauth/access_token”
                  , method: “POST”
                  , parameters: []
            };
            message.parameters.push([“oauth_consumer_key”, TwitterAPI.ConsumerKey]);
            message.parameters.push([“oauth_token”, TwitterAPI.Token]);
            message.parameters.push([“oauth_signature_method”, TwitterAPI.SignatureMethod]);
            message.parameters.push([“oauth_verifier”, TwitterAPI.Pin]);
            message.parameters.push([“oauth_timestamp”, “”]);
            message.parameters.push([“oauth_nonce”, “”]);
            message.parameters.push([“oauth_signature”, “”]);
            OAuth.setTimestampAndNonce(message);
            OAuth.SignatureMethod.sign(message, accessor);
            var parameterMap = OAuth.getParameterMap(message.parameters);


            var params = TwitterAPI.buildParameters(parameterMap);


            var request = WidgetAPI.createXmlHttpRequest();
            request.open(message.method, message.action, true);
            request.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”);
            request.setRequestHeader(“Content-length”, params.length);
            request.setRequestHeader(“Connection”, “close”);


            // Set the callback
            request.onreadystatechange = function() {
                if (request.readyState == 4) {
                    var params2 = request.responseText.split(“&”);
                    for (var i = 0; i < params2.length; i++) {
                        var bits = params2[ i ].split(“=”);
                        if (bits.length == 2 && bits[0] == “oauth_token”) {
                            TwitterAPI.Token = bits[1];
                        }
                        if (bits.length == 2 && bits[0] == “oauth_token_secret”) {
                            TwitterAPI.TokenSecret = bits[1];
                        }
                    }


                    if (params2<2 || !TwitterAPI.Token || TwitterAPI.Token == null) {
                        errorcallback();
                        return;
                    }


                    callback();
                }
            };


            request.send(params);
        },
        PostComment: function(comment, callback, errorcallback) {
            accessor = { consumerSecret: TwitterAPI.ConsumerSecret
                   , tokenSecret: TwitterAPI.TokenSecret
            };
            message = { action: “
http://twitter.com/statuses/update.json”
          , method: “POST”
          , parameters: []
            };
            message.parameters.push([“oauth_consumer_key”, TwitterAPI.ConsumerKey]);
            message.parameters.push([“oauth_token”, TwitterAPI.Token]);
            message.parameters.push([“status”, comment]);
            message.parameters.push([“oauth_version”, “1.0”]);
            message.parameters.push([“oauth_signature_method”, TwitterAPI.SignatureMethod]);
            message.parameters.push([“oauth_timestamp”, “”]);
            message.parameters.push([“oauth_nonce”, “”]);
            message.parameters.push([“oauth_signature”, “”]);


            accessor.tokenSecret = TwitterAPI.TokenSecret;
            OAuth.setTimestampAndNonce(message);
            OAuth.SignatureMethod.sign(message, accessor);
            var parameterMap = OAuth.getParameterMap(message.parameters);


            message.action = “http://twitter.com/statuses/update.json”;
            params = TwitterAPI.buildParameters(parameterMap);
            //params += “&status=test”;
            request = WidgetAPI.createXmlHttpRequest();
            request.open(message.method, message.action, true);
            request.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”);
            request.setRequestHeader(“Content-length”, params.length);
            //request.setRequestHeader(“Connection”, “close”);


            // Set the callback
            request.onreadystatechange = function() {
                if (request.readyState == 4) {
                    callback();
                }
            }
            request.send(params);
        },
        buildParameters: function(parameterMap) {
            var params = “”;
            for (var p in parameterMap) {
                if (params.length > 0) {
                    params += “&”;
                }
                params += OAuth.percentEncode(p) + “=” + OAuth.percentEncode(parameterMap[p]) + “”;
            }
            return params;
        }
    };
} ();


var Url = {


    // public method for url encoding
    encode: function(string) {
        return escape(this._utf8_encode(string));
    },


    // public method for url decoding
    decode: function(string) {
        return this._utf8_decode(unescape(string));
    },


    // private method for UTF-8 encoding
    _utf8_encode: function(string) {
        string = string.replace(/rn/g, “n”);
        var utftext = “”;


        for (var n = 0; n < string.length; n++) {


            var c = string.charCodeAt(n);


            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }


        }


        return utftext;
    },


    // private method for UTF-8 decoding
    _utf8_decode: function(utftext) {
        var string = “”;
        var i = 0;
        var c = c1 = c2 = 0;


        while (i < utftext.length) {


            c = utftext.charCodeAt(i);


            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if ((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i + 1);
                c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }


        }


        return string;
    }


}

Your Phone, My Phone, they’re all Windows Phones….

Your Phone, My Phone, they’re all Windows Phones….

Well that should be the Microsoft chant with the simultaneous release of Windows Mobile 6.5, Marketplace and a host of complementary applications such as an updated MyPhone that’s available for download via Marketplace. If you’re like me, one of the cool new “premium” features of MyPhone is the ability find your lost phone.  Here I’m showing the “known location” feature but you can get MyPhone to ring your phone, even if it’s on vibrate.  I so want that feature for when I lose my phone (no pun intended….)

image

More Windows Mobile 6.5 builds

More Windows Mobile 6.5 builds

Well almost on the eve of the public availability of Windows Mobile 6.5 I thought I’d again post about some of the new builds that I’ve been using. Over the past week or so I’ve been running build 53052 and today I’ve upgraded to build 53420. There aren’t any huge improvements over my previous post however the polish is really starting to show.  In fact over the past week or so I would confidently say that I wouldn’t consider moving away from Windows Mobile and that for my day to day life it’s easier to use than an iphone. Anyhow, here are a few screenshots showing the polish as it continues to improve:

image image

imageimage

image image