This article was written and submitted by an external developer. The Google Desktop Team thanks Yannick Stucki for his time and expertise.
"My gadget has so little space to display all this information! -- Use details
views to get more."
"Wow, playing the latest YouTube video in my gadget would be so
cool!"
Figure 1: An opened details view
Gadgets are typically designed to be small-sized applications on a user's desktop, sidebar or even their iGoogle homepage. The sidebar especially limits the size of gadgets, yet sometimes a gadget developer would like to display more content. This can be easily achieved by using details views. The details view has been in Google Desktop since version 2. Over time, the functionality has improved, and we can display content in newer, better ways.
There are two different kinds of details view: the HTML details view and XML details view. In this article, I am going to introduce you to both of them.
An XML details view (Figure 1) displays content the same way it would in the main gadget view.
A details view is defined in a details.xml
, which is similar to the main.xml
.
Both files use the same XML markup as shown in the following example of an empty
details.xml
:
<view height="348" width="427" onopen="detailsView_onOpen()" > <script src="details.js" /> </view>
With the script
tag, you can add as many script files to the details view as you
want, but it is recommended to have a separate file for details view specific code as we've done
here (details.js
).
detailsView_onOpen()
is
triggered when the details view opens.
Nobody wants an empty details view, so fill this XML file with all the tags you would normally use in a gadget.
The easiest way to get started is to load the gadget in the Designer and visually edit details.xml
.
Now that you've filled the details view with buttons, lists, and images, you're probably wondering how to open and display this details view at runtime. First, we need to create a details view object:
var detailsView = new DetailsView(); detailsView.SetContent( "", // Item's displayed website/news source. -> not used for an XML details view undefined, // Time created "details.xml", // The XML file false, // Whether time is shown as absolute time 0); // Content layout flags
After setting up the details view object, you can show it like this:
plugin.showDetailsView(detailsView, "", gddDetailsViewFlagNone, onDetailsViewFeedback); function onDetailsViewFeedback(detailsViewFlags) { //This function is called when there is feedback from the details view }
The following flags could be passed for the third parameter in the showDetailsView function:
gddDetailsViewFlagNone
gddDetailsViewFlagToolbarOpen
gddDetailsViewFlagNegativeFeedback
gddDetailsViewFlagRemoveButton
gddDetailsViewFlagShareWithButton
To close an opened details view, call this:
plugin.CloseDetailsView();
NOTE: there can only be one details view shown at a time (HTML or XML). Once you open a new one, the old one gets automatically closed. There is no need to close the old one beforehand.
You now know how to set up, open, and close an XML details view. The next step is to add dynamic content to the details view. You might want to fill a label with specific text or show different pictures.
Fortunately, there is an easy way to send data to and from the details view. Let's assume I had fetched data about a movie:
var showtimes; var rating; var duration; var title;
This might be a lot of information to display in a gadget (especially if there are several movies), so I might want to display them in a details view whenever someone clicks on a movie from a list.
To send this information to a details view, run the following lines before opening it:
detailsView.detailsViewData.putValue("showtimes", showtimes); detailsView.detailsViewData.putValue("rating", rating); detailsView.detailsViewData.putValue("duration", duration); detailsView.detailsViewData.putValue("title", title);
To extract the data, put the following lines into the details.js
file:
function detailsView_onOpen(){ showtimes=detailsViewData.getValue("showtimes"); rating=detailsViewData.getValue("rating"); duration=detailsViewData.getValue("duration"); title=detailsViewData.getValue("title"); }
Now you can access those variables from inside the details view. Cleaner code might make use of Object Oriented Programming (See "Object-oriented Programming Principles in Gadgets" for more information). So instead of sending those four variables individually, you could do this:
var movie; movie.showtimes=showtimes; movie.rating=rating; movie.duration=duration; movie.title=title; detailsView.detailsViewData.putValue("movie", movie);
function detailsView_onOpen(){ movie=detailsViewData.getValue("movie"); //use it, e.g: label1.innerText=movie.duration; }
Now that you've learned how to send data to the details view, you might be
interested in knowing how to send data from the details view to the main gadget.
We earlier set up a function onDetailsViewFeedback
that is called by the details view when it has something to report.
One example of this feedback, is notification that the the details view is closed.
Clicking the titlebar or remove buttons also trigger feedback events.
So let's assume you had opened a
details view with the flag gddDetailsViewFlagRemoveButton
instead of
gddDetailsViewFlagNone
. The following code listens for two events:
the closing and the removal of the details view.
function onDetailsViewFeedback(detailsViewFlags) { if (detailsViewFlags == gddDetailsViewFlagNone) { // The user closed the details view. Do something here if you have to. } else if (detailsViewFlags == gddDetailsViewFlagRemoveButton) { // The user clicked on the remove button. Remove this element from the list. he doesn't want it. } }
We seen a lot of code snippets so far. Let's regroup and show all the snippets as they would appear in an actual gadget:
main.js
function callDetailsView(){ var detailsView = new DetailsView(); detailsView.SetContent("",undefined,"details.xml",false,0); detailsView.detailsViewData.putValue("anyKeyWord", anyVariableOrObject); plugin.showDetailsView(detailsView, "", gddDetailsViewFlagRemoveButton, onDetailsViewFeedback); } function onDetailsViewFeedback(detailsViewFlags) { if (detailsViewFlags == gddDetailsViewFlagNone) { // The user closed the details view. Do something here if you have to. } else if (detailsViewFlags == gddDetailsViewFlagRemoveButton) { // The user clicked on the remove button. Remove this element from the list. he doesn't want it. } }
details.js
function detailsView_onOpen(){ var something=detailsViewData.getValue("andKeyWord"); }
Instead of having an XML details view, you can also have a details view that is filled with HTML content. The HtmlDetailsView sample in the Google Desktop SDK is a good example to learn from, especially if you compare it to the XmlDetailsView sample (also in the SDK).
HTML may be an appealing option for those who are familiar with it, but in my opinion using XML is
easier because you can use the Designer for its development.
However, the HTML details view also has its advantages.
One example is playing a YouTube video.
The code example below is lengthy, but try to focus on the bold lines.
You need to call showYoutube
whenever you want to play a video.
Furthermore, the the other bold lines highlight differences between an XML implementation.
var htmlDetailsView=null; showYoutube("http://www.youtube.com/v/yavHaoKAJaI", "The Kingdom Trailer"); function showYoutube(url,title) { var snippet = "<object id=\"movie\" width=\"100%\" height=\"100%\"><param name=\"movie\" " + "value=\"" +url + "&autoplay=1\"></param><embed " + "src=\"" + url + "&autoplay=1\" name=\"movie\" type=\"application/x-shockwave-flash\" width=\"100%\" " + "height=\"100%\" bgcolor=\"#000000\"></embed></object>"; var detailsHTML = "<html><head><script>window.external.window = window;</script><style type=\"text/css\"> body{font-family:arial,sans-serif; font-size: 10px; margin-left: 3px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px;}</style></head><body bgcolor=\"#000000\" text=\"#FFFFFF\" link=\#FFFF00\ vlink=\#FFCC99\ alink=\#FFFF00\ scroll=\"no\">"; detailsHTML += snippet; detailsHTML += "<br /><br /></body></html>"; plugin.closeDetailsView(); htmlDetailsView = new DetailsView(); htmlDetailsView.html_content = true; htmlDetailsView.setContent("", undefined, detailsHTML, false, 0); htmlDetailsView.external = {}; // Show the details view plugin.showDetailsView(htmlDetailsView, title, gddDetailsViewFlagNone, onHtmlDetailsViewFeedback); } function onHtmlDetailsViewFeedback(detailsViewFlags) { // Remove the movie from DOM to make it stop playing var window = htmlDetailsView.external.window; var movie = window.document.getElementById("movie"); if (movie != null) movie.parentNode.removeChild(movie); if (detailsViewFlags == gddDetailsViewFlagNone) { // User closed the details view } else if (detailsViewFlags == gddDetailsViewFlagToolbarOpen) { // User clicked on the title of the details view var winShell = new ActiveXObject("Shell.Application"); winShell.ShellExecute(linkArray[index]); winShell = null; } htmlDetailsView=null; CollectGarbage(); }
One cool thing about that player is that you can resize the window (drag it at the edges) and the video screen will adjust to fill the window.
Figure 2: YouTube movie in a details view.
If you want to play YouTube videos, you obviously need a URL to the video. For video searches, Google Video provides better results. That's why we employ a trick here and search YouTube videos via Google Video:
fetchYoutube("The Kingdom trailer") function fetchYoutube(name){ var youtubeHttp; youtubeHttp= new XMLHttpRequest(); youtubeHttp.onreadystatechange = function(){OnReceivedYouTube(name,youtubeHttp);}; youtubeHttp.open("GET", "http://video.google.com/videofeed?type=search&q=site%3Awww.youtube.com+"+name+"&so=0&num=20&output=rss", true); youtubeHttp.send(); } function OnReceivedYouTube(name,youtubeHttp){ if (youtubeHttp.readyState == 4) { var doc = new DOMDocument(); doc.loadXML(youtubeHttp.responseText); Tags = doc.getElementsByTagName("item"); if (Tags == null || Tags.length <= 0) return; var swf; for (var node = Tags[0].firstChild; node != null; node = node.nextSibling) { if (node.nodeName == "link"){ swf = node.text; swf=swf.substring(swf.indexOf("www.youtube.com%2Fwatch%3Fv%3D")+30,swf.length); swf=swf.substring(0,swf.indexOf("&")); swf="http://www.youtube.com/v/"+swf; break; } } //swf now is the url we want showYoutube(swf,name); youtubeHttp=null; } }
Unfortunately, there is no direct way to define an HTML details view's size.
However, the size of an HTML details view is equal to the size of the last details
view opened, including XML details views. If you have a gadget that uses HTML details view, quickly
open and close an invisible (opacity=0
) XML details view set to a particular size beforehand.
Before you incorporate all this code into your own gadget, please be aware of
this: a user may be annoyed if gadgets behave unexpectedly.
For example, if you have a list of items, and clicking on an item opens a details view,
the user expects that re-clicking the item closes the details view.
To implement this, you could have a variable currentlyOpen
that always stores the
name of the item that is open at the moment or is empty if no details view is
open. If a user clicks an item,
check to see if it's the same as what's in
currentlyOpen
and
then close the current details view instead of opening a new one.
If you want more information
about this subject, I recommend that you read
Meeting User Expectations,
where this technique is mentioned with many other ones that should improve
your gadget.
I hope this article showed you how to deliver useful content to your users or entertain them with cool videos. Unfortunately, details views aren't yet fully documented in the API reference, but here are some resources that might be helpful:
Of course you can also do more than just display YouTube videos with an HTML details view. Tinker around with it, and if you need help you can always ask your question in the developer group.
I am currently a student and started programming a few years ago. It's always tough to produce complete programs yourself (that are actually used by others) because as a hobbyist programmer, you just don't have the time and resources of a fully-staffed team. That's why I love gadgets programming: gadgets range from very simple to fairly complex, but most importantly, you can get them out there and have people actually using them.
Besides programming, I love playing Rugby. And in winter, I go snowboarding in the Swiss Alps.
You can read more about my gadgets on my homepage. There you'll also find contact information if you need more help or have suggestions.
This work is licensed under a
Creative
Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.