My favorites | English | Sign in

Google Desktop APIs (Labs)

Alerting Users About Updates to your Desktop Gadgets

This article was written and submitted by an external developer. The Google Desktop Team thanks Glenn Rivkees for his time and expertise.


Glenn Rivkees, Gadget Developer
January 2008

Introduction

No piece of software will ever be perfect. There is always an improvement that could be made after it is released. The problem many developers encounter is how to tell users of these updates. In this article you will learn how to do exactly that in a Desktop gadget.

Making latest version information available

In order for update checks to work, the latest version number needs to be available to the gadget in a computer readable form. For this we will create an RSS feed called latestversion.xml:

<?xml version="1.0"?>
<rss version="2.0">
<channel>
<link>URL_TO_THIS_FEED</link>
<title>GADGET_NAME Latest Version</title>
<description>LATEST_VERSION_NUMBER</description>

  <item>
  <title>Changes in version LATEST_VERSION_NUMBER: </title>
  <description>CHANGES_IN_LATEST_VERSION</description>
  <link>URL_TO_DOWNLOAD_PAGE</link>

  </item>
</channel>
</rss>

Customize this file with the following (all content must be properly escaped):

  • URL_TO_THIS_FEED: Eventual URL to this feed.
  • GADGET_NAME: The name of your gadget.
  • LATEST_VERSION_NUMBER: The latest version number of your gadget. Here we follow the X.X.X.X convention used in Desktop gadgets.
  • CHANGES_IN_LATEST_VERSION: What is new in this version? Helps to convince users to update. Lengthy text can be broken up with line breaks.
  • URL_TO_DOWNLOAD_PAGE: URL to a page where the user can download the gadget. Alternatively, it can be a direct URL to the .gg file.

Of course the feed must be valid XML and really should be valid RSS. It's highly recommended that you run it through a validator after you're done.

This file must now be hosted in one fixed location. Make sure that this is indeed a permanent location. If the file moves, the gadget will no longer be able to find it to determine the latest version information. For our examples, we will use a location of http://example.com/latestversion.xml.

Downloading and parsing the version information

I suggest you put all the code related to updates in a separate JavaScript file called update.js. While this is not necessary, it makes your code cleaner and easier to work with.

Below is the portion of update.js that downloads and parses latestversion.xml.

// CONFIGURATION - EDIT FOLLOWING:
var latestversionxmlURL="http://example.com/latestversion.xml"
var gadget_version_number="1.0.0.0";

function checkupdate(manualrequest)
{
  //load xml code
  var updatexmlDoc = new ActiveXObject("Microsoft.XMLDOM");
  function updateloadXML(uxmlFile) 
  {
    updatexmlDoc.async="false";
    updatexmlDoc.onreadystatechange=verify;
    updatexmlDoc.load(uxmlFile);
    updatexmlObj=updatexmlDoc.documentElement;
  }

  function verify() 
  {
    if (updatexmlDoc.readyState != 4) 
    {
      return false;
    }
  }

  //try to load latestversion.xml and parse file
  try 
  {
    updateloadXML(latestversionxmlURL);
    //set xml version info to variables
    latestVis=updatexmlObj.childNodes(0).childNodes(1).text
    changesinvertitle = updatexmlObj.childNodes(0).childNodes(2).childNodes(0).text
    changesinverdesc = updatexmlObj.childNodes(0).childNodes(2).childNodes(1).text
    downloadurl = updatexmlObj.childNodes(0).childNodes(2).childNodes(2).text
    //
    //This is where we will later put the code to alert the user of an update.
    //
  } 
  catch (e) 
  {
    // if anything above raised an error (downloading or parsing), only alert user if it was a manual, user-initiated request. 
    // this will be explained later. 
    if (manualrequest) 
    {
      alert("Error checking for updates. Please check your network connection.")
    }
  }
}

The only things you need to change are the variables under //CONFIGURATION - EDIT FOLLOWING:.

  • latestversionxmlURL: The URL to the latestversion.xml feed.
  • gadget_version_number: The current version of your gadget. When you release a new version, be sure to update this value in the feed.

Conveying update information to the user

Now that we are able to retrieve the version information, we also need some way to inform the user of updates. The simplest way is using alerts. These can be quite effective for grabbing the user's attention, but many people find this distracting. With a little effort, we can instead create a 'dialog' within the gadget. We'll see examples of both below.

Before we look at the code, you should know the parsed version data is stored in the following variables:

  • latestVis: The latest version number.
  • changesinverdesc: Changes in the new version.
  • downloadurl: Download URL.

Using alert

Here's an implementation using alert:

Notifying user of updates using alert method.
Figure 1: Notifying user of updates using alert method.
if (gadget_version_number != latestVis) 
{
  //IF THERE IS A NEW VERSION TELL THE USER
  alert("A newer version of \""+GADGET_NAME+"\" is available.\nIt is recommended that you update.\n\n" + 
  "You currently have version "+gadget_version_number +
        " and the latest version is "+latestVis+".\n\n"+changesinvertitle+"\n"+changesinverdesc)
  //ask if they want to download
  if (confirm('Do you wish to go to the gadget download website?')) 
  {
    var winShell = new ActiveXObject("Shell.Application");
    winShell.ShellExecute(downloadurl); 
  }
}
//if there is no update, and the user has manually asked tell them
else if(manualrequest) //Once again, this will be explained later.
{
  alert("You have the latest version.\n\nYou currently have version "+gadget_version_number +
        " and the latest version is "+latestVis+".")
}

Here, we are first checking to see if the user has the latest version. If they do not, we alert them (Figure 1) and ask them if they would like to download the latest version. If there is no update, and the user has manually requested the update check, we tell them that they are running the latest version.

Using appendElement to simulate a dialog

Notifying user of updates using appendElement.
Figure 2: Notifying user of updates using appendElement.

Here is a slightly more complicated, but better looking alternative:

if(gadget_version_number!=latestVis)
{
  //IF THERE IS A NEW VERSION TELL THE USER
  view.appendElement('<div height="150" name="updatediv" width="318" background="#00CCEE"/>');
  updatediv.appendElement('<label height="27" width="307" x="6" y="6" size="8" wordwrap="true">A newer version of "'+GADGET_NAME+'" is available. It is recommended that you update.</label>');
  updatediv.appendElement('<label height="29" width="307" x="6" y="38" size="8" wordwrap="true">You currently have version '+gadget_version_number+' and the latest version is '+latestVis+'.</label>');
  updatediv.appendElement('<label height="16" width="200" x="41" y="68" size="8">'+changesinvertitle+'</label>');
  updatediv.appendElement('<edit height="55" width="137" x="40" y="86" size="8" background="#00CCEE" multiline="true" readonly="true" value="'+changesinverdesc+'" />');
  updatediv.appendElement('<a height="16" width="64" x="246" y="100" href="'+downloadurl+'">Download</a>');
  updatediv.appendElement('<button height="22" width="64" x="234" y="126" onclick="view.removeElement(updatediv);" downImage="stock_images\\button_down.png" image="stock_images\\button_up.png" overImage="stock_images\\button_over.png" caption="Close"/>');
// END TELL THE USER
}
//if there is no update, and the user has manually asked tell them
else if(manualrequest) //Once again, this will be explained later.
{
  view.appendElement('<div height="80" name="updatediv" width="318" background="#00CCEE"/>');
  updatediv.appendElement('<label height="27" width="307" x="6" y="6" size="8" wordwrap="true">You have the latest version.</label>');
  updatediv.appendElement('<label height="29" width="307" x="6" y="20" size="8" wordwrap="true">You currently have version '+gadget_version_number+' and the latest version is '+latestVis+'.</label>');
  updatediv.appendElement('<button height="22" width="64" x="6" y="45" onclick="view.removeElement(updatediv);" downImage="stock_images\\button_down.png" image="stock_images\\button_up.png" overImage="stock_images\\button_over.png" caption="Close"/>');
}

This method uses the appendElement function to create a div and labels inside of it filled with the update information. If you are going to use this code, you should also change the catch{} clause to do something like this:

catch (e)
{
  if(manualrequest)
  {
    view.appendElement('<div height="60" name="updatediv" width="318" background="#00CCEE"/>');
    updatediv.appendElement('<label height="27" width="307" x="6" y="6" size="8" wordwrap="true">Error checking for updates. Please check your network connection.</label>');
    updatediv.appendElement('<button height="22" width="64" x="6" y="30" onclick="view.removeElement(updatediv);" downImage="stock_images\\button_down.png" image="stock_images\\button_up.png" overImage="stock_images\\button_over.png" caption="Close"/>');
  }
}

Running the update check

Now that we have the mechanism to convey the version update alert to the user, all we have to do is run the update check.

Using view.onopen

When you created your gadget in the Gadget Designer, it added a view.onopen handler for you. To check, look under the view object for the name of the onopen handler. You can set this to the function view_onOpen() if it is not set. Add a call to checkupdate in the handler:

function view_onOpen() 
{
  //Check for update when the gadget is open.
  checkupdate(false);
}

Your gadget will now automatically check for updates when it is opened. In addition to this, you should also add a button in the gadget menu as described in the next section.

Using the gadget menu

Adding menu items is fully explained in this article. I will give you the guts of what is necessary. Add the following to your onopen handler:


// Set up menu management handler.
pluginHelper.onAddCustomMenuItems = onAddCustomMenuItems;

This is the code that actually adds the menu items:

//---
//add menu item
//---
function onAddCustomMenuItems(menu) 
{
  // Adds custom menu item
  menu.AddItem("Check for Update", 0, runcheckupdate);
  //runs the check update function with manualrequest=true
  function runcheckupdate() { checkupdate(true) }
}

Conclusion

And that's it! Now you can continue to improve your gadgets long after they have been released without worrying about how your users will know about new versions. For a bare-bones sample implementation of this gadget, check out the resources section below.

Resources

Example Gadget Screenshot
Figure 3: Example Gadget Screenshot

A bare-bones implementation of what is described in this article:

Helpful Google sites:

Other helpful sites:

Author Bio

Glenn Rivkees

I am a student in high school who was bored one day and found the Google Desktop API. I decided it would be tons of fun to make a gadget, so I did, and it was. I find the best way to learn a language is just to use it. My most popular gadgets are Am I Blocked?, gPod Controller, and Quotes of Truthiness. Visit my site at http://glennrivkees.com.


Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.