Symbian OS
read last symbian news on www.newlc.com read last symbian reviews on www.newlc.com
read last symbian tutorial on www.newlc.com read last symbian download on www.newlc.com
15 nov. 2007 - 10:11
Keywords :

Web Run-Time and introduction to widgets

S60's OSS web browser is a revolution on the use of Internet by users with mobile devices. It allows you to browse the same web pages accessible by desktop computer browsers. With features like: thumbnails, visual page history, mini-map, zooming and text search, the user experience has become much richer and interactive. Image 1 shows Nokia N95's web browser visiting a normal web page, www.s60.com

image001.jpg
Image 1: Nokia N95's web browser in action

S60's browser new functionality, Web Runtime (WRT), allows you to develop application that use standard Web technologies (such as: XHTML, CSS, JavaScript), which are called widgets. Using widgets, developers will be able to offer their users the experience of their web services accessible through a single click from S60's applications menu. In the next sections, let's get into more details on the powerful world of S60's WRT Widgets.

Widgets – concepts and components

Widgets are basically applications that can be developed with Web tecnologies, packaged and installed on devices, have an icon and a name associated to them and therefore be first class citizens on S60's environment, allowing users to start them up from the applications menu, besides allowing for the normal cycle of installation, updating and removal common to other S60 applications. It's not necessary that the user open the browser, type in the URL of your web service, wait for all the HTML pages, CSS and JavaScript to load and then start using it. Everything is pre-loaded and packaged with the widget, allowing it for a smooth experience on the use of web applications.

To build a widget, a developer needs to use the following components, shown on Image 2:



widget_components.jpg

Image 2: Main S60 WRT widget components

Let's now get in detail about each and every component of a widget:

  • XHTML: Extensible Hypertext Text Markup Language, provides the same text markup functionality as HTML, but adapted to the XML syntax. Initially XHTML (as its cousin HTML) were used for both mark-up and formatting of text and data; however, creation of CSS made the separation of content and formatting clearer, and today XHTML is used for its original purpose: to tell the browser how to display a page: what is the title, what is a header, which data should be displayed in tables, etc.
  • CSS: Cascading Style Sheets, technology created to move formatting out of HTML. That allows for the same set of data presented as HTML have different formatting views, by only changing the style sheet associated to it, without having to modify the underlying data.
  • JavaScript: JavaScript is the heart of a widget; it provides the programming language with which the data and their formatting can be dynamically updated, loaded from the Internet, modified according to the device's browser features, and so on. JavaScript's role is to allow data presented by HTML and formatted with CSS to be dynamic and interactive and can be modified by the users to show them productive and useful information.

Besides components shown on Image 2, which are the core elements of widgets, two more elements are needed so that widgets be complete and can be installed on devices:

  • Info.plist: this is the widget's descriptor file. It contains information about the widget, such as: name, internal name, version, main html page, and some others..
  • Icon: as a widget is installed the same way as a native S60 application, it also needs an icon to be shown on the applications menu. Image 3 shows some icons from widgets installed on an S60 device.:

image003.jpg
Image 3: Icons of installed widgets on an S60 device

As widgets are composed of standard Web technologies, you can use nearly any authoring tool to write your HTML, CSS and JavaScript codes, and any browser to test them (given that you resize the screen to fit the target device's). However, for cases like:

  • Using of additional, S60 APIs (Sysinfo, Menu and Widget)
  • Testing with the correct screen size and orientation
  • Packaging and installation testing
  • Widget icon testing

It is highly recommended that you download the SDK (Software Development Kits) on which the WRT environment is enabled. At the time of this writing, the correct SDK is S60 3rd Edition SDK for Symbian OS, supporting Feature Pack 2, for C++, Beta, available for free download at Forum Nokia tools page

After downloading file S60_3rd_Ed_SDK_FP2_Beta_b.zip, unzip it and install the SDK clicking "setup.exe". After installation, you can execute the emulator by clicking “Programs > S60 Developer Tools > 3rd Edition FP2 SDK > C++, Beta > Emulator”. Image 4 shows the emulator being executed:

image004.jpg
Image 4: S60 3rd. Edition Feature Pack 2 for C++ emulator being executed

Creating the components

As we've mentioned before, a widget's componetes are:: (X)HTML, CSS, JavaScript, o Info.plist file e an icon. Supported versions of each tecnology are:

  • HTML 4.01, XHTML 1.0 (basic and mobile profile)
  • CSS Level 2 revision 1 (CSS 2.1)
  • JavaScript 1.5 (ECMA-262 3rd Edition)
  • DOM Level 2

To begin development, our first step will be creation of a folder anywhere in your disk, called "HelloWorld". This will be the root folder for our project.

Now let's develop the HTML page, which will be the main screen for our widget. Clique on the HelloWorld folder, and within it create a file called index.html. Listing 1 shows us the HTML code to be used in this file:

Listing 1. index.html code

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
        <title>Hello World</title>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
        <link rel="stylesheet" type="text/css" href="helloworld.css" />
        <script type="text/javascript" src="helloworld.js"></script>
</head>

<body id="body">
        <div id="greeting">
                <div id="greetingText">Hello World!<br/>
                <a class="link" href="javascript:doAsking()">Click me</a>
                </div>
            </div>
   
            <div id="asking">
                <div id="askingText">How are you doing?<br/>
                <a class="link" href="javascript:doGreeting()">Click me</a>
                </div>
        </div>
        <div>
                <input type="button" value="Asking" onClick="doAsking();" />
                <input type="button" value="Greeting" onClick="doGreeting();" />
        </div>
</body>
</html>

Some important comments about the HTML code:

  • Both JavaScript code and CSS (helloworld.css) code are loaded from external files, helloworld.js and helloworld.css respectively. This enforces the separation between code, data, structure and formatting, what allows for flexibility when the layout and the code are changed: the data doesn't need to be changed.
  • You can notice the presence of several <di> tags exist, each one with its components, which generally call JavaScript components, which are used to update the HTML interface as we'll see ahead.
  • One limitation of widgets is that only one HTML page can exist, therefore all interface manipulation, visibility or not of certain components, HTML code, among others, need to be done within the same HTML page, as there's no possibility of linking to another page, as there would not be the possibility of coming back to the previous widget page.

Having commented about HTML code, let's now go to the exciting, dynamic and interactive part of our widget: JavaScript code, which is responsible for most user interaction and all UI updating. Createa a new file, called helloworld.js, within our HelloWorld project folder, and type the code shown on Listing 2:

Listing 2. helloworld.js JavaScript code

//Menu item constants

CMD_ASK = 10;
CMD_GREET = 11;
CMD_CURSOR = 12;
CMD_TAB = 13;

window.onload = setup;

// Creating a menu
function setup()
{

        mItemAsk = new MenuItem("Asking", CMD_ASK);
        mItemGreet = new MenuItem("Greeting", CMD_GREET);
        mItemCursor = new MenuItem("Cursor mode", CMD_CURSOR);
        mItemTab = new MenuItem("Tab mode", CMD_TAB);

        mItemCursor.onSelect = menuItemSelected;
        mItemTab.onSelect = menuItemSelected;
        mItemAsk.onSelect = menuItemSelected;
        mItemGreet.onSelect = menuItemSelected;

        window.menu.append(mItemTab);
        window.menu.append(mItemCursor);
        window.menu.append(mItemGreet);
        window.menu.append(mItemAsk);
}

// handling menu events
function menuItemSelected(id)
{
        switch (id)
        {
        case CMD_ASK:
                doAsking();
                break;
        case CMD_GREET:
                doGreeting();
                break;
        case CMD_CURSOR:
                widget.setNavigationEnabled(true);
                break;
        case CMD_TAB:
                widget.setNavigationEnabled(false);
                break;
        default:
                break;
        }
}

// change view and display text
function doAsking()
{
        var greeting = document.getElementById("greeting");
        var asking = document.getElementById("asking");

        if (window.widget)
               // freezes the widget and prepares it for flipping
               widget.prepareForTransition("fade");                       
        greeting.style.display="none";                // hide the greeting view
        asking.style.display="block";                // show the asking view
        document.getElementById("body").style.
         backgroundColor = document.defaultView.getComputedStyle(asking, null).
                        getPropertyValue('background-color');       

        if (window.widget)
                setTimeout ('widget.performTransition();', 0);        // flip the widget over       
}

// change view and display text
function doGreeting()
{
        var greeting = document.getElementById("greeting");
        var asking = document.getElementById("asking");

        if (window.widget)
                // freezes the widget and prepares it for flipping
                widget.prepareForTransition("fade");       

        asking.style.display="none";                        // hide the asking view
        greeting.style.display="block";                        // show the greeting view
        document.getElementById("body").style.
         backgroundColor = document.defaultView.
         getComputedStyle(greeting, null).getPropertyValue('background-color');       

        if (window.widget)
                setTimeout ('widget.performTransition();', 0);        // flip the widget over
}

As the JavaScript code is the heart of our widget, let's comment it in detail. First off, there's the setup() function, which does the job of creating the menu itens for our application, using two parameters: the label that will be used in the menu item, and a number code which will be used further to identify the menu item clicked by the user. These operations are performed in the excerpt below:

        mItemAsk = new MenuItem("Asking", CMD_ASK);
        mItemGreet = new MenuItem("Greeting", CMD_GREET);
        mItemCursor = new MenuItem("Cursor mode", CMD_CURSOR);
        mItemTab = new MenuItem("Tab mode", CMD_TAB);

The rest of setup() code associates a callback function to be called when a certain menu item is clicked by the user ( mItemCursor.onSelect = menuItemSelected ) and to create the structure to be presented to the user. All items are associated to the same callback, menuItemSelected(id), which through checking the menu item code value (defined above: CMD_ASK, CMD_GREET, etc.) will know which item was clicked and will take needed action. The final result of the setup() function the its arrange of the widget's menu can be seen on Image 5:

image005.jpg
Image 5: Application menu created by setup() function

After setup(), we have two other functions: doAsking() and doGreeting(). Basically, they perform the same task: select of one the <div> sections to be invisible, define the other as visible, insert a text message and show it on the screen. Therefore we'll analyze only the code in doAsking() given the fact the functions are almost identic.

First thing to be done is to obtain (using DOM) references to the <div>s to be modified, in this case named "asking" and "greeting", whose IDs were defined in the HTML page.


        var greeting = document.getElementById("greeting");
        var asking = document.getElementById("asking");


All the elements in an HTML page are acessible to JavaScript via DOM (Document Object Model), that's basically an object model through which JavaScript can access all the HTML objects. DOM is like the JavaScript "sees" HTML.

As we obtain references to the <div> objects being modified, the next step is to change their attributes and modify text, HTML or CSS formatting used by them, using the methods provided by the DOM. However, it's important to notice that, before updating the attributes of the page, we should "freeze" the widget interface, by calling the widget.prepareForTransition() method. An HTML page may contain several elements that can be modified dynamically by JavaScript, so that lots of updating done simultaneously could cause a lot of screen flicker. By calling this method, the interface changes are buffered and performed as a block, once you call the "flushing" counterpart method of prepareForTransition(), which is widget.performTransition().

Therefore the graphical elements update flow is as follows:

  • Call widget.prepareForTransition() to "freeze" the widget
  • Change HTML objects such as <d>'s attributes
  • Change element visibility attributes
  • Call widget.performTransition(), so that all updating to the UI be performed as a block and the screen does not flicker.

Translating the update flow into JavaScript code, we have:


        if (window.widget)
               // freezes the widget
               widget.prepareForTransition("fade");                       
        greeting.style.display="none";                // hide "greeting" DIV
        asking.style.display="block";                // shows "asking" DIV
        document.getElementById("body").style.
         backgroundColor = document.defaultView.getComputedStyle(asking, null).
                        getPropertyValue('background-color'); //changes background color

        if (window.widget)
                // performs all updates
                setTimeout(‘widget.performTransition();’,0)

This function, doAsking(), is called when one of the buttons defined in the HTML page (named "asking") is pressed. The same thing happens when the "greeting" button is fired, calling the doGreeting() function. The connection between the button click and the JavaScript function call happens in the "onClick()" event, as we can see in the excerpt below:

 
        <div>
                <input type="button" value="Asking" onClick="doAsking();" />
                <input type="button" value="Greeting" onClick="doGreeting();" />
        </div>

As a function can be reused in several parts of the code, notice that the menu callback function, menuItemSelected, also calls doAsking() and doGreeting(), so the menu can also be used to update the GUI of the widget:

        mItemAsk.onSelect = menuItemSelected;
        mItemGreet.onSelect = menuItemSelected;

// handling menu events
function menuItemSelected(id)
{
        switch (id)
        {
        case CMD_ASK:
                doAsking();
                break;
        case CMD_GREET:
                doGreeting();
                break;

}

Having created our HTML page, where the widget data is, and the JavaScript code, which updates both the data and the view itself, we need now create the formatting of the elements on our widget so they become visually appealing. As mentioned before, this is done using style sheets (CSS - Cascading Style Sheets). To create the style sheet, first create an empty file named "helloworld.css" in our project folder, HelloWorld, and type in the following code, shown on Listing 3:

Listing 3. helloworld.css CSS code
body {
        margin: 0;
        background-color: silver;
}
div#greetingText {
        font: 20px "Lucida Grande";
        font-weight: bold;
        text-align: center;
        color: #4D112A;
        position: absolute;
        top: 50px;
        left: 10px;
        width: 180px;
}
div#askingText {
        font: 20px "Lucida Grande";
        font-weight: bold;
        text-align: center;
        color: green;
        position: absolute;
        top: 50px;
        left: 10px;
        width: 180px;
}
div#greeting {
        display: block;
        background-color: silver;
}
div#asking {
        display: none;
        background-color: white;
}
.link {
        font-size: 10px;
        font-weight: normal;
        text-align: center;
}

As we can see, CSS code's function is clearly formatting all the objects of the HTML page, including attributes such as: background color, fonts, positioning, text aligning, etc. However, the most important thing to notice here is that only the "greeting" <div> has its "display" attribute set as "block". The other <div> has the same attribute defined as "none". This means that when the widget is loaded, only the "greeting" <div> will be visible, while the other will remain hidden. Why did we do it? Because, as said before, widgets can have only one HTML page, therefore there's no choice of going back and forth between different HTML pages, and all dynamic manipulation of the graphical elements and the formatting must be performed on the elements of a single HTML page using JavaScript and DOM.

We have already got the HTML page, the JavaScript code and the CSS stylesheet ready, so the essentials of our widgets are ready. Let us now create the widget descriptor file and it's icon, so we can package everything and install it as an application on the emulator.

Create a new file, named Info.plist, in our project folder, and type in the following code:

Listing 4. Widget descriptor code, from Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Nokia//DTD PLIST 1.0//EN" "http://www.nokia.com/NOKIA_COM_1/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>DisplayName</key>
        <string>Hello World</string>
        <key>Identifier</key>
        <string>com.nokia.helloworld</string>
        <key>Version</key>
        <string>1.00</string>
        <key>MainHTML</key>
        <string>index.html</string>
</dict>
</plist>

Our descriptor file contains some attributes of the widgets, which will be used by installer for a different purposes. The attributes and their meanings are described in Table 1:

NameTypeStatusExampleDescription
DisplayNameStringRequired“Hello World!”Widget name, to be shown on the applications menu.
IdentifierStringRequiredcom.nokia.helloworldA unique string identifying the widget. Will be used during installation, updates and removal of the widget.
MainHTMLStringRequiredindex.htmlName of the HTML page to be loaded when the widget is initialized..
AllowNetworkAccessBooleanOptionaltrue | falseDecides whether the widget has or not network access.
VersionStringOptional1.0Widget's numeric version.
Table 1: Components of Info.plist, widget's descriptor file

The content of info.plist is self-explained, as the attributes are simple and described on Table 1. To finish our widget, we need an icon file, that should be a PNG (Portable Network Graphics) file with 88x88 pixels of recommended size. We can download an icon by clicking here:

Widget packaging and testing using the emulator

We have now created all needed files and folder for our widget. If everything was done correctly, our folder structure is the same as the one shown on Image 6:

image006.jpg
Image 6: Hello World project folder

Packaging a widget for installation is extremely easy. Just follow these steps:

  • Create a .zip file whose content is the files in folder HelloWorld, as in Image 7

    image007.jpg
    Image 7: Creating HelloWorld.zip

  • The expected result is the presence of the HelloWorld.zip file in a folder right above the project folder.
  • Rename HelloWorld.zip to HelloWorld.wgz. The ".wgz" file extension informs S60's installer that the package is a widget. The result is shown on Image 8:

    image008.jpg
    Image 8: Creating HelloWorld.wgz

Vamos agora testar nosso widget com o emulador.

  • Click the Start menu, then "Programs > S60 Developer Tools > 3rd Edition FP2 SDK > C++, Beta > Emulator. This will open the emulator as shown in the Image 4.
  • With the emulator running, click on "File > Open" and browse to the HelloWorld.wgz file created by you. Click "Open". The installation prompt will be shown as in Image 9:

    image009.jpg
    Image 9: Installing HelloWorld.wgz

  • Click “Yes” and finish the installation.
  • To locate your newly-installed widget, click on "Menu" (emulator's left softkey), then "Installations". You will then see your widget installed along the other applications on the system. Image 10 shows the icon for the "Hello World" widget:

    image010.jpg
    Image 10: Hello World widget installed along the native applications

To test our widget, just position the cursor over its icon and click "Open". You should see the screen shown on Image 11:

image011.jpg
Image 11: "Hello World" widget being executed

As we have seen in the "helloworld.css" style sheet, only the "greeting" <div> is shown; the other one, "asking", is invisible.You can modify this behavior according with the JavaScript code we shown before, by clicking on "Asking" or "Greeting" buttons or on the relevant menu itens, which will produce the results shown on Images 12 and 13:

image012.jpg
Image 12: Clicking the "Asking" button changes the view

image013.jpg
Image 11: Clicking the "Greeting" menu item changes back to the first view

With this we finish our small example of widget for S60 WRT, but it's just the iceberg tip. With support to JavaScript, DOM and CSS, we can write sophisticated Web 2.0 applications using knowledge from standard Web technologies. For example, Listing 5 shows support to the XMLHttpRequest object, which is the core of all Ajax applications, besides the use of DOM to parse an RSS (Really Simple Syndication) feed, used by the majority of sites to export their content in XML format.:

Listing 5: Using Ajax and XML parsing with widgets

//Loading the feed

function loadRSSFeed(url) {
  if (null == req) {
    req = new XMLHttpRequest();
  }         
 
  req.onreadystatechange = ReqStateChange;
   
  req.open("GET", url, true);
  req.send(null);

  document.getElementById("content").innerHTML = "Updating" + url;
}

//Callback for the request return
function ReqStateChange() {
  if (req.readyState == 4) {

    if (req.status == 200) {
            UpdateContent(req);
    }
    else {
      alert("error");
    }
  }
}

//XML parsing and RSS decoding
function UpdateContent(reqst) {
  var d = null;
  var el = document.getElementById("content");
 
  document.getElementById("content").innerHTML = "Updating!";
   
  var rss = null;
 
  var html = "";
  rss = reqst.responseXML.documentElement;

  if (rss != null) {
    var itemTitleNodes = rss.getElementsByTagName("title");
    var itemLinkNodes = rss.getElementsByTagName("link");
    var itemDescNodes = rss.getElementsByTagName("description");
 
    var c=itemTitleNodes.length;

    el.innerHTML="Displaying " + c + " items...";
       
    if (c<=0) {
      return;
    };
       
    if (c>4) c=3; // limit to four stories
   
    for (var i = 0; i < c; i++) {
      var itemLink, itemTitle, itemDesc;
       
      if ((itemTitleNodes[i+2].childNodes[0] != null) &&
          (itemLinkNodes[i+2].childNodes[0] != null) &&
          (itemDescNodes[i+1].childNodes[0] != null)) {
                               
        itemTitle = itemTitleNodes[i+2].childNodes[0].nodeValue;
        itemLink = itemLinkNodes[i+2].childNodes[0].nodeValue;
        itemDesc = itemDescNodes[i+1].childNodes[0].nodeValue;
      }
      else {
        itemTitle = "RSS feed missing";
        itemLink = "???";
        itemDesc = "RSS broken";
      }
      html = html + "<div class='item'><div class='linking'
      onClick='widget.openURL(\"" + itemLink +
      "\");'>"+itemTitle+"</div></br><div
      class='description'>"+itemDesc+"</div></div></br>";
    }
  }

  el.innerHTML = html;
  html = null;
  el = null;
  req = null;
         
  d = document.getElementById("lastupdate");               
}

Besides using Web and Ajax, you'll also use exclusive S60 APIs, such as Widget, Menu and SysInfo. All allow for a closer interaction with the device's functionalities, and are only available to widgets, not normal web pages. You can obtain more detailed information on the APIs on the "Links" section of this article. Also there you'll be able to download more sophisticated examples of WRT widgets for S60.

Testing the widget on a real device

We we said at the beginning of this article, Widgets and Web Runtime technology will only be available from S60 3rd. Edition Feature Pack 2 on. At the time of this writing, there are no Feature Pack 2 devices available, but you can start developing widgets right now, using the tools and the SDK, and as great news, Forum Nokia offers a remote application testing service, called RDA (Remote Device Access), with which you are able to test your Java or C++ applications (along with multimedia content), via a client application installed on your PC. The devices are located in our labs and can be used as if they were in your hands, with all the functionality, including network access. Fantastic news for widget developers is that some of the devices available on RDA are flashed with a special firmware version that includes WRT and therefore you can test your widgets on real devices. Access to the RDA service is free for all Forum Nokia members. To become a member, register for free at http://forum.nokia.com.

Wrap-up

Using widgets for developing mobile services and applications brings a series of benefits to developers:
  • Allows leveraging knowledge obtained from standard Web technology to develop mobile applications for the S60 platform.
  • Provides the ease of "mobilization" of existing web services and porting of other plataforms' widgets with minimal effort.
  • Allows interaction with the application installer, easing the updating of deployed widgets.
  • Makes available, through the use of the S60's WRT exclusive APIs, closer interaction with the device's functionality. For now this interaction happens through the Widget, Menu and Sysinfo APIs, however Nokia is commited to developing widgets and integrating more functionality as the versions evolve through time.

Important links

Here it goes a collection of important links on widget development:

Daniel Rocha is a Senior Technology Expert for Forum Nokia, Nokia's developer relations organization.
http://forum.nokia.com

Tutorial posted novembre 15th, 2007 by dcrocha

Soumis par nutan_ce le jeu, 2008-03-13 15:19.

WRT is a good start for next generation web based applications , WRT tools are ready to use as Beta, I found it on google group "Nokia WRT Tools beta" , check it out ! Smiling



copyright 2003-2009 NewLC SARL