2010-04-12

Providing for mobile web site visitors

When I'm using a mobile or an iPod Touch the web sites that detect my device and show me a mobile friendly page are more likely to be added to my favourites. For example Digg has a mobile version of their site which you are automatically taken to.

You can detect a mobile device by looking at the HTTP User Agent and Accept headers. Here are these two headers for an Android phone as captured in my application log:
INFO: Mozilla/5.0 (Linux; U; Android 1.5; en-gb; T-Mobile_G2_Touch Build/CUPCAKE) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1
INFO: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5,application/youtube-client
and for an iPod Touch:
INFO: Mozilla/5.0 (iPod; U; CPU iPhone OS 3_1_3 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7E18 Safari/528.16
INFO: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Detecting all the many and varied mobile devices out there would be a real pain without the help of MobileESP. This project supports multiple languages but I'm using Java on Google App Engine so all I needed to do was place the single java source file in my project.

I then wrote a servlet to handle requests to the home URL which redirects to the normal or mobile version of the site like so:
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
    String userAgent = req.getHeader("User-Agent");
    String accept = req.getHeader("Accept"); 
     
    UAgentInfo info = new UAgentInfo(userAgent, accept);
       
    boolean iPhone = info.detectTierIphone();
          
    if (iPhone) {
        res.sendRedirect("/Mobile.html");
    } else {
        res.sendRedirect("/Normal.html");
    }
} 
This code simply passes the headers to the UAgentInfo object and then detects iPhone like devices. The Javadoc for detectTierIphone() says:
The quick way to detect for a tier of devices. This method detects for devices which can display iPhone-optimized web content. Includes iPhone, iPod Touch, Android, Palm WebOS, etc.

2010-03-18

Using UserPref with URL content type gadgets

Following on from my previous post I needed to add a user preference to my gadget. By following the documentation here my previous example changed to be something like this:
<?xml version="1.0" encoding="UTF-8"?>
<Module> 
      <ModulePrefs 
            height="100" 
            title="My Simple Gadget" 
            description="Test gadget" 
            author="Anonymous" 
            author_email="anonymous+gg@gmail.com"/>
      <UserPref 
            name="profileId" 
            display_name="Profile ID" 
            datatype="string" 
            urlparam="id" 
            required="true"/> 
      <Content 
            type="url" 
            href="http://yourappid.appspot.com/gadget.html" /> 
</Module>

Note that I used the urlparam attribute so that the querystring used to fetch the gadget content would be something like
http://yourappid.appspot.com/gadget.html?id=123.

My GWT code could now read this userpref like this:
public void onModuleLoad() {
    String id = Window.Location.getParameter("id");
    . . . .

Unfortunately there is a minor issue with this. It seems that some gadget containers ignore the urlparam attribute. When the gadget is hosted in the iGoogle home page it works fine but in Blogger and some other places the id parameter was not coming through in the querystring. Instead you get something like like
http://yourappid.appspot.com/gadget.html?up_profileId=123
Basically the UserPref name prefixed with up_. This meant a minor change to the GWT code:
public void onModuleLoad() {
    String id = Window.Location.getParameter("id");
    if (id == null) {
        id = Window.Location.getParameter("up_profileId"); 
    }
    . . . .

UPDATE:
It seems that Google have broken the URL type gadgets. The parameters are no longer sent in the URL to the server! See this iGoogle Developer blog post

This makes the above code rather useless. I'll be working on a new version...

UPDATE 2:
According to this forum post the problem has been fixed temporarily. I think the advice is to use HTML content type gadgets if you want to use UserPrefs. Shame.

UPDATE 3:
URL type gadgets are back for good! See this post. To summarize:
I've been talking with developers on the iGoogle team about the best way to go forward here and it looks like keeping UserPrefs in the query string for type url gadgets is the thing to do.

2010-02-27

Simple Google Gadget on GAE

I've just found how easy it is to create Google Gadgets and host them on App Engine. All you need to do is create a simple page in your App Engine application to be the content of the gadget and then host a URL content type gadget specification that points to that page.

Your Gadget specification will look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<Module> 
      <ModulePrefs 
            height="100" 
            title="My Simple Gadget" 
            description="Test gadget" 
            author="Anonymous" 
            author_email="anonymous+gg@gmail.com"/> 
      <Content 
            type="url" 
            href="http://yourappid.appspot.com/gadget.html" /> 
</Module>

In my case I'm using a very simple GWT application. Make sure that it looks good at 300 pixels wide. The height can vary but don't make it too tall because people are less likely to install it if it takes up too much space.

The URL content type approach allows you to make a normal web page using any tools you like and you can debug it like any normal page.

To put it all together simply save your gadget spec into the war directory of your GAE application and deploy it so it's available on a URL like this: http://yourappid.appspot.com/gadgetspec.xml

Now you can test it in the iGoogle Sandbox which (after you've signed up) allows you to test unpublished gadgets. Add this developer gadget to make life easy.

When you're happy with it submit your gadget and you're done.