This article was written and submitted by an external developer. The Google Desktop Team thanks Glenn Rivkees for his time and expertise.
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.
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.
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.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.Here's an implementation using alert
:
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.
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 label
s 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"/>'); } }
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.
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.
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) } }
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.
A bare-bones implementation of what is described in this article:
Helpful Google sites:
Other helpful sites:
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.
This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.