Getting started

From WidSets Developer Site

Jump to: navigation, search

Getting Started with WidSets Widget Development

Contents

[edit] OVERVIEW

This getting started document provides developers with essential information about how to start developing widgets for WidSets. The document also discusses instructions of how to setup the development environment for using the WidSets development kit. The Step by step section will take you through all steps in creating a simple but interesting widget for WidSets. The complete code of the example widget is provided in a separate .zip file.

It is our intention to keep things as simple as possible and that is why detailed explanations in certain areas are beyond the scope of the document. We recommend you to read also reference materials where they are referred to through out the document.

[edit] INTRODUCTION TO WIDGET FOR WIDSETS

Widgets for WidSets are mobile applications, which run on the so-called “WidSets Virtual Machine” resided on a mobile device. The WidSets virtual machine is an installable client application for any mobile device, which supports Java MIDP 2.0. Widgets can be standalone applications (e.g. a calculator, a game etc.) or server-based client applications (e.g. RSS feeds, Flickr image viewers, etc.) depending on how they are implemented.

Terminology explanation within this document context:

  • WidSets: a service system that consists of a client side application (WidSets client) and a server (WidSets server) providing services to widgets.
  • Widget: an application that runs on WidSets.
  • WidSets client: the WidSets application that hosts widgets when they are installed and run in a mobile device.
  • WidSets server: a server, where widgets are to be uploaded and compiled before they are downloaded into a mobile device. Also widget server side services are done on WidSets server. Server side services must be defined in your widget.xml file before using. Current services are httpservice for fetching http content from the internet to widget, picviewer service for scaling images and syndication/webfeed service for accessing RSS and Atom feeds.
  • WidSets manager: a web application, that provides WidSets users with utilities functions such as synchronizing widgets, managing user’s account information, monitoring traffics etc.
  • WidSets library: a server-side library where published widgets reside.
  • Helium: a scripting language used in widgets’ functions composing.

[edit] WIDGET PROGRAMMING TECHNOLOGY

[edit] Prerequisite knowledges

WidSets employs the powerful XML mark-up language and utilizes the CSS-like (Cascading Style Sheets) syntax with basic data types in its widget application architecture. Understanding XML and CSS standards will be an asset for the widget development curve. It would be a smooth start if you were familiar with Java MIDP, thanks to the similarity between Java MIDP and the Helium API (to be discussed in more detailed later). If you would intent to learn about syndication widgets, you will need to have also generic knowledge about RSS or Atom technology.

The mentioned technologies above are essential but their explanations are unfortunately out of the scope of this document. If you are not familiar with them, it is recommended you to acquire the prerequired knowledges before reading this article further.

[edit] Widget component files

A typical widget project comprises of several component files as listed below:

  1. A widget.xml file: this xml file contains widget’s specific details, such as the meta data of the widget, services used by the widget, the stylesheet of the widget, resources and layout of the widgets’ views.
  2. A [script].he file: this file contains the Helium scripts for extending widget’s functionalities.
  3. Three resource files for WidSets Website:
    • web_icon.png – This image is the icon used in the widget library or in the widget shelf of the WidSets manager. The pixel size of this image has to be exactly 60*40px. This image is also visible in a mobile device under the system widget’s library (see Figures 1 and 2 for illustration).
      Figure 1: MemoryGame widget’s web minimized image, web_icon.png inside the blue circle.
      Figure 1: MemoryGame widget’s web minimized image, web_icon.png inside the blue circle.
    • web_minimized.png – This image is used for displaying the widget in the WidSets manager. It presents the minimized view of the widget on the Website. The width of the image must be exactly 110 pixels and the height is flexible but should not exceed the maximum height of the widget (see Figure 2 for illustration).
      Figure 2: Memory game widget in Manager view (inside red oval) and web icon (in side blue circle) displayed in the WidSets manager
      Figure 2: Memory game widget in Manager view (inside red oval) and web icon (in side blue circle) displayed in the WidSets manager
    • web_maximized.png – This image is the preview image for the widget in the widget library on the WidSets Web Site. It is used as an image to illustrate how the widget will look like in the open view. The pixel size of this image has to be exactly 176*208px (see Figure 3 for illustration).
      Figure 3: MemoryGame widget’s web maximized image displayed in the WidSets library
      Figure 3: MemoryGame widget’s web maximized image displayed in the WidSets library

[edit] Introduction to Helium scripting language

This section only gives an introduction to the basic syntax of the Helium scripting language as well as an introduction to some widget’s key interfaces. For detailed decriptions of widget interfaces, please refer to the WidSets APIs reference document.

Helium is a language to be used in writing widgets for WidSets. The basic syntax of Helium language is listed below:

  • Comments
/* block/line comments */
// line comments 
  • Literals
;             // line ends
”lorem ipsum” /* string */
’\n’          /* character, produces int */
123456        /* integer */
0777777       /* octal */
0xcafebabe    /* hexa */
0b101010101   /* binary */
  • Statements
if  else  while  do  for  switch  case  default
return  break  continue  foreach
  • Operators
+   -   *   /   %   &   |   ^   <   >   !   ~
<<  >>  +=  -=  *=  /=  %=  &=  |=  ^=  <<=  >>=
<=  >=  ==  !=  &&  ||  ++  --
instanceof  new ?:
  • Primitives
boolean /* true/false */
int     /* signed 32 bit */
long    /* signed 64 bit */
  • Keywords
class const false null return struct true void

If you were familiar with software development, the above syntax should be trivial and understandable for you. If not, I recommend you to search from the Web site for the explanations of Java programming language syntax, which is very close to the syntax of Helium.

Widget’s key functions can be devided into several categories based on their functionality.

a) Interfaces concerned the widget’s lifecycle.
void startWidget();
void stopWidget();

When a user starts to run WidSets, WidSets client will call the startWidget() function of all installed widgets on a mobile device. In most cases, you should implement this function to create the widget’s minimized view to be displayed on the WidSets dashboard. The stopWidget() function is called when a widget is reloaded or removed from the dashboard or when WidSets client is terminated. You should implement this function to release system resources (if any) before it is terminated.

Shell openWidget();
void closeWidget();

When user selects a widget from dashboard, it is opened to maximized mode and openWidget() function is called. You need to implement this function if your widget has a maximized mode (most widgets do). Here you typically create Shell using views defined in widget.xml. closeWidget() in the other hand is called when a widget exits from maximized mode by popping (popShell() or slideOut()) the last widget-created shell or when widget is stopped for any reason. Here you might usually want to cancel yours timers or atleast set them to tick less often.

b) Interfaces associated with the widget’s menu control
MenuItem getSoftKey(Shell shell, Component focused, int key);
Menu getMenu(Shell shell, Component focused);

When a user clicks on a softkey of a mobile device (normally a softkey is associated to the widget’s menu), WidSets will call the getSoftkey()to inform the widget which one of the two softkeys was just clicked. You must implement this function to detect the softkey and tell WidSets what to do with the event. The getMenu() function is called when a softkey is designed to associate with an open menu (more explanation will be discussed in section 5.3).

Note: Through out this document, when using the term “mobile device”, we implicitly refer to Nokia mobile devices, which support two softkeys (left and right) associated to an application’s menu.
c) Interfaces responded to widget’s user actions
void actionPerformed(Shell shell, Component source, int action);
boolean keyAction(Component source, int op, int code);

The actionPerformed() function is called as a result of the key-pressed action on a softkey and the keyAction() function is called when the user clicks on an alpha-numeric key (including the navigation key). You can implement these functions to detect user’s actions and process them accordingly. Remember that actionPerformed()and keyAction()are system callback functions and you must never call any callback functions directly.

d) Interface of widget’s view elements construction
Flow createView(String name, Object context)
Component createElement(String viewId, String elementId, 
                        Style style, Object context);

The createView() function is used for creating a view for the widget. A view can be a minimized view or a maximized view depending on the widget’s running status. Widget’s UI components can be constructed within the createElement() function. This is a callback function and it gets called as a result of every time the createView() function is called.

e) Callback function on timer events
  void timerEvent(Timer timer);

You can set up timers that notify your widget after specified time passed by, or continuously. Timers are created using schedule(…) functions. These timers will then call timerEvent(Timer timer) function of your script, or separate TimerCallback function if you defined one. To release timer resource, you need to call the cancel() function of the Timer objects you have created. This should be performed as soon as you don’t need the timer anymore or when a widget is terminated via the stopWidget() function.

f) Callback functions on server communication events
 void onSuccess(Object state, Value returnValue);
 void onFailure(Object state, String errorMessage);


Server-side services can be invoked by using the call(Object state, String service, Value argument) function. Responses from a server (as the result of the call() function) are dispatched via onSuccess() or onFailure() callback function indicating the server call was successful or failed respectively. In case of multiple server-side service calls, the state parameter in these callback functions can be used to identify which call the response belongs to.

As mentioned earlier, if you were familiar with Java programming language or even with any other programming languages, working with Helium language would not be a difficult task at all. Consider how to implement a simple function, which calculates and return the sum of two numbers in Helium language:

int calculateSum(int number1, int number2) {
  return (number1 + number2);
}

Helium language does have its own rules and below are several fundamental rules:

1. All script functions and variables MUST be implemented within the ONLY class of a widget
class 
{
  int globalVar = 100;

  void firstFunction()
  {
    int localVar = 0;
  }
}

However, there can be inner functions which can make many things easy to do while managing to keep code elegant:

Flow createAddressView(String name, String addr, String city)
{
  Flow flow = new Flow(getStyle(“address.view”));
  
  add(“Name”, name);
  add(“Address”, addr);
  add(“City”, city);

  void add(String name, String value)
  {
    String text = format(“%s: %s”, name, value);
    Label label = new Label(“address.field”), text);
    label.setPreferredWidth(-100);
    label.setFlags(VISIBLE|LINEFEED);
    flow.add(label);
  }

  return flow;
}

Inner functions can access variables defined in enclosing function and also invoke other inner functions. This makes them partically useful. Unlike in Java you don’t have to mark variables as final in order to access them from inner functions.

2. All variables must be initialized and they exist within the scope where they are declared
void firstFunction()
{
  int j; /* error, missing assigment */
  {
    int c = 0;
  }
  {
    int d = c; /* error, ’c’ is not visible here */
  }
}
3. Explicit type-casting
boolean flag = false;
Text displayText;

void someFunction()
{
  int zeroOrOne = int(flag); /* casting boolean to int type */
  ...
  String str = String(zeroOrOne); /* casting int to string type */
  displayText.setText(str);
}

[edit] WIDGET DEVELOPMENT ENVIRONMENT AND TOOLS

Widgets can be developed by using any XML editor or even just with a text-editing program. However, using an XML editing program will be much convenient as it may offer numbers of utility functionalities such as row number, syntax highlight etc.

Currently there is no dedicated text-editing program supporting Helium language syntax in the market. Though, numbers of text-editing softwares do support the add-on feature to enable users defining new syntax for new language. For your convinience, we provide the syntax files for the UltraEdit and the EditPlus text-editing programs. Those syntax files are included in the WidSets SDK package mentioned later in this section.

[edit] WidSets Developer Kit

The WidSets developer kit (DevKit) is available for download from the dev.widsets.com Web site. The DevKit is a light automated set of tools to help you produce feature-rich widgets. In the current version (0.99.1), the DevKit comprises of several basic tools such as the built-in Helium script compiler, an emulator, and WidSets API references in HTML format as well as numbers of sample widgets (please refer to the release note file (SDK Directory/docs/CHANGES) in the DevKit to find out exact supported features of a current DevKit). In addition to the WidSets Website tool, the DevKit also provides alternertive way to upload your widgets to the WidSets server for testing or for the preparation of deployment your widget (in this context, deployment is referred to as publishing a widget in the widget library and that feature is not supported by the DevKit. See section 5.8 “publishing widget” for instructions of how to publish a widget).

Note: WidSets.com website doesn’t allow 3rd party developers to upload or publish their own widgets. Developer MUST use dev.widsets.com website to test their widgets. Developer site is a mirror copy version of the actual WidSets.com Website, and is meant to act as developer play ground where new widgets can be developed and tested. When a widget has been proven to work and be safe to use by the community, it can be uploaded to WidSets.com for end users to use.

DevKit is configured to connect to dev.widsets.com as default.

The DevKit package is compressed in a .zip file. Unzip the package into your local drive and run the DevKit by simply open a command prompt (Windows) or opening new shell (Unix), type in devkit and press enter, this will produce a list of all available options and commands with short description onscreen.

DevKit uses following syntax:

devkit <options> command <arguments>

If you are behind proxy, or VPN tunnel, you need to set options described in following step before the command (and it’s arguments) you are about to execute.

Let’s go through a few steps to setup the DevKit in your environment and login to the WidSets server with your WidSets account.

1. Your current network connection goes through a proxy (HTTP)?
If “Yes”, set the proxy parameter:
devkit --proxy yourproxyaddress:port <COMMAND>
If you have defined a proxy before and want to remove it (it’s off by default):
devkit –-noproxy <COMMAND>
2. Your current network connection goes though a VPN tunnel?
devkit –-tunnel <COMMAND>
3. You are now ready to login
devkit login yourusername yourpassword
In order to login to dev.widsets.com you need to create an account on http://dev.widsets.com/ website. Note that normal WidSets.com accounts won’t work here - developer site is completely separate system because of security reasons.
Note 1: It’s adviced that you make a login.bat file with the @echo option set “off” to avoid retyping this everytime you login and to hide your password from the one who stands behide you :).
Note 2: Usually when devkit prints out “Connection to [URL] could not be established in timely manner.” it means that you are behind a firewall and need to configure a http proxy.
4. Ready to see the emulator?
devkit run
Figure 4: WidSets emulator in Basic skin
Figure 4: WidSets emulator in Basic skin

[edit] Syntax highlighting

[edit] EditPlus Configuration

Follow the steps below to configure the EditPlus software to support Helium language syntax:

  1. Copy the helium_highlight_editplus.stx and helium_highlight_editplus.acp files from the WidSets SDK docs folder to the main folder of EditPlus program (e.g \Program Files\EditPlus 2\)
  2. Run EditPlus and from the program’s Tools menu, choose “Preferences”
    Figure 5: Open the preferences dialog
    Figure 5: Open the preferences dialog
  3. Select Files -> Settings & syntax from the “Categories” list in the Preferences dialog
  4. Click on the Add button and fill in the form as illustrated in the picture below:
    Figure 6: Configure EditPlus to support Helium syntax highlight
    Figure 6: Configure EditPlus to support Helium syntax highlight
  5. You can also change the syntax colors and set other setting parameters as you wish
  6. Click the Apply button and return to the editing space. Congratulation! EditPlus is ready now for you to work nicely with Helium script source files.

[edit] UltraEdit Configuration

Follow the steps below to configure the UltraEdit to support Helium language syntax:

  1. Open Advanced -> Configuration -> Syntax Hightlighting dialog
  2. Click Open-button to start editing syntax highlight configuration file
    Figure 7: Open the Syntax file
    Figure 7: Open the Syntax file
  3. Click OK on Configuration dialog to close it.
  4. Open helium_highlight_ultraedit.txt from the WidSets SDK directory under /docs directory.
  5. If you are not entirely sure what you are doing, you might want to make a copy of your existing wordfile.txt. Normally it is located in C:\Program Files\IDM Computer Solutions\UltraEdit-32.
  6. Scroll to the end of the file and search backwards for “/L”, that is the starting mark of syntax highlight definition. Last one in my UltraEdit 11.00a+ was “/L10"VBScript…”
  7. If the sequence number in your UltraEdit differs from number 10, you need to change the helium_highlight_ultraedit.txt file so that it will be the next number. If there are definitions with same numbers or there are “empty numbers” between, syntax highlighting wont work.
  8. When sequence numbers are OK, just copy all (Control-A) from helium_highlight_ultraedit.txt and paste (Control-V) it at the end of your wordfile.txt. Save the file (Control-S) and you are done. Now when you edit Helium (.he prefixed) files, you should see them syntax highlighted.
Note: In future version of WidSets and the DevKit, we might add more API classes/functions or Helium syntax and you might need to do this again so that your highlighting will work 100%.

[edit] Emacs Configuration

The java-mode of emacs provides rudimentary syntax highlighting for Helium language.

Add the following line to your emacs configuration file (usually .emacs) to use java-mode for Helium script files:

(setq auto-mode-alist (cons '("\\.he$" . java-mode) auto-mode-alist))

[edit] STEP BY STEP TO DEVELOP YOUR FIRST WIDGET

This section explains in details all steps to develop a WidSets’ widget. To make it more interesting but simple enough, we will not create a Hello World widget, but a Flag Selector widget.

[edit] Create a widget project

There is no project file required for a widget development. Each widget should simply have its component files stored in a folder. PNG images for your widget can be found under SDK examples/flagselector.

Here is our Flag Selector widget folder structure and component files:

\flagselector\
  widget.xml
  flagselector.he
  web_icon.png
  web_minimized.png
  web_miximized.png
  background.png
  finland.png
  eu.png
  un.png
Tips: The name of the folder does not necessarily be the same as the name of the widget or its Helium script source files’ name.

[edit] Create the widget.xml

The widget.xml file contains different types of information of a widget, in order to keep it simple we will split the file contents into numbers of subareas and only essential areas are discussed in this document. (For more details about the widget.xml contents please refer to dev.widsets.com/wiki).

First, lets get familiar with the format of a widget.xml file then we will go through each element block (saying element block, it is referred to the block of information within the mentioned element‘s start-tag and the end-tag according to the “well-formed” rule of XML) in the following subsections.

Figure 8: Widget.xml file format
Figure 8: Widget.xml file format

[edit] The widget element

The widget element is the root element of the widget.xml content. Every widget’s information must be placed within this root element. The widget element has one attribute and it must be specified to declare the WidSets’s specification version. Currently WidSets supports the specification version 2.0. Widget configuration a backwards compatible system and changes that break compatibility will be announced in the WidSets developer forum.

More profound information about the widget specification can be found here Widget Configuration 2.0.

Also the widget.xml file MUST be encoded with UTF-8 encoding. Thus, our Flag Selector widget would have the following XML code lines in its widget.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<widget spec_version="2.0">
  ...
</widget>

[edit] The info element

It could be a good practice to start with the widget’s Meta data such as the name of the widget, its version, the author’s name and a short description about the widget. The info element block is where these properties are defined for your widget. The info element has numbers of mandatory and optional subelements. In this document, we will only discuss those mandatory fields.

Subelement name Min length Max length Description Name 1 32 Name of the widget. This name is not used for showing in the name tag of the widget in the WidSets server version major_ver.minor_ver Version of the widget. This version number is shown in “published widgets” list of the WidSets manager. author 3 32 Name of the author. This doesn’t need to be the uploader and publisher of the widget. shortdesc 3 160 Short description of the widget

Here is the info block for our Flag Selector widget:

<info>
  <name>Flag Selector</name>
  <version>1.0</version>
  <author>Developer</author>
  <shortdescription>Displaying some flags</shortdescription>
</info>

[edit] The parameters element

Parameters can be used in numerous different use cases. Basically parameters are helpful dynamic containers of settings related data. Paramter values are always kept on the WidSets server, and for example reloading a widget always defaults to these parameters.

In comparison, values that are kept in the client side storage are always scrapped when the widget is reloaded. Usable parameters need to be defined in the widget.xml and thereafter can be used and changed in the code of the widget.

Widget parameters are also tightly integrated into the WidSets client UI and parameter values can be changed under each widgets settings page (Widget / Settings). Additionally the widget developers have control over the editability, visibility and of course name and type of parameters while creating their own widgets. Parameters do not necessarily have to be sent to the mobile either.

<parameters>
  <parameter type="string"
               name="widgetname"
               description="Name of widget"
               help="This is the name of the widget"
               editable="false"
               sendtomobile="true"
               visible="true">
    <value>
      Flag Selector
    </value>
  </parameter>
</parameters>

[edit] The resources element

A widget may have some graphics and styles to be displayed in its user interface components, it may also contain Helium scripts to perform some interactivities. The resources element block is where you can define all these resources for your widget.

To define the graphic resources, use the img element and define its attributes as follows:

<img scale=”true” src="someimage.png"/>

The scale attribute is set to “true” or “false” to tell the WidSets server whether or not to rescale the image size to be suitable for a mobile device’s screen resolution while the widget is downloaded to that mobile device.

It is good to know that the CSS structure used in WidSets is W3C’s CSS specifications compliant. However, WidSets uses its own version of CSS properties and values for defining widget’s UI styles. All style definitions are defined within the stylesheet element. The style definition syntax is made up of three parts: a style name, a property and a value or values (note that some property can have multiple values).

The following example styles are defined and used through out our Flag Selector widget:

<stylesheet>
  bkg {
    background: vgradient white silver;
  }
  
  frame {
    background: solid silver;
  }
  
  flag {
    align: hcenter vcenter;
    border: 2 2 2 2;
    border-type: rectangle red;
  }
  
  nametext {
    align: hcenter vcenter;
    color-1: green;
    font-1: medium bold;
    background: vgradient silver white;
  }
  
  greetingtext {
    align: hcenter vcenter;
    color-1: blue;
    font-1: medium bold;
    background: vgradient white green;
  }
  
  about {
      align: hcenter vcenter;
      color-1: blue;
      font-1: large bold;
      background: solid orange;
  }
</stylesheet>

CSS properties and values are quite self-explanatory terms. For instance in the snippet codes above, we have defined the style named bkg for the background of the widget. The bkg style has the background property assigned with a value of vertical gradient [vgradient] with the colour starting from white and gradually changing to silver. For more information about styles and their usage, please refer to the Online WidSets Wiki documentation. If a widget has extension functions, they should be implemented in a Helium-language script file with a filename extension .he (e.g. flagselector.he), and the source file must be declared in the code element as shown below:

<code src=”flagselector.he”/>
  • Note: Each widget can have only one scripting source file!

[edit] The layout element

Each widget is constructed out of views and the layout element embraces all views of a widget. It has two possible direct child element types as view and/or webview element. The layout element also has one mandatory attribute named “minimizedheight”, which is used for defining the height of the widget when displayed in the WidSets client view (a.k.a the WidSets’s dashboard). Each view or webview element has attributes and possible children elements as shown below: Attributes: class id top right bottom left width height. Children: label text decorate img script. Let’s have a look at our Flag Selector layout definition:

<layout minimizedheight="60px">
  <view id="miniView">
    <img src="minimized.png"/>
  </view>
  <view class="bkg" id="mainView">
    <script id="flag" top="10%" right="80%" bottom="60%" left="20%"/>
    <script id="name" top="65%" right="100%" bottom="80%" left="0%"/>
    <script id="greeting" top="80%" right="100%" bottom="100%" left="0%"/>
  </view>
</layout>

From the codes above, we have defined the minimum height of 60 pixels for our widget to be displayed on the dashboard. The minimized view of the widget is represented by the minimized image, where the resource is minimized.png.

Another view of our widget is called the main view, which is identified by the id attribute (i.e. id=”mainView”). It is designed to have the main view’s background style as defined in the bkg stylesheet (i.e. class=”bkg”). The mainView element has three children and in this case, all are script elements. You can see that each script element also has an id attribute for identification, and other attributes for defining its position on the screen. We don’t give these UI components a style yet, since we will set its style when constructing them in the Helium script file as explained in the next section.

[edit] Create and implement a Helium language script file

It is time to write a few functions for our Flag Selector widget, we are now going to create a Helium script file and implement some basic WidSets functions.

Create a new text file and define the only class of our widget as follows:

class
{
}

Save this file as flagselector.he. It is ready to define and implement any functions within the class above.

Let’s start with the startWidget() function (remember that Widget’s key functions descriptions were explained in section 3.3),

void startWidget()
{
  setMinimizedView(createView("miniView", null));
}

Within this function, we call the createView() function and pass two parameters as required, the identifier of the view and the context (in this example, the context is set null). The createView() function returns an object of Flow type, which is an input for the setMinimizedView() function. As a result, you will see the minimized image of the widget on the dashboard.

We now continue with the openWidget() function,

Shell openWidget()
{
  flags = new List()
    .add(getImage("finland.png"))
    .add(getImage("eu.png"))
    .add(getImage("un.png"));
   
  mainShell = new Shell(createView("mainView", null));
  updateScreen();
  return mainShell;
}

First, we have a global variable called ‘flags’ and it is an instance of the List object. Creating the list and adding images to that list will be much convenient for us to display the image later when needed. One important note here is that, this is only a demo purpose to display some images as the widget’s graphic resources. It is not the technique to implement widget that displaying pictures as content downloaded from a server such as Flickr.

We also have a global variable named ‘mainShell’ which is an instance of the Shell object, This time we call the createView() and passing the “mainView” as a parameter to create the main view of our widget. updateScreen() is our own function that we will implement later to update the data and display them on the screen. openWidget() function then returns the “mainShell” to the system.

Calling createView() will trigger the system to call the createElement() callback function. Here is the implementation of the createElement() function in our widget:

Component createElement(String viewId,
                        String elementId, 
                        Style style,
                        Object context)
{
  if (elementId.equals("flag")) {
    flag_flow = new Flow(getStyle("frame"));
    flag_flow.setPreferredSize(-100, -100);
    return flag_flow;

  } else if (elementId.equals("name")) {
    name_flow = new Flow(getStyle("nametext"));
    name_flow.setPreferredSize(-100, -100);
    nameStr = new Text(getStyle("name"), "");
    name_flow.add(nameStr);
    return name_flow;
  
  } else if (elementId.equals("greeting")) {
    greeting_flow = new Flow(getStyle("greetingtext"));
    greeting_flow.setPreferredSize(-100, -100);
    greetingStr = new Text(getStyle("greetingtext"), "");
    greeting_flow.add(greetingStr);
    return greeting_flow;

  } else {
    return null;
  } 
}

Since we defined only one view in the layout element block in the widget.xml file, (i.e the “mainView”, “miniView” can be ignored because it does not have other UI component than the minimized image) we don’t need to detect the viewId. Using ”if” and “else if” statements to check the element’s id, in this case, the script element’s id, which was defined in the widget.xml within the “mainView” element.

Take the case that we detect the element’s id, which is equal to “greeting”,

else if (elementId.equals("greeting"))

We then construct the greeting UI component as follows:

We have two global variables called “greeting_flow” and “greetingStr” as an instance of the Flow object and of the Text object respectively. The “greeting_flow” is a container, which will hold the “greetingStr” when we call the add() function and passing “greetingStr” through the function’s parameter.

greeting_flow.add(greetingStr);

The getStyle() function returns a Style object named “greetingtext”, the style was defined in the stylesheet in the widget.xml file. When creating the container, we give it the style that returned from the getStyle() function (that is why we did not need to specify the class attribute while defining the “greeting” script element). The style “greetingtext” is also set for the “greetingStr” instance.

greeting_flow = new Flow(getStyle("greetingtext"));
greetingStr = new Text(getStyle("greetingtext"), "");

The createElement() function then returns the greeting_flow to the system. The createElement() function is called as many times as the number of child elements of the view element being created.

Next steps are to create a menu for the widget. We start just with a simple menu with a “Back” option associated with the right softkey of a mobile device. To do this, we define in the global scope the widget’s menu properties as follows:

const int CMD_BACK = 1;
MenuItem BACK = new MenuItem(CMD_BACK, "Back");

Then we implement two functions to handle menu events as follows:

MenuItem getSoftKey(Shell shell, Component focused, int key)
{
  if (key == SOFTKEY_BACK) {
    return BACK;
  }
  return null;
}

void actionPerformed(Shell shell, Component source, int action)
{
  switch(action)
  {
    case CMD_BACK:
      popShell(shell);
      break;
  }
}

From the codes above, you can see that we have a menu command id CMD_BACK and a menu item BACK, which is assigned with the CMD_BACK command id and the text string “Back” as its name (this name will appear on the right side of the softkey pane of the widget). The getSoftKey() function is called every time when a user clicks on one of the two softkeys of a mobile device. Since our widget is a simple one with only one view, we may ignore the “shell” and “focused” parameters passed along in this function. Now we need to detect which one of the two softkeys was just clicked. WidSets provides two identifiers as SOFTKEY_BACK and SOFTKEY_OK for the right softkey and for the left softkey respectively. In our widget, we assign the right softkey with the BACK menu item. That is why we return the BACK menu item to the system when the “key” equals to SOFTKEY_BACK. At this point, if the user clicks on the left softkey, we will return “null” and nothing will happen.

After the getSoftkey() function is called, the system will call the callback actionPerformed() function so that we can detect which command action the user has just selected in order to perform appropriate actions. Supposed that the user clicks on the right softkey, CMD_BACK is the command action, and what happens next is that our widget should return to the minimized mode. To do so, we call popShell() function to pop the current view (contained in the “shell” parameter) out of the view stack. Since currently the widget has only one view on the stack, it returns to the minimized mode. It is good to mention here also that when a shell is created in the openWidget() function, it is returned to the system and automatically pushed into the widget’s view stack. We will discuss more about menu and view stack manipulation later when we add more advanced features to the widget.

It is time to have a look at our own functions, which handle the image and texts to display on the screen.

void updateScreen()
{
  if (index == 3) {
    index = 0;
  }

  switch (index) {
    case 0:
      nameStr.setText(FINNISH_TEXT);
      greetingStr.setText("Terve Suomi!");
      break;
    case 1:
      nameStr.setText(EU_TEXT);
      greetingStr.setText("Hello European Union!");
      break;
    case 2:
      nameStr.setText(UN_TEXT);
      greetingStr.setText("Hello United Nations!");
      break;
  }
  changeFlag(index);
  index++;
  flushScreen(true); 
}

void changeFlag(int index)
{
  flag_flow.remove(0);
  Picture flagImage = new Picture(getStyle("flag"),
                                  Image(flags[index]));
  flagImage.setFlags(VISIBLE);
  flagImage.setPreferredSize(-100, -100);
  flag_flow.add(flagImage);
}

In the updateScreen() function, we just detect the index value and set the texts to be displayed accordingly. The index value is increased by 1 every time we call this function and it is reset to 0 when it rises equally to 3. We also call the changeFlag() function and passing the index value via the function parameter.

In the changeFlag() function, we remove the first item in the “flag_flow” container (this is actually not necessary for the first time this function is called since the container is empty when it is constructed in the createElement() function). We declare and create a “flagImage” variable as an instance of the Picture object. The reference image source is fetched from the “flags” list at the position defined by the index value. The image of a flag is loaded by casting the image source to an Image object.

Image(flags[index]);

We also need to set “flagImage” to be visible and its preferred size. Finally, we add the “flagImage” to the “flag_flow” container and return to the updateScreen() function, where we call flushScreen() function to force the system repainting the widget’s view immediately.

[edit] Create the widget graphic resources

The size of each graphic resource was already mentioned in section 3.2. Besides the resource files for WidSets Website and the minimized image, our widget has three other graphic resources, which is the flag of Finland, the flag of the European Union and the flag of the United Nations.

[edit] Packing the widget files for uploading to WidSets server

If you want to deploy a widget to the WidSets server by using the upload form in the WidSets Website, the widget project’s files must be compressed in a .zip file before it can be uploaded. The .zip file must not contain the project’s folder itself. To achieve this, select all the project’s files then zip them rather than click on the project folder and zip it.

Another (easier) way to upload a widget to the WidSets server is to use the devkit tool from the WidSets DevKit. All you need to do is to login to the server, then at the prompt, issue the “run” command as follows:

devkit run flagselector

Where flagselector is the Flag Selector widget project’s folder.

Devkit will first zip files in your directory, upload them to server and then start the emulator. When emulator is started in run widgetdirectory mode it will automatically re-upload the widget whenever you change the widget.xml file, and remove possible old widget with the same widgetName from your dashboard. Also if you change your Helium script file while emulator is running, the widget will be reloaded with new script.

Figure 9: WidSets manager
Figure 9: WidSets manager

[edit] Loading the widget to a target device

If you have installed the WidSets client into your mobile device, start WidSets client and login to the WidSets server the widget will be automatically loaded into the WidSets dashboard. If the WidSets client is already started and you uploaded it using web form you need to click on the “synchronize” button on the WidSets manager to load it to your device.

Note: If you are using same dev.widsets.com user account on both devkit and your mobile device, you need to close the emulator or turn it into offline mode before using mobile device, and vice versa. Only one device can be connected to the server at same time using same user account.

[edit] Publishing the widget

Uploading your widget to the WidSets server is just to make your widget available in your own account. If you wish to publish your widget in the WidSets developer library, use the WidSets manager with following steps:

  1. Select the widget from the manager board. The Option button below the board will be enabled automatically.
  2. Click on the Option button. This will bring you to the next page where you can choose to publish your widget as a new widget or to update if it was published before.
  3. Let’s select the “new widget” radio button and click the Ok button. This will again bring you to the next page, where you should fill on several fields such as description etc. and accept the disclaimer before WidSets can actual allow you to publish your widget.
Note: To get your widget published to www.widsets.com library, it must be tested on dev.widsets.com by multiple users, then you can ask on developer forums if admins can publish it to the end user library.

[edit] ADDING SOME ADVANCED FUNCTIONS TO THE WIDGET

Now we can go on a little bit further to give our widget some advanced functionalities.

[edit] Handling menu options

First, we add a menu option with several menu items associated with the left softkey.

To do so, we define in the global scope some more menu properties as follows:

const int CMD_MANUAL = 10;
const int CMD_TIMER = 11;
const int CMD_ABOUT = 12;

MenuItem OPTIONS = new MenuItem(OPEN_MENU, "Options");

Menu MENU = new Menu()
  .add(CMD_MANUAL, "Manual")
  .add(CMD_TIMER, "Timer")
  .add(CMD_ABOUT, "About");

Now our widget has three more menu command ids defined and a menu item OPTIONS. This OPTIONS is associated with a system defined constant OPEN_MENU, which tells the system that this is an open menu. The OPTIONS menu item also has a name as “Options”, which is shown on the left side of the softkey pane.

We also need to create a menu called MENU and add the three command ids as shown in the codes above. Each item in the menu will have a command id and a name.

Then we modify the two functions implemented in section 5.3 and implement one more system function as follows:

MenuItem getSoftKey(Shell shell, Component focused, int key)
{
  if (key == SOFTKEY_OK) {
    return OPTIONS;
  }
  else if (key == SOFTKEY_BACK) {
    return BACK;
  }
  return null;
}

void actionPerformed(Shell shell, Component source, int action)
{
  switch(action) {

    case CMD_BACK:
      if (scheduler != null) {
        scheduler.cancel();
      }
      popShell(shell);
      break;

    case CMD_TIMER:
      scheduler = schedule(2000, 2000);
      break;

    case CMD_MANUAL:
      if (scheduler != null) {
        scheduler.cancel();
      }
      updateScreen();
      break;

    case CMD_ABOUT:
      MENU.enable(CMD_TIMER, false)    
          .enable(CMD_MANUAL, false)   
          .enable(CMD_ABOUT, false);
          ...
      break;
  }
}

Menu getMenu(Shell shell, Component source)
{
   return MENU;
}

In the first function, when we detect that the left softkey was click (i.e. key equals to SOFTKEY_OK), we return the OPTIONS menu item to the system. Since this is an open menu (as explained previously), the system will call the getMenu() function to get a menu that the widget wants to open. In this case, we return the MENU, which was created previously. As a consequence of the action above, the widget will have its Options menu opened with three selections named “Manual”, “Timer” and “About” as we defined them for the widget’s menu.

Detecting user’s actions within the switch case loop in the actionPerformed() function is the same way as we discussed in section 5.3. Handling user’s actions and enabling/disabling the menu commands will be discussed in the next subsections.

[edit] Using a timer

Supposed that the user has just clicked the “Options” menu and select the “Timer” command. We detect the user’s action and create a timer using the schedule() function. Our widget will use this timer to change the displayed flag and texts every 2 seconds.

To do so, we set the timer and implement the callback function as follows:

Timer scheduler;
...
case CMD_TIMER:
  scheduler = schedule(2000, 2000);
  ...

void timerEvent(Timer timer) {
  updateScreen();
}

The schedule() function being used in this example takes 2 parameters (there are several overloaded schedule() functions), the first parameter is the delay time, after which the first time the timer will expire. The second parameter is the time interval, after which the timer repeatedly strikes. When the timer strikes, the system will call the callback timerEvent() function, where we will call updateScreen() to change the displayed flag and texts. Timer is system “expensive” resource. Therfore, you should carefully design your widget to limit the usage of such “expensive” resource and/or free the resource as soon as you don’t need it anymore. You will see through out the example, the scheduler is cancelled when switching to other view or when the widget is in its minimized mode.

[edit] Creating new view dynamically

Widget’s view can be created dynamically without the need of defining a view element in the widget.xml. We are now going to create dynamically the “About” view for the Flag Selector widget.

Let’s continue where the user has just clicked the “Options” menu and seleted the “About” command.

case CMD_ABOUT:
{
  MENU.enable(CMD_TIMER, false)
      .enable(CMD_MANUAL, false)
      .enable(CMD_ABOUT, false);

  if (scheduler != null)
      scheduler.cancel();

  Text about_text = null;
  Flow about_flow = null;
  Shell infoShell = null;

  about_flow = new Flow(getStyle("About"));
  about_flow.setPreferredSize(-100, -100);

  about_text = new Text(getStyle("About"), "Congratulation!");
  about_text.setFlags(VISIBLE|WRAP);
  about_text.setPreferredSize(-100, -100);
  about_flow.add(about_text);   

  infoShell = new Shell(about_flow);
  pushShell(infoShell);
  break;
}

Since we are about to leave the main view, and in the “About” view we don’t need the “Options” menu, so we are going to hide all the menu commands of the “Options” menu by calling the enable() function of the Menu object and passing the menu command id and the flag set to “false”. We do so for the three menu commands as shown in the code above.

We won’t need the timer anymore when entering the “About” view. That is why we check if the timer is active, we then cancel it to release the resource.

Next steps is to create the “About” view and display it. We define three local variables as an instance of Text, Flow and Shell objects. We also need to initialize them with “null” value (Helium strict rule). The construction of the “about_flow” container and the “about_text” object is exactly the same way as implemented and explained previously in the createElement() function implementation. What is new here is the “infoShell” which contains the “about_flow” and pushed into the view stack via pushShell() function.

When a new view is pushed into the view stack, the system will automatically update the widget’s view with the new view.

When a user clicks the “Back” menu, we will return to the main view simply by calling the popShell() function:

case CMD_BACK:
  if (scheduler != null) {
     scheduler.cancel();
  }
  popShell(shell);
  break;

If the “Back” menu is clicked while we are in the main view, there is no more view in the view stack, the widget will return to the background. That is its minimized mode on the dashboard and waiting for user’s action to open it again.

[edit] More information

For more information about creating widsets, please also check other articles in the WidSets Wiki.

Check also the WidSets Developer Forum, where you can discuss with other members of the WidSets developer community.

Personal tools