Friday, December 18, 2009

Web Runtime Widget Tutorial – Part 1: Creating a “Hello World” widget with Nokia WRT & Aptana Studio

Grab the code!

This is part one of five of a tutorial that will take you, step by step, through the process of building a simple Digg client using Nokia WRT and Aptana Studio.

Getting the tools we need

One of the good things about developing Nokia WRT widgets is that the necessary tools are free! It is perfectly possible to create WRT widgets using nothing more than your favourite text editor & a free WRT download from Forum Nokia.

However, there are more advanced tools available that will make your development life easier and more productive. Best of all, you can also download them for free!

This tutorial uses Aptana Studio and the Nokia WRT plugin for Aptana Studio. You can download a free copy of Aptana Studio from www.aptana.org. Once you have Aptana Studio installed and running, you should install the Nokia WRT plugin for Aptana Studio, following the instructions given here: http://tools.ext.nokia.com/wrt/prod/aptana/plugin/ 

Note: This tutorial has been put together using Aptana Studio version 1.5.1 & version 2.3.0 of the Nokia WRT plugin for Aptana Studio.

Creating a new WRT project using Aptana Studio

Now you’ve got the tools you need, we can get started.

From Aptana, select File->New Project.
Create a new project in Aptana Studio

In the New Project dialog, select “Nokia Web Runtime (WRT)” –> “New Nokia Web Runtime Widget” wizard and click on the “Next” button.
Select a project wizard in Aptana Studio

In the New Nokia Widget Project dialog, select the “Basic Widget with WRTKit” template and click on the “Next” button. We’re using the Basic Widget with WRTKit template rather than the version without WRTKit support because we want to take advantage of the ready made UI controls Nokia provides for us in WRTKit, rather than having to create everything from scratch ourselves.
Select a template for your project

Give the project a name, for example, “DiggClient”. Either tick the “Use default location” box or select your preferred location for the project files then click on the “Next” button.
Define your project name & location

We don’t need to change the Widget name or identifier. Leave the “Enable HomeScreen” box unchecked as we won’t be discussing HomeScreen widgets in this tutorial.

At this point, the template has enough information to create a skeleton project, however, rather than clicking the “Finish” button at this stage we will click the “Next” button instead.
Define your widget's name & identifier

Replace the default names for the main HTML, CSS & JavaScript files with “diggclient” and click the “Finish” button.
Define initial file names for your project

That’s it! Aptana has created a fully functional “Hello World” Web Runtime Widget implementation for you. Before we see our new widget in action, let’s take a look at what Aptana & the Nokia WRT plugin created for you.

What makes up a WRT Widget project?

Aptana created a set of files for you when you clicked the Finish button. If you expand the DiggClient project within Aptana you’ll see something similar to the following:

Your project file list

Info.plist

The first file we’ll look at is Info.plist.

Info.plist is a mandatory part of each and every widget you will develop and resides in the root directory of your widget project. The file itself is an XML dictionary that provides the Web runtime with key information about your widget.

If you examine the contents of the Info.plist file created for you by Aptana, you will see something like the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Nokia//DTD PLIST 1.0//EN" "
http://www.nokia.com/DTDs/plist-1.0.dtd">
<
plist version="1.0">
    <dict>
        <key>DisplayName</key>
        <string>DiggClient</string>
        <key>Identifier</key>
        <string>com.DiggClient.basic.widget</string>
        <key>Version</key>
        <string>1.0</string>
        <key>MainHTML</key>
        <string>diggclient.html</string>
        <key>AllowNetworkAccess</key>
        <true/>
        <key>MiniViewEnabled</key>
        <false/>
    </dict>
</plist>

Notice that the project name you supplied to the template appears in the Info.plist file as the widget’s DisplayName, as does the widget identifier.

Notice also that diggclient.html is defined as the value of the MainHTML key. The file referred to by the MainHTML key is loaded by the Web runtime when your widget is launched.

Also included in the Info.plist file is the key Version, which corresponds to the version number of your widget implementation. Finally, notice that your widget is permitted to access network resources because the AllowNetworkAccess key is defined as true, whilst MiniViewEnabled is defined as false, which means this widget does not support the HomeScreen.

diggclient.html

A widget must contain one and only one HTML file, the name of which is defined by the MainHTML key in the Info.plist file. The file can be called <anything>.html; I like to use the name of my widget – diggclient.html in this case. Other people like to use index.html, the choice is yours. As with the Info.plist file, your HTML file is mandatory and must reside in the root directory of your widget project.

Your diggclient.html file should look something like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="
http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />       
        <script type="text/javascript" src="diggclient.js"></script>
        <script type="text/javascript" src="WRTKit/WRTKit.js"></script>
        <link rel="stylesheet" href="diggclient.css" type="text/css">
        <META NAME="Generator" CONTENT="Nokia WRT plug-in for Aptana Studio 2.3.0" />
    </head>
    <body onload="init()">
    </body>
</html>

Because our widget is using the WRTKit for it’s UI elements we don’t require much HTML. The example file shown above is very typical of WRTKit based WRT widgets. Almost all of the real work is done by JavaScript in a WRTKit based widget.

What we do need to do in HTML is define a basic document structure for your widget, referring to one or more JavaScript files within the <head> section of your HTML document. Usually it will be these referenced JavaScript files that contain the logic that implements your widget. In the case of our tutorial widget, we refer to diggclient.js, which is our widget’s own JavaScript file and WRTKit.js which is the entry point for the Nokia WRTKit UI library.

To define the layout & appearance of your widget’s UI elements, you are encouraged to make use of cascading-style sheets, so our HTML file links to diggclient.css, our widget’s own cascading-style sheet.

Note: If you open diggclient.css, you will see the file is currently empty; the layout & appearance of our widget is being handled by WRTKit controls.

Technically there is nothing to stop you adding all of your CSS definitions and JavaScript code to your widget’s HTML file, but it is easier to manage your projects if you separate the HTML, CSS & JavaScript from each other.

It should also be noted that it is perfectly possible to create a WRT widget entirely in HTML without using CSS or JavaScript. Such widgets tend not to be very visually appealing as they can’t make use of the WRTKit or any other JavaScript libraries, however it is possible to create such widgets and therefore the usage of JavaScript and CSS files is optional rather than mandatory.

Both your JavaScript & CSS files, if present, can reside in any directory within your widget project, including the root directory.

Our widget does use JavaScript and the function called when the widget is loaded is given by the value of the onload attribute of the body element. So our widget calls the JavaScript function, init() on loading.

Icon.png

Notice that Aptana has included an Icon.png file in the created project files. An icon is optional.

If you do include an icon for your widget, it must reside in the root directory of your widget project and must be in PNG format. The recommended resolution for an icon is 88 by 88 pixels. The icon will be scaled by the runtime depending on the device on which the widget is running.

If you do not include an icon for your widget, the runtime will provide the default S60 application icon to represent your widget in the device shell.

diggclient.js 

diggclient.js is where the bulk of our widget’s implementation is to be found. But rather than examine the file in detail now, let’s come back to it after you see your widget come to life.

Seeing is believing

To see your widget in action, simply double click on diggclient.html in the list of files in your Aptana project. Aptana will open the file and display the contents in the main editor pane.

At the bottom of the main pane in Aptana Studio, you should see two tabs – one labelled “Source”, which is the tab opened by default, and another tab labelled “Nokia Web Runtime (WRT)”.

Source & Nokia Web Runtime tabs

Note: The “Nokia Web Runtime (WRT)” tab is only visible when your project’s HTML file is open and focussed in Aptana.

Select the “Nokia Web Runtime (WRT)” tab and you will see a fully interactive preview of your widget running in Aptana Studio. If all is well, you should see something similar to this:

Running widget

Play around with your widget. Enter your name in the text box and click the “Say Hello!” button to receive a predictable greeting:

Hello Widget

Notice that the Options menu is functional, giving an example of a simple About notice and an option to exit the widget.

Not bad for less than 15 lines of HTML! But of course, by now you realise the real work is being done by the as yet unseen JavaScript code.

Show me the script!

At the start of the script, we declare some JavaScript variables which will be used to represent parts of our widget’s UI:

Variables

This small block of code shows that we initially declare variables to represent various UI objects: uiManager, mainView, helloButton, nameField and aboutLabel.  Finally, we declare MENU_ITEM_ABOUT, which defines the position of the “About” option in the widget’s option menu.

uiManager, mainView, helloButton, nameField and aboutLabel will all become WRTKit UI objects. The uiManager will become the root element in a hierarchy of WRTKit objects in our widget; one or other of the mainView or aboutView will be set as the current view on the uiManager object at all times. The helloButton & nameField will be controls in the mainView and aboutLabel will be a control in the aboutView.

Implicit variable declaration

“What aboutView?” you might be asking. Welcome to JavaScript! Whilst it’s certainly very good practice to declare your variables before use – and as a seasoned C++ & C# coder, I can’t think of doing anything else - JavaScript will declare variables at the point of first use, which is illustrated nicely by (a bug in) the wizard that generated the code. There is no variable called aboutView defined here! Yet code later in the script happily creates a WRTKit ListView object and assigns it to aboutView without complaint.

Although it’s not essential at this stage, I recommend you add the following line:

var aboutView;

to the generated JavaScript, immediately after the declaration of
var mainView;

Declaring your variables up front, before you use them, gives you the advantage of controlling your variable’s scope, although to be fair, all of the variables declared so far are global anyway. The wizard that generated the code for us gets away with not declaring aboutView explicitly simply because this widget is trivial and there are no issues with scope caused by creating the aboutView variable at the point of first use.

Right, let’s do something with these variables…

Our “entry point”

Recall from the discussion of diggclient.html, that we set the onload attribute of our HTML document’s <body> tag to “init()”, meaning that this is the JavaScript function that will be called when our HTML document is loaded. We’ll take the init() function piece by piece. To kick the function off, we have the following:

First part of init() funtion

The first thing we do in our init() function is set up our options menu. We do this by creating a MenuItem object, passing the string to represent this item in the menu, together with the position in the menu this item is to occupy, to the MenuItem’s constructor. Next, we set the MenuItem’s onSelect property to point to a function that will be called when this menu item is selected. In this case, we want the function menuItemSelected() to be called when our menu item is selected. The last thing we do to prepare the menu is append our MenuItem object to the widget’s menu.

Notice that this last step shows another benefit of explicitly declaring your variables; we haven’t declared anything called “menu” anywhere, so JavaScript is just going to create it for us, yes? No! In this case, menu is actually an object provided by WRT to each widget. In fact, WRT provides a set of objects to each widget, the most fundamental of which are:

  • widget – provides a set of methods & properties that apply globally to your widget as a whole. Can be accessed through the keyword widget.
  • menu – provides APIs to manipulate the options menu & softkeys of your widget. Can be accessed through the keyword menu. Note that the options menu is always associated with the left softkey and your code cannot change this – one implication of this is that the left softkey text is always “Options” in English, or the equivalent in the current UI language of the device. An option to Exit the widget is always added by WRT to the set of menu items your widget defines.
  • MenuItem – provides APIs to represent and manipulate the items in a widget’s menu. MenuItem is a reference type and objects of type MenuItem are always created by using the new operator.

Next we enable tab navigation and show the softkeys – but only if the widget is running within the WRT:

Selecting navigation mode and showing softkeys

By default, cursor mode navigation is enabled, but we don’t want this for our widget so we call setNavigationEnabled(false) to enable tab navigation instead. Some advanced widgets would use cursor navigation, which relies on the use of an onscreen pointer, similar to the S60 browser. Tab navigation is typically used by simpler widgets that only have a few UI elements, usually arranged in a vertical stack. Note that your widget can change between tab and cursor navigation at any time as required.

Why do we need a WRT environment check? We’re creating a WRT widget aren’t we? Well, yes we are, but the brains of your widget are just JavaScript functions. This JavaScript can be run in a variety of environments, such as your PC’s browser or on your phone. Also, in principle, some or all of the code that makes up your widget may come from elsewhere – a website, a widget from another platform, a JavaScript library. Alternatively, you may want to reuse the code you write to power your widget in another project. To support these scenarios – and to aid debugging - it’s good to ensure you’re in the WRT environment before you try and use WRT specific features.

Now it’s time to create some UI objects:

Creating WRTKit objects

When we set our widget project up, we selected the Basic Widget with WRTKit support template. We did this so that we could take advantage of the richer UI controls provided by Nokia in the WRTKit. In order to use the WRTKit, we need to create a UIManager object which will manage the presentation of our widget’s views and manage resources for us.

Having created the WRTKit UIManager object, we create two views – mainView and aboutView. Notice that both views are created as ListView objects. ListView is one of the UI controls provided by the WRTKit which arranges controls added to the list control as a vertical stack. Two parameters are passed to the ListView constructor, both of which are strings: an id that uniquely identifies the resulting list view and a caption that occupies the area at the top of the list. One reason to specify an id for an object is to be able to identify a specific object as the source of an event in an event handler. However, if you do not need to identify a given object in an event handler, you can set the id to null. The caption can be an empty string if you want to have a caption area without any content, or null if you want a list view without a caption area.

At this point, we’ve created a UI manager and two empty views. To add some content to our views we do the following:

Creating & initialising WRTKit UI controls

Here we create a TextField object which allows the user to enter some text, with a prompt of “Enter your name”. We then add the TextField to our mainView. Recalling that mainView is a ListView control and that ListView controls present their content as a vertical stack, you can see that the text “Enter your name” will appear at the top of our view, beneath the mainView’s caption.

Next we add a FormButton control to the mainView. A FormButton is a basic button control that allows us to attach an event handler (or EventListener in WRTKit language) which will be called when the button is pressed. When we call the FormButton constructor we again pass in an id (null) and also a string which is used as the label on the face of the button.

We bind an event handler to the FormButton control by calling addEventListener() on the FormButton object, passing the name of the event that we want to handle (ActionPerformed) and the name of the function we want called when the event occurs (helloButtonClicked()). If you are wondering where the event name ActionPerformed comes from, it is defined as an event by the abstract class ActionControl, from which FormButton is derived.

The about view is trivial and consists simply of a label control that will show some information about the widget to the user. So we create an instance of the WRTKit’s Label class and add this to our aboutView.

All that remains to complete the construction and initialisation of our widget is simply to tell the uiManager object which view to use initially:

Showing the initial view

Button Event Handler & Notifications

Let’s look now at what happens when the user clicks our widget’s “Say Hello!” button. We already know that we’ve configured the FormButton control to call a function called helloButtonClicked() when the button is pushed. Here is the code of that function:

Button event handler

In order to present a popup notification to the user, the first thing we need to do is get the current text that the user has entered in the text field control, which we do by assigning the result of calling the getText() method on nameField to a variable.

We then call the uiManager’s showNotification(…) method to present a notification dialog to the user. If the nameField is empty, we present a warning note, asking the user to enter their name. If the nameField contains at least one character, we present an info note, greeting the user to the world of WRT widgets. In each case, three parameters are passed to showNotification(…):

  • duration (in milliseconds) to show the note
  • type of note (which influences the icon shown in the note dialog)
  • message to be shown to the user

When the duration for which the note is to be shown expires, the note is removed from the screen and destroyed by the uiManager.

Notice that we make no use of the event parameter passed to our event handler. There is no need in this simple case, but we will look again at event handlers and the information passed to them later in this tutorial.

Menu Event Handling

In our init() method, we set aboutMenuItem’s onSelect property to refer to the function menuItemSelected(). Whenever “About” is selected from our Options menu, the function menuItemSelected() will be called.

Menu event handler

You can use the same function to handle the selection of several or all of your menu options, which is why WRT passes an integer id to your function. This id is the position of the selected item in the menu. Recalling that the position of our “About” option is defined by the variable MENU_ITEM_ABOUT, you can see that whenever the user selects “About” from the Options menu, the function showAboutView() is ultimately called.

Handling About option

showAboutView() sets the textual content of the aboutLabel to a message about the software that is included in your widget by passing the message as a string to the setText() function of the Label object.

Before we can display the aboutView, we need to modify the softkeys in use. We’re only really concerned about the right softkey because if we leave it as “Exit”, there will be no way for the user to return to our widget’s main view. We change the softkeys for the about view by calling another function, setAboutViewSoftkeys().

Now we can display our about view to the user by calling the uiManager’s setView() function, passing the aboutView object as the parameter.

Changing the Softkeys and Returning to the Main view

As noted above, we need to change the right softkey used in the About view to provide a mechanism for the user to return to the main view. We could have achieved this aim by modifying the options menu – perhaps by removing the About option and replacing it with a Main option – but this wouldn’t be a very intuitive solution.

The way we will achieve this is to change the right softkey from “Exit” to “Ok”. At the same time, we will assign a handler function to implement the logic of switching back to the main view when the “Ok” softkey is selected by the user.  The code to do this is as follows:

Setting softkeys for About view

As you can see from the code, the function we call when the user selects our “Ok” softkey is showMainView():

Showing the main view again

showMainView() resets the right softkey to “Exit” by clearing the text currently assigned to the right softkey and setting the handler function to null. WRT notices this and supplies the “Exit” text and an appropriate handler function automatically.

Then all we need to do to return to our widget’s main view is tell the uiManager to show our mainView again.

Coming next…

This concludes the first part of our tutorial on creating a Digg client with Nokia WRT & Aptana Studio. Clearly we’ve not done very much yet, indeed, if we’re honest, we’ve done close to nothing – Aptana & the Nokia WRT project wizard has done all the work so far. 

In the next part of this tutorial we will start taking some steps towards turning our Hello World widget into a Digg client.


Part 2>

No comments:

Post a Comment