Creating C/C++ Apps

The guides and tutorials in this section of the documentation provide guidance and examples for programmers writing C/C++ applications with MoSync. We cover the three basic development models that you can follow with MoSync, and show how to use some of the many library functions.

C/C++ Beginner's Guides

Creating Your First C/C++ Application

This tutorial walks you through the creation of a simple "Hello World" application in C/C++ using the MoSync IDE and introduces you to some of the basic terminology we use throughout our guides, tutorials, and examples.

Creating a New Project

Start by Launching MoSync. MoSync prompts you to select a workspace:

Click OK to accept the default path. The MoSync IDE will open.

Register you copy of MoSync if you have not already done so. After registration, close the Welcome page if it is showing.

Create a new project by right-clicking in the Project Explorer view then choosing New > Project

( If you have hidden the Project Explorer view, you can show it again by selecting Window > Show View > Other > General > Project Explorer and then clicking OK.)

When the New Project window opens, expand the MoSync folder and select MoSync Project:

Click Next.

The project name and location screen appears:



Enter the Project name "HelloWorld". (To ensure compatibility with all platforms and devices, avoid using spaces in project names. It is always safe to use the underscore character.)

Tick the Use default location box. Click Next.

A new window will open showing you the templates that are available for you new project:

For more information about these templates, see Creating Projects from Templates.

Select the MoSync Moblet Project template. Select it and click Finish.

Your new project will now be created from the template and loaded in the MoSync IDE:

Creating HelloWorld

Edit the code in the main.cpp file shown in the main window so that it looks like this:

#include <MAUtil/Moblet.h>

using namespace MAUtil;

class MyMoblet : public Moblet
{
public:

    MyMoblet()
    {
        maSetColor(0xFFFFFF),
        maDrawText(0, 32, "Hello World!");
        maUpdateScreen();
    }

    void keyPressEvent(int keyCode, int nativeCode)
    {
	if(keyCode == MAK_0 || keyCode == MAK_BACK || keyCode == MAK_SOFTRIGHT)
        {
            close();
        }
    }
    void keyReleaseEvent(int keyCode, int nativeCode)
    {
    }
};

extern "C" int MAMain()
{
    MyMoblet myMoblet;
    Moblet::run( &myMoblet );
    return 0;
};

Save your main.cpp file.

(If you would like to understand more about the code we just asked you to paste in, read our beginner's tutorial called Hello World, Deconstructed.)

Running Your Application

Your application is now ready to be built and run.

Click on your project's name in Project Explorer view so that it is highlighted.

Now click the Run button on the IDE's toolbar (or press Ctrl+F11). Your project will be built, and your application will run in MoRE, the MoSync emulator:



Congratulations, you have now compiled and executed your first MoSync program!

What Next?

In our User Guides you will find extensive information about the MoSync IDE, including how to create projects from templates, how to use device profiles, doing bluetooth discovery and transfer, and how to use the MoRE emulator, the Debugger, the Finalizer, and MoSync's tools.

In our Programmer Guides we teach how to use MoSync's classes, functions, and syscalls in your applications, and we also provide tips for creating applications that work across multiple platforms like iPhone, Android, and Windows Mobile.

Our extensive Tutorials library covers all aspects of using the MoSync SDK to creating working applications. You will find lots of helpful code snippets here, and step-by-step instructions for common coding tasks.

We provide many Example Applications in the SDK itself, so you can see how we do it ourselves. If you are new to C/C++, we strongly recommend that you spend some time examining the code of our "Hello" series of applications: they are extremely well-commented so that you can unserstand exactly what is happening in every line of code.

At our website you can find many resources for developers, including our developer forum, and links to our code repositories and issue tracking systems.

Have fun, and good luck!

Hello World

This screencast shows how to write a simple C++ program using the Moblet template. The program sets the background colour, draws text "Hello World", updates the screen, and handles a key press event.

HelloWorld

HelloWorld is a well-commented example for beginners of a very simple MoSync application that uses the Moblet framework. The application demonstrates how to structure the simplest possible application that responds to key events.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When run, the words “Hello World!” should appear on an otherwise blank screen. Examine the source code of the application (in the file helloworld.cpp) to learn how the program works. We also provide detailed documentation for this example in our Hello World, Deconstructed tutorial.

Key Presses

  • 0 key, back button, or right-softkey - exits the program

HelloWorld, Deconstructed

In our beginners tutorial called Creating Your First Application we skipped over many features of the "Hello World" application that we asked you to paste into the IDE. In this tutorial we are going to take a much deeper look at that code, examine the basic structure of a C++ application, and discuss some basic design decisions we have made in the MoSync approach to mobile application development.

A note to absolute beginners: C/C++ applications can be a little overwhelming at first, but with a little persistence you will soon get the hang of it. As you go through this tutorial, try typing each line into the MoSync IDE, rather than just cutting and pasting. That way you''ll get a better feel for laying out the code and getting the syntax right. As you type, you will be given interactive help by the IDE, which can be very useful. Remember to use the Moblet Project template when you create you project.

A note to experienced C++ developers: You probably know a lot of this stuff already, so feel free to skip ahead to our extensive collection of tutorials and example applications. On the other hand, if you do read on, maybe you will learn something important you didn't know about C/C++ or MoSync, like that tricky MAMain entry point....

Hello World (Naked)

Here is the code we asked you paste into the MoSync IDE in our Creating Your First Application tutorial:

#include <MAUtil/Moblet.h>

using namespace MAUtil;

class MyMoblet : public Moblet
{
public:
    MyMoblet()
    {
        maSetColor(0xFFFFFF),
        maDrawText(0, 32, "Hello World!");
        maUpdateScreen();
    }

    void keyPressEvent(int keyCode, int nativeCode)
    {
        if(keyCode == MAK_0 || keyCode == MAK_BACK || keyCode == MAK_SOFTRIGHT)
        {
            close();
        }
    }

    void keyReleaseEvent(int keyCode, int nativeCode)
    {
    }
};

extern "C" int MAMain()
{
    MyMoblet myMoblet;
    Moblet::run( &myMoblet );
    return 0;
}

Now let's take a close look at what is going on in each of these lines of code.

Including Library Classes

The program starts with a directive to include the contents of the Moblet.h header file from MoSync's MAUtil directory:

#include <MAUtil/Moblet.h>

In C/C++, header files are an important way to connect together the different files of a program. It is common practice to put the generic code that you might want to use in many places in a separate file and two create a header file that contains forward definitions (declarations of classes, functions, and data types) for that generic code. Then you use an #include statement to include the header file in your other code modules. The header file acts like an interface to your generic code. That way you can reuse your generic code in many places, but only have to make updates to it in one place. For information, see Working with MoSync Libraries.

Here we are using a header file to connect our main application file with the pre-compiled library file (mautil.lib) that contains the generic code for Moblets.

The "#" sign indicates that this directive is to be handled the preprocessor — a program that will be called by the compiler as the first step of translating our source code into an executable binary file. In this case the directive instructs the preprocessor to read in the declarations in Moblet.h. We will explain what a Moblet is, and how you use it, a little later on.

Using Namespaces

Our next statement specifies that we will be using the MAUtil namespace as the base reference for our objects:

using namespace MAUtil;

The effect of using this namespace is to say "look in the set of things called "MAUtil" to find the definitions of the identifiers I use in this program". By specifying the namespace here our code becomes more readable. If we didn't have this line, we would have to explicitly reference the scope of MAUtil objects, we would have to write MAUtil::Moblet every time we wanted to use the Moblet class.

Inheriting From a Base Class

The next thing we need to do in our application is define its classes. Here we define a new class, based on the Moblet class, called MyMoblet:

class MyMoblet : public Moblet {

Moblet is a MoSync base class - a class which is meant to be built upon to do something useful. In the C++ world, a class is essentially a set of data and functions which manipulate that data (often referred to as instance variables and methods in other object-oriented languages).  A class is a blueprint: in a running application the class is used to create actual objects (instances) as they are needed.

Usually a class represents something concrete — although it's sometimes a little hard to understand what that thing is. In the case of Moblet it represents a simplified event handler for a mobile device.

As we will see later, Moblet takes care of the application main loop for you. All you need to do is subclass it and implement functions for the events that you want to your program to respond to (such as key presses, screen touches, and new connections). A Moblet application responds to events in a consistent way regardless of the device it runs on. That means we do not need different code for an Android device, Symbian device, a Windows Mobile device, or a phone running Java ME.

To learn more about the events that Moblet can detect, and the various types of listeners you can set up, look up Moblet in the MoSync API Reference Guide in the IDE under Help > API Reference > Classes > MAUtil::Moblet.

Our new class, MyMoblet inherits all the chracteristics of the Moblet base class, and adds to it. What we will be adding are functions that will perform actions when events occur.

The keyword "public" before the Moblet identifier means that other classes and functions in our application can access all the public members of the Moblet base class when using MyMoblet. That is to say, what is defined as public in Moblet becomes public in MyMoblet, even if we don't get around to listing them in MyMoblet.

Declaring Public Members

The data members (instance variables) and member functions (methods) within a class can be either public or private. If they are in the public section of the class, they can be accessed from other classes. Here we start the list of public members of our MyMoblet class:

public:

Defining a Constructor

The first public member of our MyMoblet class is a method, the constructor. The constructor gets called when an instance of the class (an object) is created. In this case it is a parameterless constructor as we don't need to pass in any data:

    MyMoblet() {

(In the case of MyMoblet there is no destructor to clean up after the object once it has fulfilled its usefulness, simply because it would be empty. Constructors and destructors can be implicit: if you don't specify a constructor or destructor, the compiler will put it in.)

Using Syscalls

The MyMoblet constructor contains three function calls which will be executed in sequential order. (You can distinguish function calls from data members in a class because they are immediately followed by 0, 1, or more parameters in parentheses.)

These three function calls - we call them syscalls - are to low-level, device-independent functions that are defined in the main MoSync syscall header file, maapi.h. You'll be using the functions defined in maapi.h a lot in your applications, so it's worth getting to know it intimately. Read about maaip.h syscalls in the MoSync API Reference Guide in the IDE under Help > API Reference > Files > maapi.h.

The first syscall sets the colour of the output to white:

        maSetColor(0xFFFFFF);

From now on, until it is explicitly changed, all output from text and drawing commands will be displayed in this colour. Note that each statement within the method is terminated by a semicolon in C/C++.

The second syscall in the MyMoblet constructor renders the text "Hello World!" to the backbuffer at the coordinates x=0 and y=32:

        maDrawText(0, 32, "Hello World!");

The coordinates 0,0 are the top left corner of the screen. The y-axis increases as you head downwards, the x-axis increases as you head rightwards.

To make screen update flicker free, drawing is done to a so called backbuffer, which later, when everything is drawn, will be copied in to the display. Thus the text is not yet visible at this point in the program.

The third syscall is what updates the physical screen of the device with the contents of the backbuffer:

        maUpdateScreen();

If you forget this last statement, and that's easy to do, you would just see a blank screen when you ran your application!

Grouping Statements With Brackets

Curly brackets are used to group statements in C/C++. Now that we are at the end of the constructor, we close the opening curly bracket at the beginning:

    }

(The bracket that marks the beginning of the class will be closed it later on, when our class is complete.)

Detecting Key Presses

The second public method of our MyMoblet class is keyPressEvent:

    void keyPressEvent(int keyCode, int nativeCode) {

Before the name of the method, we need to state the type of the data that the method returns. In this case, the method does not return anything ("void" = nothing).

The keyPressEvent method is called whenever a key is pressed on the device's keypad. It is passed two parameters by the MoSync runtime — the runtime is that part of MoSync which interfaces with the device's operating system and which handles the syscalls from your application.

The first parameter is a device-independent code representing a key, a MAK code. Regardless of the device, if the 0 key on the device's keypad is pressed, the keycode will be MAK_0, and if the device has a back key it will be MAK_BACK.

You can find all the MAK codes in the MoSync API Reference Guide in the IDE under Help > API Reference > File Members > Defines > M.

The second parameter is device-specific (native) key code. That's needed because the MoSync MAK codes are not an exhaustive list of all the buttons ever made available on a mobile device, instead they are a common subset. Your phone may have a button which is specific to that model, for instance the volume keys or the back button on an Android phone. If you know what the value of those keys, you can act on them here.

The keyPressEvent method is one of several methods we have inherited from the Moblet base class. Here we replace (override) the method with our own version. When the program runs and an instance of MyMoblet is created, the C++ runtime system will detect that the instance has a method that replaces the method in the base class. Methods that can be overridden like this are called "virtual methods" in C++.

Closing Applications

Now we define what should happen if the zero key on the keypad has been pressed (or the back button, or the soft-right key), and in this case we are just going to exit the program using Moblet's close function:

        if(keyCode == MAK_0 || keyCode == MAK_BACK || keyCode == MAK_SOFTRIGHT)
        {
            close();
        }
    }

The close method releases any resources used by the object and ends the execution of the program.

The Moblet base class also includes a closeEvent method. That method can be used to detect a forced application close detected by the MoSync runtime on the device. By overriding it in your Moblet-based class, you can perform essential data-saving tasks - you only have a second or so! - before your application is forced to terminate by the devices OS.

Detecting Other Events

There are many other event detection methods in the Moblet base class that you can override, including pointer movements, focus changes, Bluetooth connection, timers, and so on. The keyReleaseEvent which comes next in our example is another method that you will commonly want to implement in a Moblet-based application. It isn't actually needed for this simple application, but it works in a similar way to the keyPressEvent we used above.

    void keyReleaseEvent(int keyCode, int nativeCode) {

For a complete list of the events See the MoSync API Reference Guide in the SDK under Help > API Reference > Classes > MAUtil::Moblet.)

Commenting Code

We've left a comment which you can replace with your own code for key release events:

        // todo: handle key releases - put your code here.
    }

Single line C/C++ comments are preceded by two forward slash characters // and terminate at the line end. Anything on the right side of both forward slashes would not be read by the compiler (actually the parser).

You can also use a single forward slash followed by an asterisk to indicate the start of a comment. To end the comment, type an asterisk followed by a forward slash, for example:

/* This is a comment */ 

This type of comment can be spread over several lines.

There are no private data members or functions needed for the MyMoblet class, so we can just close its definition now. Note that we need to put a semicolon after bracket that closes a class.

};

Now we have completed our definition of the MyMoblet class, which is the only class we need in this simple application. If we needed more classes, we would put them here.

Defining the Application Start Point

We move on to defining the entry point of our program. This is the place where the execution of the application starts when it is run:

extern "C" int MAMain() {

In C you always have a function called main() that is called when the program execution starts. In a MoSync application, the main() function is actually implemented in the runtime. (Remember, that's the part of the application that interfaces with the device's operating system, and hides the complexity of the OS from your application.) In your application you need to use MAMain() to mark its start point. Just think of MAMain() as a synonym for main().

Because C++ is an object-oriented programming language which includes (and is built upon) the procedurally-oriented C language, C++ programs can consist of both C++ classes (like MyMoblet) and C statements like this one. Unlike our earlier methods, this one is a C function (hence the extern "C" bit). A function is like a method, but is not associated with an object, and is defined outside of the classes in the program. In C++, you can freely mix between C and C++ but you always have to start your applications in C.

Running our Moblet Application

There is a static method in the Moblet class called run (a static method is associated with the class, not with any instance). This is what starts your application. To call the run method you need to pass it a pointer to a new instance of our MyMoblet class, so we create one with the "new" keyword:

    Moblet::run(new MyMoblet());

This constructs the MyMoblet instance and runs the code in the MyMoblet constructor.

Returning Results

C main methods should always return an integer (int), so we return 0, by which we indicate that the program has successfully ended:

    return 0;
}

And that's it. Our application is now ready to build and run.

Hello World (Fully Clothed)

/** 
* This application provides a very basic example of how to use MoSync
* to print the text "Hello World" to a device's screen. The code is very
* well commented so that you can see exactly what's happening at each step.
* The application makes use of MoSync's Moblet framework to handle events.
*
* @file helloworld.cpp
*
* @author Chris Hughes
*/

/*Include the header files for MoSync Moblets so that we can
*access the Moblet library code from our application.
*/
#include <MAUtil/Moblet.h>

/*Declare the MAUtil namespace so that we can use the short forms of
*identifiers in our code. This allows us to write, for example,  "Moblet"
*instead of "MAUtil::Moblet".
*/
using namespace MAUtil;

/*Create the wrapper for the entire application. It is here that we will manage
*the application and handle events. To create the wrapper we make our own
*implementation of the MoSync Moblet base class. There can be only one Moblet
*in an application.
*/
class MyMoblet : public Moblet
{

// Define our new class's public methods.
public:
	/*First, the constructor, which we will call whenever we need an
	*instance of MyMoblet. In this case, the constructor consists
	*of three syscalls.
	*/
	MyMoblet() 
	{
		// The first syscall sets the background colour.
		maSetColor(0xFFFFFF);

		// The second syscall writes the text "Hello World" to the backbuffer.
		maDrawText(0, 32, "Hello World!");

		// The third syscall copies the contents of the backbuffer to the
		// physical screen.
		maUpdateScreen();
	}

	/*Next, a method for detecting key presses. This is a method we have
	*inherited from the Moblet base class and here we will override that
	*method with some processing of our own.
	*/
	void keyPressEvent(int keyCode, int nativeCode)
	{
		//Close the application if the zero, back, or soft-right key is pressed.
		if(keyCode == MAK_0 || keyCode == MAK_BACK || keyCode == MAK_SOFTRIGHT)
		{
			close();
		}
	}

	// Finally, a code stub we might need to use later for another event type.
	void keyReleaseEvent(int keyCode, int nativeCode) 
	{
		// to do: handle key releases - put your code here.
	}
};

/*Now we get to the entry point for the application - the place where
*processing starts.
*/
extern "C" int MAMain() 
{
	// Create the instance of MyMoblet
	MyMoblet myMoblet;

	// Run the Moblet to start the application.
	Moblet::run( &myMoblet );

	/*MyMoblet will run until it is closed by the user pressing key 0. When
	*it's closed we end our program in a well-behaved way by returning zero.
	*/
	return 0;
}

What Next?

If you are new to C++, there are many useful references and tutorial sites on the Internet that can help you learn more. We particularly recommend the C++ language tutorials at http://www.cplusplus.com/doc/tutorial.

We have several other beginner's application examples which you can find in the MoSync IDE, for example: HelloWorld, HelloMoblet, HelloNativeUI, HelloOpenGLES.

We have a more advanced example of a Moblet application in our programmer's guide Event-Driven OO Applications, and in our tutorial Starting a New Moblet Project.

Development Models

MoSync supports several application development models. We call them the "classic procedural", "event driven, object oriented" and "full GUI-based". The model that you should choose when developing an application depends both on the type of application you are developing and your personal preference.

The Classic Procedural Model

Using the classic procedural approach means starting out with an empty main function and implementing your own main loop, including all the event handling. Examples...

Advantages Disadvantages When to use
  • Offers full control of the program flow
  • Provides ultimate flexibility in the design of the application
  • Supports programming in pure C, without C++
  • Resembles popular, procedural frameworks
  • All the burden of constructing well-behaved, resource efficient applications is on the programmer
  • Sometimes requires the programmer to reinvent the wheel
  • Might be unintuitive to people with a OOP background
  • Your application requires full control of the program flow and events
  • You want to implement your own higher-level layer on top
  • You prefer C over C++
  • You're porting existing code to MoSync

The Event-Driven, Object-Oriented Model

This approach is embodied in the use of the MAUtil::Moblet class. Inheriting it lets you implement functions such as keyPressEvent() instead of explicitly implementing an event loop yourself while providing TimerListeners and IdleListeners to facilitate execution of code outside of responding to events. Examples...

Advantages Disadvantages When to use
  • Takes care of boilerplate event handling correctly
  • Provides higher-level abstraction of program flow
  • Produces well-behaved, resource efficient programs by default
  • Resembles popular, event driven frameworks
  • Imposes a predefined application lifecycle model
  • Might be awkward for frame-based applications such as games
  • Requires use of C++
  • Most of the time when the GUI-based approach below is overkill

The Full GUI-Based Model

Using the MAUI library, you gain access to a variety of ready-made widgets such as labels, list boxes, text edit boxes, images, and layouts. You add logic by registering different types of listeners with the widgets, thus responding to higher-level events than with Moblets - things like selection and slider position changes. Examples...

Advantages Disadvantages When to use
  • Allows rapid development of GUI applications
  • Reduces program flow programming to responding to GUI events
  • Imposes a predefined UI model
  • Requries familiarity with the MAUI library
  • Requires use of C++
  • Whenever you're developing a reasonably traditional GUI application
  • When radically cutting development time outweighs full customizability

Classic Procedural Applications

In this development model you have full control over (and responsibility for) how the application behaves. This model is most suited when you are porting existing C applications, or you want full control over program flow and events .

It is important to understand how to correctly implement a MoSync event loop. There three most important points to consider are:

  • Checking for events often enough. The MoSync event queue is not infinite, so if you don't check often enough you might miss events.
  • Using maWait() rather than busy-waiting, to conserve CPU usage and battery power.
  • Responding to and handling the close event. After a close event is posted, your application will be forcibly terminated within a short period of time. However, your application should voluntarily exit as soon as possible, having saved any important data. (Note that after the close event has been posted, no further events will be posted and most syscalls will have no effect. See syscalls in the API Reference Guide for further information on this topic.

Example: A Simple, Well-Behaved Application

#include <ma.h>
int MAMain() {
	// Application initialization goes here

	for(;;) {
		MAEvent e;
		// Wait until we have an event
		maWait(0);  
		// Process all events that have occured
		while(maGetEvent(&e)) {
			if(e.type == EVENT_TYPE_CLOSE) {
				// do cleanup
				maExit(0);
			}
			else if(e.type == EVENT_TYPE_KEY_PRESSED) {
				// It's good practise to always provide one key
				// to exit the application.
				if(e.key == MAK_0) {
					// do cleanup
					maExit(0);
				}
				// handle other key presses
			}
		}
	}
	return 0;   
}

This example only checks for key presses, but note that results of asynchronous operations are also passed as events and should be handled similarily. See the syscall reference for more information.
Working with connections in classic applications involves responding to events whose type is EVENT_TYPE_CONN. Connection operations are executed asynchronously, and these events are the way in which your application is notified of their progress, results and termination.

Example: A Classic application Using Connections

 

#include <maapi.h>
#include <conprint.h>
int MAMain() {
    char buffer[160];
    // Application initialization goes here.
    InitConsole();

    // Create a connection.
    printf("Connecting...\n");
    MAHandle conn = maConnect("http://www.example.com/");  // Will cause a CONNECT event.
    // The event loop.
    for(;;) {
        MAEvent e;
        // Wait until we have an event.
        maWait(0);  
        // Process all events that have occured.
        while(maGetEvent(&e)) {
            if(e.type == EVENT_TYPE_CLOSE) {
                maExit(0);
            }
            else if(e.type == EVENT_TYPE_KEY_PRESSED) {
                if(e.key == MAK_0)
                maExit(0);
            }
            else if(e.type == EVENT_TYPE_CONN) {
                if(e.conn.opType == CONNOP_CONNECT) {
                    // The Connect operation is complete.
                    if(e.conn.result < 0) {
                        printf("Connect error %i\n", e.conn.result);
                        // Close the connection, freeing resources.
                        maConnClose(conn);
                    } else {
                        printf("HTTP result %i\n", e.conn.result);
                        // Start reading data.
                        maConnRead(conn, buffer, sizeof(buffer) - 1);  // Will cause a CONN READ event.
                    }
                } else if(e.conn.opType == CONNOP_READ) {
                    // The Read operation is complete.
                    if(e.conn.result == CONNERR_CLOSED) {
                        printf("Connection closed.\n");
                        maConnClose(conn);
                    } else if(e.conn.result < 0) {
                        printf("Read error %i\n", e.conn.result);
                        maConnClose(conn);
                    } else {
                        printf("Read %i bytes:\n", e.conn.result);
                        // Zero-terminate buffer.
                        buffer[e.conn.result] = 0;
                        PrintConsole(buffer);
                        // Read more data.
                        maConnRead(conn, buffer, sizeof(buffer) - 1);
                    }
                }
            }
        }
    }
    return 0;
} 

Event-Driven OO Applications

The MAUtil::Moblet C++ class provides boilerplate event handling and produces well-behaved, resource-efficient programs. It helps provide a higher-level abstraction of program flow.

The MAUtil::Moblet class takes care of the application main loop for you. All you need to do is subclass it and implement virtual functions to respond to the events.

Example: A Basic Moblet Application

#include <MAUtil/Moblet.h>
using namespace MAUtil;
class MyMoblet : public Moblet {
public:
	MyMoblet() {
		// Application initialization
	}
	void keyPressEvent(int keyCode) {
		// Handle key presses here
	}
	void keyReleaseEvent(int keyCode) {
		// Handle key releases here
	}
private:
};
// Since this is a C++ program, the main function
// needs to be declared extern "C"
extern "C" int MAMain() {
	Moblet::run(new MyMoblet());
}

The static function Moblet::run() implements the actual event loop. When it gets events, it distributes them to all registered listeners. The Moblet, being both a KeyListener and a CloseListener, will recieve these event types. It is recommended to handle other event types by letting your moblet inherit the corresponding listener types, such as BluetoothListener and ConnectionListener.

Working with connections in Moblet-based applications involves using the ConnectionListener interface. You inherit the class and implement the connEvent() function. You can use one listener for several connections, but each connection can only be associated with one listener. Once the events are received, you should process them in the same way as in the classic model.

Example: A Moblet Application Using Connections

#include <conprint.h>
#include <MAUtil/Moblet.h>
using namespace MAUtil;
class ConnMoblet : public Moblet, public ConnListener {
private:
	// Variables survive past individual function calls.
	char mBuffer[160];
	Handle mConn;
public:
	ConnMoblet() {
		// Application initialization goes here.
		InitConsole();
		ConsoleDelay = 0;
		ConsoleLogging = 1;
		// Create a connection.
		printf("Connecting...\n");
		mConn = maConnect("http://www.example.com/"); // Will cause a CONNECT event.
		// Register for events.
		setConnListener(mConn, this);
	}
	void closeConn() {
		maConnClose(mConn);
		removeConnListener(mConn);
	}
	void connEvent(const CONN_EVENT_DATA& data) {
		if(data.opType == CONNOP_CONNECT) {
			// The Connect operation is complete.
			if(data.result < 0) {
				printf("Connect error %i\n", data.result);
				closeConn();
			} else {
				printf("HTTP result %i\n", data.result);
				// Start reading data.
				maConnRead(mConn, mBuffer, sizeof(mBuffer) - 1); 
 // Will cause a CONN READ event.
			}
		} else if(data.opType == CONNOP_READ) {
			// The Read operation is complete.
			if(data.result == CONNERR_CLOSED) {
				printf("Connection closed.\n");
				closeConn();
			} else if(data.result < 0) {
				printf("Read error %i\n", data.result);
				closeConn();
			} else {
				printf("Read %i bytes:\n", data.result);
				// Zero-terminate buffer, so it can be printed.
				mBuffer[data.result] = 0;
				PrintConsole(mBuffer);
				// Read more data.
				maConnRead(mConn, mBuffer, sizeof(mBuffer) - 1);
			}
		}
	}
	void keyPressEvent(int keyCode) {
		if(keyCode == MAK_0)
		maExit(0);
	}
};
extern "C" int MAMain() {
	Moblet::run(new ConnMoblet());
} 

MoSync for Java and C# Developers

Mobile offers developers a new world, with cool devices and nice, easy, small projects compared to that monstrous and boring enterprise code you've been doing in Java or C#, right? Right! However, there are somethings I'm going to tell you to help you move your Java/C# skill to C++ in MoSync. The reality is that both Java and C# are based on syntax from C, so you are going to be able to read C++ source code without anyone having to explain how curly braces work or reminding you to put a semi-colon at the end of the line, which is cool. Java and C# both also have a runtime environment which manages memory and performs garbage collection for you, which C++ doesn't, which isn't cool. Also, it isn't too difficult, and I'm here to guide you through the processes which will save you time and effort, and when you into trouble, the answer will probably already be here. 

As you are a Java or C# developer, then you've spent time gathering some fine object-oriented design skills, so we're not going to touch on C code here, this will be about C++ (mostly). 

This guide is in three sections, firstly a quick reminder of how C++ works, then a section of how memory management works, and finally unlearning lots of really nice design principles which are perfect for enterprise development, but will cause you grief in mobile. 

One final note: this obviously isn't an exhaustive description of how to write in C++. This is what you need to know coming from a Java or C# background to start writing C++, so don't expect this to be the definitive description. This is a quick start guide.

How C++ works.

Ok, this isn't really everything you need to know about how C++ works, this is just a reminder about how it can be different. In C#, if you want to create a new class, you right-click and give the class a name and Visual Studio creates a new templated .cs file for you. You add in a bit of functionality, and your new class is available within its namespace. The same is nearly true in C++. Normally, in C++ you will split your class into two files, a header file and a code file. The header file will normally have a .h or occasionally a .hpp extension, whilst code files will have .c (for C code) or .cpp (for C++ code).   

The header file contains the interface definition of your class, although it can contain implementation as well. In C# when you want to use a specific class you just need to simply include the namespace of the class with a using statement. In C++, you need to provide a definition of a class before you can use it. You do this by including the header file of the class in your code using #include "classname.h". The header is literally included in source code passed to the compiler. This leads to an issue which you don't get in managed environments. If you have two classes, Alpha and Beta, you can include them in your new class Gamma like this:

#include "Alpha.h"
#include "Beta.h"

However, if Beta.h itself includes Alpha.h, you will get a compilation error when you build Gamma. This will be because the compiler inserts the code from Alpha.h, then inserts the code from Beta.h, which in turn inserts the code from Alpha.h. The compiler will complain that methods of Alpha have already been defined. To prevent this, at the top of your header file, you need to add an include guard. This is a short compiler directive telling the compiler not to include the header if it has already been included. In Alpha.h you will write something like this

#ifndef __ALPHA_H
#define __ALPHA_H

When you create a new class in MoSync, the template should do this for you. You can read this as "if there isn't a token called __ALPHA_H then create a token called __ALPHA_H." At the bottom of the code there will be an #endif to round this off. You can change the naming convention if you wish, but if you write a header file from scratch, then don't forget the include guard. 

So, in your class you may want to use some classes without specifying their namespace everytime. This is a very familiar command 

using namespace <namespace>;

Note the semi-colon at the end; this is a C++ command and not a compiler directive. 

You can create your classes in a namespace in the header file, and again it is familiar

namespace MyNamespace
{
...
};

Again note the semi-colon at the end.  

Inside any optional namespace declaration, you can create one or more classes (or structs).

class MyClass
{
...
};

Once more, take notice of the semi-colon at the end of the class definition. 

Inside the class, you can create methods. C++ doesn't support properties in the way you may be used to. The visibility of methods is also different, as you are able (and encouraged to) define methods in groups by visibility. A class definition in a header file might typically look like this:

class Gamma
{
public:
Gamma();
virtual ~Gamma();
void incrementCounter();
int getCounter();
protected:
int mCounter;
private:
bool isActive;
};

So there are a few things here we can see. Firstly, there is a class definition as we've seen before. Inside this are three blocks of member definitions, ordered by visibility. In the public section, we can see a constructor for Gamma and a destructor (~Gamma()). The constructor will be called when the class is instantiated and the destructor when it is destroyed. This maybe obvious, but these are going to become very important in the next two sections of this guide. 

After the destructor, then there are two method declarations, incrementCounter() and getCounter(). We can see that incrementCounter() doesn't return any value and that getCounter() returns an integer. We've then got two members mCounter and isActive. You can if you really want define the visibility with each member, but this is unconventional. 

Creating the code to go with the header means creating a .cpp file as well. To continue this example, we could have Gamma.h and Gamma.cpp to contain the implementation.

#include "Gamma.h"
Gamma::Gamma()
{
mCounter = 0;
}
Gamma::~Gamma()
{
}
void Gamma::incrementCounter()
{
mCounter++;
}
int Gamma::getCounter()
{
return mCounter;
}

Several things to notice here. Each method needs to start with the class scope Gamma::. This means that you can implement several classes in the same .cpp, but it isn't normally recommended. Secondly, methods in C++ generally start with a lower-case letter, unlike most Java and C# classes. Thirdly, C++ doesn't support properties directly in the same way as C#, you need to create a method for get and set. Finally, as you'd expect, when you create an instance of Gamma, mCounter is set to 0 in the constructor. There is actually another way to do this, and one which is generally preferred in C++ for two reasons. The constructor could look like this

Gamma::Gamma() : mCounter(0)
{}

mCounter will be initialised with a value of 0. This can be important in environments where it may be possible to access the class before the constructor has finished executing. With the initialisation as part of the constructor specification then you are guaranteed availability. Secondly, if your class defines references (more later), then these cannot be null. By specifying them in the constructor, then you can assign the reference and avoid this issue. More on objects, pointers and references soon! 

So, if my class Gamma requires other objects or functions, then the include file can be added in the C++ file, and not in the header. It is a very good rule to only have the includes you need in the header, and if the class is used entirely internally, then #include it in the cpp file.

It is possible to have objects as public members in a C++ class, for instance

class Delta
{
public:
int counter;
};

This is as bad an idea in C++ as it is in Java and C#.  There is nothing intrinsically wrong with it, but you don't get any opportunity to clean input into mCounter, or to check that its value is only changed when it is safe to do so.

Inheritance

C++ supports multiple inheritance, that is actual base classes with actual implementation and not just interfaces. When you specify your class, you can specify which classes to inherit from, as well as the visibility of the inheritance

class Delta : public Alpha, public Beta, private Gamma

So there are two things to say about multiple inheritance in a MoSync context. Firstly, be careful. We're sure that you regularly observe SOLID design principles and the Liskov Subsitution Principle, but C++ can be a handgun. It's an incredibly powerful piece of technology, but easy to abuse. Don't get lost in inheritance. Secondly, lots of casting of your classes between interfaces and classes is slow. The device has to do looks ups to get the correct addresses to access the object according to the interface or inherited class. We'll come back to this later. 

So, those are the warnings, and here is the good news: inheritance is cool, it makes your coding easier if you've thought it through properly. Multiple inheritance means that you can use classes which in C++ are called mix-ins. You can have a little utility class which you may want to access in a variety of contexts. In C++, you can mix in this functionality directly into your class, without having the overhead of instantiating a new class to do it. Your class may be a little larger, but there are benefits in not having to create a new instance of the utility whenever you want to use it. You can also then cast classes as these mix-ins if you need to, as long as you are prepared to change your mind about this if it gets too slow. 

Interfaces in C++ are just classes, there is no 'interface' declaration. You create a class to inherit from with virtual methods. It is unusual to prefix interface classes in C++ with the letter I.

In C#

public interface Alpha
{
public abstract void doSomething();
}

In C++

class Alpha
{
public:
virtual void doSomething() = 0;
};

Virtual methods can be overridden, but there is no need for an 'override' command.

class Beta : public Alpha
{
public:
void doSomething();
};

The implementation of doSomething will be the one used, as there is no implementation in Alpha. This is called pure virtual method and it can be identified by the = 0.

This is what makes it a true interface. Any class which inherits from Alpha must implement doSomething(). Alternatively, you can provide an empty method

class Beta
{
public:
virtual void performOperation() {}
};

This method provides an empty implementation, which means that you don't have to implement the method performOperation().

Objects, Pointers and References

One of the great advantages of memory-managed environments like .net and Java is that you don't have to worry about whether objects are created in the heap or on the stack. You can go through an entire career in managed programming without ever having to worry about it. C++ is more complicated, but instead of 'complicated' most people use the word 'powerful'.  

Just to remind you, stack memory is local to the scope of the code where it was created. Heap memory is shared across the entire scope of the application. You need to consider when you create an object whether it is on the stack or in the heap. Objects on the heap need to be explicitly destroyed with the command delete. Objects on the stack will be deleted when they go out of scope. The rule with C++ generally is to only create objects on the heap when you absolutely need to. Creating objects on the heap can be much slower than creating them on the stack. When you've been working in managed environments for a while, you forget that searching for free memory in the heap is an expensive operation. Every time you use new then it has to do that search. Creating objects on the heap is necessary though when you want to be able to control their lifespan, for instance in factory classes which may create objects which have a lifespan greater than that of the factory itself. 

To create an object on the stack, simply declare it.

String myString;

To create an object on the heap, you need to use new

String* myString = new String();

The asterisk (*) here means that you have created a pointer to a String object. This is the address a particular object or interface starts. The actual position of the asterisk can vary, so you may read

String *myString = new String();

or

String * myString = new String();

The first form, with type*, is preferred and recommended for two reasons. Firstly, the command is in the format <type> <name>. The type is String pointer, and it is called myString. If you don't put the asterisk next to the type, then you are saying that myString is a String, which it isn't. Secondly the asterisk has some other meanings in other contexts.  *myString means something else, so its use is ambiguous if you declare as <type> *<name>. 

There is another type called a reference. A reference is an address like a pointer, but unlike a pointer it cannot have a null value. References are declared with an ampersand (&).

String* myString = NULL; //Is OK
String& myString; //Will cause a compiler error

References are particularly valuable as a way to pass stack objects.

String& Gamma::getHeadline()
{
return myString;
}

In this example, myString has previously been created as an object on the stack.  getHeadine() will return a reference to myString and not pass the entire object back as a return value.

String& headline = gamma.getHeadline(); 

Shows how you can create a reference with an assignment. 

When you access methods on an object, you need to know whether it is an object or a reference, or a pointer. Members are accessed in objects and references using a period (.) e.g.

myString.clear();

When you have a pointer, you access members using -> e.g.

myString->clear();

You can covert between pointers and reference using an asterisk. Firstly, you can dereference a pointer and treat it as an object.

String* myString = new String();
myString->append("hello world", 11);
String& strref = *myString;
strref.clear();

Interesting. The asterisk is used to say "the object that this pointer points to".  

You can create pointers to objects, even if they are on the stack. This also means that having a pointer to an object is not the same as knowing that the object on the heap. This is useful when you want to use objects with methods which expect a pointer.

int Gamma::getStringLength(String* testString)
{
return testString->length();
}

String hello = "Hello World";
int strLen = getStringLength(&testString);

This is a bit of a contrived example, but you can see that by using an ampersand before the object name, it is saying "the address of this object". Both String* and &testString will have the same value. 

Memory Management

Being able to create objects in both the stack and the heap, and the lack of an automatic garbage collector, means that you have to think about the lifespan of each and every object that you create and where you create it. As we've said above, create objects onto the stack wherever possible. When you create an object on the stack, it will have a scope of the lifespan of it's location.  

For instance, if you create an object in a method

void Gamma::doSomething()
{
// String will be created here
String myString;

// String will be automatically destroyed and the memory
// freed because we've reached the end of the method. We
// don't need to do anything else.
}

Objects can also have the scope of an object. Consider this header file 

class Gamma
{
public:
Gamma();
virtual ~Gamma();
private:
String mString;
};

In the second example, there will be an object called mString on the stack. It will be created when an instance of Gamma is created, and destroyed when Gamma is destroyed. You don't need to do anything else. Easy, isn't it? 

You need to be much more careful when you are creating objects on the heap. They won't be automatically destroyed when the method or object goes out of scope. Objects on the heap are really useful because of this. You can create an object in one class and pass the pointer to another class, or you can use it internally. 

There is a design pattern called Resource Allocation is Initialisation (RAII). This pattern says that when you create a class, create the objects you need in the constructor and destroy then in the destructor.

//Constructor
Gamma::Gamma()
{
// Instance heap objects you need here
myString = new String();
}
// Destructor
virtual Gamma::~Gamma()
{
delete myString;
}

This means that although myString will be created on the heap, then when Gamma goes out of scope myString will be destroyed.   

There are many cases where this isn't appropriate though. You could have an application which needs to send complex messages between classes. Each message is of the class 'Message'. 

class Message
{
public:
void setMessage(const char* message);
const char* getMessage();
void setStatus(int status);
int getStatus();
private:
String mMessage;
int mStatus;
};

It’s a little class which can store a text message and a status value. We can see that when Message is instanced, a string and an int are allocated on the stack. 

Class Gamma needs to send messages to class Delta. Delta has the following method.

void Delta::receiveMessage(Message* message)
{
// Got the message
}

Objects on the heap are really powerful, but with that power comes great responsibility. Here is a quick review of some things to remember 

  • If at all possible, create objects on the stack, not in the heap. Its faster and less likely to introduce a bug. 
  • Normally, the class which created an object should destroy it. RAII is a good pattern. 
  • Whenever you write the word 'new' think about where the word 'delete' is going to go. It has to go somewhere or you will have a memory leak. 
  • When you pass responsibility for deleting an object, then add a comment to that effect so it is clear where the responsibility lies. 
  • Only have one place where the object should be destroyed. If you try to destroy the same object twice, you will get a runtime error.

If you look on the MoSync forum, or in the MAP library, you will find some debug methods for creating objects. These functions replace new and delete and keep a track of which objects have been instanced on the heap and which source file did it. When your application exits on the emulator, it will report about which objects haven't been deleted. This can help you track down memory leaks. To use them, replace all of your new keywords with newobject() and delete with deleteobject().

Design Considerations and Optimization

So you've got some killer design skills for Java or .net. Maybe you've mastered IoC containers or double dispatch and you can see how they are going really help in C++. This is good, and you can certainly use these techniques, but there are some other considerations you need to keep in mind with mobile development. If you've been doing desktop or enterprise development, then you're used to working in environments which are much more powerful than mobile. You will have lots and lots of small, specialised classes which are great for working with. Each class supports multiple interfaces and has been created to perform one small task. The reality of non-managed environments and polymorphism is that creating classes is slow, and casting objects is slow. Not so slow that you shouldn't do it, or you will be waiting all the time for your application, but slow enough that if you do use design patterns which involve a lot of tiny classes, and a lot of casting between interfaces, then you may be wondering why the application isn't as speedy as you imagined. 

You will get better speed by creating fewer classes with fewer interfaces, and on the stack rather than on the heap. Flatter designs will result in faster code. Of course, this is then a trade between the design you would use in Java and the speed you want in C++, and in many instances you won't find the code slow, but if you've got routines which create lots of instances of objects (in a loop for instance), then you need to be aware of where you can improve speed. 

If you can combine classes to put methods together in the same object, then you will save memory and be faster. Better still, if you can create utility functions in C rather than C++ objects then this will be even faster. Typically factories make a good example where C code can be used, as well as utilities like decorators don't have to be in classes. Many developers on the web will tell you to code in C by default, and only use C++ and classes where you need to.

Platforms, OS, Devices

Optimizing Mobile Applications

When you develop an application you want it to be as fast and as memory-efficient as possible. And that's even more important when you are developing for mobile devices which have slower CPUs, limited memory, and limited power resources. The MoSync SDK lets you target a huge range of devices with your applications, but you should always bear in mind the limitations of the devices you are writing apps for. Here we provide some guidelines to help you optimize your application's performance, size, and power consumption.

MoSync Libraries and Tools

MoSync includes many libraries that we have carefully optimized for performance on a wide range of devices. Try to use these libraries wherever possible. In particular, if you are developing C++ applications, make full use of:

  • The Moblet framework for event handling -- it keeps battery usage to a minimum.
  • MoSync's MTXml parser -- it is optimized for rapid parsing of XML yet keeps memory usage to a minimum.
  • The MAUI graphical user interface library -- it only redraws GUI elements that change and uses caching mechanisms to improve speed.
  • The MAUtil storage classes -- String, Vector, Set, Map and HashMap are efficient when correctly used.

Code Optimization via Pipe-Tool

Pipe-Tool is MoSync's code transformation engine. It is, among other things, a code verifier and optimizer and it performs dead code elimination to produce small, efficient output files.

Pipe-Tool is automatically invoked by the MoSync IDE during the normal build process. It can also be run on-demand from the command line. The dead code elimination process is optional and still in an experimental state, but you can turn it on by enabling Activate Dead Code Elimination in your project's build settings (select Project > Properties > MoSync Project > Build Settings > Compiler Flags) or, on the command line, by passing the -elim flag to Pipe-Tool. 

Asynchronous Operations

We've chosen to not support threads directly in MoSync, but instead we have implemented all essential threaded operations asynchronously. Whenever you start such an operation, it will run in the background and send a notification in the form of an event when it's done. By encapsulating asynchronous operations in the runtime we can ensure that the behavior is consistent on all devices, and you never have to care about thread synchronization. Some platforms implement asynchronous operations internally without threads, for instance Symbian, where they strongly encourage the use of Active Objects over threads.

To keep you application responsive when it's doing a lot of computations, it is a good idea to periodically check for events. This can be done either by invoking some event handling mechanism or by using the Moblet framework, and by doing the computations in blocks so that the execution returns to the main loop from time to time. It is also necessary to make sure the EVENT_TYPE_CLOSE event is received and handled, as it indicates that the application has been forced to quit for some reason.

Dealing with Hardware Limitations

Slow CPUs

The CPUs in many mobile devices are based on the ARM architecture. The CPUs can have different clock frequencies and cache sizes, and some have no cache at all. What mobile CPUs generally have in common is that they are all far less powerful than the ones you're used to develop for on a regular PC.

Any piece of code that is going to use a lot of CPU time should be carefully optimized. Finding the right algorithm is more likely to be important than doing low-level optimizations. Also such optimizations are usually done by the compiler (in our case GCC), so it is always good to know your compiler in order to keep the amount of work to a minimum.

If you're in need of extreme performance, basic speed optimization tricks can help:

  • Pre-calculate stuff
  • Unroll loops when possible
  • Inline small and frequently used functions
  • Give as much information to the compiler as you can (const etc.).

Many mobile devices do not have a floating-point unit (FPU). Their floating-point operations are done in software. That results in much slower processing than on regular PCs. To improve performance, the best way to deal with decimals on these devices is to use fixed-point arithmetic with integers (see, for example, http://en.wikipedia.org/wiki/Fixed-point_arithmetic).

MoSync syscalls (defined in maapi.h) incorporate platform-dependant code that is optimized for the architecture they are running on. Try to use them as much as possible, even if the same functionality is possible to re-implement in other ways. This will not only make things faster, but also smaller.

Divisions may be really slow as they aren't available natively on most ARM processors. Use shifts when possible or pre-calculate reciprocal 1/x values and do multiplications instead.

If you have to read a lot of small pieces of data from a data object, try to buffer data in memory. Doing a lot of calls to maReadData may be slow.

If your application needs to read from and write to memory a lot, try to access memory as integers whenever possible. This is even more important when targeting Android and Java ME devices. XML parsing, for instance, relies on heavy use of byte accesses. Whenever possible use some more approriate binary format.

Try experimenting with different GCC optimization flags (right-click on your project and select Build Configurations > Manage > Build Settings > Compiler Flags > Additional GCC Switches). The -o3 swtich should in most cases be slightly faster than -o2, although it may slightly enlarge the size of the binary.

It's worth mentioning that if you are used to programming in Java, you tend to use as few classes as possible, due to their size and speed overhead. But C++ objects are pretty lightweight and faster than their Java equivalent so you do not need to be so restricted.

ARM Recompiler

MoSync features a recompiler that transforms MoSync code into native ARM code at start up. For CPU-intensive applications, the recompiler can provide a substantial speed boost. As of June 2010, this recompiler is available on Windows Mobile, Symbian s60v3 and s60v5. Work is in progress to bring the recompiler to s60v2 and Android. (on our MDLbenchmark test on Symbian 5th edition, the MoSync ARM recompiler increased performance by more than 300%.)

Battery Power

Battery power is drained when hardware is in use, especially the CPU. It's therefore important to optimize for performance, making the CPU complete its work faster. The syscall maWait puts the thread in suspend mode until an event has been sent, leaving the CPU for use by other threads/processes, thus using less CPU and battery power. The Moblet framework does this automatically.

Screen Size

All phones have different screen sizes. They are often very small with low resolutions, making it more important to choose graphics wisely. Fonts or images should be visible on all sizes of screens.

Data Storage

Many mobile devices have small RAM sizes and most likely a lot better permanent storage capabilities. It's important to take this into account when developing applications for them. Unloaded binaries (.ubin) and unloaded media files (.umedia) are good formats to use for resources as they will be kept in permanent storage as long as they aren't used. (For more information about these resource types, see the Resource Compiler Reference.)

Resources such as images and media files can take up a lot of space in the final package so try to keep them as small as possible. There are many free and commercial tools available for optimizing and compressing resources without noticeably reducing their quality. Generally, you should use the PNG format for images: we have ensured support for it across all capable devices (PNG is generally smaller than GIF anyway). If you have photographic images, you should to use the JPEG format. Most modern devices support it, although some of the older ones may not so it may be worth checking.

Try not to do too many small heap allocations as this will fragment the heap. The result will be slower heap allocations and more memory usage. As you code in C or C++ it's also good to verify that you haven't got any memory leaks. This can be done using a simple trick shown in MAP/MemoryMgr.h: basically you save information about in which file, function, or line every allocation is done, and remove that whenever it is freed. This information can be dumped at anytime to see what memory leaks you have.

If you have more tips about optimizing applications for mobile devices, please share them with us by leaving a comment below.

Developing Android Applications

Android developers will find much to like in the MoSync SDK. Like the native Android SDK, the MoSync SDK utilizes Eclipse as its development environment. Here we provide some advice on building Android application using the MoSync SDK.

Android Package Settings

Package settings for Android applications can be found under Properties > MoSync Project > Android for your project. Here you can set the application's package name and version code and sign the package with your credentials.

The application package name is used by the Android OS to identify your application. The default package name is com.mosync.app_<Project Name>.

The application version code is used by the Android Market to check for application updates.

Application Signing

You need to sign your Android application before it can be installed on an Android device. You can create your own self-signed certificate (see http://developer.android.com/guide/publishing/app-signing.html). Just add the information about your certificate in MoSync and your application will be signed when you build it. 

Sending Applications to Devices via USB

From within the MoSync IDE you can search for connected Android devices. Click the small down-arrow next to the icon of a mobile phone and magnifying glass on the top bar of the IDE then select Scan for Android USB device. (If your device is not found make sure you have installed the connection software which came with the device.)

Sending Applications to Devices via Bluetooth

Newer Android devices (2.1 onwards) can also send applications via Bluetooth. This is enabled on your phone. To search for Bluetooth devices, choose Scan for Bluetooth device instead. When you have selected your device and choosen a default device profile for it, you can send the application to the device.

Bluetooth is not supported by Android 1.5 and 1.6.

Background Processing

One of the more powerful features of the Android platform is its ability to let applications run in the background. However, many devices on other platforms (particularly Java ME) do not allow you to do this, they just stop working if you switch to another application. Therefore, be careful if you are creating Android applications that rely on being able to run in the background and you intend to port them to other platforms.

 Screen Resolution Support

MoSync applications built for Android 1.6 and later uses the Android manifest settings to handle all types of screen resolutions. At this time all resolutions are allowed. 

 Touch Input and Sound 

Applications based on MoSync are able to access touch and key input, and can access all the image and sound formats supported by the Android OS.

Known Issues with Android

MoSync 2.7: 

  • When your application loses focus and receives an EVENT_TYPE_FOCUS_LOST your application may be killed immediately, so make sure that you don't need to do anything important in the background.
  • If your application uses too much memory the Android OS sends a warning before killing the process. This event never reaches the MoSync application, so be careful not to use more resources then you need.
  • You can't specify which screen resolutions your application will work with, it defaults to be supported on all screens. This will change later.
  • The ARM recompiler is not yet operational on the Android platform.
  • Bluetooth service discovery behaves different from the other platforms. Please check the API Reference Manual in the IDE (Help > API Reference Manual) for further information.
  • Fully transparent pixels in PNG images lose their colour values when retrieved by maGetImageData.
  • In maSoundPlay the parameter offset is not supported, it should be set to zero, and the audio data (starting with the mime header) should begin at the start of the data handle referenced by parameter sound_res.
  • In existing Android applications, you may need to increase memory settings for heap space (possibly also total data size) in "Project/Properties/MoSyncProject/Build Settings/Compiler Flags/Heap Size" if you get an error message saying "Malloc failed. You most likely ran out of heap memory. Try to increase the heap size."
  • In some rare occations there are problems with maWait( 0 ) blocking the event queue until a new event arrives. This can for example happen when doing intensive networking; it can happen that an event of type  EVENT_TYPE_CONN does not cause maWait( 0 ) to wake up and return. If you should observe this behaviour in your program (app seems to be hanging until a new event triggers), just update maWait( 0 ) to a non zero parameter, for example maWait( 100 ). This will cause maWait to wake up every 100 milliseconds, thus solving the blocking behaviour. In the upcoming MoSync 2.7.1 release, this problem will have been fixed. If you are using class MAUtil::Moblet or one of its subclasses, you should not experience this problem as the Moblet class never calls maWait( 0 ).

 

 

 

Developing iPhone Applications

All of Apple's iPhone, iPad and iPod touchscreen devices run the same operating system: iOS. Currently, the only way to download applications to an iOS device without voiding its warranty is via the Apple App Store. Here we provide advice on building successful iPhone apps with the MoSync SDK.

Building iOS Applications with the MoSync SDK

Apple imposes strict rules and guidelines on how applications made available through its App Store are to be built. That means to successfuly build an iOS application with the MoSync SDK, you will need to install the MoSync SDK for OS X on an Apple Mac running Snow Leopard (OS X version 10.6). You will also need Xcode 3.2.4 with the iPhone OS SDK. To build and distribute an application for anything else than the simulator you will need to join Apple's Developer Program.

If you do not have an Apple Mac available right now, you can install the MoSync SDK for Windows on an Windows machine. You will still be able to build your project, but the output will be an Xcode project which will then need to be built on an Apple Mac Snow Leopard running Xcode version 3.2.5.

Building iOS Apps with the MoSync SDK for OS X

  1. Set an iOS device (iPhone, iPad, etc.) as a target device profile in the MoSync IDE.
  2. Build your application. An Xcode project will be created for you.
  3. Open the Mac Finder and find the Xcode project file: <ProjectDirectory>/Output/Release/Apple/iPhone/package/xcode-proj/<ProjectName>.xcodeproj
  4. Double-click on the Xcode-project to open it with Xcode.
  5. In Xcode, choose the target device or simulator that you want to build for.
  6. Press Build and run to start the application on the target.

Building iOS Apps with the MoSync SDK for Windows

  1. Set an iOS device (iPhone, iPad, etc.) as a target device profile in the MoSync IDE.
  2. Build your application. An Xcode project will be created for you.
  3. Open Windows Explorer and find the Xcode project file: <ProjectDirectory>/Output/Release/Apple/iPhone/package/xcode-proj/<ProjectName>.xcodeproj
  4. Move the created Xcode project to an Apple Mac Snow Leopard running Xcode to finalize the application.

Apple Guidelines and Exiting an App

Apple provides detailed guidelines describing how a well-behaved iPhone application should be written.

Generally, MoSync runtimes are completely conformant with Apple's guidelines, so you should have no trouble getting your application in the Apple App Store. Note, however, that Apple expects all applications to be closed by the "Home" button on their device, and are likely to reject applications which explicitly show an "Exit" button. If you want such a button on other platforms, simply skip it on iOS:

#include <maprofile.h>
...
#ifndef MA_PROF_SUPPORT_OS_IPHONEOS
   <code for initializing an exit button>
#endif

Storing the Application's State

An application may be closed when the phone receives a call or if the application uses too much memory. Therefore one thing users rely on is that the state of an application is stored at all times, so that when the application is restarted, its state can restored. This can easliy be implemented using MoSync's store syscalls defined in maapi.h (maOpenStore, maWriteStore...). When the application receives an EVENT_TYPE_CLOSE event, make sure you write the state to a store, so that it can be restored whenever the application is restarted.

Image and Sound Support

JPEG and PNG are the supported image resource formats.SVG images, including application icons, are not supported by iOS.

The playback audio codecs are described in the iOS Reference library.

Known Issues and Limitations

MoSync 2.5, 2.6

  • maLoadProgram is not supported (Apple does not permit this functionality for applications distributed through its App Store).
  • No event is sent when a didReceiveMemoryWarning event is cached, so make sure you keep the amount of resources stored in memory to a minimum.
  • Stores are written to and read from the /Documents folder.
  • Text drawing can be a little slow because of the way Apple's Core Graphics text-rendering API works. Try to render images with text ahead of time whenever possible.
  • maResetBacklight isn't implemented due to limitations in the iPhone SDK.
  • maVibrate doesn't take the time argument into account (it vibrates for a while and then stops) due to limitations in the iPhone SDK).
  • If you get the error "Missing Base SDK" you are probably running the wrong version of Xcode. More information here.
  • For files, the Documents directory is the home directory, e.g. if you open a file with a path that
    looks like this "hello.txt" it will look for a file that is called hello.txt in the Documents directory.
    In the future you will be able to get the path to different directories like the bundle/caches/preferences directories.
  • If you want to transfer files to the phone, use an application like "iPhone explorer".
    You can also add the UIFileSharingEnabled key to the application plist-file with a value set to YES
    and transfer individual files to the Documents folder using iTunes.

MoSync 2.6

  • For OpenGL the renderbuffer, depthbuffer and framebuffer is automatically set up for you. It will always uses the full retina display resolution if it is available and the best color depth available. In the future this step will be configurable.

Developing Symbian Applications

When you package an application for a Symbian device, you need to specify a UID for the package. MoSync can generate test UIDs for you to use during development. Optionally, can also self-sign your packages by specifying a key file and certificate. Here we describe how to set UIDs and do self-signing.

Symbian Unique Identifiers (UIDs)

Symbian requires that each application has a UID to uniquely identify it.

When you create a project, MoSync automatically generates random UIDs for you within the range reserved by the Symbian Foundation for development and testing use. There are different ranges of these reserved UIDs in different editions of Symbian.

To view or change the current UIDs for an application, right-click on your project in the Project Explorer view, and select Properties > MoSync Project > Symbian.

If you need to, you can generate new random UIDs in the reserved range by clicking the Generate Test UIDs button.

These UIDs are intended just for use while you are doing development on your own machine. When you application is ready for public distribution, you will need to enter, in these boxes, UIDs provided by the Symbian Foundation for your application. Visit www.symbiansigned.com for more information.

Self-Signing a Symbian Application

To install Symbian applications on 3rd and 5th edition devices, they must be signed. MoSync generates a default private key file and certificate for self-signing.

If you wish to use your own key file and certificate to sign a particular application, right-click on your project in the Project Explorer view, and select Properties > MoSync Project > Symbian > Self-Signing.

(The default key file and certificate for all projects is shown in this screenshot.)

Tick the Enable Project Specific Settings checkbox. Enter the paths to your key file and certificate. In the Passkey field, enter  the password for the key file. Click Apply.

Using OpenSSL

The MoSync SDK includes OpenSSL which you can use to generated private key files and certificates. OpenSSL can be found in the MoSync /bin folder. Visit www.openssl.org for more information.

Known Issues with Symbian

MoSync 2.5:

  • With the Symbian uninstaller on 2nd edition and with 3rd edition using OS 9.1 and 9.2 if you install two MoSync applications on the same device and then uninstall one of them, the other application will crash. This is because the uninstaller also removes the MoSync server used for location services. Reinstall the required application and everything will be fine.
  • For 2nd edition Symbian on Mac OS X the total length of your workspace path cannot be more than 32 characters.

Application Icons

It's easy to add application icons to your project. All you need to do is to add a file to the project with the *.icon extension, for example App.icon (you can pick a name you like), write in a few lines of XML, and add the icon images to the project. MoSync does the rest.

Here is an example of a .icon file:

<?xml version="1.0" encoding="UTF-8"?>
<icon> 
  <instance size="default" src="Icon.svg" /> 
  <instance size="64x64" src="Icon.svg" /> 
  <instance size="32x32" src="Icon.png" /> 
</icon>

It is generally sufficient to just specify the default instance size option, the other options are not mandatory:

<?xml version="1.0" encoding="UTF-8"?>
<icon> 
  <instance size="default" src="star2t.svg" /> 
</icon>

SVG is the recommended image format and works for all platforms. An example of an open-source drawing program you can use to create SVG files is Inkscape.

To know if the application icon was added correctly during the build process, you only need to look at the last few lines of the IDE console for guidance.

Some quirks that have been observed:

  1. When adding icons for a Java device, it is important to take a look at the device profile information in the MoSync SDK. The device profile should contain the parameters MA_PROF_CONST_ICONSIZE_X and MA_PROF_CONST_ICONSIZE_Y and the size of the image file should be consistent with the icon size listed in the profile. (Alternatively, make sure that all your target devices support icon of the size you provide.)
  2. It has also been observed that on Symbian devices the application icon may not appear if the application is reinstalled. In this case, a quick restart of the phone makes the icon visible.

Please keep in mind that this feature of the MoSync SDK is experimental. Some platforms may show unexpected results. Let us know if you find issues.

Audio, Sound, Music

Playing Sounds and Music

In many games and applications you will want to play a sound.  This may be backing music in a game, sound effects or alerts which require user input.  The MoSync SDK provides an audio API to help you do that.

Sound files can come from two places, either sounds you've packaged up with your application when you've distributed it, or they can be downloaded over the data network at runtime.  Either way, you need to create a resource which the API can recognise and pass to the operating system.

The 'Adding Resources to a Project' goes into a detail on how to add sound files to your project at build-time for packaging with your release.  In short, a sound file needs to be added as either a .media or a .umedia resource with a MIME type.

Due to limitations in the platform-provided API's there are a set of restrictions. Currently, it's not possible to play MP3 files on the Windows Mobile platform and the API only supports playing of one sound at a time.  If you want to make sure that all MoSync supported devices will be able to play your resources it is recommended that you use wav files.

To play sound files you have downloaded, then the easiest way is with the AudioDownloader.  There is an example which downloads and plays an MP3 file in the tutorial 'Downloading Audio from the Internet'.

Specifying MIME types

Whether you are planning on packaging audio with your application, or planning on downloading it, it is essential that you specify the MIME type of the audio before you want to play it.  The two tutorials linked above contain much more detail about how to do this in each scenario, but this is the short version.

Specifying MIME types at build-time

If you are packaging your audio, you will have an entry in the resource file which looks something like this:

.res MUSIC
.media "audio/mpeg", "music.mp3"

The directive .media means that it will be treated as a loaded binary resource.  That is, it will be loaded into the application memory when the application starts.  Alternatively, you can use .umedia which will mean that the audio is loaded on demand, but there will be a slight delay.

After the .media directive, then there is the MIME type as a string.  You can find common MIME types on Wikipedia.  The final part is the file name you want to enclose.

Specifying MIME types at run time

If you are downloading and playing sounds at run time, then you should probably use the AudioDownloader class.  This is create and configure an appropriate resource for you.  When you specify the URL to download from, you also specify the MIME type.  The web server you are downloading from (assuming it is a web server) may also provide the MIME type, and may also overwrite your MIME specification.

Playing Audio Files

To play a sound you use the maSoundPlay function.

maSoundPlay(RES_SOUND);

Just provide the correct resource and the audio file will start playing immediatly if successful. If the function returns any negative value it means that it failed.

If you, for some reason, wish to stop playing the sound you just use the maSoundStop function.

maSoundStop();

If you wish to restart the sound you can use this:

if(maSoundIsPlaying())
maSoundStop();
maSoundPlay(RES_AUDIO, 0, maGetDataSize(RES_AUDIO));

First you can check if your sound is still playing. If so, you can stop it before playing it again. maSoundPlay always plays the sound from the beginning.

Audio volume in MoSync is defined as values between 0 and 100, where 100 is the maximum volume. To get the volume you use:

int volume = maSoundGetVolume();

and to set the volume you use:

maSoundSetVolume(volume);

Example Source Code

/**
 * @file PlayAudio.cpp
 *
 * This program shows how one can work with playing sounds and music.
 *
 * Todo: You need to add an audio resource file which you want to play.
 * In this example we have used Resource.lst file with following contents:
 * < .res RES_AUDIO
 *   .media "Audio/x-waf", "mobilesorcery2.wav"  >
 *
 * @Author Anders Malm
 */

#include <MAUtil/Moblet.h>
#include "MAHeaders.h"

using namespace MAUtil;

/*
 * Moblet class for playing music.
 */
class MyMoblet : public Moblet
{
public:
    MyMoblet();
    void keyPressEvent(int keyCode);
    void setVolume(int change);
};

/*
 * The constructor for Moblet class
 */
MyMoblet::MyMoblet()
{
    MAExtent e = maGetScrSize();
    maSetColor(0x0);
    maFillRect(0, 0, EXTENT_X(e), EXTENT_Y(e));
    maSetColor(0xffffff);
    maDrawText(0, 0, "Press 5 to Restart sound");
    maDrawText(0, 20, "Press 8 to Stop sound");
    maDrawText(0, 40, "Press 7 to Decrease volume ");
    maDrawText(0, 60, "Press 9 to Increase volume ");
    maDrawText(0, 80, "Press 0 to Exit");
    maUpdateScreen();

    // Panic message in case of failure.
    if(maSoundPlay(RES_AUDIO, 0, maGetDataSize(RES_AUDIO)) < 0)
        maPanic(0,"error playing sound!");
}

/*
 * For controlling music volume.
 */
void MyMoblet::setVolume(int change)
{
    int volume = maSoundGetVolume();
    volume += change;
    if(volume<0) volume = 0;
    else if(volume>100) volume = 100;

    maSoundSetVolume(volume);
}

/*
 * Key press events
 * Pressing key 0, Exits the program.
 * Pressing key 8, Stops playing sound.
 * Pressing key 5, Starts playing sound.
 * Pressing Keys 7 and 9, sets volume down and up respectively.
 */
void MyMoblet::keyPressEvent(int keyCode)
{
    switch(keyCode)
    {
        case MAK_0:
            maExit(0);
            break;
        case MAK_8:
            maSoundStop();
            break;
        case MAK_5:
            if(maSoundIsPlaying())
                maSoundStop();
            maSoundPlay(RES_AUDIO, 0, maGetDataSize(RES_AUDIO));
            break;
        case MAK_7:
            setVolume(-10);
            break;
        case MAK_9:
            setVolume(10);
            break;
    }
}

/**
 * Main execution of the program starts from here.
 */
extern "C" int MAMain()
{
    Moblet::run(new MyMoblet());
    return 0;
};

MoSound

This example application hows how to use MoSync's sound API. The example demonstrates how to play and loop a sound once.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

This application plays an mp3 file with the message "Mobile sorcery - making mobile magic".

Key Presses

  • Press 0 or right-softkey to exit the application.

Cameras, Capture

CameraDemo

CameraDemo is a simple application built with MoSync that demonstrates how to control a device's camera. It also uses the MoSync Widget API.

Main screen, AndroidMain screen, iOS/iPhoneSettings screen, Android

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

The application makes extensive use of the MoSync Widget API. It has three different screens:

  • MainScreen is the default screen in the application form which the user can take snapshots, zoom in or out (if supported by the device), reach the other screens.
  • SettingsScreen contains controls for configuring the camera and setting its properties. It also provides the ability to switch between cameras, where that is supported. This screen may differ from one device to another based on the available functionality on the device. 
  • ImageScreen contains the latest captured image.

In the Code

The project is divided into several files. Each screen is implemented in a separate set of header and cpp files.

The main.cpp file is the main file of the project. It includes the code for creating MainScreen.

ImageScreen.cpp contains the code for creating and handling the ImageScreen, while  SettingScreen.cpp implements a class that creates and handles the SettingsScreen.

WidgetUtil.cpp contains simple wrappers for setting and getting properties of the widgets (similar to the files seen in our example application HelloNativeUI).

The .h header files contain the forward declarations for the code in the .cpp files.

Touch responses

  • Tap buttons to capture image, switch screens, zoom in/out.

 

 

Controlling Cameras

In this tutorial we take a look at how to control a device's cameras through the MoSync Camera API. With the Camera API you can discover the number of cameras a device supports, set properties like zoom and image format, embed previews in your application, and, of course, take a picture. We also have an example application for you to look at, CameraDemo, that demonstrates some of the basic principles described here.


On Android and iPhone (iOS) devices, MoSync's Camera API lets you take full controll of a device's cameras. One of the major aspects of our implementation is the ability to use the camera preview as a NativeUI widget that can be controlled through the Widget API.

Camera API Syscalls

Here are the list of the syscall functions in the Camera API:

  • maCameraNumber returns an integer indicating the number of available cameras on the device.
  • maCameraSelect selects a camera associated with the index. The index is between zero and the number returned by maCameraNumber - 1. The default selected camera is the back-facing camera (0) which will be used if the user does not use this function. 
  • maCameraSetPreview binds the selected camera to the widget specified by the handle. If called before calling maCameraSelect (for the first time) the default camera (back facing) will be bound to the preview.
  • maCameraSetProperty adjusts the properties on the selected (or default) camera.
  • maCameraGetProperty gets the assigned value for the specified property, or reads the values for read only properties.
  • maCameraFormatNumber returns the number of available formats in the device.
  • maCameraFormat stores a image size format in the list of user formats.
  • maCameraSnapshot takes a picture and stores it in memory in the place reserved by a placeholder. To use this function with the default device's picture size, a user can pass -1 as the formatIndex.
  • maCameraStart starts the live view on the preview widget. If no native preview is set, a fullscreen preview will automatically be initiated.
  • maCameraStop stops the live view. When using the fullscreen default preview it will destroy that view and returns to the old view of the application.

General Usage

The recommended way of using the new Camera API is together with Widget API. To use the camera together with the Widget API follow these steps:
 
1. Get the number of available cameras on the device by calling maCameraNumber.
 
2. Add an instance of MAW_CAMERA_PREVIEW to your application with maWidgetCreate.
 
3. Set the widget parameters (only width and height are recommended).

4. Select one of the cameras by calling maCameraSelect:

maCameraSelect(MA_CAMERA_CONST_BACK_CAMERA);

5. Bind the preview and the camera by calling maCameraSetPreview so that the camera uses that preview for its live view.

void createCameraWidget ()
{
    mCameraPreview = maWidgetCreate(MAW_CAMERA_PREVIEW);
    widgetSetPropertyInt(
        mCameraPreview,
        MAW_WIDGET_WIDTH,
        MAW_CONSTANT_FILL_AVAILABLE_SPACE);               
    widgetSetPropertyInt(
        mCameraPreview,
        MAW_WIDGET_HEIGHT,
        MAW_CONSTANT_FILL_AVAILABLE_SPACE);
    //bind the widget to the default camera
    maCameraSetPreview(mCameraPreview);
    maWidgetAddChild( mMainLayoutWidget, mCameraPreview);
}

6. Check the available functionality on the device’s camera (zoom support, flash support, etc.) by calling maCameraGetProperty.

    char buffer[256];
    maCameraGetProperty(MA_CAMERA_FLASH_SUPPORTED, buffer, 256);

7. Start the camera by calling maCameraStart.

8. Change the properties as required by calling maCameraSetProperty.

9. Call maCameraSnapshot to take snapshots. Here You can see an example function that takes picture snapshots and stores them in a single place holder called mLastEnc.

void takeSnaphot()
{
if(mLastEnc != 0)
   maDestroyObject(mLastEnc);
mLastEnc = maCreatePlaceholder();
int res = maCameraSnapshot(mCurrentSizeIndex, mLastEnc);
if (res != MA_CAMERA_RES_OK)
    maPanic(res, "Failed to takeSnapshot");
}

10. Call maCameraStop to stop the camera when you are finished with it.

11. Destroy the Widget if required.

Camera Properties

Camera properties can be set and read through the maCameraSetProperty and maCameraGetProperty functions:

Read/Write:

  • MA_CAMERA_FLASH_MODE -- Sets the mode of flash for the camera (auto, infinity, macros, fixed).
  • MA_CAMERA_FOCUS_MODE -- Sets the mode of focusing for the camera (auto, infinity, macros, fixed).
  • MA_CAMERA_IMAGE_FORMAT -- Sets the image format (MA_CAMERA_IMAGE_JPEG, MA_CAMERA_IMAGE_RAW).
  • MA_CAMERA_ZOOM -- Sets the zoom level (zero to MA_CAMERA_MAX_ZOOM).

Read Only:

  • MA_CAMERA_ZOOM -- The maximum zoom supported by the device.
  • MA_CAMERA_ZOOM_SUPPORTED -- True if the device supports zoom.
  • MA_CAMERA_FLASH_SUPPORTED -- True if the device supports flash.

For more details about these functions, see the MoSync API Reference Manual.

Implementation

Known Issues and Limitations

The Camera API for the MoSync Android runtime currently uses Android 2.2 (API Level 8) which only supports one camera.

Collections, Containers

Collections in MoSync

The MoSync SDK provides several different collection objects, suitable for different occasions.  Collections let you deal with sets of object collectively, but the way you access the collection varies.  

Collections are not implemented consistently in MoSync though, so different collection types are not as interchangable as you might expect.

Collection Memory Usage

Typically, collections will be created on the stack rather than the heap.  This will mean that if you have a collection of object pointers (the objects themselves will be in the heap), and the collection goes out of scope, you may loose the pointers to the objects in the heap and you won't be able to access them.  The objects will still exist though, and you've got a memory leak.  Keep close track of collections, and ensure that they are in a suitable scope.  Object scope is very commonly used.

class MyClass
{
public:
    MyClass();
    virtual ~MyClass();
private:
    Vector<MyObject*> myVector;
};

In the above example, the Vector of MyObject pointers will be created when the object is created, and destroyed when the object is destroyed.  The objects the pointer point to will not be destroyed unless you specifically destroy them.  You can do this in the class destructor if you do not wish to refer to those objects again.

MyClass::~MyClass()
{
    //Destroy the objects in my Vector
    Vector_each(MyObject*, itr, myVector) {
        delete *itr; //Delete the object
    }
}

Always be aware of the lifespan of objects in collections, and delete the objects as necessary.

Iteration

In most collections, there are ways to iterate through the contents.  These are specific to the collection type, and methods for iteration are described with each collection type.  They are not consistent, so don't assume that because you can use one method for iterating one type of collection it will be exactly the same for another.

In C++ there isn't an iteration interface, nor is there a keyword for iteration.  For example, in C# there is the IEnumerable interface and the foreach keyword which lets you iterate through any collection:

foreach(String in myStringCollection)
{
}

This isn't supported in C++, so iteration is slightly more verbose.

Arrays

Arrays and Vectors are the simplest collections in MoSync.  They provide easy access to stored objects and data, and can be returned by functions and methods.

The simplest collection type is an array.  This is supported in C and C++ rather than being a collection supported in MoSync specifically.  It provides random access (that is, you can access any item in the collection), but it stores its contents sequentially in memory.  This can cause problems if you are storing a lot of large objects, as the contiguous memory may not be available.

You can create an array by using the [] operators.  Arrays can be of any object or primitive type.  You must specify the size of the array when you create it and arrays cannot be easily resized, nor can items be easily inserted into them.  They are small, require no additional overhead and are fast.  This code will create an array of twenty chars.

char myCharArray[20];

Any item in the array can be accessed by its zero-based index number.

char thirdChar = myCharArray[2]; // Zero-based

Values can be written as well

myCharArray[2] = 'c';

When you have finished with an array, you will need to use the delete [] instruction, and not delete.

delete [] myCharArray;

Which will then delete every item in the array.

Typically in arrays (and most collections), you'll store a primitive value (char, int, byte et cetera), or an object pointer.  You can store objects in the array directly, but enough contiguous memory must be available.  

To iterate through an array, you access the array with the [] operator sequentially.  You do need to know the size of the array before you start, but you need to know that to create it.  For example

for(int i = 0; i < 20; i++)
{
   printf("%c", myCharArray[i]);
}

Vectors

Vectors are part of the MAUtil namespace.  To use them, you will need to include MAUtil/Vector.h. 

#include <MAUtil/Vector.h>
using namespace MAUtil;

A vector is an object wrapper around an array.  All the storage will be done in the array, but the vector will let you iterate through the collection, insert items into the collection and resize the collection.

Vectors are similar to C# and Java List<> collections.  MoSync List<> collections are not. 
See below for details of MoSync List<>.

You don't need to specify a size when you create a vector.

Vector<MyObject*> myVector;

What you do need to specify is the type of data you are going to store, by adding the type name between the angle brackets.  Once you specify this, you can only add items of that type.  In the above instance, these are MyObject pointers. You can only add MyObject pointers to this vector, and not MyObjects themselves or pointers of a different class.

You can add items into the vector using the .add() method.

myVector.add(new MyObject());

You can also insert items at a specific point. 

myVector.insert(2, new MyObject());

Existing items will be moved down the underlying array.  This is a comparitvely expensive operation, so only do it if the order is important.

Vectors maintain the order in which you add items.  If you iterate through the vector, you
will find objects in the same order you added or inserted them.

Individual items can be removed from vectors using the remove() method. There are three different ways you can remove items from a vector.

myVector.remove(MyObject**); 

This is a pointer to a MyObject pointer.  We'll look at iterators shortly, but MyObject** will probably be an iterator.

myVector.remove(1);

Removes the second item from the zero-based index.

myVector.remove(1, 3);

To completely empty a vector, you can use the clear() method.

Removes the second, third and fourth items from the vector.  Remember, this does not delete any objects if your vector contains pointers.  It only removes the pointers.  If you want to delete the object you must still use delete.

The vector starts with a small array of the type you've specified.  By default it will create an array of the type containing four items.  If you add a fifth, then the a new array is created, and all the items are copied across.  The new array will be twice the size of the old array.  If you exceed the reserved space, then the array will be recreated and the data carried across again.  The obviously adds overhead.  If you know that you want 20 items, then create it with space for 20 items.  You can do this in three ways.

Firstly, you can set the size in the constructor.  As Vectors are normally created on the stack, then there is no opportunity to use the new instruction.  Instead, in C++ you can specify it when your object is created.  In your header file (.h) you can have something like this:

class MyClass
{
public:
    MyClass();
    virtual ~MyClass();
private:
    Vector<MyObject*> myVector;
};

And in the code file (.cpp) you can specify the size of the vector in the constructor.

#include "MyClass.h"

MyClass::MyClass() : myVector(20)
{
}

This will initialise the vector with a size of 20.  There are also two methods for sizing the vector, resize() and reserve().  Both will set the size of the underlying array, but resize() will move the current cursor to the end of the array.  This means that if you resize(20) and then .add(new MyObject()) then the vector will have 21 items in it and have a capacity of 40.  In general, use reserve() when you want to allocate memory.  You cannot resize() or reserve() to a size smaller than you are currently using.

You can get the current usage by using the size() and capacity() methods.  If you want to know how many items are in a vector, use size().  To find out how many it can take, use capacity().  To easily find if the vector is empty, use the empty() method.

if(myVector.empty())
{
    // Do something.
}

Vectors, like arrays, are random access.  You can access any item in the vector using [].

MyObject* myObject = myVector[3];

You can also get direct access to the array using the pointer() method.

To iterate a vector, you use a vector iterator.  These are pointers to each item in the vector, and are of the type Vector<Type>::iterator.  For example

Vector<MyObject*>::iterator

You can get iterators to the first object, and to the point beyond the last object using the begin() and end() methods.  Because the underlying array is in contiguous memory, then the iterators are sequential.  The iterator is really a pointer to the type of the object (or value) stored.  In our example, the iterator will be of a type MyObject**.  It is a pointer to a MyObject pointer.

We can use the iterators from begin() and end() to test our iteration.

for(Vector<MyObject*>::iterator itr = myVector.begin(); 
itr != myVector.end(); 
itr++)
{
    // Do something.
}

This will then give us a MyObject** for each loop, called itr.  You can use the object stored as normal if you dereference it.

MyObject* currentObject;
for(Vector<MyObject*>::iterator itr = myVector.begin(); 
itr != myVector.end(); 
itr++)
{
    currentObject = *itr; // Dereference it.
    currentObject->method();
}

There is also a macro you can use a short cut for iterating vectors.  Vectors are now the only collection to have such a macro.

Vector_each(Type, iterator variable, vector);

For instance, instead of the long for statement earlier, we can have the more readable

Vector_each(MyObject*, itr, myVector)
{
    // Do something.
}
If you change the vector during an iterative process, the iterator may become invalid, 
with undefined consequences.  Don't add or remove items from a vector whilst you are 
iterating through it.

Lists

Lists are part of the MAUtil namespace.  To use them, you will need to include MAUtil/List.h.

#include <MAUtil/List.h>
using namespace MAUtil;

If you come from a Java or C# background, List will not behave as you expect.  The MoSync equivalent of a Java/C# List is Vector<>.  In MoSync Lists are doubly-linked lists.

MoSync lists allow you to create a collection of items, where each item knows which is the item before it and after it in the list.  This has a big impact on how you iterate through a list.

As there isn't an underlying array, then you don't need to specify the size of your collection, nor is there an impact on performance for inserting an item in the list.  Each object or value you store will be wrapped in a very light object, so there is a very small memory overhead, but its flexibility is superb.

The downside of this is that there isn't any method of random access.  You have to navigate the list serially, although you can go backwards and forwards through the list.

You create the list in a similar way to the vector.

List<MyObject*> myList;

You can then add items very easily to either the beginning or the end of the list by using addFirst() and addLast() respectively.

You can only add items into the middle of the list, or remove items from the if you've got an iterator.

You can get an iterator in the same way as you can with a vector, using the begin() and end() methods.  You can then access the objects and values stored using the prev() and next() methods.  You can test to see if you are at the start or end of a list using the hasPrev() and hasNext() methods.

The iterators for lists are of the type List<type>::ListIterator and List<type>::ConstListIterator.  Note the capitalisation of Iterator for this iterator.

List<MyObject*>::ListIterator itr = myList.begin();
MyObject* currentObject;
while(itr.hasNext())
{
    currentObject = itr.next();
}

Note that you don't increment the iterator like you do with vectors.  The next() and prev() methods return the value stored and increment the iterator.

You can insert items into list without much of a perform hit as there is no array to reallocate.  You do have to do this from an iterator though.

itr = myList.begin();
while(itr.hasNext())
{
    if(itr.next() == 1)
    {
        // Add the value 2 after this item.
        myList.insert(itr, 2);
    }
}

Equally, remove is done through the iterator.  When you add an item using insert it is placed after current iterator.  When you remove() an item, it removes the item from the current position.  ListIterators are not corrupted if you alter the collection, unlike vectors.  Remember, if you remove a pointer to an object, you are not destroying the object itself.  You still need to call delete.

The code below shows creating and altering a List<int>.

#include <MAUtil/Moblet.h>
#include <conprint.h>
#include <MAUtil/List.h>

using namespace MAUtil;

class MyMoblet : public Moblet 
{
public:
    List<int> myList;

    MyMoblet() 
    {
        // Create a list with an item missing.
        myList.addFirst(0);
        myList.addLast(1);
        myList.addLast(3);

        // Show the original list
        showList();

        // Now add 2 into the list
        List<int>::ListIterator itr = myList.begin();

        itr = myList.begin();
        while(itr.hasNext())
        {
            if(itr.next() == 1)
            {
                // Add the value 2 after this item
                myList.insert(itr, 2);
                lprintfln("Added 2");
            }
        }

        // Now show the list again
        showList();

        // Now remove 1 from the list
        itr = myList.begin();
        while(itr.hasNext())
        {
            if(itr.next() == 1)
            {
                // Remove this item
                myList.remove(itr);
                lprintfln("Removed 1");
            }
        }

        // Now show the list again
        showList();
    }

    void keyPressEvent(int keyCode, int nativeCode) 
    {
        this->close();
    }

    void showList()
    {
        lprintfln("I've got %d items", myList.size());
        List<int>::ListIterator itr = myList.begin();
        while(itr.hasNext())
        {
            lprintfln("Next value : %d", itr.next());
        }
    }
};

extern "C" int MAMain()
{
    Moblet::run(new MyMoblet());
    return 0;
}

Stack

There are two conceptually related, but utterly separate collections in MoSync - Stack and Queue.  They both are designed for serial access to collections where the order of the collection is vital.

Stacks are part of the MAUtil namespace.  To use them, you will need to include MAUtil/Stack.h.

#include <MAUtil/Stack.h>
using namespace MAUtil;

The Stack collection (capitalised to show that we don't mean the stack program memory, but a Stack<> collection), is a vector, but where you can only access the most recently added item.  You can put as many items as you want onto the Stack, but you can only get the latest item.  If you imagine a stack of books, you can see that it is only the book at the top which is accessible.  This kind of collection is also called first-in-last-out or very occasionally, FILO

You create a Stack like you would a vector

Stack<MyObject*> myStack;

Unlike vectors though, you cannot specify a size in advance.  It uses the vector's default initial size of 4, and will resize itself when it needs to.

When you want to add an item on a Stack, you are said to push it onto the Stack.  The method for adding to a stack is then

myStack.push(new MyObject());

When you want to remove an item from the stack, you are said to pop it off the Stack.

myStack.pop();

Remember, if you have a collection of object pointers, popping an item off the stack will not destroy the object.  You have to call delete yourself.

To get access to the next item on the Stack using the method peek().

MyObject* nextObject = myStack.peek();

You can test the size of the Stack using size() although you cannot test its capacity.  You can also see if it has no items in it using empty().  You can remove all the items from the stack at once using clear().

There are no iterators in the stack.  If you want to find an item, you have to keep popping items off the top until you get the one you want.  If you want to remove an item from the middle, you need to copy the data to another Stack.

As you'll see from the example below, iterating Stacks is laborious.  They are best suited to times when the order is important.  They are frequently used to allow users to retrace their steps.  For instance, you can keep track of the order the user navigated the screens in your application.  When the users wants to go back one screen, then you can pop the last value off your stack.

#include <MAUtil/Moblet.h>
#include <conprint.h>
#include <MAUtil/Stack.h>

using namespace MAUtil;

class MyMoblet : public Moblet 
{
public:
    Stack<int> myStack;

    MyMoblet()
    {
        // Create a list with an item missing.
        myStack.push(3);
        myStack.push(1);
        myStack.push(0);

        // Show the original stack
        showStack();

        // Now add 2 into the stack
        // We need another stack to copy removed items to.
        Stack<int> tempStack;
        while(myStack.peek() != 1)
        {
            // Copy the top of the stack to another stack.  Remember that the new stack will
            // have everything in reverse order
            tempStack.push(myStack.peek());
            myStack.pop();
        }
        
        // Add the value 2
        myStack.push(2);

        // Now copy everything back again
        while(tempStack.size() > 0)
        {
            myStack.push(tempStack.peek());
            tempStack.pop();
        }

        // Now show the Stack again
        showStack();

        // Now remove 1 from the stack
        while(myStack.peek() != 1)
        {
            // Copy the top of the stack to another stack.  Remember that the new stack will
            // have everything in reverse order
            tempStack.push(myStack.peek());
            myStack.pop();
        }
        
        // The item at the top is 1.  Remove it
        myStack.pop();

        // Now copy everything back again
        while(tempStack.size() > 0)
        {
            myStack.push(tempStack.peek());
            tempStack.pop();
        }

        // Now show the Stack again.
        showStack();
    }

    void keyPressEvent(int keyCode, int nativeCode) 
    {
        this->close();
    }

    void showStack()
    {
        lprintfln("I've got %d items", myStack.size());
        // We need to copy the Stack to another stack whilst we go through it.
        Stack<int> tempStack;
        while(myStack.size() > 0)
        {
            lprintfln("Next value : %d", myStack.peek());
            tempStack.push(myStack.peek());
            myStack.pop();
        }

        // OK, we've shown them all, now we need to put myStack back together.
        while(tempStack.size() > 0)
        {
            myStack.push(tempStack.peek());
            tempStack.pop();
        }
    }
};

extern "C" int MAMain()
{
    Moblet::run(new MyMoblet());
    return 0;
}

Queues

Queues are part of the MAP namespace.  To use them, you will need to include MAP/Queue.h, and ensure that you've got MAP.lib included as a library in your project settings.  Alternatively, copy the file Queue.h out of the includes directory and include it directly in your project, although if you do this, you will need to replace newobject() with new and deleteobject() with delete, or alternatively, implement the MemoryMgr class as well.

#include <MAP/Queue.h>
using namespace MAP;

Queues are logically very similar to Stacks, but the implementation is quite different.  Differences in implementation will be highlighted as the collection is described.  

Logically, a queue is the same as a Stack, with the exception that the first item is the only one available, not the last item.  This is sometimes called first-in-first-out or very occasionally FIFO.

To create a queue, it is almost the same as stack

Queue<MyObject> myQueue;

The important difference to note is the data type you provide it.  MoSync queues only contain pointers.  You provide it with the type of data pointer you want.  The above code will create a Queue of MyObject pointers, and not MyObject, despite what it looks like.  If you had the code

Queue<MyObject*> myQueue;

You would get a queue of MyObject**.  Equally then

Queue<int> myQueue;

Will create a queue of int* and not int.

When you add an item to a queue, this is called enqueueing, and the method is enqueue().  To remove a method, you dequeue() it.  Whereas with the Stack, when you peek() at a value it leaves it on the Stack and have to pop() it off, dequeue() will remove it from the collection.  It is the responsibility of the code calling dequeue()  to delete the object after use if necessary to prevent memory leaks.

myQueue.enqueue(new MyObject());
MyObject* currentObject = myQueue.dequeue();

Whilst Queues were not designed for random access, there are methods in the MoSync implementation for accessing values at will.  There aren't any iterators with queues however.

You can read values using peekAt() method, providing the method with the index number of the value you want, or peek() which will return the next item, although these will leave the item in the queue. 

You can also find the location in the queue of a specific object.  The find() method will return an items index number.  

Like vectors, you can set capacity of the queue, and it has an array internally for storage.  Unlike a vector, you cannot resize a queue, so when it is full, then that's it.  Your item will not be added, no return value will be given by enqueue() and no error will be raised, so it is important to check the amount of space left in the queue using getCapacity() (not capacity() like in vector) and getCount() (not size()).

You can remove all the items in the queue (but not delete the objects) using clear().

Queues are useful for marshalling activities.  For instance, if you've got a long list of items which need to be downloaded, rather than creating an instance of Downloader for each of them, you can have one Downloader and a queue.  You can create a class which gets the next URL from the queue and downloads the data.  Once it is complete, it can check the queue again for the next request.

Dictionary-based collections

There are some collections in MoSync which are a little more powerful, allowing you to access their contents through you own index.  The basis for these is the Dictionary.  Whilst you never create a Dictionary on its own, only classes derived from Dictionary they have a common syntax.  

Dictionaries are collections of Pairs (you may know these also as Key/Value pairs).  Each pair has a key with which you can reference your value.  Often, you don't need to know about the pairs in the collection, but if you iterate through a Dictionary, then your iterator will be a pair.

To create a Dictionary, you need to create one of the derived classes, Map, HashMap or Set.  They all follow the same pattern

Map<Key Type, Value Type> myMap;
HashMap<Key Type, Value Type> myHashMap;
Set<Key Type, Value Type> mySet;

For example, you can create a collection of the names of your friends and their phone numbers.  Both their names and numbers will be stored as strings.  The names will be the key and the phone number the value.

Map<String, String> myFriends;

You can then add items to the collection.

myFriends.insert("Rupert Bear", "Norwood 1212");

The insert() method actually returns another Pair, this time with a Dictionary::Iterator and a boolean value.  The iterator points to the value you've just added, and the boolean contains true if the insert was successful.

Note, unlike Vectors, there is no add method, because Dictionaries don't keep the same 
concepts of ordering.

Dictionary classes are all random-access, you can have access to any item at any time.  We can find Rupert's phone number at any time.  The find method returns a Pair<String, String>.  The Pair has two properties for the key and value.  Rather than being called key and value, they are called first and second.  The key is in first, and the value is in second.

String& rupertsNumber = myFriends.find("Rupert Bear")->second;

Map and HashMap also provide support for [] operators.  This doesn't return the Pair but just the value.

String& rupertsNumber = myFriends["Rupert Bear"];

With all Dictionary classes, no two items can have the same key.  If you were to insert another value using the same key, then the old value is overwritten.

Items can be removed from a Dictionary using the erase() method.  This takes an iterator or a key, so
myFriends.erase("Rupert Bear");

is valid.  Note that the method is erase() and not remove()  as it is on vectors and lists.

So, Dictionaries are power collections we can use very flexibly to store data.  There are some differences between the three collections.

Map

The Map is the simplest of the Dictionary classes.  It is a simple mapping between the key and the value.  For a given key, it will return the location of the value.  It achieves this by comparing the key provided with its list of keys.  It checks each one, and when it finds a match it will return the value.  For this reason, Map is most suitable when the collection is small.  With a very large collection, any search operation may have to compare every key in its collection until it finds the correct value. The elements in Map are sorted from lower to higher key values.

HashMap

The HashMap converts the key you provide using a hash function.  It then assigns the value into a location in memory depending on the value of the hash.  This is important, as it can calculate this hash again when you want to retrieve the value.  This means that it doesn't have to compare all of the keys looking for the value you want.  It does mean that it does a little more work than a Map, but it is much more suitable for larger collections, e.g. collections which are likely to have more than five entries. 

Set

The Set is like a map, except that values are unique as well as keys.  As the values are unique, then the storage is in the order of the values, and not the keys.  This means that it is unlikely that when you iterate through a collection, that the values are in the same order as they were entered in.  Sets are essential when having unique keys and unique values is essential.  Set does not support the [] operator, so you have to use the .find() method to retrieve values.  The keys in a Set are not hashed.

Iteration

Dictionary Iterators cannot be unassigned, which makes iterating through Dictionaries quite verbose.  There also isn't a handy macro like there is for vector.  You have to either already have an Iterator or create one in the for statement.

for(Map<String, String>::Iterator itr = myFriends.begin();
itr != myFriends.end(); 
itr++)
{
       // Do something.
}

To create an iterator, you need to type it exactly as the Dictionary is typed, so if you've got

Map<String, String> myFriends;

Then the Iterator has a type of

Map<String, String>::Iterator

As described above, Iterators provide access to Pairs.  You need to use the first and second properties on them for the key and value respectively.

for(Map<String, String>::Iterator itr = myFriends.begin();
itr != myFriends.end();
itr++)
{
   lprintfln("Friend: %s Number: %s", itr->first.c_str(), itr->second.c_str());
}

Below is code to create an manipulate a HashMap<String, String>.  You will see how the order changes.

#include <MAUtil/Moblet.h>
#include <conprint.h>
#include <MAUtil/HashMap.h>
#include <MAUtil/String.h>

using namespace MAUtil;

class MyMoblet : public Moblet 
{
public:
    HashMap<String, String> myFriends;

    MyMoblet() 
    {
        // Add some friends
        myFriends.insert("Rupert Bear", "Norwood 1212");
        myFriends.insert("Noddy", "Playtown 2020");
        myFriends.insert("Windy Miller", "Trumpton 2323");

        // Show the original dictionary
        showDictionary();

        // Find a friend
        String friendsNumber;
        friendsNumber = myFriends.find("Rupert Bear")->second;
        lprintfln("Rupert's number is %s", friendsNumber.c_str());

        // Erase a friend
        myFriends.erase("Noddy");

        // Show the new dictionary
        showDictionary();

        // Find a friend by iterator
        HashMap<String, String>::Iterator myFriend = myFriends.begin();
        lprintfln("My first friend is %s whose number is %s", 
        myFriend->first.c_str(), myFriend->second.c_str());
    }

    void keyPressEvent(int keyCode, int nativeCode) 
    {
        this->close();
    }

    void showDictionary()
    {
        lprintfln("I've got %d items", myFriends.size());
        for(HashMap<String, String>::Iterator itr = myFriends.begin(); 
        itr != myFriends.end();
        itr++)
        {
            lprintfln("Friend: %s Number: %s", itr->first.c_str(), 
            itr->second.c_str());
        }
    }
};

/*
* Main program starts here.
*/
extern "C" int MAMain()
{
    Moblet::run(new MyMoblet());
    return 0;
};

Using MAUtil Set, Map, HashMap

The MAUtil classes Set, Map and HashMap provide generic containers similar to std::set, map and unordered_map in the STL or Java's Set, Map and Hashtable. They provide many of the same familiar operations. Here we describe examples for Set, Map, and HashMap:

Set

/**
 * @file Set.cpp
 * @author Naveed Asif
 *
 * Description:
 *
 *  In this tutorial we will learn the use of Set
 *  container. Initially a list of numbers is
 *  created and then the difference between erase()
 *  clear() is shown.
 */

#include <MAUtil/Moblet.h>
#include <conprint.h>
#include <MAUtil/Set.h>

using namespace MAUtil;

class MyMoblet : public Moblet
{
public:
	// Set is declared.
	Set<int> myNumbers;

	MyMoblet()
	{
		// Add some numbers.
		myNumbers.insert(786);
		myNumbers.insert(111);
		myNumbers.insert(222);
		myNumbers.insert(333);

		// Shows the original dictionary in the console.
		// Note: The dictionary values will be displayed in
		// ascending order.
		showDictionary();

		// Locates and Erases any specific Number.
		myNumbers.erase(333);

		// Look at the dictionary and notice that 333 is erased.
		showDictionary();

		// This will delete all the members of container.
		myNumbers.clear();

		// Will show that you got 0 members.
		showDictionary();
	}

	/**
	 * Called when a key is pressed on the keypad.
	 */
	void keyPressEvent(int keyCode, int nativeCode)
	{
		if (MAK_BACK == keyCode || MAK_0 == keyCode)
		{
			close();
		}
	}

	/**
	 * showDictionary function is used to display all the
	 * members in myNumbers container.
	 */
	void showDictionary()
	{
		printf("I've got %d Numbers", myNumbers.size());
		if (myNumbers.size() > 0)
		{
			printf("Numbers are:");
		}
		for(Set<int>::Iterator iter = myNumbers.begin();
			iter != myNumbers.end();
			iter++)
		{
			printf("%d", *iter);
		}
	}
};

/*
 * Main function where the program starts
 */
extern "C" int MAMain()
{
    Moblet::run(new MyMoblet());
    return 0;
}

Map

/**
 * @file Map.cpp
 * @author Naveed Asif
 *
 * Description:
 *
 *  In this tutorial we will learn the use of Map
 *  data structure. Initially a list of friends is
 *  created and then the Iterator's methods begin(),
 *  erase(), and find() are used.
 */

#include <MAUtil/Moblet.h>
#include <conprint.h>
#include <MAUtil/Map.h>
#include <MAUtil/String.h>

using namespace MAUtil;

class MyFriend
{
private:
	String myName;
	String myAddress;

public:
	MyFriend(String name, String address)
	{
		myName = name;
		myAddress = address;
	}

	void show()
	{
		printf("%s, %s", myName.c_str(), myAddress.c_str());
	}
};

class MyMoblet : public Moblet
{
public:
	// Map is declared.
	Map<String, MyFriend*> myFriends;

	MyMoblet()
	{
		// Add some friends.
		addFriend("Rupert Bear", "Norwood, England");
		addFriend("Ghuman", "Stockholm, Sweden");
		addFriend("Windy Miller", "Trumpton 2323");

		// Shows the original dictionary in the console.
		// Note: The dictionary values will be displayed in
		// ascending order.
		showDictionary();

		// Finds a friend using the find method and displays it
		// in the console.
		printf("--------------");
		printf("Found friend:");
		MyFriend* myFriend = myFriends.find("Rupert Bear")->second;
		myFriend->show();

		// Locates and erases a friend.
		myFriends.erase("Ghuman");

		// Look at the dictionary and notice that "Ghuman" is erased.
		showDictionary();

		// Finds the first entered friend.
		printf("--------------");
		printf("My first friend is:");
		Map<String, MyFriend*>::Iterator iter = myFriends.begin();
		iter->second->show();
	}

	/**
	 * Add a new friend to the dictionary.
	 */
	void addFriend(String name, String address)
	{
		myFriends.insert(name, new MyFriend(name, address));
	}

	/**
	 * Called when a key is pressed on the keypad.
	 */
	void keyPressEvent(int keyCode, int nativeCode)
	{
		if (MAK_BACK == keyCode || MAK_0 == keyCode)
		{
			close();
		}
	}

	/**
	 * showDictionary function is used to display all the
	 * pair values in myFriends Map function
	 */
	void showDictionary()
	{
		printf("--------------");
		printf("I've got %d friends:", myFriends.size());
		for(Map<String, MyFriend*>::Iterator iter = myFriends.begin();
			iter != myFriends.end();
			iter++)
		{
			iter->second->show();
		}
	}
};

/*
 * Main function where the program starts
 */
extern "C" int MAMain()
{
    Moblet::run(new MyMoblet());
    return 0;
}

HashMap

The HashMap works same like Map does except that Map always displays results in ascending order. You can simply change Map to HashMap in above example and it will work.

Similarities

The containers have a number of things in common. They have default constructors, copy constructors and destructors. Elements are copied by value, so care should be taken when dealing with pointers. Large elements should be passed by pointer or reference-counted. There are Iterators and ConstIterators. The functions find(), begin() and end() can return either type. The erase() function works either with a Key or with an Iterator, but not with a ConstIterator. We can also use size() and clear() functions.

The constructor has an optional argument for specifying the comparison function. The default comparison function uses the Key's operator<, so if your Key doesn't have it and you can't readily add it, this argument is useful.

Differences

Set has only one datum per element, whereas Map and HashMap have two data per element: a Key and a Value. Both must be specified when inserting a new element. Both are also accessible from Iterators. Map and HashMap also have the operator[].

HashMap's constructor has an optional argument for specifying the hash function. The default value is THashFunction< Key >. This template function has no default implementation. MoSync ships with implementations for MAUtil::String and int. If you use another key type, you'll need to implement a hash function for it.

Set and Map are sorted. The comparison function decides the sorting order.

HashMap is unsorted. The comparison function is only used in case of hash collision. While HashMap Iterators present the elements in a certain order, that order may change whenever an element is added or removed and should not be relied upon.

Further information

All the classes have reference documentation. Also, the source code is available in $MOSYNCDIR/include/MAUtil/ in your MoSync package.

Using MAUtil Vector

The MAUtil::Vector class provides a generic, dynamically resizeable container similar to std::vector in the STL, Java's Vector or a .NET List. It provides many of the same familiar operations. 

Starting with Vectors

A Vector can be used as a generic collection, allowing you to grow the set whilst maintain the order in which items were added.  Vectors add items as their value type.  When you add or insert an item on the list, it adds the value or object to that list.  If you have a Vector of a complex object type, it will add that object to the list, and not a pointer to it, unless you explicitly request it.

For example

Vector<UserDetail> mUserDetails;

Will create a Vector of UserDetail objects, and not a Vector of UserDetail*.  Not all collections in MoSync behave in this manner.  To create a Vector of pointers to UserDetails objects, use

Vector<UserDetail*> mUserDetails;

Vectors are generic collections, so you define the type of object you want to store when you define the Vector.  The snippet below demonstrate creating Vector of int on the stack.

Vector<int> mInts;

Vectors store their items in contiguous memory.  Internally, there is an array which is used for storage with the Vector class maintaining the order.  Collections, including Vector, can cause you to run into memory allocation (malloc) errors if there isn't enough free contiguous memory left.  Broadly speaking, if you are storing a complex object type like the UserDetail object in the example above, you are always better off storing pointers than the values.  If you are using primitive types (int, byte), then store the values.

Adding items to a Vector

There are two ways you can add new items into a Vector, the add() and the insert().  The method add() appends the new item at the end of the list.

mInts.add(5);

You can also insert an item into the Vector at a requested position using the method insert().

mInts.insert(2, 100);

Where the first parameter is the index number you wish to insert the number (on a zero-based index) and the second is the value you wish to store.  All subsequent entries are moved along.  This has the potential to be very slow on large collections, so try to sort items before you enter them into the Vector

As the Vector is based on an array, you can still access the items using the [] operators.  You can use the index number to change the value of an item in an array.  This won't cause objects to be deleted, so if you are storing pointers to objects, then you will need to delete the item to prevent a memory leak.

UserDetails* mNewUser;
Vector<UserDetails*> mUserDetails;
...
// Overwrite the first entry.
UserDetails* temp = mUserDetails[0];
mUserDetails[0] = mNewUser;
delete temp; // Delete the old entry

Removing Items from a Vector

You can remove an item from a Vector using the remove() method.  There are two ways this can be called.  Firstly, you can request for a specific index number to be removed, but you can also remove with an iterator.  There is more about iterators below.

mUserDetails.remove(0);

Again, removing items from the Vector won't delete them from memory if they are pointers to objects.  You need to delete them explicitly or you will have a memory leak.

UserDetails* temp = mUserDetails[0];
mUserDetails.remove(0);
delete temp;

Removing items from the end of the list is very quick, but if you remove from the middle (or the start) of the list, then the other items need to be reordered, which can be slow.

Iterating through Vectors

You can work you're way through all the items in a Vector in two different ways.  As the Vector is based on an array, you can iterate through the items by index number 

Vector<int> numbers;
.... 
 // iterate through the vector using the 
// [] operator, printing each element.
for(int i = 0; i < numbers.size(); i++) {
    printf("numbers[i]: %d", numbers[i]);
}

A second method is to use the iterator.  Iterators are pointers to the items in the Vector, but are of a type

Vector<T>::iterator

Note that with Vectors the iterator has a low-case 'i' whilst in other collections (Set for example) the iterator has an upper-case 'I'.

Vector<int> numbers;
for(Vector<int>::iterator i = numbers.begin(); i < numbers.end(); i++)
{
  printf("%d", *i);
}

You can use the iterator to step through items in the Vector using the iterators returned by the methods begin() and end().  Also note that the iterator is a pointer to the value, so you will have to dereference it with an *.

Finally, there is a macro to make iteration more readable.  You can use the macro Vector_each to iterate the Vector

Vector_each(int, i, numbers)
{
    // The iterator needs to be dereferenced.
    printf("numbers[i]: %d", *i);
}

Vector_each takes three arguments; the type stored by the Vector, the variable name you want to refer to the iterator and finally the Vector itself.

Size and Capacity

There's an important distinction between the size and capacity of a Vector. Vectors grow dynamically to accomodate additional elements that are added to them. When an element is added, exceeding the current capacity, the capacity is doubled. However, the size only increases by one. This snippet demonstrates the behaviour:

for(int i = 0; i < 10; i++)
{
    numbers.add(i);
    printf("Size: %d  Capacity: %d", numbers.size(), numbers.capacity());
}


The output looks like this:

  Size: 1 Capacity: 4
  Size: 2 Capacity: 4
  Size: 3 Capacity: 4
  Size: 4 Capacity: 8
  Size: 5 Capacity: 8 
  Size: 6 Capacity: 8
  Size: 7 Capacity: 8
  Size: 8 Capacity: 16
  Size: 9 Capacity: 16
  Size: 10 Capacity: 16

In this case, the memory used to store the Vector elements is reallocated twice, each time requiring all the elements to be copied to the newly allocated memory location.

It is possible to reserve() a certain capacity, which is useful in cases when the number of elements to add is known beforehand (but not at compile time) in order to avoid superfluous reallocations:

numbers.reserve(11);
for(int i = 0; i < 10; i++)
{
    numbers.add(i);
    printf("Size: %d  Capacity: %d", numbers.size(), numbers.capacity());
}

The output looks like this:

  Size: 1 Capacity: 11
  Size: 2 Capacity: 11
  Size: 3 Capacity: 11
  Size: 4 Capacity: 11
  Size: 5 Capacity: 11 
  Size: 6 Capacity: 11
  Size: 7 Capacity: 11
  Size: 8 Capacity: 11
  Size: 9 Capacity: 11
  Size: 10 Capacity: 11


Note
Most functions that somehow manipulate the size of the vector do not affect the capacity if the new size is smaller than the previous. Do not assume that resize(0) will free up memory if the Vector previously contained thousands of elements.

Internal Storage

The elements of a Vector are stored linearly in an array. This means that they can be accessed using pointers. There is a function pointer() which returns a T* pointer to the internal storage array. There are also two types of typedefed iterators, one regular and one const. These map directly to T* pointers as well.

Communication, HTTP, Bluetooth

HTTP Connections

This tutorial will show you the basics of HTTP connections in MoSync. You will see how easy it is to write applications which can access HTTP-based information. You will also see how easy it is to stream information over HTTP. This example is written from a Moblet template.

Initializing

#include <mautil/connection.h> 
#include <mastdlib.h>
#include <conprint.h>
#define CONNECTION_BUFFER_SIZE 1024

connection.h is included to provide the basic connection functionallity.
mastlib.h is included to add the atoi function which converts the contents of an array to an int.
conprint.h adds the printf function which writes text to the screen as a simple console.
CONNECTION_BUFFER_SIZE defines the size of the buffer in which data is collected.

 class MyMoblet : public MAUtil::Moblet, private MAUtil::HttpConnectionListener 

To provide the functionallity we inherit function declarations from the HttpConnectionListener class.

  void httpFinished(MAUtil::HttpConnection *conn, int result); 
void connRecvFinished(MAUtil::Connection *conn, int result);
void connReadFinished(MAUtil::Connection *conn, int result);

The httpFinished function is a callback which is called whenever a http connection is initiated and we can recieve a response. When we use the recv function for the http connection connRecvFinished is called everytime a new chunk of data is recieved. If we use the read function instead, the connReadFinished function will be called when the read is done. More about these functions later in this tutorial.

  char mBuffer[CONNECTION_BUFFER_SIZE]; 
MAUtil::HttpConnection mHttp;

mHttp is a MAUtil::HttpConnection object which represents the actual connection. Each one of these may only handle one connection at the time so to enable multiple connections we need to define multiple HttpConnection objects. Always make sure that a connection is closed before connecting to it again.

 MyMoblet::MyMoblet() : mHttp(this) 

The constructor sets itself as the listener for the HttpConnection object so that the callbacks declared in MyMoblet will be used.

  InitConsole(); 
gConsoleLogging = 1;

In our entry point, MAMain we initialize the console and enable logging. This means that everything that everything that we send to the console is also logged in a log file. If you handle large amount of data and would want to output information to verify behaviours of your application this will help you. This file is located in the Output folder of your project and is called log.txt.

Connections and Responses

Too initiate an http connection we call the create function of the HttpConnection object. All you need to provide is the url and if you need to use GET or POST. In this example we send a POST request to a server.

  int res = mHttp.create(url, HTTP_POST); 
if(res < 0) {
printf("unable to connect - %i\n", res);
} else {
mHttp.finish();
mIsConnected = true;
}

If the create function returns a value below zero this means that it have failed for some reason. Please check the API references for all the possible return values. It's only possible to have one active connection on each HttpConnection object. Due to this we must make sure that we don't try to reconnect an active connection. If the create call was successful we can now set request headers. Since we don't need it we can call the finish function directly.

When we have a connection with the server the callback function httpFinished will be called.

  MAUtil::String contentLengthStr; 
int responseBytes = mHttp.getResponseHeader("content-length", &contentLengthStr);
int contentLength = 0; 
if(responseBytes == CONNERR_NOHEADER) 
printf("no content-length response header\n"); 
else { 
printf("content-length : %s\n", contentLengthStr.c_str()); 
contentLength = atoi(contentLengthStr.c_str()); 
} 

First we check for the "content-length" response header, if it's not found we will recieve an CONNERR_NOHEADER response. If it's found we recieves the actual content length as an char array of numbers. It's then converted it to an int using the atoi function.

  if(contentLength >= CONNECTION_BUFFER_SIZE || contentLength == 0) { 
printf("Receive in chunks..\n");
mHttp.recv(mBuffer, CONNECTION_BUFFER_SIZE);
} else {
mBuffer[contentLength] = 0;
mHttp.read(mBuffer, contentLength);
}

If we don't have any content length or it's larger then our recieving buffer we can't just read all the data. We have to read multiple times until we reaches the end. This is done by calling the recv function of the HttpConnection object.

If we know the content length and the recieving data fits inside the buffer we can read it all directly. This is done by using the read function in HttpConnection.

The difference between the two is that recv specifies the total amount of bytes which it can recieve while read specifies the number of bytes it shall read. Each of these methods has its own callback functions. Depending on the function you use connRecvFinished or connReadFinished callback functions will be called.

  if(result >= 0)
printf("connReadFinished %i\n", result);
else
printf("connection error %i\n", result);
mHttp.close();

The connReadFinished callback function first check if the result is not negative. A negative result means an error might have happened. Check the return value against the API documentation. Positive results means that data was received. The connection shall be closed manually now since no more data will be received on this connection.

  if(result >= 0) {
printf("connRecvFinished %i\n", result);
mHttp.recv(mBuffer, CONNECTION_BUFFER_SIZE);
return;
}
else if(result == CONNERR_CLOSED) {
printf("Receive finished!\n");
} else {
printf("connection error %i\n", result);
}
mHttp.close();

The connRecvFinished callback function works a little bit different. If the result is positive we have received that amount of bytes. If so we can't close the connection since more data will be received. If it's negative we must check if it's CONNERR_CLOSED. CONNERR_CLOSED doesn't need to be an error. Usually it means that the sending server has sent its data and closed the connection to verify the receiver about it. When a negative value is returned the connection shall be closed.

In this tutorial we have had a look at how easy it is to access HTTP information and also stream such information to your application. With this basic knowledge you will easily build your applications which will use web based information.

For further information also read the sockets tutorial and the tutorial concerning the downloader.

Example Source Code

#include <MAUtil/Moblet.h>
#include <mautil/connection.h>
#include <mastdlib.h>
#include <conprint.h>
#define CONNECTION_BUFFER_SIZE 1024
class MyMoblet : public MAUtil::Moblet, private MAUtil::HttpConnectionListener
{
public:
    MyMoblet();

    void httpFinished(MAUtil::HttpConnection *conn, int result);
    void connRecvFinished(MAUtil::Connection *conn, int result);
    void connReadFinished(MAUtil::Connection *conn, int result);
    void keyPressEvent(int keyCode);
private:
    void initiateConnection(const char* url);
    char mBuffer[CONNECTION_BUFFER_SIZE];
    MAUtil::HttpConnection mHttp;
    bool mIsConnected;
};
MyMoblet::MyMoblet() : mHttp(this)
, mIsConnected(false)
{
    printf("http connection tutorial.\n");
    printf("press softkeys to send http requests.\n");
    printf("press 0 to exit\n");
}
// connect to the given url if not other connection is active
void MyMoblet::initiateConnection(const char* url) {
    if(mIsConnected) {
        printf("already connected\n..");
        return;
    }
    printf("\nconnecting to %s", url);

    int res = mHttp.create(url, HTTP_POST);
    if(res < 0) {
        printf("unable to connect - %i\n", res);
    } else {
        mHttp.finish();
        mIsConnected = true;
    }
}
void MyMoblet::httpFinished(MAUtil::HttpConnection* http, int result) {
    printf("HTTP %i\n", result);

    MAUtil::String contentLengthStr;
    int responseBytes = mHttp.getResponseHeader("content-length", &contentLengthStr);
    int contentLength = 0;
    if(responseBytes == CONNERR_NOHEADER)
    printf("no content-length response header\n");
    else {
        printf("content-length : %s\n", contentLengthStr.c_str());
        contentLength = atoi(contentLengthStr.c_str());
    }
    if(contentLength >= CONNECTION_BUFFER_SIZE || contentLength == 0) {
        printf("Receive in chunks..\n");
        mHttp.recv(mBuffer, CONNECTION_BUFFER_SIZE);
    } else {
        mBuffer[contentLength] = 0;
        mHttp.read(mBuffer, contentLength);
    }

}
void MyMoblet::connReadFinished(MAUtil::Connection* conn, int result) {
    if(result >= 0)
    printf("connReadFinished %i\n", result);
    else
    printf("connection error %i\n", result);
    mHttp.close();

    mIsConnected = false;
}
void MyMoblet::connRecvFinished(MAUtil::Connection * conn, int result){
    if(result >= 0) {
        printf("connRecvFinished %i\n", result);
        mHttp.recv(mBuffer, CONNECTION_BUFFER_SIZE);
        return;
    }
    else if(result == CONNERR_CLOSED) {
        printf("Receive finished!\n");
    } else {
        printf("connection error %i\n", result);
    }
    mHttp.close();
    mIsConnected = false;
}
// Press 0 to exit. Soft left and soft right will initiate new connections
void MyMoblet::keyPressEvent(int keyCode) {
    switch(keyCode) {

    case MAK_0:
        maExit(0);
        break;

    case MAK_SOFTLEFT:
        initiateConnection("http://www.example.com/");
        break;
    case MAK_SOFTRIGHT:
        initiateConnection("http://www.mosync.com/");
        break;
    }
}
extern "C" int MAMain() {
    InitConsole();
    gConsoleLogging = 1;
    MAUtil::Moblet::run(new MyMoblet());
}

Using Connection Sockets

Here we take a look at how to use the MAUtil's Connection API to communicate over sockets using TCP. We will illustrate how to use them by making a simple FTP client.

An FTP client begins by setting up a socket to the FTP server, authorize itself and then continues by sending instructions. All communication is done in plain text which makes the protocol both easy to understand and use. For further information about the FTP protocol see cr.yp.to/ftp.html.

Implementation

We will implement all functionality as functions in a class inherited from Moblet, so we begin by creating a project from a Moblet template, adding a few #include directives, and declaring inheritance from the MAUtil::ConnectionListener class:

#include <MAUtil/Moblet.h> 
#include <MAUtil/Connection.h> 
#include <MAUtil/Util.h> 
#include <conprint.h>
#include <mastdlib.h>
using namespace MAUtil;
class MyMoblet : public Moblet, ConnectionListener 
{

Then add the member variables of the class, a Connection instance, and a temporary string buffer. The string buffer will be used to store the incoming responses from the server:

private:
Connection mConnection;
char lineBuffer[1024];

We continue by defining the Constructor. In the initialization list we initialize the connection by passing a pointer to this (the MAUtil::ConnectionListener). In the constructor we connect the connection to a socket, i.e. the FTP server we're going to communicate with. FTP communication defaults to port 21, but in some cases FTP servers may use other ports. We verify that the connection has been successfully initiated by checking the return value from connect.

public: 
MyMoblet() : mConnection(this)
{
    int res = mConnection.connect("socket://ftp.sunet.se:21");
    if(res < 0) 
    {
        maPanic(res, "mConnection.connect failed");
    }
}

Now we will add some helper functions to receive, parse, and send the FTP response and requests. First we add a function that receives incoming data to the temporary string buffer:

void getNextFtpResponse()
{
    mConnection.recv(lineBuffer, 1024);
}

When the response has been received the connRecvFinished function derived from the ConnectionListener, which we will implement later, is invoked. Next we implement a function to put an FTP request. It adds a line breaking sequence that conforms to the FTP standard and writes the request to the connection. The connWriteFinished function will be invoked when the request has been written.

void putFtpRequest(const char *req)
{
    char temp[1024];
    int len = sprintf(temp, "%s\015\012", req);
    mConnection.write(temp, len);
}

Finally, we add a function that parses the response code of a response line. (The link to the FTP protocol description at the top of this guide describes the format of a response.)

We first trim the spaces in the beginning and then parse the following number:

int parseFtpResponseCode(const char *lineBuffer)
{
    int i=0;
    char temp[16];
    while(lineBuffer[i]==' ') { i++; } 
    
    // trim spaces in the beginning
    while(isdigit(lineBuffer[i])) temp[i++] = lineBuffer[i];
    temp[i] = 0;
    return atoi(temp);
}

Now that the helper functions are ready we can implement the actual communication. First we implement the connectFinished function derieved from the ConnectionListener which checks if everything went well and if that is the case, receives the next FTP server response.

void connectFinished(Connection* conn, int result)
{
    if(result < 0) 
    {
        printf("mConnection.connectFinished failed");
        return;
    }
    getNextFtpResponse();
}

The connWriteFinished will look exactly the same. Whenever we've sent an FTP request we want to receive a new FTP response.

void connWriteFinished(Connection* conn, int result)
{
    if(result < 0)
    {
        printf("mConnection.write failed");
        return;
    }
    getNextFtpResponse();
}

When we've received a response from the server we can decide on what to do next. The final function connRecvFinished derived from the ConnectionListener will be called when a response has been received. First we split the lines of the response into a list of strings, one for each line. The response code will be the same for each line, so we parse the response code of the first line. By analyzing the response code, the client program can determine what state the FTP connection is in and choose what to do next.

We will only implement a few steps in the initial handshaking procedure. After the PASV command has been sent, a response with a new IP address and port is recieved. This IP address should be used to set up a data connection. All data traffic will be handled over this connection. Even the result from requests like LIST, which list all files in a directory. Implementing this functionality is left as an exercise for the reader.

void connRecvFinished(Connection* conn, int result)
{
    if(result < 0) 
    {
        printf("mConnection.recv failed");
        return;
    }

    // We may have recieved several lines in one response, 
    // but all of them will begin with the same response code.
    Vector<String> responses;
    stringSplit(lineBuffer, "\015\012", responses);

    // the last one will always be an empty string
    responses.resize(responses.size()-1); 
    for(int i = 0; i < responses.size(); i++) printf("%s", responses[i].c_str());
    int code = parseFtpResponseCode(responses[0].c_str());
    switch(code)
    {
    // 220 welcome - we respond with a user (anonymous in this case)
    case 220: putFtpRequest("USER anonymous"); break;

    // 331 identify yourself in a password - we can send any password as we're anonymous.
    case 331: putFtpRequest("PASS dummy"); break;

    // 230 thanks - we enter passive mode
    case 230: putFtpRequest("PASV"); break;
    }
}
};


If you've done everything right, the result will look like this:

Creating a Bluetooth Server

Many people are developing Bluetooth applications in MoSync. Some of the most interesting applications involve creating a new Bluetooth service so that the user’s phone can receive incoming messages on Bluetooth and act on them. In this tutorial we take a look at using the MAUtil::Server class to provide Bluetooth services.

The Server Base Class

In the MoSync MAUtil library there is a class called Server. It is a base class that you can adapt to accept network connections over Bluetooth and TCP. You create a new Bluetooth service by creating a new Server instance and inheriting from ServerListener. Your class becomes a wrapper around Server and can process both incoming and outgoing messages.

The ServerListener interface allows your service class to respond to events raised by your Server instance. There are two methods that you need to implement: serverAccepted and serverAcceptFailed. These methods inform your application when a new connection has been made to your server, or when an attempt to connect has failed.

When you receive a new connection, the serverAccepted method is called, and it is passed a pointer to a new Connection object. This is the same class as you would use for outgoing connections so all of the same methods for reading and writing data are available. The only difference is that you’ve got to code responses to incoming data.

A very simple Server implementation would look like this:

#include <MAUtil/Moblet.h>
#include <MAUtil/Server.h>
#include <conprint.h>
using namespace MAUtil;
class MyServer : public ServerListener
{
	private:
		Server mServer;
		MAHandle mConn;
	public:
		MyServer() : mServer(this), mConn(NULL)
		{
		}
		~MyServer()
		{
			//Close the server before we exit
			if(mServer.isOpen())
				mServer.close();
		}
		void MyServer::connect()
		{
			if(mServer.isOpen())
				lprintfln("Already open");
			else
			{
				//Start accepting incoming TCP requests on port 81
				mConn = mServer.start("socket://8103");
				if(mConn < 0)
					lprintfln("Service failed - is the port blocked?");
				else
					lprintfln("Service started");
			}
		}
		//ServerListener
		void MyServer::serverAcceptFailed(Server* server, int result)
		{
			lprintfln("Connection failed");
		}
		void MyServer::serverAccepted(Server* server, Connection* conn)
		{
			lprintfln("Connection accepted");
		}
};
class MyMoblet : public Moblet
{
	public:
		MyMoblet()
		{
			MyServer myServer;
			myServer.connect();
		}
};
extern "C" int MAMain()
{
	Moblet::run(new MyMoblet());
	return 0;
};

As you can see from the code, the class MyServer creates a new server and binds itself to TCP port 81. Any requests to the phone on that port will now be answered by MyServer.

Creating Servers for Bluetooth Connections

Binding to Bluetooth addresses is a little more complex. Instead of simply binding to a port you need to bind to the Bluetooth address of the device, and provide a service UUID.

Each Bluetooth service has its own UUID (universally unique identifier). These are common across platforms, just as TCP ports are. If you create a brand-new service, you should generate a new UUID for it. If you are implementing an existing service (such as OBEX Push), then you should use the existing UUID. Common UUIDs have been defined in the file MAUtil/mauuid.h.

When Bluetooth clients are searching for your service, they will need to know what the UUID is. If you are writing the client software as well, you’ll need to use the UUID to discover the service. See our tutorial on Discovering Devices and Services with Bluetooth for more information.

In the MoSync SDK, Bluetooth service UUIDs are defined as being a struct of an int[4]. Bluetooth UUIDs are 32 bytes long, but large parts of the UUID are common to all Bluetooth UUIDs. If you want to create your own UUID, you would need to check that it isn’t being used by any other Bluetooth service, and also ensure that its unique segment (the first int in the struct) ANDs with 0x1000 (4096). The following is a line from mauuid.h, defining the UUID for the serial port service.

DEFINE_BTMAUUID(SerialPort_Service_MAUUID, 0x1101);

Its unique segment is 0x1101. The first "1" signals that it is a specific service, and not a service collection (compare with DEFINE_BTMAUUID(RFCOMM_PROTOCOL_MAUUID, 0x0003);). The remaining value 0x101 (257) is the part that matches the standard definition for serial port.

Creating Server IDs

Your UUID must be unique. In this example, we will use a value of 355. We can then define our new service UUID as follows:

DEFINE_BTMAUUID(Example_Service_MAUUID, 0x1163);

The format of a Bluetooth service URL is:

btspp://localhost:<service uuid>[;name=<plain text name of the service>]

If we were creating a new service, we would need to have created an MAUUID which we could write to the string. When translated, our MAUUID is 0000116300001000800000805f9b34fb.

Formatting Connection URLs

This little function will create a valid URL:

void MyServer::formatUrl(char* output, MAUUID& serviceID, const char* servicename)
{
	sprintf(output, "btspp://localhost:%08x%08x%08x%08x;name=%s\0", serviceID.i[0], serviceID.i[1], serviceID.i[2], serviceID.i[3], servicename);
}

You can now use the URL to start the server. The connect method takes this URL instead.

#include <MAUtil/Moblet.h>
#include <MAUtil/Server.h>
#include <MAUtil/mauuid.h>
#include <conprint.h>
using namespace MAUtil;
DEFINE_BTMAUUID(Example_Service_MAUUID, 0x1163);
//This class sets up the server connection, and manages the bindings.
class MyServer : public ServerListener
{
	private:
		Server mServer;
		MAHandle mConn;
		
		void MyServer::formatUrl(char* output, const MAUUID& serviceID, const char* servicename)
		{
			sprintf(output, "btspp://localhost:%08x%08x%08x%08x;name=%s\0", serviceID.i[0], serviceID.i[1], serviceID.i[2], serviceID.i[3], servicename);
		}
	public:
		MyServer() : mServer(this), mConn(NULL)
		{
		}
		~MyServer()
		{
			//Close the server before we exit
			if(mServer.isOpen())
				mServer.close();
		}
		void MyServer::connect()
		{
			if(mServer.isOpen())
				lprintfln("Already open");
			else
			{
				//Start accepting incoming BT requests for Example_Service_MAUUID
				char buffer[255];
				const char* servicename = "Example Service";
				formatUrl(&buffer[0], Example_Service_MAUUID, servicename);
				lprintfln("url: %s", buffer);
				mConn = mServer.start(buffer);
				if(mConn < 0)
					lprintfln("Service failed");
				else
					lprintfln("Service started");		
			}
		}
		//ServerListener
		void MyServer::serverAcceptFailed(Server* server, int result)
		{
			lprintfln("Connection failed");
		}
		void MyServer::serverAccepted(Server* server, Connection* conn)
		{
			lprintfln("Connection accepted");
			mConnHandler->start(conn);
		}
};
class MyMoblet : public Moblet
{
	private:
		MyServer myServer; 
	public:
		MyMoblet()
		{
			myServer.connect();
		}
};
extern "C" int MAMain()
{
	Moblet::run(new MyMoblet());
	return 0;
};

This won’t let you start accepting connections though. This is just the server binding information. To accept connections, you need to create a ConnectionListener.

Accepting Connections

If you’ve run the above examples, you will notice that neither the serverAccepted or the serverAcceptFailed methods have been called. To make the server binding work, you need to call the accept method, and pass it a ConnectionListener.

ConnectionListener will be familiar to anyone who has developed a network client in MoSync. It responds to connection events from the Connection class. The ConnectionListener has the following methods you need to implement:

 void connectFinished(Connection* conn, int result);
 void connRecvFinished(Connection* conn, int result);
 void connWriteFinished(Connection* conn, int result);
 void connReadFinished(Connection* conn, int result);

When your server starts, it will return you a normal Connection object. Just as when you are developing a client application, you need to be able to respond to incoming and outgoing data, and to be able to read from and write to the stream. The ConnectionListener you will create will do this for you, and actually implement the details of your protocol.

Creating ConnectionListeners

A common way to develop this ConnectionListener is to create a connection handler. This is the class which will maintain the connection state and manage the data flow.

class MyConnectionHandler : public ConnectionListener
{
	private:
		Connection* mConn;
		char buffer[64];
	public:
		MyConnectionHandler();
		~MyConnectionHandler();
		//Provide a method to tell the connection handler to start receiving data
		void MyConnectionHandler::start(Connection* conn)
		{
			mConn = conn;
			//Wait to receive a few bytes
			mConn->recv(&buffer, 63); //Don't receive more than the buffer size.
		}
		//ConnectionListener interface
	 void MyConnectionHandler::connectFinished(Connection* conn, int result)
	 {
	 	lprintfln("Connection to a client has been established");
	 }
	 void MyConnectionHandler::connRecvFinished(Connection* conn, int result)
	 {
	 	lprintfln("%d bytes have been received.", result);
	 }
	 void MyConnectionHandler::connWriteFinished(Connection* conn, int result)
	 {
	 	lprintfln("%d bytes have been written", result);
	 }
	 void MyConnectionHandler::connReadFinished(Connection* conn, int result)
	 {
	 	lprintfln("%d bytes have been read", result);
	 }
};

This example is just about as simple as it can be. The class inherits from ConnectionListener and provides the methods for receiving notification of data reads and writes. When one of these is called, it just writes the notification to the console.

Processing a connection

There is also a method start. This is so we can start the connection handler with a valid connection, and it can start requesting data from the stream. As an example, we’re going to create a server that receives data, counts the number of bytes it has received in total, and respond to the client with this value. We’ve got a buffer to read into, but we’re not really interested in the data, just the values, so we can write the data to the console and keep track of the quantity. When data is received, we’re going to increase the total counter, create a text value to that amount and write it to the output stream.

	 void MyConnectionHandler::connRecvFinished(Connection* conn, int result)
	 {
	 	if(result > 0)
	 	{
				lprintfln("%d bytes have been received.", result);
				//Increase the byte counter
				total += result;
				//Write the data to the console
				lprintfln("%s", buffer);
				//Write the running total to the output stream
				sprintf(&writeBuffer[0], "%9d", total);
				mConn->write(&writeBuffer, 10);
				//Reset the read buffer
				memset(&buffer, 0, 64);
				//Receive some more data
				mConn->recv(&buffer, 63);
	 	}
	 	else
	 	{
	 		//An error condition has been reached
	 		lprintfln("Error - %d", result);
	 	}
	 }

We’ve also put an error check in there to ensure that no error codes have been sent.Now we’ve got a connection handler that can read and write data, we can call the accept method on the Server.

		{
			if(mServer.isOpen())
				lprintfln("Already open");
			else
			{
				//Start accepting incoming BT requests for Example_Service_MAUUID
				char buffer[255];
				const char* servicename = "Example Service";
				formatUrl(&buffer[0], Example_Service_MAUUID, servicename);
				lprintfln("url: %s", buffer);
				mConn = mServer.start(buffer);
				if(mConn < 0)
					lprintfln("Service failed");
				else
				{
					lprintfln("Service started");
					//Accept the connection
					mServer.accept(new MyConnectionHandler());
				}
			}
		}

Once the server has bound to the port or service, the serverAccepted method is called, and you can start your connection handler receiving data.

In a more sophisticated example, you would also need a listener to the connection handler for it to report errors to, and to close the connection when it has finished. The complete listing is below

#include <MAUtil/Moblet.h>
#include <MAUtil/Server.h>
#include <MAUtil/mauuid.h>
#include <conprint.h>
using namespace MAUtil;
DEFINE_BTMAUUID(Example_Service_MAUUID, 0x1163);
//This class handles the connection and implements the details of the protocol
class MyConnectionHandler : public ConnectionListener
{
	private:
		Connection* mConn;
		char buffer[64];
		char writeBuffer[10];
		int total;
	public:
		MyConnectionHandler() {}
		//Provide a method to tell the connection handler to start receiving data
		void MyConnectionHandler::start(Connection* conn)
		{
			total = 0; //Receive 0 bytes so far
			mConn = conn;
			//Wait to receive a few bytes
			mConn->recv(&buffer, 63); //Don't receive more than the buffer size.
		}
		//ConnectionListener interface
	 void MyConnectionHandler::connectFinished(Connection* conn, int result)
	 {
	 	lprintfln("Connection to a client has been established");
	 }
	 void MyConnectionHandler::connRecvFinished(Connection* conn, int result)
	 {
	 	if(result > 0)
	 	{
				lprintfln("%d bytes have been received.", result);
				//Increase the byte counter
				total += result;
				//Write the data to the console
				lprintfln("%s", buffer);
				//Write the running total to the output stream
				sprintf(&writeBuffer[0], "%9d", total);
				mConn->write(&writeBuffer, 10);
				//Reset the read buffer
				memset(&buffer, 0, 64);
				//Receive some more data
				mConn->recv(&buffer, 63);
	 	}
	 	else
	 	{
	 		//An error condition has been reached
	 		lprintfln("Error - %d", result);
	 	}
	 }
	 void MyConnectionHandler::connWriteFinished(Connection* conn, int result)
	 {
	 	lprintfln("%d bytes have been written", result);
	 	//Clear the write buffer
	 	memset(&writeBuffer, 0, 10);
	 }
	 void MyConnectionHandler::connReadFinished(Connection* conn, int result)
	 {
	 	lprintfln("%d bytes have been read", result);
	 }
};
//This class sets up the server connection, and manages the bindings.
class MyServer : public ServerListener
{
	private:
		Server mServer;
		MAHandle mConn;
		MyConnectionHandler* mConnHandler;
		void MyServer::formatUrl(char* output, const MAUUID& serviceID, const char* servicename)
		{
			sprintf(output, "btspp://localhost:%08x%08x%08x%08x;name=%s\0", serviceID.i[0], serviceID.i[1], serviceID.i[2], serviceID.i[3], servicename);
		}
	public:
		MyServer() : mServer(this), mConn(NULL)
		{
		}
		~MyServer()
		{
			//Close the server before we exit
			if(mServer.isOpen())
				mServer.close();
		}
		void MyServer::connect()
		{
			if(mServer.isOpen())
				lprintfln("Already open");
			else
			{
				//Start accepting incoming BT requests for Example_Service_MAUUID
				char buffer[255];
				const char* servicename = "Example Service";
				formatUrl(&buffer[0], Example_Service_MAUUID, servicename);
				lprintfln("url: %s", buffer);
				mConn = mServer.start(buffer);
				if(mConn < 0)
					lprintfln("Service failed");
				else
				{
					lprintfln("Service started");
					//Accept the connection
					mConnHandler = new MyConnectionHandler();
					lprintfln("Created the handler");
					lprintfln("Requesting accept");
					mServer.accept(mConnHandler);
				}
			}
		}
		//ServerListener
		void MyServer::serverAcceptFailed(Server* server, int result)
		{
			lprintfln("Connection failed");
		}
		void MyServer::serverAccepted(Server* server, Connection* conn)
		{
			lprintfln("Connection accepted");
			mConnHandler->start(conn);
		}
};
class MyMoblet : public Moblet
{
	public:
		MyMoblet()
		{
			MyServer myServer;
			myServer.connect();
		}
};
extern "C" int MAMain()
{
	Moblet::run(new MyMoblet());
	return 0;
};

Creating Bluetooth Clients

In our Discovering Devices and Services with Bluetooth tutorial we demonstrated how the MAUtil::BluetoothDiscoverer class can be used to locate nearby devices and find out which Bluetooth services they support. Once you’ve discovered a device and a service you will need to write a client for that service and open a data connection to the server. That's what we will be doing in this tutorial.

Opening Connections

To create a connection to the Bluetooth device that you want to communicate with, you can use the Connection class in the MAUtil library (#include <MAUtil/Connection.h>). The Connection class wraps up common connection commands into one easy-to-use object.

Creating ConnectionListeners

Creating Connection objects is discussed at length in the tutorial Downloading Data from the Internet, but the one thing you do have to do though is to implement ConnectionListener. The ConnectionListener has the following methods you need to implement:

void connectFinished(Connection* conn, int result);
void connRecvFinished(Connection* conn, int result);
void connWriteFinished(Connection* conn, int result);
void connReadFinished(Connection* conn, int result);

When the Connection object reads to writes data, it will inform your ConnectionListener of the result.

//ConnectionListener
void connectFinished(Connection* conn, int result)
{
 lprintfln("Connection established");
}
void connRecvFinished(Connection* conn, int result)
{
 lprintfln("Received %d bytes", result);
}
void connWriteFinished(Connection* conn, int result)
{
 lprintfln("Wrote %d bytes", result);
}
void connReadFinished(Connection* conn, int result)
{
 lprintfln("Read %d bytes", result);
}

To create the Connection, you need to be able to format a URL to the desired Bluetooth server correctly. This is the only real difference between connecting to a Bluetooth service and connecting to an HTTP resource on the Internet.

This tutorial assumes that you’ve either already followed the discovery processes in our tutorial Discovering Devices and Services with Bluetooth. That tutorial explains how to find the service you want to connect to, and introduces the classes BtDevice and BtService.

Formatting Bluetooth URLs

When you want to create a URL to connect to a Bluetooth service, you need the MABtAddr address class from BtDevice and the port number from BtService. The example we are working through today assumes you’ve already got the both a BtDevice and a BtService.

The Bluetooth URL is in the format:

btspp://<device address>:<service port>

The device address is in an MABtAddr object in BtDevice.address. The MABtAddr is a struct that contains byte[6]. Each of these bytes needs to be converted to two-character hex value in array order.

This little function will convert it for you:

void BluetoothClient::formatConnection(char* output, BtDevice& device, int port)
{
	MABtAddr a = device.address;
	sprintf(output, "btspp://%02x%02x%02x%02x%02x%02x:%i",
		a[0], a[1], a[2], a[3], a[4], a[5], port);
}

Reading and Writing to the Service

Once connected, you’ve got the same Connection object as you have for connecting to the Internet. You can read and write into the connection.

In this example, we are going to create a client which is for the Bluetooth service created for the tutorial Creating a Bluetooth Server. In that tutorial, the service simply receives a stream of bytes, counts them, and then returns a running total to the client. Our client is going to connect to that server, send a short string, and then listen for the count to be returned.

To do this, we need to expand the implementation of ConnectionListener we produced earlier. The first stage is to make the connection to the correct URL. This means we need to write a short method to start the connection:

void BluetoothClient::connect(BtDevice& device, BtService& service)
{
	char buffer[100];
	formatConnection(&buffer[0], device, service.port);
	mConn.connect(buffer);
}

We use the formatConnection method from earlier to create the URL, and make a connection. If successful, our connectFinished method will be called:

void BluetoothClient::connectFinished(Connection* conn, int result)
{
 lprintfln("Connection established");
 //Connection finished, start sending data
 mConn.write("123456", 6);
}

Once the connection is open, we can write data into it. As with all Connection objects, the write is asynchronous, so don’t write from local variables as they can go out of scope before the write has completed. Here, we are just writing six bytes representing the characters one to six. Once the write has finished, our connWriteFinished method will be called.

void BluetoothClient::connWriteFinished(Connection* conn, int result)
{
 lprintfln("Wrote %d bytes", result);
 //Listen for response
 mConn.recv(&readBuffer, 63);
}

We know that we’ve written data to the server, so now we can wait for the server to respond with the lenght of the data we’ve sent. There is a char[64] already declared to read the response into. When data has been received, our connRecvFinished method will be called.

void BluetoothClient::connRecvFinished(Connection* conn, int result)
{
 lprintfln("Received %d bytes", result);
 //Received the data
 lprintfln("Server response: %s", readBuffer);
 //Finished
 mConn.close();
}

We can echo the server’s response to the screen, and hopefully it tells us that we’ve sent six bytes. Now that we’ve completed our exchange, we can close the connection. Obviously, this is a simple example to demonstrate the process, but all application level protocols can be implemented in this fashion.

The Completed Client

There is a conversation between the client and the server, but the responses and the actions will depend on the protocol you wish to create. The complete source code follows:

#include <MAUtil/Moblet.h>
#include <MAUtil/Connection.h>
#include <MAUtil/BluetoothDiscovery.h>
#include <conprint.h>
using namespace MAUtil;
class BluetoothClient : public ConnectionListener
{
	private:
		Connection mConn;
		char readBuffer[64];
		void BluetoothClient::formatConnection(char* output, BtDevice& device, int port)
		{
			const byte* a = device.address.a;
			sprintf(output, "btspp://%02x%02x%02x%02x%02x%02x:%i",
				a[0], a[1], a[2], a[3], a[4], a[5], port);
		}
	public:
		BluetoothClient() : mConn(this)
		{}
		~BluetoothClient()
		{
			if(mConn.isOpen())
				mConn.close();
		}
		void BluetoothClient::connect(BtDevice& device, BtService& service)
		{
			char buffer[100];
			formatConnection(&buffer[0], device, service.port);
			mConn.connect(buffer);
		}
		//ConnectionListener
	 void BluetoothClient::connectFinished(Connection* conn, int result)
	 {
	 	lprintfln("Connection established");
	 	//Connection finished, start sending data
	 	mConn.write("123456", 6);
	 }
	 void BluetoothClient::connRecvFinished(Connection* conn, int result)
	 {
	 	lprintfln("Received %d bytes", result);
	 	//Received the data
	 	lprintfln("Server response: %s", readBuffer);
	 	//Finished
	 	mConn.close();
	 }
	 void BluetoothClient::connWriteFinished(Connection* conn, int result)
	 {
	 	lprintfln("Wrote %d bytes", result);
	 	//Listen for response
	 	mConn.recv(&readBuffer, 63);
	 }
	 void BluetoothClient::connReadFinished(Connection* conn, int result)
	 {
	 	lprintfln("Read %d bytes", result);
	 }
};

Discovering Devices and Services with Bluetooth

Implementing Bluetooth in your application is usually done in three stages. Firstly, there is device discovery: getting the phone to scan for other devices in range. Secondly, there is a service discovery: querying a discovered device to see which protocols and services it supports. Lastly, there is the implementation of a service, a specific transfer of data. This tutorial covers the first two steps: discovering devices and services.

Device Discovery

The first stage working with Bluetooth is discovering other Bluetooth devices. These could be other phones or mobile devices, laptop or desktop computers or any other Bluetooth-enabled device.

Note: The MoRE emulator you use to develop and test MoSync applications supports Bluetooth if the PC you are using supports it. Developers with Bluetooth enabled (as is very common on laptops) will see their PC search for devices just as their phones will.

We provide several C++ Bluetooth discovery classes in the MoSync SDK. These classes perform an asynchronous search of Bluetooth devices, and return information about each device, including its name and its unique address. To use the Bluetooth discovery classes, include the header file MAUtil/BluetoothDiscovery.h in your application.

BluetoothDeviceDiscoveryListener

To implement Bluetooth discovery, you need to create a class which will respond to Bluetooth events. If you are creating a MAUI application, this will often be a MAUI::Screen class. In the following example, we've built a screen called Devices. It searches for and reports on Bluetooth devices in detectable range. When we define this class, we make it inherit from the interface class BluetoothDeviceDiscoveryListener:

class Devices : public Screen, public BluetoothDeviceDiscoveryListener
{

We also need it to implement the two methods defined in BluetoothDeviceDiscoveryListener, btNewDevice and btDeviceDiscoveryFinished:

#include <MAUtil/BluetoothDiscovery.h>
... 
    //Members for the BluetoothDeviceDiscoveryListener
    void btNewDevice (const BtDevice &dev);
    void btDeviceDiscoveryFinished (int state);

To perform a Bluetooth device discovery, you will need an instance of BluetoothDiscoverer:

    //The BT discovery class
    BluetoothDiscoverer* mDiscoverer;

The process will be that you create an instance of the BluetoothDiscoverer, and set the BluetoothDiscoverer's listener to be your new class. This instructs the BluetoothDiscoverer to inform your class whenever it finds a Bluetooth device, and when it has finished looking for them.

The specific method for starting a device discovery is startDeviceDiscovery() to inform your class whenever it finds a Bluetooth device, and when it has finished looking for them:

  //Create the bt discoverer
  mDiscoverer = new BluetoothDiscoverer();
  mDiscoverer->startDeviceDiscovery(this, true); 

Unlike many other event-driven classes in the MoSync SDK, you provide a single listener class when you start a device discovery, not when you create the class. You can start the discovery process and pass it a pointer to your BluetoothDeviceDiscoveryListener class ('this' in the example), and a boolean value to say whether you want it to discover the devices' names.

Processing Discovered Devices

The reason you have to specify the listener as a parameter in the method call is that the same BluetoothDiscovery class is used for device discovery and service discovery, but the listeners which are required to respond to events are different.

Getting the names can take a few seconds longer, so if you don't need them, you don't need to spend time on getting them all.

Note: Getting names is not guaranteed. The discovery class will negotiate with each device and request its name, but some devices are slow and time out, and sometimes they move out of range between detecting their existence and retrieving their names. You should always code defensively for device names, and either substitute their address or leave them out of your UI if you can't detect their name.

When the discoverer detects a device, it will call the listener's btNewDevice method. It will pass a reference to a BtDevice (BtDevice&). BtDevice is a struct which contains a MAUtil::String for the name, and another struct containing the Bluetooth address called MABtAddr.

void Devices::btNewDevice (const BtDevice &dev)
{
  lprintfln("Found new device");
  mDevices.add(dev);
  mContentBox->add(createLabel(dev.name.c_str()));
}

When the discover has completed its search, it will call the btDeviceDiscoveryFinished() method of the listener:

void Devices::btDeviceDiscoveryFinished (int state)
{
  lprintfln("Device discovery finished");
  mStatus->setCaption("Idle");
  mIsWorking = false;
}

Below is the code for our example screen. It searches for devices and builds a list of nearby devices. (Note: this code requires UIBuilder.cpp and UIBuilder.h, which are available as a zip file at the bottom of this tutorial.)

Devices.h

/**
 * @file Devices.h
 * @author Sam Pickard, Naveed Asif
 *
 * Description:
 *
 * In this file we define the specifications for the Devices.
 *
 */

#ifndef DEVICES_H_
#define DEVICES_H_

#include <MAUI/Screen.h>
#include <MAUI/Layout.h>
#include <MAUI/Widget.h>
#include <MAUI/ListBox.h>
#include <MAUtil/Moblet.h>
#include <MAUtil/BluetoothDiscovery.h>
#include <MAUtil/Set.h>
#include "UIBuilder.h" // Is attached at the bottom of this tutorial.
#include "Services.h"
#include <conprint.h>

using namespace MAUI;
using namespace MAUtil;

/*
 * Devices class declaration.
 */
class Devices : public Screen, public BluetoothDeviceDiscoveryListener
{
public:
    Devices(Moblet* moblet);
    virtual ~Devices();

    // Members for the BluetoothDeviceDiscoveryListener.
    void btNewDevice (const BtDevice &dev);
    void btDeviceDiscoveryFinished (int state);

    void keyPressEvent(int keyCode);

private:
    Layout* mMainLayout;
    ListBox* mContentBox;
    Label* mStatus;
    Label* mTitle;
    Label* mInstructions;
    Moblet* mMoblet;
    Services* mServices;

    // The BT discovery class.
    BluetoothDiscoverer* mDiscoverer;

    bool mIsWorking;

    // Keep a list of the devices you've found.
    Vector<BtDevice> mDevices;

    void reset();
};

/*
 * MyMoblet class definition.
 */
class MyMoblet : public Moblet
{
public:
	MyMoblet();

private:
	Devices* mainScreen;
};

#endif /* DEVICES_H_ */

Devices.cpp

/**
 * @file Devices.cpp
 * @author Sam Pickard, Naveed Asif
 *
 * Description:
 *
 * In this file we define the different methods for the Devices class.
 *
 * Instructions:
 *
 * Pressing * key will search for the Bluetooth devices, select any
 * of the devices and press key 5 to see what services this device
 * supports.
 *
 */

#include "Devices.h"

MyMoblet* moblet;

/*
 * Devices class implementation.
 */
Devices::Devices(Moblet* moblet) : mMoblet(moblet)
{

	// Create the main layout.
	mMainLayout = (Layout*)createMainLayout("", "Exit");
	this->setMain(mMainLayout);

	// These are the labels that will be displayed in layout.
	mTitle = (Label*)mMainLayout->getChildren()[0]->getChildren()[0];
	mInstructions = (Label*)mMainLayout->getChildren()[0]->getChildren()[1];
	mStatus = (Label*)mMainLayout->getChildren()[0]->getChildren()[2];

	// The list box that will display the discovered Bluetooth devices.
	mContentBox = (ListBox*)mMainLayout->getChildren()[0]->getChildren()[3];

	// Captions for the labels.
	mStatus->setCaption("Idle \n");
	mTitle->setCaption("Bluetooth Devices");
	mInstructions->setCaption("Press * to search for Devices");

	// Create the services screen.
	mServices = new Services(this);

	// Create the bt discoverer.
	mDiscoverer = new BluetoothDiscoverer();

	// Not searching yet.
	mIsWorking = false;
}

/*
 * Destructor for the Devices class.
 */
Devices::~Devices()
{
  reset();
}

/*
 * Key press events
 */
void Devices::keyPressEvent(int keyCode)
{
	switch(keyCode)
	{
    	case MAK_SOFTRIGHT:
			mMoblet->close();
			break;
    	case MAK_STAR:
			// Clear any old results
			Vector_each(BtDevice, itr, mDevices)
			delete itr;
			mDevices.clear();
			Vector_each(Widget*, itr, mContentBox->getChildren())
			delete *itr;
			mContentBox->clear();

			// Start the bluetooth discovery
			if(!mIsWorking)
			{
				mIsWorking = true;
				mStatus->setCaption("Searching");
				mDiscoverer->startDeviceDiscovery(this, true);
			}
		    break;
		case MAK_2:
		case MAK_UP:
		case MAK_4:
			mContentBox->selectPreviousItem();
			break;
		case MAK_8:
		case MAK_DOWN:
		case MAK_6:
			mContentBox->selectNextItem();
			break;
		case MAK_5:
		case MAK_FIRE:
			mMainLayout->clear();
			mMainLayout->drawWidget();
			// mMainLayout->requestRepaint();
			// You cannot perform two bluetooth operations at the same time.
			if(!mIsWorking)
			{
				mServices->getServices(mDevices[mContentBox->getSelectedIndex()]);
				mServices->show();
			}
			break;
	}
}

/*
 * Finds devices and add them to the content list.
 */
void Devices::btNewDevice (const BtDevice &dev)
{
	lprintfln("Found new device");
	mDevices.add(dev);
	mContentBox->add(createLabel(dev.name.c_str()));
}

/*
 * Device discovery finished.
 */
void Devices::btDeviceDiscoveryFinished (int state)
{
	lprintfln("Device discovery finished");
	mStatus->setCaption("Idle");
	mIsWorking = false;
}

/*
 * Reset the device list.
 */
void Devices::reset()
{
	// Remove the current items.
	Vector_each(Widget*, itr, mContentBox->getChildren())
	delete *itr;

	// Clears the list box contents.
	mContentBox->clear();

	// Clear the Bluetooth device storage.
	Vector_each(BtDevice, itr, mDevices)
	delete itr;
	mDevices.clear();
}

/*
 * MyMoblet class declaration.
 */
MyMoblet::MyMoblet()
{
	// The default font and skins are declared.
	gFont = new MAUI::Font(RES_FONT);
	gSkin = new WidgetSkin(RES_SELECTED, RES_UNSELECTED, 16, 32, 16, 32, true, true);

	// Returns a reference to the single instance of
	// Engine class, using lazy initialization.
	Engine& engine = Engine::getSingleton();
	engine.setDefaultFont(gFont);
	engine.setDefaultSkin(gSkin);

	// This returns screen coordinates.
	MAExtent screenSize = maGetScrSize();
	scrWidth = EXTENT_X(screenSize);
	scrHeight = EXTENT_Y(screenSize);

	// Creates a new instance of the main screen.
	mainScreen = new Devices(this);
	mainScreen->show();
}

/*
 * Entry point of the program. The MAMain function
 * needs to be declared as extern "C".
 */
extern "C" int MAMain()
{
    moblet = new MyMoblet();
    Moblet::run(moblet);
    return 0;
}

Service Discovery

The second stage of the process of impementing Bluetooth is service discovery. Once you've got the address of a Bluetooth device, you can query it to see what services it can perform. These will vary greatly between devices. A mobile phone will provide different services from a printer. This is very important if you are trying to implement a specific Bluetooth service on your platform, as you can test other Bluetooth devices to see if they support the protocols you are trying to create.

BluetoothServiceDiscoveryListener

Just as in device discovery, we use the BluetoothDiscoverer class to find services. Instead of calling the startDeviceDiscovery method, we call startServiceDiscovery. To do this, we need a class which inherits from BluetoothServiceDiscoveryListener. In the device discovery phase this was the screen class.

The example shows a different screen for service discovery, so we can use the Services screen as the listener as well.

class Services : public Screen, BluetoothServiceDiscoveryListener

To implement BluetoothServiceDiscoveryListener, you need to implement the following methods:

    void btNewService (const BtService &serv);
    void btServiceDiscoveryFinished (int state);

When a new service is called, then your implementation of btNewService will be called, and just as in the device discovery, when it is complete it will call your btServiceDiscoveryFinished.

Bluetooth Service UUIDs

Your btNewService method will be passed a struct of the type BtService. This contains the name of the service (as a MAUtil::String), a port number, and vitally, a Vector<MAUUID>.

MAUUIDs are the MoSync SDK implementation of Bluetooth UUIDs (universally unique identifiers). These UUIDs are constants, and are common to all Bluetooth implementations. Each UUID maps to a Bluetooth service. If you decide to create a brand new Bluetooth service, then you should generate a new UUID for it. If you implement an existing Bluetooth service, then you should use the existing UUID. The MoSync SDK contains definitions of common Bluetooth services in MAUtil/mauuid.h for you to use.

To start a service discovery phase, you need to call the startServiceDiscovery method. This is being called from a method in our example Services object, where the Devices object has passed a BtDevice to the Services object for it to use in the search for services.

void Services::getServices(BtDevice& device)
{
  if(!mIsWorking)
  {
    mIsWorking = true;
    mStatus->setCaption("Searching");

    mDiscoverer->startServiceDiscovery(device.address, RFCOMM_PROTOCOL_MAUUID, this);
  }
}

Unlike the device discovery phase, you also need to specify the service class you are interested in. This specifies that we want to find Bluetooth services which operator over radio, so we are looking for the RFCOMM_PROTOCOL_MAUUID.

This UUID is also specified in MAUtil/mauuid.h. When you search in this way, you are searching for all the services which are available over Bluetooth radio, rather than over TCP or UDP. You can also search for more specific service groups like OBEX or UPnP, or even a specific service like OBEX push.

Processing a new Service

A word of caution: If you try to start a service discovery before the previous discovery has finished, the application will crash. The BluetoothDiscoverer class can't tell you directly when it is busy, so you will need to keep your own flag, which in this example is mIsWorking.

Once your service search is running, you will receive a call to your listener's btNewService(BtService&) method. This receives the BtService struct as defined above. You can filter services you may be interested in by comparing their UUIDs with values in MAUtil/mauuid.h. In our example, we are going to keep a Vector<BtService> of the services we've discovered, but also report the service names to the screen. The names come through populated, and you don't have to look up the name in a table.

void Services::btNewService (const BtService& serv)
{
  lprintfln("Found new service %s", serv.name.c_str());
  mContentBox->add(createLabel(serv.name.c_str()));
  mServices.add(serv);
}

When the service discovery phase is complete, your listener's btServiceDiscoveryFinished method is called.

void Services::btServiceDiscoveryFinished(int state)
{
  lprintfln("Service discovery finished");
  mStatus->setCaption("Idle");
  mIsWorking = false;
}

The complete listing for Services is below

Services.h

/**
 * @file Services.h
 * @author Sam Pickard, Naveed Asif
 *
 * Description:
 *
 * In this file we define the specifications for the Services.
 *
 */

#ifndef SERVICES_H_
#define SERVICES_H_

#include <MAUI/Screen.h>
#include <MAUI/Layout.h>
#include <MAUI/Widget.h>
#include <MAUI/ListBox.h>
#include <MAUtil/Moblet.h>
#include <MAUtil/BluetoothDiscovery.h>
#include <MAUtil/Set.h>
#include "UIBuilder.h" // Is attached at the bottom of this tutorial.
#include <MAUtil/MAUUID.h>
#include <conprint.h>

using namespace MAUI;
using namespace MAUtil;

/*
 * Services class declaration.
 */
class Services : public Screen, BluetoothServiceDiscoveryListener
{
public:
    Services(Screen* previous);
    virtual ~Services();

    void getServices(BtDevice& device);

    // Members for the BluetoothServiceDiscoveryListener.
    void btNewService (const BtService &serv);
    void btServiceDiscoveryFinished (int state);

    void keyPressEvent(int keyCode);

private:
    Screen* mPrevious;
    Widget* mMainLayout;
    ListBox* mContentBox;
    Label* mStatus;
    Label* mTitle;

    // The BT discovery class.
    BluetoothDiscoverer* mDiscoverer;

    bool mIsWorking;

    // Keep a list of the services you've found.
    Vector<BtService> mServices;

    void reset();
};

#endif /* SERVICES_H_ */

Services.cpp

/**
 * @file Services.h
 * @author Sam Pickard, Naveed Asif
 *
 * Description:
 *
 * In this file we define the methods for the Services class.
 *
 */

#include "Services.h"

/*
 * Services class implementation.
 */
Services::Services(Screen* previous) : mPrevious(previous)
{
	// Create the UI.
	mMainLayout = createMainLayout("", "Back");
	this->setMain(mMainLayout);

	// These are the labels that will be displayed in layout.
	mTitle = (Label*)mMainLayout->getChildren()[0]->getChildren()[0];
	mStatus = (Label*)mMainLayout->getChildren()[0]->getChildren()[1];
	mContentBox = (ListBox*)mMainLayout->getChildren()[0]->getChildren()[2];

	// Captions for the labels.
	mStatus->setCaption("Idle");
	mTitle->setCaption("Bluetooth Services");

	// Create the bt discoverer.
	mDiscoverer = new BluetoothDiscoverer();

	// Not searching yet.
	mIsWorking = false;
}

/*
 * The desctructor.
 */
Services::~Services()
{
	reset();
	delete mDiscoverer;
}

/*
 * Key press events.
 */
void Services::keyPressEvent(int keyCode)
{
	switch(keyCode)
	{
		case MAK_SOFTRIGHT:
			mPrevious->show();
			break;
		case MAK_2:
		case MAK_UP:
		case MAK_4:
			mContentBox->selectPreviousItem();
			break;
		case MAK_8:
		case MAK_DOWN:
		case MAK_6:
			mContentBox->selectNextItem();
			break;
	}
}

/*
 * Starts searching for the services.
 */
void Services::getServices(BtDevice& device)
{
	if(!mIsWorking)
	{
		mIsWorking = true;
		mStatus->setCaption("Searching");
		mDiscoverer->startServiceDiscovery(device.address, RFCOMM_PROTOCOL_MAUUID, this);
	}
}

/*
 * Returns all the services supported by the device.
 */
void Services::btNewService (const BtService& serv)
{
	lprintfln("Found new service %s", serv.name.c_str());
	mContentBox->add(createLabel(serv.name.c_str()));
	mServices.add(serv);
}

/*
 * Service discovery finished.
 */
void Services::btServiceDiscoveryFinished(int state)
{
	lprintfln("Service discovery finished");
	mStatus->setCaption("Idle");
	mIsWorking = false;
}

/*
 * Reset the services list.
 */
void Services::reset()
{
	// Remove the current items.
	Vector_each(Widget*, itr, mContentBox->getChildren())
	delete (*itr);
	mContentBox->clear();

	// Clear the Bluetooth device storage.
	mServices.clear();
}

With this example, we can now detect Bluetooth devices in range, and query them for the services they offer.

Now that we have done that, we probably want to Create a Bluetooth Client to access those services.

AttachmentSize
UIBuilder.zip2.01 KB

BluetoothClient

This example application acts as a Bluetooth client. It is designed to work with our BluetoothServer example application.

Start up/connection
Sending data

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Using this Example

To use this example application with our BluetoothServer application, you will need two Bluetooth capable devices. Make sure that Bluetooth is turned on on both devices and that the devices are paired before running the server and the client applications.

The file Common.h from the BluetoothServer project is included in the client. It is assumed that both project folders BluetoothClient and BluetoothServer are located in the same directory. Common.h contains variables and functions shared between the BluetoothServer and BluetoothClient projects (including the service UUID), and contains a full description of how to use the projects together.

Note: Before you build and run this application, you need to enter the address of the BluetoothServer device in sServerAddress in Client.cpp. When you start up our BluetoothServer application on a device it will show you this address.

Note that when making your own projects that use Bluetooth, you need to enable Bluetooth for your project under Properties > MoSync Project > Application Permissions.

Behaviour

When this application starts up on a device or in the MoRE emulator it will attempt to connect to the server identified by sServerAddress. If the connection is sucessfully established you will see the message "Connected to server". (If you are having trouble getting Bluetooth clients to connect to Bluetooth servers, you are not alone! Check out our dedicated forum topic Bluetooth Ache for some helpful advice.)

The zero or back key exits the application, but pressing other keys on the keypad or touching the screen will send the data to the Bluetooth server application: the keycode and the data sent are echoed on the client's screen.

Key Presses

  • 0 or back key — closes the application.
  • All other keypad keys — sent as data to the server. (Non-printable characters are ignored.)
  • Screen touches — sent as data to the server as the string: "Touch event: <x,y>", where <x,y> is the touch coordinate, for example: "Touch event: 234,318".

BluetoothServer

This application acts as a Bluetooth server. It is designed to work with our BluetoothClient example application.

On start up Connection established Receiving data

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Using this Example

To use this example application with our BluetoothClient application, you will need two Bluetooth capable devices. Make sure that Bluetooth is turned on on both devices and that the devices are paired before running the server and the client applications.

The file Common.h contains contains a full description of how to use the projects together, along with the variables and functions shared between the BluetoothServer and BluetoothClient projects (including the service UUID).

Note: when making your own projects that use Bluetooth, you need to enable Bluetooth for your project under Properties > MoSync Project > Application Permissions.

Behaviour

This application sets up an RFCOMM service with a particular UUID and listens for connections. When this application starts up successfully on a device (or in the MoRE emulator on a computer) with activated Bluetooth capabilities you will see the message "Server started successfully" followed by the server device's Bluetooth address in hexadecimal. You need to supply this address in the BluetoothClient application as the value of sServerAddress.

When a connection is accepted from the BluetoothClient application, the connection ID is shown along with the client device's Bluetooth address.

As data is received from a client (in BluetoothClient when a key is pressed or the screen is touched), the connection ID and the data are printed to the screen. The data is usually sent one character at a time, but may be buffered if the connection is slow.

Key Presses

  • 0 or back key — closes the application.

Downloading Data from the Internet

There are a thousand reasons why you might want your application to get information from the Internet. Perhaps you want to download today’s Dilbert cartoon, or get specific data for the user, or serve adverts, or  connect to a web interface. Maybe you want to implement a new protocol, like FTP or Jabber. In this tutorial we’re going to show you how to get data over HTTP.

The Connection Object Model

MoSync provides a wide range of connection objects and interfaces. There are three levels of abstraction for Internet connections, and you can write code against any of them:

  • Using the maConnect() API method
  • Using the MAUtil::Connection class
  • Using the Downloader classes

Using maConnect() API Method in C/C++

At the lowest level, you can use functions in the MoSync API directly. You can use the method maConnect() to open a socket to connect to any port on the Internet. You can implement protocols which aren’t supported in MoSync on a higher level (like HTTP and HTTPS) by formatting a URL like this:

socket://<server name>[:<port>]<parameters>

so you can create your own protocol support for IMAP and POP3 to make an email client, and the documentation come with the basics of an FTP client.

You can use this method to connect to Bluetooth devices as well, with the format

btspp://<address>

We are going to concentrate on HTTP connections in this tutorial though, and although the API provides some HTTP specific methods, we’re going to focus on the download components in MAUtil.

Using the MAUtil::Connection Class in C++

The second level of abstraction is with MAUtil::Connection objects. These are C++ objects and are much more suitable for use in a Moblet application.

  • MAUtil::Connection allows you direct access to the send and receive streams.
  • MAUtil::HttpConnection builds HTTP 1.0 connections.

With HttpConnection objects you can access the HTTP 1.0 protocol so you can set HTTP headers before you send, and create new wrapper classes for implementing specific HTTP functions. For instance, you can use  HttpConnection to implement an online authentication system, which you can then reuse.

Connections send messages to ConnectionListeners. When you are working with either a Connection or an HttpConnection, you will almost certainly need to create a class which inherits from ConnectionListener or HttpConnectionListener, and implements its methods. We find that very often this class is "my screen", which we define as:

class MyScreen : public Screen, ConnectionListener

and then implement the methods:

void connectFinished(Connection* conn, int result);
void connRecvFinished(Connection* conn, int result);
void connWriteFinished(Connection* conn, int result);
void connReadFinished(Connection* conn, int result);

These methods are your opportunity to respond to data coming back, and you can do whatever you want in there.

Using the Downloader Classes in C++

At the highest level are the Downloader classes. These are very high level classes that wrap much of the mechanics of getting data over HTTP for you. These are the classes which we use most often, and if you just want to download some XML or a picture then these are the classes you should use too.

The Downloader class itself wraps HttpConnectionListener, and it is very simple to use. You simply tell it the URL you want to get data from and give it an MAHandle to load the data into.

If you want to know when your download has completed, you have to implement a listener too. You do this by inheriting from DownloadListener and implementing the interface. There is an example of this in the next section of the tutorial.

A second downloader is the BuffDownloader. This is rather more specialised, and instead of downloading to a placeholder, it downloads to the heap.

There are two downloaders at an even higher level: AudioDownloader and ImageDownloader. These are both wrappers for Downloader, but create the appropriate resources in memory for handling these items. More on these later.

Finally, you can create your own custom Downloader. You can inherit from Downloader, and perform some of your own downloading tasks, just as you can implement Connection to manage your own application protocol.

Downloading Data from the Web

In all probability, what you actually want to do is download some data from the web. The easiest way to do this is to use the Downloader class and to create a DownloadListener to go with it. To do this we can make my Screen which requests the download from the DownloadListener. To do this in a reusable way, we create a DownloadScreen class.

DownloadScreen.h

#ifndef _DOWNLOADSCREEN_H_
#define _DOWNLOADSCREEN_H_
#include <MAUI/Screen.h>
#include <MAUI/Layout.h>
#include <MAUtil/Downloader.h>
#include "IScreenCoordinator.h"
#include "..\Utilities/Util.h"
#include "..\Utilities\IDownloadScreenListener.h"
#include "..\Widgets\SoftKeyBar.h"
using namespace MAUI;
using namespace MAUtil;
class DownloadScreen : public Screen, public DownloadListener, public ISoftKeyBarListener
{
public:
    DownloadScreen(Screen* previous, IScreenCoordinator* mainScreen);
    ~DownloadScreen();
    void download(const char* url, const char* storeName);
    void keyPressEvent(int keyCode);
    void setDownloadListener(IDownloadScreenListener* dll);
    void notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes);
    void finishedDownloading(Downloader *dl, MAHandle data);
    void error(Downloader* dl, int code);
    void downloadCancelled(Downloader* dl);
    void softKeySelected(int buttonID);
private:
    Screen* previous;
    IScreenCoordinator* mainScreen;
    Layout* layout;
    Image* image;
    ListBox* listBox;
    Downloader* dl;
    IDownloadScreenListener* listener;
    MAHandle _store;
};
#endif //_DOWNLOADSCREEN_H_

This code will build my screen, manage the downloads, and inform its own type of Listener (IDownloadScreenListener) when downloads are complete. (It doesn’t inform the IDownloadScreenListener of anything other than successful completion, as the user interaction for a failed download can be handled by this screen.)

IDownloadListener.h

#ifndef _IDOWNLOADSCREENLISTENER_H_
#define _IDOWNLOADSCREENLISTENER_H_
//Interface
class IDownloadScreenListener
{
public:
    virtual void downloadComplete();
};
#endif //_IDOWNLOADSCREENLISTENER_H_

DownloadScreen.h is implemented in DownloadScreen.cpp:

#include  "DownloadScreen.h"
//Handles a download
const char* store;

DownloadScreen::DownloadScreen(Screen* previous, IScreenCoordinator* mainScreen)
 : previous(previous), mainScreen(mainScreen)
{
    dl = new Downloader();
    dl->addDownloadListener(this);

    image = (Image*)createMainLayout(BLANK, BACK_BUTTON, this);
    layout = (Layout*) image->getChildren()[0];
    listBox = (ListBox*) layout->getChildren()[1];
    Label* l = createLabel("Starting download", 32);
    listBox->add(l);
    this->setMain(image);
}

DownloadScreen::~DownloadScreen(void)
{
    if(dl->isDownloading())
    dl->cancelDownloading();     
    delete dl;
}

void DownloadScreen::setDownloadListener(IDownloadScreenListener* dll)
{
    listener = dll;
}

void DownloadScreen::download(const char *url, const char* storeName)
{
    if(dl->isDownloading())
    {
        //lprintfln("Busy.");
        //dl->cancelDownloading();
    }
    else
    {
        lprintfln("Downloading %s", url);
        store = storeName;
        dl->beginDownloading(url);
    }
}

void DownloadScreen::downloadCancelled(Downloader *dl)
{
    //lprintfln("Cancelled");
    listBox->add(createLabel("Download has been cancelled"));
}

void DownloadScreen::error(Downloader *dl, int code)
{
    lprintfln("Error: %d", code);
    listBox->add(createLabel("Sorry, an error has occured"));
}

void DownloadScreen::finishedDownloading(Downloader *dl, MAHandle data)
{
    //Save the store
    MAHandle h = maOpenStore(store, MAS_CREATE_IF_NECESSARY);
    maWriteStore(h, data);
    maCloseStore(h, 0);

    lprintfln("Finished download");
    if(listener != NULL)
    {
        //lprintfln("Calling listener");
        listener->downloadComplete();
    }

    //lprintfln("Showing calling screen");
    previous->show();
}

void DownloadScreen::notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes)
{
    Label* l = (Label*)listBox->getChildren()[0];
    char* cap = new char[255];
    sprintf(cap, "Downloaded %d of %d bytes", downloadedBytes, totalBytes);
    l->setCaption(cap);
    delete[] cap;
    //lprintfln("Downloaded %d of %d bytes", downloadedBytes, totalBytes);
}

void DownloadScreen::softKeySelected(int buttonID)
{
    switch(buttonID)
    {
    case SoftKeyBar::LSK:
        break;
    case SoftKeyBar::FIRE:
        break;
    case SoftKeyBar::RSK:
        previous->show();
        break;
    }
}

void DownloadScreen::keyPressEvent(int keyCode)
{
    switch(keyCode)
    {
    case MAK_2:
    case MAK_4:
    case MAK_UP:
        listBox->selectPreviousItem();
        break;
    case MAK_6:
    case MAK_8:
    case MAK_DOWN:
        listBox->selectNextItem();
        break;
    case MAK_0:
        if(dl->isDownloading())
        {
            dl->cancelDownloading();
        }
    }
}

This download screen can be used from many different screen.  For instance, if you've got a screen which shows a calendar where the user can drill into different days to see what their schedule is for that day.  This screen can download a package of data from the Internet of events created for the user.  When you want to download an update, my EventScreen calls DownloadScreen and passes it the URL it wants to download.  This DownloadScreen handles all of the mechanics.

The DownloadScreen has inside it a Downloader object, and it implements DownloadListener.   It creates a Downloader when it is constructed, and deletes it when it is destroyed.  When the DownloadScreen’s download method is called, it passes the URL to the Downloader and starts downloading.

As the Downloader raises events to the DownloadScreen through the DownloadListener interface, the screen can keep the user informed about their download.  When it has finished, it informs the calling screen that it has completed, so that screen can carry on.  

To use it, call it from EventsScreen.cpp.  This is a partial listing.

#include "EventsScreen.h"
#include <mastring.h>
EventsScreen::EventsScreen(Screen* previous, MainScreen* mainScreen) 
: previous(previous), mainScreen(mainScreen)
{
	image =(Image*) createMainLayout(BLANK, BACK_BUTTON, this);
	layout = (Layout*) image->getChildren()[0];
	listBox = (ListBox*) layout->getChildren()[1];
	this->setMain(image);
	urlptr = NULL;
	dlscreen = new DownloadScreen(this, mainScreen);
	dlscreen->setDownloadListener(this);
}
EventsScreen::~EventsScreen()
{
	if(urlptr != NULL)
	delete [] urlptr;
	delete dlscreen;
}
void EventsScreen::downloadComplete()
{
	listBox->getChildren().clear();
	createEventScreen();
}
void EventsScreen::keyPressEvent(int keyCode)
{
	switch(keyCode)
	{
	case MAK_2:
	case MAK_4:
	case MAK_UP:
		listBox->selectPreviousItem();
		break;
	case MAK_6:
	case MAK_8:
	case MAK_DOWN:
		listBox->selectNextItem();
		break;
	case MAK_0:
		dlscreen->download(formatUrl(), EVENTSTORAGE);
		dlscreen->show();
		break;
	}
}

When the user presses 0 on their keypad, it tells the DownloadScreen the URL to download from, and the name of the store to write the data to.  More on that later.

When the data has been completely downloaded, the EventsScreen::downloadComplete() method is called, and the screen can repopulate itself from the downloaded data.

Receiving Events

In the above example, the DownloadScreen class implements DownloadListener.  In particular, it implemented these functions:

void notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes);
void finishedDownloading(Downloader *dl, MAHandle data);
void error(Downloader* dl, int code);
void downloadCancelled(Downloader* dl);

By doing this, the download screen is informed about what is happening to the downloader.  The implementation code for these is here:

void DownloadScreen::downloadCancelled(Downloader *dl)
{
	//lprintfln("Cancelled");
	listBox->add(createLabel("Download has been cancelled"));
}
void DownloadScreen::error(Downloader *dl, int code)
{
	lprintfln("Error: %d", code);
	listBox->add(createLabel("Sorry, an error has occured"));
}
void DownloadScreen::finishedDownloading(Downloader *dl, MAHandle data)
{
	//Save the store
	MAHandle h = maOpenStore(store, MAS_CREATE_IF_NECESSARY);
	maWriteStore(h, data);
	maCloseStore(h, 0);
	lprintfln("Finished download");
	if(listener != NULL)
	{
		//lprintfln("Calling listener");
		listener->downloadComplete();
	}
	//lprintfln("Showing calling screen");
	previous->show();
}
void DownloadScreen::notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes)
{
	Label* l = (Label*)listBox->getChildren()[0];
	char* cap = new char[255];
	sprintf(cap, "Downloaded %d of %d bytes", downloadedBytes, totalBytes);
	l->setCaption(cap);
	delete[] cap;
	//lprintfln("Downloaded %d of %d bytes", downloadedBytes, totalBytes);
}

When the Downloader tells the calling screen of an error, or that the download has been cancelled, or that it has downloaded some of the data, then you can tell the user by updating the screen.  When the download has finished, you can call the IDownloadScreenListener, and show the previous screen.

Downloading Images from the Internet

Often when using images in MoSync, then the images are available to you as a developer, and you can package them with your application, as described in the tutorial 'Adding Resources to a Project'.  However, there will be many scenarios where you want to show a picture you've not been able to package.  This may be because there are  too many pictures, or you are responding to user input.  An image search of Google or Bing would be an example of this, there you cannot possibly know what the user is going to search for, nor could you package the images in advance.

The ImageDownloader

There is a class which has been specifically created to remove some of the complexities of downloading images from the Web.  The ImageDownloader class is defined in MAUtil/Downloader.h, and will create an image resource for you.  To use the ImageDownloader, you will need to create a new class which inherits from DownloadListener.  By doing this, your program will be informed when the image has downloaded and is ready to display.

/**
 * @file DownloadImage.cpp
 *
 * This program downloads and displays an Image file from the Internet.
 *
 * Todo: You need to goto Project -> Properties -> Build Settings and
 * add MAUtil.lib, MAUI.lib libraries in Additional Libraries.
 *
 * @author Sam Pickard, Naveed Asif
 */

#include <MAUtil/Moblet.h>
#include <MAUI/Screen.h>
#include <MAUI/Image.h>
#include <MAUtil/Downloader.h>
#include <conprint.h>

using namespace MAUtil;
using namespace MAUI;

/*
 * Class that downloads an image file from
 * the Internet.
 */
class MyScreen :
	public Screen,
	DownloadListener
{
public:

    MyScreen()
    {
    	// Message: Starting application.
        lprintfln("Starting application");

        // A new instance of ImageDownloader is created.
        mImageDownloader = new ImageDownloader();
        mImageDownloader->addDownloadListener(this);
        mImageResource = maCreatePlaceholder();

        // Message: Starting download.
        lprintfln("Starting download");

        // The image shall be downloaded from the following url.
        mImageDownloader->beginDownloading(
        	"http://shop.abc.net.au/multimediaitems/images/product_images/4/482912.png",
        	mImageResource);
    }

    /*
     * The destructor.
     */
    virtual ~MyScreen()
    {
        delete mImageDownloader;
        maDestroyObject(mImageResource);
    }

    /*
     * The function is fired in case of download cancellation.
     */
    void downloadCancelled(Downloader* downloader)
    {
        lprintfln("Cancelled");
    }

    /*
     * Method displays error code in case of error in downloading.
     */
    void error(Downloader* downloader, int code)
    {
        lprintfln("Error: %d", code);
    }

    /*
     * On successful download completion, the image file is shown on screen.
     */
    void finishedDownloading(Downloader* downloader, MAHandle data)
    {
        lprintfln("Completed download");

        // Create a new image on screen with this picture.
        Image* myImage = new Image(0, 0, 240, 320, NULL, false, false, mImageResource);
        this->setMain(myImage);
        this->show();
    }

    /*
     * Notifies download progress.
     */
    void notifyProgress(
    	Downloader* downloader,
    	int downloadedBytes,
    	int totalBytes)
    {
        lprintfln("Downloaded %d of %d bytes", downloadedBytes, totalBytes);
    }

private:
    ImageDownloader* mImageDownloader;
    MAHandle mImageResource;
};

/*
 * Moblet for the image download.
 */
class MAUIMoblet : public Moblet
{
public:
    MAUIMoblet()
    {
        // Create the screen.
    	mScreen = new MyScreen();
    	mScreen->show();
    }

    /*
     * Key press events are handled here.
     */
    void keyPressEvent(int keyCode)
    {
        if(keyCode == MAK_0 || keyCode == MAK_BACK)
        {
            maExit(0);
        }
    }

    void keyReleaseEvent(int keyCode)
    {
        // todo: handle key releases
    }

    /*
     * The destructor.
     */
    virtual ~MAUIMoblet()
    {
        delete mScreen;
    }

private:
    MyScreen* mScreen;
};

/**
 * Main function that starts the program.
 */
extern "C" int MAMain()
{
    Moblet::run(new MAUIMoblet());
    return 0;
}

In this example, the DownloadListener is also a MAUI Screen class.  When the finishedDownloading method is called, the MAHandle 'data' contains a handle to the downloaded image.  You can then display this as any other MAUI image, or in a game or a C application, you can use the function maDrawImage().

If you tried this with a Downloader object instead of an ImageDownloader, you’ll get an ‘Invalid Resource Type’ error message when you tried to create the image.  Instead, you’d need to call:

maCreateImageFromData(h, source, position, imageLength);

to covert the resource type for you.

Downloading Audio from the Internet

In addition to the standard methods of downloading data using the Connection, HttpConnection, or Downloader objects, the MoSync SDK provides a special downloader, the AudioDownloader class, for retrieving audio files from the Internet. The AudioDownloader is a useful wrapper for the Downloader class and is specifically for audio files.

Usually, when you create an audio resource on a server you set its MIME type. The MIME type determines how the device should play the sound file. You can find a list of common MIME types on Wikipedia. You can also specify a MIME type for the audio file when you download it.

It is very important to understand how the MIME type set on the server interacts with the MIME type set on the download to determine the final MIME type of the file. This is because the server MIME type has precedence.

  • If you do not set a MIME type for a download, and a MIME type has been set on the server file, the server MIME type will be used.
  • If you do specify a MIME type, and the Web server provides another, then Web server's MIME type will be used and the value you specify will be ignored.
  • If neither you nor the web server have set a MIME type, it will cause a panic.

The conclusion from this is that you should always set the MIME type for the download.

This code below will download and play an MP3 file from the web and set its MIME type to 'audio/mpeg', which is suitable for MP3 files.

/**
 * @file DownloadAudio.cpp
 *
 * This program downloads and plays an audio file from the Internet.
 *
 * Todo: You need to goto Project -> Properties -> Build Settings and
 * add MAUtil.lib, MAUI.lib libraries in Additional Libraries.
 *
 * @author Sam Pickard, Naveed Asif
 */

#include <MAUtil/Moblet.h>
#include <MAUI/Screen.h>
#include <MAUtil/Downloader.h>
#include <conprint.h>

using namespace MAUtil;
using namespace MAUI;

/*
 * Class that downloads and plays audio file from
 * the Internet.
 */
class MyScreen :
    public Screen,
    public DownloadListener
{
public:

    MyScreen()
    {
    	// Message: Starting application.
        printf("Starting application\n");

        // A new instance of AudioDownloader is created.
        mAudioDownloader = new AudioDownloader();
        mAudioDownloader->addDownloadListener(this);
        mAudioResource = maCreatePlaceholder();

        // Message: Starting download.
        printf("Starting download\n");

        // The audio resource starts downloading here.
        mAudioDownloader->beginDownloading(
            "http://downloads.bbc.co.uk/doctorwho/sounds/exterminate.mp3",
            mAudioResource,
            "audio/mpeg",
            true);
    }

    /*
     * The destructor.
     */
    virtual ~MyScreen()
    {
        delete mAudioDownloader;
        maDestroyObject(mAudioResource);
    }

    /*
     * The function is fired in case of download cancellation.
     */
    void downloadCancelled(Downloader* downloader)
    {
        lprintfln("Cancelled");
    }

    /*
     * Method displays error code in case of error in downloading.
     */
    void error(Downloader* downloader, int code)
    {
        lprintfln("Error: %d", code);
    }

    /*
     * On successful download completion, the audio file is played.
     */
    void finishedDownloading(Downloader* downloader, MAHandle data)
    {
        lprintfln("Completed download");

        // Plays the downloaded file.
        maSoundPlay(mAudioResource, 0, maGetDataSize(mAudioResource));
    }

    /*
     * Notifies download progress.
     */
    void notifyProgress(
        Downloader* downloader,
        int downloadedBytes,
        int totalBytes)
    {
        lprintfln("Downloaded %d of %d bytes", downloadedBytes, totalBytes);
    }

private:
    AudioDownloader* mAudioDownloader;
    MAHandle mAudioResource;
};

/*
 * Moblet for the download audio.
 */
class MAUIMoblet : public Moblet
{
public:
    MAUIMoblet()
    {
        // Create the screen.
        mScreen = new MyScreen();
        mScreen->show();
    }

    /*
     * Key press events are handled here.
     */
    void keyPressEvent(int keyCode)
    {
        if(keyCode == MAK_0 || keyCode == MAK_BACK)
        {
            maExit(0);
        }
    }

    /*
     * The destructor.
     */
    virtual ~MAUIMoblet()
    {
        delete mScreen;
    }

private:
    MyScreen* mScreen;
};

/**
 * Main function that starts the program.
 */
extern "C" int MAMain()
{
    Moblet::run(new MAUIMoblet());
    return 0;
}

Custom Downloaders

There are obvious occasions when you want do download data from the Internet for your games or application.  There are even obvious occasions when you want to download images and sounds using the ImageDownloader and AudioDownloader.  What might appear less obvious is the use of a Custom Downloader.

Custom Downloaders

Custom Downloaders are more useful than you might think.  These are classes where you have implemented ConnectionListener, but got it to do something with the data before consuming it.

A great, if lengthy, example is the mapping component below.  This is a custom widget (not part of the MoSync MAP SDK) to make scrollable, zoomable maps using the CloudMade map tiles.  Initially, this used an ImageDownloader to get each and every tile separately from CloudMade.  This was very slow, not because the data connection was slow, but because the HTTP latency was so great.  It needed to negotiate a connection to the server 36 times to build a screen.

Instead, this connects to a small web application which will take requests for an indefinite number of tiles at once.  It will go and get those tiles from CloudMade, cache them in memory for quick retrieval later, and return them to me in a single download.  The application can then rip the tiles out of the stream and pass them to the map for display.

This has been implemented as a CustomDownloader.  It allows for additional logic to be wrapped in the download mechanism, so other classes do not need knowledge of the connection to get the correct data.

MapTileDownloader.h

#ifndef _MAPTILEDOWNLOADER_H_
#define _MAPTILEDOWNLOADER_H_
#include <MAUtil/Connection.h>
#include "MapTileDownloadListener.h"
using namespace MAUtil;
using namespace DatiloMapping;
namespace DatiloMapping
{
	class MapTileDownloader : public ConnectionListener
	{
	public:
		MapTileDownloader();
		virtual ~MapTileDownloader();
		void startDownloading(const char* url);
		void cancelDownloading();
		void removeListener(MapTileDownloadListener* listener);
		void addDownloadListener(MapTileDownloadListener* listener);
		void connectFinished(Connection* conn, int result);
		void connRecvFinished(Connection* conn, int result);
		void connReadFinished(Connection* conn, int result);
		void connWriteFinished(Connection* conn, int result);
		bool isDownloading();
	private:
		Vector<MapTileDownloadListener*> downloadListeners;
		Connection* mapConn;
		bool activeDownload;
		void throwTile();
		void processDownload();
		void iterateTiles(int next);
		void readKeyLength();
		void readKey();
		void readImageLength();
		void readImage();
		int dlStage;
		ushort tileCount;
		int keyLength;
		int downloadedBytes;
		int nextTile;
		bool fetchComplete;
		bool connectionOpen;
		int tileSize;
		byte kl; //key length
		char* keyBuffer;
		MAHandle h;
	};
};
#endif //_MAPTILEDOWNLOADER_H_

 

MapTileDownloader.cpp

#include  "MapTileDownloader.h"
#include <conprint.h>
#include <madmath.h>
MapTileDownloader::MapTileDownloader()
{
	h = maCreatePlaceholder();
	keyBuffer = new char[10];
	mapConn = new Connection(this);
	activeDownload = false;
}
MapTileDownloader::~MapTileDownloader()
{
	maDestroyObject(h);
	delete mapConn;
	delete [] keyBuffer;
}
//Start a download
void MapTileDownloader::startDownloading(const char* url)
{
	if(mapConn->isOpen())
	mapConn->close();
	activeDownload = true;
	int res = mapConn->connect(url);
	if(res < 0)
	return;
}
void MapTileDownloader::processDownload()
{
	dlStage = 0;
	nextTile = 0;
	fetchComplete = false;
	mapConn->read(&tileCount, 2); //Read exactly two bytes
}
void MapTileDownloader::iterateTiles(int next)
{
	nextTile = next;
	//Read through each tile
	if(nextTile < tileCount)
	{
		//Reset the buffers
		memset(keyBuffer, 0, 10);
		readKeyLength();
	}
	else
	{
		mapConn->close();
		activeDownload = false;
		Vector_each(class MapTileDownloadListener*, itr, downloadListeners)
		{
			(*itr)->complete();
		}
	}
}
void MapTileDownloader::readKeyLength()
{
	dlStage = 1;
	mapConn->read(&kl, 1); //Read exactly one byte
}
void MapTileDownloader::readKey()
{
	dlStage = 2;
	mapConn->read(keyBuffer, kl);
}
void MapTileDownloader::readImageLength()
{
	dlStage = 3;
	mapConn->read(&tileSize, 4);
}
void MapTileDownloader::readImage()
{
	dlStage = 4;
	//Remove any existing obejct
	maDestroyObject(h);
	maCreateData(h, tileSize);
	mapConn->readToData(h, 0, tileSize);
}
//Returns a tile to the listener
void MapTileDownloader::throwTile()
{
	Vector_each(class MapTileDownloadListener*, itr, downloadListeners)
	{
		(*itr)->finishedDownloading(keyBuffer, h, tileSize);
	}
	maDestroyObject(h);
	//Get the next tile
	iterateTiles((nextTile + 1) % 36);
}
//Handle add/remove of listeners
void MapTileDownloader::removeListener(MapTileDownloadListener* listener)
{
	Vector_each(class MapTileDownloadListener*, itr, downloadListeners)
	{
		if((*itr) == listener)
		{
			downloadListeners.remove(itr);
			break;
		}
	}
}
void MapTileDownloader::addDownloadListener(MapTileDownloadListener* listener)
{
	downloadListeners.add(listener);
}
//ConnectionListener events
void MapTileDownloader::connectFinished(Connection* conn, int result)
{
	processDownload();
}
void MapTileDownloader::connRecvFinished(Connection* conn, int result)
{
}
void MapTileDownloader::connReadFinished(Connection* conn, int result)
{
	fetchComplete = true;
	switch(dlStage)
	{
	case 0:
		iterateTiles(0);
		break;
	case 1:
		readKey();
		break;
	case 2:
		readImageLength();
		break;
	case 3:
		readImage();
		break;
	case 4:
		throwTile();
		break;
	}
}
void MapTileDownloader::connWriteFinished(Connection* conn, int result)
{
}
void MapTileDownloader::cancelDownloading()
{
	if(mapConn->isOpen())
	{
		mapConn->close();
	}
	activeDownload = false;
}
bool MapTileDownloader::isDownloading()
{
	return activeDownload;
}

 

This gets a stream of tiles.  It reads the first two bytes of the stream to get the number of tiles in the stream, and then for each tile it reads the key so the map will know where this image goes.  It then reads the length of the image, and then the image data itself.  As it gets each tile, it throws it to a listener which can pass it to the map, so it is drawing tiles as they come down.  By implementing a custom listener like this, you will get a speed improvement of ten-fold!

Connection

This example application checks the ability of the application and device to connect to the internet.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When the application starts, use the keypad keys or touch the screen to connect to the Internet via HTTP or HTTPS. The application also has a "repeat " function that you can toggle on and off: if you toggle that function on and then connect, the connect and download process will loop continuously.

Key Presses

  • Fire button or touch the screen — connects using HTTP to http://www.mosync.com
  • 1 or left-softkey — connects using HTTPS to https://encrypted.google.com 
  • 5 key — toggles continuous connect/download on/off.
  • 0 or right-softkey — closes the application.

 

 

MoTooth

This example application demonstrates how to scan for Bluetooth devices and services, store the results in a database, display a list of services, and connects to one of them.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

The application scans for nearby Bluetooth devices and services, stores the results in a database, displays a list of services, and connects to one of them. The user can start to scan for Bluetooth devices by pressing '5'. When the scan is completed a list of the discovered devices and their corresponding MAC addresses is presented. The user can at any time abort the scan by pressing 0. When the scan is complete, each of the discovered devices will be searched for Bluetooth services. Finally, a list of services will be presented for each device.

When the device and service scan is completed the user can test the different services by pressing the fire key. A menu containing the discovered devices and their corresponding services can be shown by pressing the Fire key. The user can test a service on a particular device by navigating with the Up and Down keys and pressing the Fire key, the emulator then tries to connect to the selected service. Note that all devices and services might not fit on the screen, by pressing Up or Down at the ends a new batch of devices will be shown.

Key Presses

  • Press 5 to scan for Bluetooth devices and services.
  • Press 0 or left-softkey to exit the application.
  • Press Up or Down to navigate in menus.
  • The Fire button shows the services found.

No touch support is provided in this application.

 

 

 

 

OtaLoad

OtaLoad demonstrates network connections, the downloading of an application "over-the-air", and the ability of one MoSync application to run another.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

OtaLoad is a small application that downloads another MoSync application "over the air" (using HTTP), caches it in permanent storage and executes it. If there is already a copy of the application in the cache, the user is given the option to run it immediately or ask the server for a new version.

Important! Before running this application on the MoSync emulator or on a target device, do the following:

  1. Build the target application that you intend to download using the same version of MoSync that you will use to build OtaLoad.
  2. Put the target file on a webserver.
  3. Finally, in the OtaLoad.cpp file, specify the location from which the target file will be downloaded.

The target application must be a .comb file and must contain you compiled program and any resources (images, etc.) that it needs. The target application cannot contain any unloaded resources such as ubin or umedia. When you compile your target application, MoSync will put it in the /Output or /FinalOutput folder in your workspace. Find the .comb file and put on a webserver. If you haven't got a webserver you can use free services like http://www.dropbox.com to host your file.

Key Presses

  • 6 or soft-left — downloads the target application/checks for updates
  • 0 or soft-right — closes the OtaLoad application

 

Databases, File storage

Database Access and Management

Note: this API is experimental and currently available only in our nightly builds.

The MoSync Database API provides a set of syscall functions that enable you to create and access databases from your C/C++ code using the SQL query language. The Database API makes use of the SQLite database manager on the supported platforms (see Feature/Platform Support).

Database API functions

The MoSync Database API consists of a set of low-level syscall functions:

  • maDBOpen — Opens a database file. The database is created if it does not exist.
  • maDBClose — Closes a database. 
  • maDBExecSQL — Executes an SQL statement. If the statement returns a query result, a cursor handle is returned.
  • maDBExecSQLParams — Executes a parameterized SQL statement. Currently supported on iOS and MoRE.
  • maDBCursorDestroy — Deallocates a cursor returned by maDBExecSQL or maDBExecSQLParams.
  • maDBCursorNext Move the cursor to the next row in the result set. You must call this function before retrieving column data (the initial position of the cursor is before the first row).
  • maDBCursorGetColumnData — Get a field value in the current row as a data handle. 
  • maDBCursorGetColumnText — Get a field value in the current row as a text buffer (note that the string is NOT zero terminated). 
  • maDBCursorGetColumnInt — Get a field value in the current row as an integer. 
  • maDBCursorGetColumnDouble — Get a field value in the current row as a double.

General usage

These are the main steps you need to follow when you use the Database API:

  1. Open the database with maDBOpen. The database is in a file and you need to specify the full path to the file. The database is created if it does not exist.

  2. If this is the first time the application is launched, create the tables needed and populate them with initial data using maDBExecSQL. You can use a store (a file in the local file system) to save a value that indicates if the application has been initialized.

  3. Use maDBExecSQL to insert, update and query data. The SQL dialect used by the Database API is SQLite. Explore the SQLite documentation to learn mroe about this SQL dialect.

  4. For queries, use the cursor returned by maDBExecSQL to iterate over the query result with maDBCursorNext (see the code example below).

  5. Deallocate the cursor with maDBCursorDestroy when finished using it.

  6. Close the database with maDBClose.

If you want to delete the database, delete the database file using the MoSync File API.

Iterating over a query result

// Compute average age of persons in the database.

// Variable to hold the age of a person. 
int age; 

// Variable to hold the age sum. 
int ageSum = 0; 

// Number of persons. 
int numberOfPersons = 0; 

// Query all rows from the table named "person". 
MAHandle cursor = maDBExecSQL(db, "SELECT age FROM person"); 

// Iterate of the result set. 
while (cursor > 0 && MA_DB_OK == maDBCursorNext(cursor))
{
    // Get the age of the current row (first field has index zero) 
    // and update variables. 
    maDBCursorGetColumnInt(cursor, 0, &age); 
    ageSum += age; ++ numberOfPersons; 
}

// Compute average age. 
if (numberOfPersons > 0)
{
    float ageAverage = (float) ageSum / (float) numberOfPersons; 
    printf("Average age is: %.1f\n", ageAverage); 
}

Example application

Our example application DatabaseTest which is included in the MoSync SDK demonstrates more features of the Database API.


DatabaseTest

Note: this example application is currently available only in our nightly builds.

This simple example application demonstrates how to use the MoSync Database API. This example works on all platforms supported by the API (see Feature/Platfom Support).

Initial screen (MoSync Emulator)After test (MoSync Emulator)

 
This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

  • The program has one screen that displays textual output.
  • Touch the screen to start running the database tests (or click on the screen in the MoSync Emulator).
  • A database file will be created on the device, in the local file system of the application.
  • If the test is successful, the text "Database test passed successfully" is displayed at the end of the output.
  • The program exits with a panic message if a test fails.
  • Quit the program by pressing back (on Android) or key zero (in the MoSync Emulator).

In the code

The project consists of one file, main.cpp, which contains commented source code to help understand the Database API. The example database used has a table called "pet" which contains data about pets. The database is created by the program. The tests made include making queries and checking that the results match the expected values.

 

Using Data Stores

There are several ways to save or read data from a mobile device. Reading and writing to the Internet can be done with Connection classes, but can be labourious without specific helper classes. The simplest way of saving your data is with a MoSync store. This tutorial looks at creating stores, writing to them and reading them.

Creating Stores

The simplest way of saving your data is with a MoSync store. Stores are saved on the device, and are supported on all MoSync-supported platforms. A store is a single file that can easily be  read from and written to. On Symbian S60 platforms, stores can be shared between different applications; on other platforms stores are private and separate.

To create a store, use the function maOpenStore(). This function takes two parameters, a const char* with the name of the store, and an int representing the options for the store. At the moment, there is only one option, you can choose to use it or not. This is defined as MAS_CREATE_IF_NECESSARY and it will create a new store if it doesn't exist. 

The function maOpenStore() returns an MAHandle, a reference to the store that you can use with other functions.

MAHandle myStore = maOpenStore("MyStore", MAS_CREATE_IF_NECESSARY);

If the store doesn't exist on the phone, it will be created and an MAHandle will be returned. If it does exist, it will just return the handle. 

If the file cannot be created (for example, if there is no room on the device or there is a fault on the device) the value returned will match one of the STERR error values.

When you run your application in the MoRE emulator, you'll find that the stores are created in a folder called /stores in your output folder. Stores cannot currently be deployed with your application, and they have to be created at runtime.

Checking that a Store Exists

If you want to find out whether or not a store exists, call maOpenStore() with the flags set to 0. The value STERR_NONEXISTENT is returned if the store doesn't already exist.

MAHandle testStore = maOpenStore("MyStore", 0);

if(myStore == STERR_NONEXISTENT)
{
	// Store doesn't exist.
} 
else
{
	 // Store does exist, and testStore is a valid handle to it.
}

Writing to Stores

Stores don't have a sophisticated system for accessing and writing. They simply provide a method for saving application data to the device. Each time a store is written, it is overwritten. You cannot append to existing stores or edit the store directly. A store is written to from a data resource. This isn't resizable, but you can edit the contents, and you can write variables into it and read their values out again. 

You can create new data resources at runtime, but you need to define an MAHandle for it first. You also need to know how much data you need to store. To create a new data resource, you use the command maCreateData(). This returns either RES_OK if there is enough memory to create it or RES_OUT_OF_MEMORY if not. You need to pass the MAHandle to it as a parameter, along with the size. You can create a new, empty MAHandle with the function maCreatePlaceholder().

String password = "p45sw0rd";
MAHandle myData = maCreatePlaceholder();
if(maCreateData(myData, password.length()) == RES_OK)
{
}

You can now write data using the method maWriteData().

String password = "p45sw0rd";
MAHandle myData = maCreatePlaceholder();
if(maCreateData(myData, password.length()) == RES_OK)
{
	maWriteData(myData, password.c_str(), 0, password.length();
}

The maWriteData() function needs the MAHandle of the data resource, a pointer to the object you want to write, the offset from the beginning of the data resource and the length of the data you wish to write. This means that you can write the variable to where ever you want in the data resource. If you are writing several pieces of data, you'll either need to keep track of their relative positions in the data, or user DataHandler as described below to do it for you.

To write the data resource to the store, you use the method maWriteStore(). This takes the MAHandle of the store, and the MAHandle of the data. 

int result = maWriteStore(myStore, myData);

Anything which was in the store previously is overwritten. If maWriteStore() returns > 0, then it has been written correctly. Other values should map to STERR. The one to watch out for here is STERR_FULL which means that there is no more storage left.

int result = maWriteStore(myStore, myData);

switch(result)
{
	case > 0:
		// Everything is fine, and the data is saved.
		break;	
	case STERR_FULL:

		// Failed, not enough space to save the data.
		break;
	case STERR_NONEXISTENT:

		// Failed, the store doesn't exist!
		break;
	case STERR_GENERIC:

		// Unknown error, possibly a device fault.
		break;
}

A Note on Store Security

As a word of warning, although our example shows a password being written to a store you should be aware that this data is not completely private. Different systems provide different security measures. On a J2ME device, such data is fairly safe. A hacker would have to know a lot about the data to be able to access it. On S60 devices however, the user can freely browse the stores you've written using their file manager. Plain text stores can be opened up and read, and images you've written to the store can be displayed. If there is any sensitivity to the data you are writing, then it is strongly suggested that you encrypt it before you write it to the store.

Closing a Store

When you've finished writing, you also need to close the store. This is done with the function maCloseStore().

maCloseStore(myStore, 0);

The second parameter (0 in this example), indicates whether or not the store should be deleted when you close it. If it is 0, you want to keep it; if you provide any other int value the store will be deleted. This is how stores are deleted, there is no other explicit function to delete stores. 

void deleteStore(const char* storeName)
{
	MAHandle store = maOpenStore(storeName, 0);
	if(store != STERR_NONEXISTENT)
	{
		// Delete the store.
		maCloseStore(store, 1); 
	}
}

Reading from Stores

Just as you write to stores from data resources, you read from them in the same way. The function maReadStore() copies the data in a store to a data resource, indicated by an MAHandle.

MAHandle myData = maCreatePlaceholder();
MAHandle myStore = maOpenStore("MyStore", 0);
if(myStore != MAS_NONEXISTENT)
{
	// The store exists, so we can read from it.
	int result = maReadStore(myStore, myData);
	if(result == RES_OUT_OF_MEMORY)
	{
		// This store is too large to read into memory - error
	}
}

Once the store has been read into the data resource, the values can be read out of it. Of course you need to know how much data to read: you can get the size of the data resource with the function maGetDataSize(), and then read the data using the method maReadData().

char password[maGetDataSize(myData)]; 
maReadData(myData, &password, 0, maGetDataSize(myData));

To use maReadData, you need to know how many bytes to read from the data resource. In the example above, the password is the only data being stored, so we can easily read out all of the data. 

If the store contains both a username and a password, then it has to be a bit more sophisticated. You are probably not using fixed lengths for strings, so you need to store some extra data about how long each string is. If you put a byte containing the length of the string (or an int if it is a lot) before the actual string data, then you can read it back more easily. These are called Pascal strings or P-Strings, and you can also use them in MoSync resource files. To read out a P-string, you need to read the first byte.

// Read a p-string.
byte len = 0;

// You now need to track your position in the data.
int position = 0; 

// Read the length.
maReadData(myData, &len, position, 1);// Read 1 byte from position

// Move onto the next byte.
position++;

// Read the string.
char password[len + 1];  //String may not be null-terminated.
maReadData(myData, &password, position, len);

// Add a null-terminator.
password[len+1] = '/0';

// Move the position on, so we're ready for the next read.
position += len;

So, if you are reading more than one item out of the data, then you need to keep track of your place in the data.

Once you've finished with the data, you can release it from memory.

maDestroyObject(myData);

This deletes the data resource from memory, but does not destroy the store.

Using DataHandler

If you want to write several different values to the data resource, and then to the store, then there is a utility called DataHandler. This can help you write several values to the resource by tracking the offsets for you. You need to include the header file MAUtil/DataHandler.h

#include "MAUtil/DataHandler.h"
using namespace MAUtil;

...

String username = "m0sync";
String password = "p45sw0rd";

// Save the values to a data resource.
MAHandle myData = maCreatePlaceholder();

// The size of the data we need, including four bytes for 
// the length of each string.
int size = username.length() + 4 + password.length() + 4; 

if(maCreateData(myData, size) == RES_OK)
{
	DataHandler* handler = new DataHandler(myData);

	int usernameLength =  username.length();
	handler->write(&usernameLength, 4);
	handler->write(username.c_str(), usernameLength);

	int passwordLength = password.length();  
	handler->write(&passwordLength, 4);
	handler->write(password.c_str(), passwordLength);
}

int result = maWriteStore(myStore, myData);

if(result > 0)
{
	// Written successfully.
	maCloseStore(myStore, 0);
}
else
{
	// Failed, delete the store.
	maCloseStore(myStore, 1);
}

As you can see from the above example, the DataHandler class takes away some of the complexity of reading and writing to stores, treating them more like a Java StreamReader or StreamWriter class.  Unlike the example above where we were reading data out of the resource and having to keep track of the position, the DataHandler will manage it for us, and allow serial access.

MAFS Library

MAFS is a reimplementation of the Standard C File I/O routines. It operates on either a binary image attached as a resource or using local stores for permanent storage. The binary image is actually a virtual filesystem generated using our tool Bundle. This comes in handy when porting applications that use a lot of data stored as files in the file system: just use the Bundle tool on the data folder to generate an image, attach it as a resource in the MoSync application, and mount it using the API functions provided.

A policy has been implemented to handle transparently the permanent storage of files. The fopen function first looks to see if the file exists in permanent storage. If that is the case, everything is written or read from that permanent storage. If no file is found in permanent storage, it continues to search for the file in the binary image. Reading from such a file is done by buffering data from the binary resource. Whenever such a file is written to, a copy of the data is created and saved as permanent storage.

Any reference documentation for the f* functions of the Standard C File API will work as reference, but we've also included documentation for them in our own reference documentation. There are two special functions for mounting the filesystem image and unmounting it. There can only be one filesystem image mounted at once.

Use the function setCurrentFileSystem to mount it and freeCurrentFileSystem to unmount it:

  • The function setCurrentFileSystem takes two parameters, the first being a handle to the binary resource containing the Bundle-generated filesystem image. The second being an integer, where a value of 1 states that the filesystem should be treated as a case sensitive one (like on Unix systems) and a value of 0 states that it should not (like on Windows).

  • The function freeCurrentFileSystem just unmounts the current file system and releases all the memory allocated for it.

To show how it works, we begin by making a .bun file using the Bundle-tool.

First create a text-file:

Then generate the .bun-file:

Finally we include the .bun file as an unloaded binary resource:

.res R_TEST_FILESYSTEM
.ubin
.include "test.bun"

We can now write a small program that mounts the bun-file, opens and reads the text-file. Let's do it using the Moblet-architecture:

#include <MAUtil/Moblet.h>
#include <MAFS/File.h>
#include "MAHeaders.h"

using namespace MAUtil;

/*
 * MyMoblet class that will show how to use File System.
 */
class MyMoblet : public Moblet
{
public:
	MyMoblet()
	{
        char text[1024];

        setCurrentFileSystem(R_TEST_FILESYSTEM, 0);

        FILE *file = fopen("text.txt", "r");
        if(!file) maPanic(1, "Could not open file!");
        if(!fgets(text, 1024, file)) maPanic(1, "Could not read string!");

        maSetColor(0x00ff00);
        maDrawText(2, 2, text);
        maUpdateScreen();
    }
};

/*
 * The main execution of the program starts here.
 */
extern "C" int MAMain()
{
    Moblet::run(new MyMoblet());
    return 0;
};



Debugging, Testing, Benchmarking

Application Profiling in the MoSync Emulator

The MoSync SDK 2.6 includes an application profiler which can output performance statistics whenever you run the MoRE emulator. That makes it easy to track down performance issues and optimize your application.

Running the profiler

To run a profiling session, right click on your project and select Profile As > Profile on MoSync Emulator.

Your application will launch in the usual way, and a new tab will open in the editor view. As soon as the application terminates, the profiling data will show on this new tab. (At this time MoSync only supports post-mortem profiling.)

Hotspots and Call Tree tabs


There are two sub-tabs at the bottom of the view: Hotspots and Call Tree.

The Hotspots sub-tab lets you see which functions are taking the most time:


The Call Tree sub-tab shows which functions called which and the time spent in each:


The following columns are shown:

  • Function – The name of the function being profiled.
  • [%] – The percentage of time used by the function compared to the total execution time.
  • Self time (Hotspots only) – The time used by the function, not counting time spent in other functions called by it.
  • Aggregate time (Call Tree only) – The time used by the function, counting time spent in functions called by it.
  • Calls - The total number of calls made while the application was running.

Click on any of the column headings to sort the results by that column. Click again to sort them in reverse order.

Filtering results

At the top right corner of the profiling view, there is a Quick Filter text box. Whatever you type there will be used to filter out functions. For example, to see all MoSync syscalls, type "ma". The filter is not case sensitive.

As well as the Quick Filter, there are more advanced filtering options in the launch configuration dialog box. To open the launch configuration dialog box, select Run > Run Configurations from the main menu, then look for Profiled MoSync Emulated App in the tree. You will see the application that you just launched there.

Highlight your application in the tree. Information about the application will be shown on the right. Select the Profiling tab. Here you will find the advanced filtering options:

  • Name filter – similar to the Quick Filter; whatever is typed here is used to filter the function names. Several filters can be provided by using a space character as a separator.
  • File filter – the contents of this text box is used to filter the functions to those implemented in certain files. For example, writing main.cpp in this text box will grey out all functions not implemented in that file. Several filters can be provided by using a space as separator.
  • Use Regular Expressions – when checked, will allow for arbitrary regular expressions in the filter text boxes. Note: if you need spaces or backslashes in your regular expression, use backslash (\) to escape the space.

Saving a Profiling Session to file

To be able to shared profiling data, there is functionality to save a session to file. To do this, select File > Save As from the main menu and choose a file location. To open a profiling session, select File > Open and choose the file to open.

 

Using the Debugger

The MoSync SDK includes a powerful debugger that makes it easy to step through your code and get an overview of the state of your program as each statement executes.

About the Debugger

Our debugger is a reimplementation of the GNU debugger. (For more information see: http://www.gnu.org/software/gdb/.) The debugger has been customized for MoSync and is fully integrated with MoSync's Eclipse-based IDE. The debugger allows you to:

  • Break program execution at any time
  • Execute a single line of code
  • Step into/out of functions
  • Get a stack trace at every location of a program
  • Hover over any macro/variable to see its content at time of execution
  • Enable and disable breakpoints
  • View variable content in tree-view
  • Instantly see variable updates
  • Add any C/C++ custom expression
  • Inspect global variables by adding them as watch expressions

Starting the Debugger

Before you start the debugger, check that you have a Debug build configuration available, as follows:

  1. Select your project in the Project Explorer.
  2. Go to Project > Properties > MoSync Project > Build Configurations.
  3. Tick the Activate Configurations box.
    You should now see the available build configurations. The build configuration called "Debug" is the build configuration that's used by default when you start the emulator in debugging mode. If you need to, you can edit this build configuration and/or create a new one: see Creating a Debug Build Configuration.
  4. Click OK to close the Properties window.
  5. Start the debugger by clicking the Debug button on the main toolbar.
    The emulator will open and you application will execute in debug mode.

Pausing Execution

To break the execution of your program while it is running, click the Pause button:
The application will enter a suspended state and you can inspect variables and see the stack-trace.

To stop the debugging session, click the Stop button:

Note: if you happen to pause execution while inside an external library (such as the ones bundled with MoSync), you will not be able to see any source code: external libraries are not provided in the MoSync package.

Setting Breakpoints

To set a breakpoint, do one of the following:

  • Right-click in the code view border where you want the breakpoint, then select Toggle Breakpoint from the pop-up menu.
  • Double-click in the code view border.



Now click the Debug button. When the program reaches the breakpoint, the program will be suspended and you will be able to debug the application from there - inspecting variables, stepping line-by-line, setting new breakpoints, and so on.

Stepping Through Code

When execution reaches a breakpoint or you pause execution manually, you can step through the code line-by-line. This helps you see what code paths the application executes along and what values are assigned to variables after an expression has been evaluated. There are three buttons to help you step through the code:

Step Into executes a line. If the line is a call to a function, the debugger steps into it.

Step Over executes a line. If the line is a call to a function, the debugger executes the function call and breaks after it has returned .

Step Return continues the execution until the execution has returned from the current function.

Inspecting Local Variables

When execution reaches a breakpoint or you pause execution manually, you can inspect the current content of variables. All the variables in the local scope are visible, including static variables, local variables, and arguments to functions. The content of structures and arrays can be expanded by pressing the + sign next to them.

 

Inspecting Global Variables with Watch Expressions

To inspect a global variable you must add it as a watch expression. There are two ways to do this:

  • Right-click in the Expressions tab, select Add Watch Expression, type the name of a global variable, and click OK:



Right-click the name of a global variable in the editor, select Add Watch Expression , and click OK.

Creating a Debug Build Configuration

To be able to debug your program you need to have a debug build configuration with debugger-compatible build settings (for instance, all optimizations turned off).

This also applies to libraries, which is why special unoptimized debugging versions of the libraries need to be linked against instead (our debugging libraries all end with a capital "D").

A default debug build configuration will be created whenever you create a new project, but if you don't have one or you want to create another one, do the following:

  1. Goto Project > Properties > MoSync Project > Build Configurations
  2. Add a new build configuration or Duplicate an existing one.
  3. Click Edit and give the new build configuration a suitable name.
  4. Go to Build Settings > Build Configuration > Configuration.
  5. In the Configuration list box, select the build configuration you just created.
  6. Select the Paths and Files tab and set Additional Libraries to the debug ones (MAUtilD.lib for example).
  7. Select the Compiler Flags tab and turn off all optimizations by adding -O0 (and make sure that no other optimizations are turned on, i.e. no other -O switches).
  8. Finish by clicking OK.

To use your new Debug build configuration:

  1. Press the small down-arrow to the right of the Debug button and choose Debug Configurations.
  2. Select the project you want to use the new debug build configuration with.
  3. In the Configuration group, tick the Automatically switch to this configuration before debugging box.
  4. Select your new debug build configuration from the drop-down list.
    Now, when you click the Debug button in your project, you new build configuration will be used to build the application ready for debugging.

Known Issues with the Debugger

  • We use the MoSync debugger, but the Eclipse help system documentation still contains information about its native debugger, even though that debugger is disabled.
  • Templates and namespaces aren't supported by the debugger's expression evaluator yet. If you inspect a variable using explicit namespace directives or template instances the resulting behaviour may not be what you expect.
  • The C/C++ expression parser isn't completely tested yet, but it should support the most common expressions.
  • Note that functions cannot be called from expressions (that includes overloaded operators).
  • Instruction stepping mode isn't fully supported.
  • The menu options Run to line, Resume at line and Move to line are not implemented yet.

The Testify Test Framework

MoSync's test framework, Testify, simplifies the writing of complex test cases. You can use it to test several methods in a class, or just to write a single test for one function. There is not much more you need to do than write your tests (unfortunately it can't do that part for you) and press Run.

Note: From release 2.4, Testify replaces our previous test framework, MATest.

Testify Concepts

Before you start using Testify, we would like to introduce you to two important concepts: hooks and functors.

Hooks

A hook is the mechanism you use to add your test or test listener to Testify at link time. It's just a class which you instantiate, preferably in the same file as your test. A typical use of a hook looks like this:

static Testify::TestHook hook( new MyTestCase( ) );

where Testify::TestHook is the actual hook, and MyTestCase is a test case that you've written.

Functors

The workhorse of Testify are the function or method wrappers, otherwise known as "functors". If you've ever used the Boost library, you might be familiar with the concept. A functor wraps a function or a method, possibly along with its parameters, and can conveniently be called without having to care about it's details.

A typical use of a functor in Testify looks like this:

Testify::bind( MyFunction ) 

or like this:

Testify::bind( MyFunction2, 1, 2.0f ) 

where MyFunction and MyFunction2 are functions in your code. The second case wraps two parameters which are to be passed to MyFunction2 while invoking it.

When it comes to methods, it's slightly different, you also have to pass the object which the method belongs to as well, from inside the class, the constructor for instance. It could look like this:

Testify::bind( &MyClass::myMethod, this )

and of course, in this case you can wrap parameters as well.

Once you've written your tests and their hooks and you are ready to run them, you only need to link to testify.lib and run your program as usual. Testify will take care of booting itself and running the tests, you do not need to care about the details. Any existing MAMain will be overridden (not overwritten) as long as you are linking to testify.lib.

Other Features

Testify::TestFunction is used to run test functions. These are normally never used explicitly.

Testify::TestCase is a class which is inherited when you need a bit more support, such as set up and tear down before or after each test or each case.

Testify::TestListener describes an interface that can be overridden to make your own custom listeners to the events that Testify broadcasts. Events occur when a test suite starts or ends, when a test starts or ends, or when a test fails.

To add your own custom test listener to Testify, you use the hook concept, but this time you use Testify::ListenerHook, i.e:

static Testify::ListenerHook( new MyTestListener( ) ); 

Creating a Test

Now let's look at how to write a simple test case. We'll start with the testing of a simple function. The steps involved are the following:

  • Create a test suite (e.g. mytest.cpp) and add your test case to it
  • Add a hook in the test suite
  • Link with testify.lib

Example (in your application):

#include <testify/testify.hpp>

static void SumTest ( void )
{
    for ( int i = -100; i < 100; i++ )
        TESTIFY_ASSERT( (i-i) == 0 ); // This tests the boolean result of the assertion, this could be replaced with TESTIFY_ASSERT_EQUAL( i-i, 0 ). If the assertion is false, the test fails.
}

static Testify::TestHook myHook( Testify::bind( SumTest ), "Summation test" ); // The Test Case is being hooked in.

IDE Support for Writing Tests

The MoSync IDE provides full support for writing tests and executing them in the MoRE emulator.

To create a new test suite:

  1. Right-click on the project you wish to add tests for.
  2. Select New > Other > MoSync > Create MoSync Test Suite.
    A wizard opens where you can define which directory to designate as your test directory and the C++ file to generate as a starting point for writing test cases. (The wizard will specify a default test directory for you.)
  3. Click Finish.

The wizard will add two new build configurations to your project, one called Test and one called Test_Debug. These new build configurations are equivalent to the normal Release and Debug build configurations but include the designated test directories.

Both of the new test build configurations also include the Testify library (testify.lib). You can view the included libraries in your project's build setting (Project > Properties > MoSync Project > Build Settings > Paths and Files > Additional Libraries).

Once a test suite has been created and tests have been added as described in Creating a Test above, it's time to run them.

To run a test suite:

  • Right-click the project, select Run As > Run Tests on MoSync Emulator.
    The IDE will launch the emulator, run the tests and show the test results in the Unit Test view.
    (Usually this view will be activated automatically and placed in a tab to the right of the Project Explorer.)

QuakeMDL

This application either renders a Quake 1 model or, in benchmark mode, measures the raw speed of the target device's CPU. This application is useful for comparing performance across devices.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

The behaviour of this application is controlled by the project's build configuration settings. Before building the project (or running it in the MoRE emulator), select one of the Render or Benchmark build configurations from the build configuration selector on the toolbar:

When built in Render mode, this application renders a wireframe animation and shows the current frames-per-second.

When built in Benchmark mode, this application calculates the average frames-per-second that the CPU can render and can be used as a benchmark score to test the performance of different devices.

Key Presses

  • Any key  — closes the application.

Unit Test

This application provides a battery of tests to help you check the capabilities and limitations of a mobile device.

The code for this application is available from our Code repository.

Behaviour

When you run this application on a mobile device it will begin running through a series of tests. The application periodically prompt for user input. Device capabilities checked by the tests include:

  • Network connection support (Note: browsing charges may be incurred)
  • Text display
  • Audio support
  • Simple and advanced graphics support
  • Key handling and touchscreen support
  • Screen dimensioning
  • Local and UTC time support
  • Onboard storage
  • Keypad locking and unlocking*
  • Vibration support*
  • Battery charge level detection*

Press 0 to exit the application.

* Some Java phones do not support battery charge level detection and keypad locking so these tests will fail on those devices.

Debugging

This example application uses the debug logging and user panic functions in MoSync. Note: The purpose is to stress the system, and create an error. There is no output to screen.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When started, a pop up with the message “i is greater than 3” should appear.

Key Presses

None of the keys should be operable, just click OK on the popup to close it.

Events, Keys, Touch, Moblets

Detecting Events

Throughout the execution of your program, the MoSync SDK can supply it with events.  These are typically things which is happening to the phone.  For instance, a button has been pressed or the screen has been touched.  They also cover more advanced functions such as 'nothing has happened for a few seconds' or 'the phone has a new location from the GPS'.

This example is a simple "Hello World!" program.  Console-type applications that exit when they finish are quite uncommon on mobile devices, so we show in this example how provide an event loop that keeps the application running until the user presses a key, in this case the zero (0) key.

It waits for an event to occur using the maWait() function.  It then creates an MAEvent struct.  These MAEvent objects contain the details of the event which has occurred.  You can query them to see the type of event that has occurred, and also for any relevant data to that event, such as which key has been pressed, where the screen has been touched and what the new location is.

This example also shows how to detect close events.  A close event is an event that is generated when a user attempts to forcibly terminate the application in some (platform-dependent) way, commonly by using some type of task manager. The purpose of the close event is to notify the application that it will be closed down very shortly (the exact amount of time being platform-dependent) and that it should perform emergency shutdown operations, such as saving critical data.

Here is an example in C:

/**
* This program shows that how can we handle events in MoSync. 
* Here we are handling Close event using the C code.
* 
* @file EventHandlingCProgram.C
*
* @author Chris Hughes
*/

#include <ma.h>   // the standard MoSync header
#include <conprint.h>   // provides printf()

int MAMain() 
{
    printf("Hello world!\n");
	
    // Declare a MoSync event struct.
    MAEvent MyEvent;
    while(1) 
    {
        // Wait for an event
        maWait(0);
	
        // Store the latest event in the event struct.
        maGetEvent(&MyEvent);

        // determine whether or not it is a key press
        if(MyEvent.type == EVENT_TYPE_KEY_PRESSED) 
        {
            // ...and whether it was the '0' key.
            if(MyEvent.key == MAK_0)
            {
                // ...in which case we exit.
                maExit(0);
            }
        }
        else if(MyEvent.type == EVENT_TYPE_CLOSE) 
        {
            // ...and also if we get a close event.
            maExit(0);
        }
    }
    return 0;
}

 

The function maGetEvent() populates your MAEvent object with the relevant data.  You can then test the property type to see what is the event which has occurred.  The values in type are defined in maapi.h, and cover key presses, screen touching, location, your application being sent to the background, the phone changing from landscape to portrait (and vice versa) and closing.  By testing the type of the event, you only have to code for the events you are interested in.

Event handling in C++ can be quite different.  You can make use of these C functions if you wish in C++ code, but if you are building an application on the MoSync SDK in C++ then there is an event-based framework already available to you.

The basis of this framework is the Moblet class, which is a C++ class which can handle your main application loop.  The Moblet class already has virtual methods built in for handling common system events. including key presses, key releases, screen touches, screen movement and screen releases as well close events and 'custom events' for location and other events in the future.

If you are using Moblet to build your application, then you will create a new class which inherits from the Moblet base class.  Moblet only provides virtual methods for event handling, so you'll have to supply your own code, but it is a much cleaner way of processing these system events.

For instance, if you want to capture the key presses from the C example above, you would implement keyPressEvent(int keycode, int nativeCode).  You can then compare the value of button pressed with the value you are looking for.

As an aside, when you capture key presses you get two values.  The first (referred to as keyCode) is a platform independent value.  There is a long list of keys which you can handle in this way defined in maapi.h.  In the example below, we are looking for the 0 key, so we can compare it with MAK_0.  This way, we don't need to have code to check the key value for the 0 key on each platform.  There are also some keys which may be implemented on just a few handsets.  The Android 'back' key, or volume keys on a phone are not implemented in the platform independent way as their are so few phones which support them.  You can still handle key presses from these buttons through, by accessing the nativeCode value of the button, which is platform specific.

And here is a similar example in C++ which makes use of MoSync's moblet framework:

/**
* This is another program for handling events using C++ syntax.
* 
* @file EventHandlingCPPProgram.cpp
*
* @author Chris Hughes
*/

#include <MAUtil/Moblet.h>

using namespace MAUtil;

// Moblet class declaration.
class MyMoblet : public Moblet 
{
public:
    MyMoblet() 
    {
        maSetColor(0xFFFFFF);
        maDrawText(0, 32, "Hello World!");
        maUpdateScreen();
    }
    
    // Handles key press event.
    void keyPressEvent(int keyCode, int nativeCode) 
    {
        if(keyCode == MAK_0) 
        {
            closeEvent();
            close();
        }
    }
    
        // Handles key release event.
        void keyReleaseEvent(int keyCode, int nativeCode) 
        {
	
        }
};

/*
* Programs main functionality starts here.
*/
extern "C" int MAMain() 
{
    Moblet::run(new MyMoblet());
    return 0;
}

 

It's not just Moblet which has methods for handling system events.  In the MoSync SDK User Interface Library MAUI, the Screen class also has similar methods for handling system events, so you can process these events differently depending on which part of your application the user is using.

HelloMoblet

HelloMoblet is a well-commented example application for beginners. It demonstrates how to use MoSync's Moblet framework to wrap your application to ensure timely response to key events.

This example is included in the MoSync SDK installation in the /examples/Moblet folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

This application provides a very basic example of how to use MoSync's Moblet framework to detect key events and use timers. When started, it displays the text "Hello Moblet!" The text can be moved around using the joystick or you can click on the screen and the text will jump to that position.

The code is very well commented so that you can see what's happening at each step. Examine the source code of the application (in the file main.cpp) to learn how the program works.

Key Presses

  • Tap the screen - moves the word to the tapped location
  • Up key – moves the word up until it reaches the top of the screen
  • Down key – moves the word down until it reaches the bottom of the screen
  • Left key – moves the word left until it reached the edge of the screen
  • Right key – moves the word right until it reached the edge of the screen
  • 0 or right-softkey - exits the program

MultiTouch

This simple application shows how to handle multitouch events so that your applications can react to pinches, swipes and rotatation. This application is based on the MoSync Moblet framework.

This example is included in the MoSync SDK installation in the /examples/Moblet folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When the application runs, the screen is intially blank. Touch the screen in one, two, or more places. (If you are running the application in the MoRE emulator, right-click on the screen to start MoRE's multitouch emulation mode.) At each touch point a red rectangle will be displayed, along with the ID of the touch point.

(Note that only some touchscreen devices support multitouch, and that the maximum number of touch points a device can handle varies: iPhone = 5, iPad = 11, HTC Wildfire = 3, Samsung Galaxy S = 5, MoRE emulator = 2.)

Try lifting one or more fingers, and pinching, skewing, swiping - the rectangles will follow your touches. Touch IDs are reused when you lift a finger and then touch again.

Examine the source code of the application to learn how the program works. Note the use of MAUtil::Map to store the collection of touch IDs and their coordinates.

Starting a New Moblet Project

This tutorial gives you a short introduction to developing mobile applications with MoSync. If you’ve never created a mobile application with MoSync before, this is a great place to start. In this tutorial, we’re going to create a new project in Eclipse, create a new screen, and interact with the user.

What is a Moblet Project?

The MoSync platform can be used in two very broad ways. Firstly, if you’re porting an existing C game to mobile, or writing a new one in C or C++, then MoSync provides a very lightweight compatibility API to get your game running on as many phones as possible, and as quickly as possible. This provides low-level APIs to sound and screen which you can use. More about this approach is explained in the topic Classic Procedural Applications.

If you are doing something which doesn’t require direct access to graphics, then there is a higher-level set of APIs and an event-based environment ready for you to work with: Moblets. Moblets are a quick way to create rich and attractive mobile applications.

Creating a New Project in Eclipse

The MoSync SDK includes a complete IDE (integrated development environment) that you can write your programs in. The MoSync IDE is based on a popular open-source IDE called Eclipse. Java developers will be familiar with the IDE concept already, and anyone with experience of Visual Studio or other similar system will find MoSync very easy to work with.

To get started with this tutorial, Launch MoSync then close the Welcome page. When you’ve done that, you’ll have a screen which looks like this:

Here you can see all the main views of the IDE (in Eclipse terminology, each panel is called a "view"). On the left is the Project Explorer view which will soon show your project's folders and files, in the centre is the Edit view where you will write code, and on the right are the Device Profiles view and (hidden behind it) the Finalizer view.

Now select New > Project from the IDE's File menu. You will get a dialog box like this:

Select MoSync Project and then press then Next button.

Now you need to give your application a name. For this tutorial, call it MoSyncDemoApp:

Note: do not use spaces or any character which isn't a letter or a number into the application name. It can cause problems with the compiler later if the file path has a space in, or a character that can be interpreted differently.

On the next screen, you’ve got a series of options for the type of application we want to use. Although there is an option to create a Moblet project, we want to select the MAUI Project.

MAUI is the moblet user interface (UI) library from MoSync. It contains common UI controls you can use in your application. We will be creating some MAUI controls in this tutorial, and later tutorials will delve much deeper into the inner workings of MAUI.

There, you should now have a screen which looks like this, and is waiting for you to enter some code.

Creating a New Screen

To create our Moblet application, we have to use some controls from the MAUI library. The first one we want to use is the Screen control. This is a canvas which we can put other controls on, and can be shown to the user.

The code that the project wizard creates looks something like this:

#include <MAUtil/Moblet.h>
#include <MAUI/Screen.h>
#include <MAUI/Label.h>

using namespace MAUtil;
using namespace MAUI;

/**
 * MAUI is short for MoSync API User Interface.
 *
 * A Screen is a MAUI object that holds widgets.
 */
class MAUIScreen : public Screen
{
public:
    /**
     * Create widgets in the constructor.
     */
    MAUIScreen()
    {
        mBackgroundArea = new Label(
            0, // Left
            0, // Top
            0, // Width
            0, // Height
            NULL // Parent widget
            );
        mBackgroundArea->setBackgroundColor(0xFFFFFF);

        mTouchArea = new Label(
            100, // Left
            100, // Top
            50,  // Width
            50,  // Height
            mBackgroundArea // Parent widget
            );
        mTouchArea->setBackgroundColor(0x000000);

        // Set the main widget of the screen. This will
        // resize the widget to fit the screen.
        setMain(mBackgroundArea);
    }

    /**
     * Deallocate objects in the destructor.
     */
    virtual ~MAUIScreen()
    {
        delete mBackgroundArea;
        delete mTouchArea;
    }

    /**
     * Called when a key is pressed.
     */
    virtual void keyPressEvent(int keyCode, int nativeCode)
    {
        // Default color is white.
        int color = 0xFFFFFF;

        switch (keyCode)
        {
            case MAK_1:
                color = 0xFF5555;
                break;
            case MAK_2:
                color = 0x55FF55;
                break;
            case MAK_3:
                color = 0x5555FF;
                break;
        }

        // Set the new background color.
        mBackgroundArea->setBackgroundColor(color);
    }

    /**
     * Called when the screen is touched.
     */
    virtual void pointerPressEvent(MAPoint2d point)
    {
        // Center the touch area at the pointer position.
        mTouchArea->setPosition(point.x - 25, point.y - 25);

        // We need to repaint the parent when a child
        // widget is changed.
        mBackgroundArea->requestRepaint();
    }

private:
    Label* mBackgroundArea;
    Label* mTouchArea;
};

/**
 * A Moblet is a high-level class that defines the
 * behaviour of a MoSync program.
 *
 * To use MAUI you need a Moblet, but a  Moblet can
 * be used with or without the MAUI library.
 */
class MAUIMoblet : public Moblet
{
public:
    /**
     * Initialize the application in the constructor.
     */
    MAUIMoblet()
    {
        mScreen = new MAUIScreen();
        mScreen->show();
    }

    /**
     * Deallocate objects in the destructor.
     */
    virtual ~MAUIMoblet()
    {
        delete mScreen;
    }

    /**
     * Called when a key is pressed.
     */
    void keyPressEvent(int keyCode, int nativeCode)
    {
        if (MAK_BACK == keyCode || MAK_0 == keyCode)
        {
            // Call close to exit the application.
            close();
        }
    }

private:
    MAUIScreen* mScreen;
};

/**
 * Entry point of the program. The MAMain function
 * needs to be declared as extern "C".
 */
extern "C" int MAMain()
{
    Moblet::run(new MAUIMoblet());
    return 0;
}

This isn’t a tutorial on the C language, there are plenty of those already, but we can break this down into three recognisable sections.

Firstly, at the top of the code is the declaration of a new class MAUIScreen, which is inheriting from the MAUI Screen class. This is where we are going to do our work today.

Beneath that is the class MAUIMoblet. This is some standard code which creates a new instance of MAUIScreen and calls the show() method, which displays the screen on the mobile.

Finally, at the end is the main function (called MAMain) which creates our new Moblet code.

The Moblet code exists to give the developer access to some basic phone functions. It manages the environment, and it can tell you when a key has been pressed, when the screen has been touched, what the screen size is, and so on.  By basing our application on the Moblet code, we automatically have access to all of these things, regardless of the device it is going to run on. We are not going to have to change any code between an Android device, Symbian device, a Windows Mobile device, or a phone running J2ME.

To create our screen, we need to look at the code for MAUIScreen:

class MAUIScreen : public Screen
{
public:
    /**
     * Create widgets in the constructor.
     */
    MAUIScreen()
    {
        mBackgroundArea = new Label(
            0, // Left
            0, // Top
            0, // Width
            0, // Height
            NULL // Parent widget
            );
        mBackgroundArea->setBackgroundColor(0xFFFFFF);

        mTouchArea = new Label(
            100, // Left
            100, // Top
            50,  // Width
            50,  // Height
            mBackgroundArea // Parent widget
            );
        mTouchArea->setBackgroundColor(0x000000);

        // Set the main widget of the screen. This will
        // resize the widget to fit the screen.
        setMain(mBackgroundArea);
    }

    /**
     * Deallocate objects in the destructor.
     */
    virtual ~MAUIScreen()
    {
        delete mBackgroundArea;
        delete mTouchArea;
    }

    /**
     * Called when a key is pressed.
     */
    virtual void keyPressEvent(int keyCode, int nativeCode)
    {
        // Default color is white.
        int color = 0xFFFFFF;

        switch (keyCode)
        {
            case MAK_1:
                color = 0xFF5555;
                break;
            case MAK_2:
                color = 0x55FF55;
                break;
            case MAK_3:
                color = 0x5555FF;
                break;
        }

        // Set the new background color.
        mBackgroundArea->setBackgroundColor(color);
    }

    /**
     * Called when the screen is touched.
     */
    virtual void pointerPressEvent(MAPoint2d point)
    {
        // Center the touch area at the pointer position.
        mTouchArea->setPosition(point.x - 25, point.y - 25);

        // We need to repaint the parent when a child
        // widget is changed.
        mBackgroundArea->requestRepaint();
    }

private:
    Label* mBackgroundArea;
    Label* mTouchArea;
};

There is some handy code here already, which creates two labels BackgroundArea and TouchArea.  We will craete another MAUI control, a Label. Just like all the other environments, this label control will let you put text on the screen.

Lets' add some more code in the MAUIScreen constructor and put another label control in it. To do this, we first need to provide our program with a font. There is a separate more detailed tutorial available for creating new fonts in your project "Creating New Fonts".

Adding Font Resources

In MAUI, fonts are bitmap fonts, and the tools to create new fonts have been provided in the download. Creating new fonts will be the subject of another tutorial though, so here we will simplify things by using a font which has already been created.

To include the font in our application, we need to create a resource file. A resource file is a file which contains the paths to all of the external resources that an application requires, including fonts, images, and sounds, which are then packaged up with your application.

To create a resource file, right-click on the name of your application on the left hand side of the Eclipse window, and select New > Other. From the dialog box that appears, select MoSync Resource File.

Click Next. Type the file name res.lst, then click Finish. You will see the new file appear in the MoSyncDemoApp project.

Open up res.lst and add the following entry.

.res MYFONT
.bin
.include "../../examples/MAUI/MAUIex/pretty.mof"

MoSync font files have the .mof extension. This one, pretty.mof, is supplied with one of the examples.

Obviously, you’ll need to change the path here if you’ve installed MoSync anywhere other than C:\MoSync. Note that the path to the font file cannot be absolute. Furthermore you must use either forward slashes in the path or escaped backslashes like this:

.include "..\\..\\examples\\MAUI\\MAUIex\\pretty.mof"

Now that you’ve included the font file in the project, you can reference it your program. To do this, you need to add an additional #include directive. You’ll also need to reference the Font class. Add these lines to the existing list of includes:

#include <MAUI/Font.h>
#include "MAHeaders.h"

MAHeaders is a special file which is created when you build the application. It provides a reference (known as an MAHandle) to your program.

With these included, we can create a font in the application:

Font* f = new Font(MYFONT);

And we can use it to create a label:

Label* l = new Label(0, 0, 50, 50, NULL);
l->setFont(f);
l->setCaption("Welcome to my mobile application");

There is another way where we can create all of this on one line, but that is for another tutorial, or for you to explore yourself.

If you now click the Run button on the toolbar, you’ll see your first mobile application!

Tracing Your Application

You can trace the execution of your application with a command lprintfln(). This sends a formatted line to debug console in Eclipse. You need to add an include directive as:

#include <conprint.h>

If you add the line:

lprintfln("Starting");

as the first line in the constructor code, then you will see this in the console window in Eclipse. Use lprintfln as you write your program so you can see what it being executed.

Interacting with the User

You can now use the Moblet interfaces to interact with the user. We can capture key presses and cause different things to happen. In our application, we want it to exit.

There are five basic events for user interaction. Key press, key release, screen touch, screen drag and screen release. Each of these events has already been set up for you, you just need to write some code to react to these events in the screen. In fact, in the Moblet class, you can even see example stubs for these. However, I want to capture key presses in the MyScreen class

void keyPressEvent(int keyCode)
{
    lprintfln("Caught a key press");
    if(keyCode == MAK_5)
    maExit(0);
}

The function ‘keyPressEvent’ has already been set up for you by the Moblet, you just need to implement it. By adding this code to the screen, we can capture key presses. Every time a key is pressed, then it will write a line to the console, so you can see it working. The MoSync APIs also contain constants representing all of the keys. You can find the full list in the maapi.h File Reference in the MoSYnc API Reference Guide.

If the key pressed is 5 on the keypad, then we’re going to do a brute force exit of the program. There is a Moblet::close() method which is probably preferred, but this will do for our demonstration.

There we have it, a mobile application. You now got a basis for creating your own applications. If you want to check what you’ve got against my program, here is my listing:

#include <MAUtil/Moblet.h>
#include <MAUI/Screen.h>
#include <MAUI/Label.h>
#include <MAUI/Font.h>
#include "MAHeaders.h"
#include <conprint.h>
using namespace MAUtil;
using namespace MAUI;
class MyScreen : public Screen {
public:
	MyScreen() {
		lprintfln("Starting");
		Font* f = new Font(MYFONT);
		Label* l = new Label(0, 0, 50, 50, NULL);
		l->setFont(f);
		l->setCaption("Welcome to my mobile application");
		setMain(l);
	}
	~MyScreen() {
		// todo: delete main widget of this screen
	}
	void keyPressEvent(int keyCode)
	{
		lprintfln("Caught a key press");
		if(keyCode == MAK_5)
		maExit(0);
	}
private:
};
class MAUIMoblet : public Moblet {
public:
	MAUIMoblet() {
		// initialize
		screen = new MyScreen();
		screen->show();
	}
	void keyPressEvent(int keyCode) {
		// todo: handle key presses
	}
	void keyReleaseEvent(int keyCode) {
		// todo: handle key releases
	}
	MyScreen* screen;
	~MAUIMoblet() {
		delete screen;
	}
};
extern "C" int MAMain() {
	Moblet::run(new MAUIMoblet());
	return 0;
};

Simple

This example application catches key events and displays the keycodes of pressed keys.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

The screen says "Hello moblet!", after which the application awaits key presses. Each key (except the zero or right-softkey) will have its key codes (presscodes) echoed on-screen:

  • The first number is the standard MoSync keyCode for that key on all devices that have it. A keyCode of "0" means that the key has no standard MoSync keyCode.
  • The second number is the nativeCode for that key returned by the device. This will vary from device to device, although may be standard across particular platforms.

The application also keeps count of the number of keys that have been pressed and released. Key combinations, such as SHIFT-A, will have two or more consecutive presscodes and releases as shown in the example screen above.

There is no touchscreen support in this example, it just works with the physical keys of the device.

Key Presses

  • Any key to get key code
  • 0 or right-softkey - exits the application

 

 

 

MoTris

Motris is a variation of the old classic game with a similar name.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

A classic game. Highest scores are saved on the server, if a network connection is available.

This application requires the device to have a physical keyboard.

Key Presses

  • 2 or up arrow - rotate block
  • 4 or left arrow - move block left
  • 6 or right arrow - move block right
  • 5 or down arrow - drop block faster
  • Ok/Center button - instantly drop the block, or show the menu at the game's end

 

Timer

This program tests simple graphics on the phone, catching a key event and adding some action.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

Pressing FIRE bounces the yellow block.

Key Presses

  • FIRE or tap the screen to bounce
  • 0 or right-softkey to exit the application

Facebook, Twitter, Wikipedia

Using the Facebook Library

The MoSync Facebook Library implements most of the Facebook Graph API. Here we describe how to connect to Facebook through the MoSync Facebook library, retrieve information about them, post links, vidoes and other object types, create new albums, and a whole host of other cool operations.

Introduction

Facebook is a modeled as a social graph. Its official API, the Facebook Graph API, represents the objects in the graph (people, photos, albums, events...) and the connections between them (friend relationships, photo tags...).
 
Every object in the social graph has a unique ID. You can fetch any object from Facebook if you know the ID. Alternatively, people with usernames can be accessed using their username as an ID. There is also a special identifier "me" which refers to the current user.
 
You can retrieve the public information about a user (first name, last name, profile picture) by using the ID or the username. To get additional information about a user, you must first get their permission for which you need to get an access token. After you obtain the access token, you can perform authorized requests and publish on behalf of that user.
 
The MoSync Facebook Library defines a FacebookManager class that provides the interface between the Facebook platform and your own application. Use this class if you want to retrieve data, publish on Facebook, or retrieve an access token for your application.

Authentication and access tokens

Authentication involves retrieving the access token and asking for extended permissions. For the official Facebook documentation on the authorization flow see: http://developers.facebook.com/docs/authentication/.
 
Retrieving a user access token allows your application to access a user’s basic information (that is information that is available publicly or by default on Facebook). If your application needs more than this basic information, you must request specific permissions from the user.
 
To get the access token from Facebook you must first redirect the user to the Facebook OAuth Dialog. With the MoSync Facebook Library this is very easy:

String appID = "13234625675"; //the application id
mFacebookManager = new FacebookManager( app_ID );
String oAuthUrl = mFacebookManager->getOAuthUrl();
mLoginScreen = new FacebookLoginScreen();
mLoginScreen->setUrl ( oAuthUrl );
mLoginScreen->show(); //displays the login screen

After the user logs in, we are redirected to the redirect_uri. The access token is passed in the redirect_uri. The redirect_uri must be parsed to get the access token. An example of how to extract the access token can be found in our FacebookDemo example application in FacebookDemoMoblet::customEvent.
 
After retrieving the access token, we must send it to the FacebookManager:

mFacebookManager->setAccessToken(accessToken); 

Permissions

You must request specific permissions from the user if your app needs more information than the user has made available to everyone on Facebook. To do this, pass the FacebookManager the permissions you need.

Note: If your application also needs permissions, you must pass those permissions to FacebookManager when it is first created.
 
In the MoSync Facebook Library we specialize the GetPermissionsFor template class for each Facebook object type. It is used to fill a Set object with the permissions that are needed to make connections to a Facebook object or to publish an object.

MAUtil::Set<MAUtil::String> permissions;

To get the permissions needed to retrieve a Note object for the current user or current user’s friend (also for the connections of Note objects) we use:

// "user_notes" "friends_notes"
GetPermissionsFor<Note>::retrievingData(permissions); 

To get the permissions needed to publish a note on behalf of the user or user’s friend we use:

//"create_note", "publish_stream"
GetPermissionsFor<Note>::publishingData(permissions); 

To get all the permissions available, so that we can make any request (retrieve data, connections, and publish), we use:

// "user_notes" , "friends_notes", "create_note", "publish_stream"
GetPermissionsFor<Note>::allPosibleRequests(permissions); 

You can also just get the permissions needed to retrieve a field from a user object (for example, a biography), or for a connection, as we show in the example below:

MAUtil::Set<MAUtil::String> permissions;
 
permissions.insert("offline_access"); //ask for a access_token that doesn’t expire
GetPermissionsFor<User>::Retrieve::onlyConnections(permissions);
GetPermissionsFor<User>::publishingData(permissions);
GetPermissionsFor<Album>::allPosibleRequests(permissions);
GetPermissionsFor<Checkin>::allPosibleRequests(permissions);
GetPermissionsFor<Comment>::allPosibleRequests(permissions);
GetPermissionsFor<Note>::allPosibleRequests(permissions);
GetPermissionsFor<Photo>::allPosibleRequests(permissions);
GetPermissionsFor<Post>::allPosibleRequests(permissions);
 
//the app id that is generated when you create your application
String application_ID = "7125765764";
 
mFacebookManager = new FacebookManager(application_ID, permissions);
String oAuthUrl = mFacebookManager->getOAuthUrl();
 
mLoginScreen = new FacebookLoginScreen();
mLoginScreen->setUrl(oAuthUrl);
mLoginScreen->show(); //this displays the login screen

Implemented Graph API objects

Our Facebook Library implements most of the Facebook objects described in the Graph API documentation (http://developers.facebook.com/docs/reference/api/). The implementation can be found in /Facebook_lib/GraphAPI/GetFacebookObjects/FacebookObjects in your installed MoSync package.

Objects currently implemented include:

  • Album
  • Checkin
  • Comment
  • Event
  • FriendList
  • Group
  • Link
  • Note
  • Photo
  • Post
  • StatusMessage
  • User 
  • Video

Fetching public information about an object

You can access the properties of an object using the FacebookManager. To retrieve an object your application has to register as an ObjectRequestListener with the FacebookManager. To do this, we inherit from ObjectRequestListener and override the functions we are interested in.

If we want to retrieve User objects, we override void facebookObjectReceived(const User &). If we want to retrieve Album objects, we override void facebookObjectReceived(const Album &).

For example, to access the public information about a User, with the username btaylor and an album with the id 99394368305 (Coca-Cola's wall photos):

class MyApplication: public ObjectRequestListener
{
public:
   // ObjectRequestListener override. This function will be called by the
   // FacebookManager when a User object is retrieved from Facebook
   virtual void facebookObjectReceived(const User &object);

   // ObjectRequestListener override. This function will be called by the
   // FacebookManager when an Album object is retrieved from Facebook.
   virtual void facebookObjectReceived(const Album &object);

   //ObjectRequestListener override. Called when the request fails.
   virtual void queryError(int code, const MAUtil::String &path);
};

String application_ID = "13234625675"; //the application id
mFacebookManager = new FacebookManager( application_ID );

mFacebookManager->setObjectRequestListener(this);
mFacebookManager->requestObject<User>(btaylor);
mFacebookManager->requestObject<Album>(99394368305); 

When the response from Facebook is received, the JSON data is parsed and converted to the type of object we requested (e.g User). FacebookManager sends this object to the listener.

Retrieving connections

We use FacebookManager to retrieve connections for an object. To make a connection request we need to know the ID of the object. If the Facebook object is the current user, we can use me instead of an ID. We also need to inherit from ConnectionsManagerListener and register with the FacebookManager as a listener.
 
To see what connections are available for an object we use the Connections template class found in /GetConnections/Connections.h. The template is specialized for every Facebook object type defined by the library.
 
To see what connections are available for an album, we use the Connections<Album> class. Connections<Album> has four static functions that correspond to the four connections an Album can have: likes, photos, comments, picture. Each returns a string representing the keyword for that connection. For example, Connections<Album>::likes() returns "likes".
 
The Connections<Checkin> class defines two functions that return the keywords for the two connections that a Checkin has (likes and comments), so to ask for all the connections a Checkin we use:

mFacebookManager->requestConnection( ckeckin_id, Connections<Checkin>::likes() );
mFacebookManager->requestConnection( checkin_id, Connections<Checkin>::comments() );

To request the albums, feed, and notes connections for the current user, and also request the comments connections for one of the user’s albums we use:

class MyApplication : public ConnectionsManagerListener
{
public:
    MyApplication();
 
    //The following functions are ConnectionsManagerListener  overrides.
    //connType parameter - represents the keyword for the connection we requested.
    //For the news feed for example it will be "feed".
    //objectId parameter -  string representing the id of the object for which
    // we requested the connection.
 
    //Function called by FacebookManager when the "albums" connection
    //is retrieved from Facebook.
    void received( const Vector<Album> &albums, const String &connType, const String &objectId );
 
    //Function called by FacebookManager when the "feed"  connection
    //is retrieved from Facebook.
    void received( const Vector<Post> &posts, const String &connType, const String objectId );
 
    //Function called by FacebookManager when the "notes"  connection
    //is retrieved from Facebook.
    void received(   const Vector<Note> &notes, const String &connType, const MAUtil::String objectId ) ;
 
    //Function called by FacebookManager when the "comments" connection
    //is retrieved from //Facebook
    void received( const MAUtil::Vector<Comment> &comments, const MAUtil::String  &connType, const String &objectId )
 
    //override of ConnectionsManagerListener called when the request failed
    void queryError(int code, const MAUtil::String &path) ;
};
 
MyApplication::MyApplication()
{
    //the application id
    String application_ID = "13234625675";
    mFacebookManager = new FacebookManager( application_ID );
    mFacebookManager->setConnectionRequestListener(this);
   
    //request for the albums connection for the curent user
    mFacebookManager->requestConnection( Connections<User>::albums(), "me" );
   
    //request for the notes connection for the curent user
    mFacebookManager->requestConnection( Connections<User>::notes(), "me");
 
    //request for the feed connection for the curent user
    mFacebookManager->requestConnection( Connections<User>::feed() , "me");   
}

void MyApplication::received(  const Vector<Album> &albums, const String &connType, const String &objectId)  
{   
    Album firstAlbum = albums[0];
    mFacebookManager->requestConnection( Connections<User>::comments(), firstAlbum.getId() );
}

We can request only the fields you want from an object. For example, if we just want to retrieve the name and ID of all of the user’s albums we do not want any other fields. To do this we call the overload function requestConnection and pass it a vector with jus the fields we want:

Vector<String> fields;
fields.add("id");
fields.add("name");

mFacebookManager->requestConnection("albums", fields, "me");

Publishing To and Deleting From Facebook   

We use FacebookManager to publish to Facebook or to delete Facebook objects. To get the result of a publish or delete request, we inherit from the PublishingListener class and register with the FacebookManager as a PublishingListener:

class MyApplication: public PublishingListener
{
public:

    //PublishingListener override
    //This function is called when a remove or an unlike request
     //was completed successfuly
    void publishingResponseReceived(bool success, const String &path);

    //PublishingListener override
    //This function is called when a new Facebook object was created successfully.
    void publishingResponseReceived(bool const String &newObjectId, const String &path);

    //PublishingListener override.  Called when the request fails.
    virtual void queryError(int code, const MAUtil::String &path);
};

String application_ID = "13234625675";  //the application id
mFacebookManager = new FacebookManager( application_ID );

MyApplication *myApp = new MyApplication();

//register as a PublishingListener, so that MyApplication is informed
//about the result of the publish request.
mFacebookManager->setPublishingListener(myApp); 

Liking and unliking objects

To like a Facebook object we call the FacebookManager function void Like(const String &id). The "id" parameter is the ID of the object to be liked. To like an album, for example, we use:

mFacebookManager->like( album.getId() );

Similarly, to unlike a Facebook object we call the FacebookManager function void Unlike(const String &id).

mFacebookManager->unlike( comment.getId() ); 

Here comment represents some Comment object we retrieved from Facebook.

Adding an object

Various types of objects can be added to Facebook through the functions in the FacebookManager class. For a comprehensive list of the functions available through this class, see the MoSync API Reference.

Objects that can be added include:

  • Comments
  • Users to FriendLists or Groups
  • FriendLists
  • Events and EventResponses
  • Notes
  • Albums
  • Checkins
  • Posts, Links, and StatusMessages on Walls

To add a comment on a Facebook object we call the FacebookManager function addComment:

String myMessage = "I like this post";
mFacebookManager-> addComment(post.getId(), myMessage);

Here we use post.getId to identify the object to which we wish to add the comment. Similarly we can use the FacebookManager function addUser to add a User object to a FriendList:

mFacebookManager->addUser( myFriendList.getId(), "7692649746983");

Here "7692649746983" is the unique ID of the friend we want to add.

Some functions in FacebookManager are a little more complicated. To add an event on behalf of the current user (or one of that user's friends) we use the function addEvent which requires several parameters, along with some optional ones:

void addEvent(
     const MAUtil::String &USER_ID,                                //mandatory
    const MAUtil::String &eventName,                           //madatory
    const UnixTimeStamp &eventStart_time,                      //mandatory
    const UnixTimeStamp &eventEnd_time = UnixTimeStamp(),      //optional
    const MAUtil::String &message= "",                         //optional
    const MAUtil::String &location = "",                       //optional
    const MAUtil::String &privacyType = "OPEN");               //optional

For example:

//event name
String eventName = "party";   

//event starts in 06/12/2012 at 8:15 :30
UnixTimeStamp startTime(Date("2012", "12", "6"), Time("8","15","30"));

//event ends in 06/12/2012 at 11:20:00
UnixTimeStamp endTime(Date("2012", "12", "6"), Time("11", "20","00"));

//the event’s description
String eventDescription = " MoSync annual Christmas party";

//the event takes place in Stockholm
String eventLocation = "Stockholm";

mFacebookManager->addEvent(  "me",
eventName,
startTime,
endTime,   
eventDescription,
eventLocation);

To add a Post on the user’s wall (or on one of the user's friends walls) we call addPostOnWall:

void addPostOnWall(  
    const MAUtil::String &ID,                       //mandatory
    const MAUtil::String &message,                  //mandatory
    const MAUtil::String &link,                     //mandatory
    const MAUtil::String &name = "",                //optional
    const MAUtil::String &caption = "",             //optional
    const MAUtil::String &description = "");        //optional           

For example:

mFacebookManager->addPostOnWall ( "me",                   //id
    "Post added with MOSYN SDK",                          //message
    "http://www.youtube.com/watch?v=FL7yD-0pqZg",         //link
    "New Post :)",                                        //name
    "Link from You Tube"                                  //caption       
    "Testing adding a post on wall with MOSYNC_SDK");     //description

The result will be a wall post that looks something like this:

Removing objects

FacebookManager provides a set of functions for removing objects the Graph API. The objects that can be removed include:

  • Users from FriendLists and Groups
  • Comments
  • FriendLists
  • Notes

To remove a user from a FriendList we call the FacebookManager function removeUserFrom:

String myFriendListID = "1234567";
String friendID = "0101010101";
mFacebookManager->removeUserFrom ( myFriendListID, friendID);

A larger example

In this example we retrieve albums, posting a link on the user's wall, post a link for all the user's friends, add a like and a comment to an album, and delete a FriendList.

 

class MyApplication: public MAUtil::Moblet, public ConnectionsManagerListener, public PublishingListener
{
public:

   MyApplication();

   // ConnectionsManagerListener overrides

   /*
   * This function is called when a "albums" Connection was completed sucessfully
   */
   virtual void received(const MAUtil::Vector<Album> &albums,
   const MAUtil::String &connType,
   const MAUtil::String &objetcId);

   /*
   * This function is called when the "likes", "movies", "music", "books", "accounts",
   * "activity", "friendlists", "interests" and "television" Connection was requested.
   * @param connType - the ConnectionType. It can have the following values: "likes",
   * "movies", "music", "books", "accounts", "activity", "friendlists", "interests"
   * and "television"
   */
   virtual void received(const MAUtil::Vector<CategoryData> &likes,
   const MAUtil::String &connType,
   const MAUtil::String &objetcId);

   /*
   * This function is called when the "friends", "members" or "tags" Connection
   * was requested.
   * @param connType - the ConnectionType. It can have the following values:
   * "friends", "members" or "tags"
   */
   virtual void received(const MAUtil::Vector<IdNamePair> &friends,
   const MAUtil::String &connType,
   const MAUtil::String &objetcId);

   /*
   * This function is called when a Connection was requested, and the request failed
   */
   virtual void errorReceivingConnection(int code,
   const MAUtil::String &connType,
   const MAUtil::String &id);

   // PublishingListener overrides

   / *
   * This function is called when a remove or an unlike request was
   * completed sucessfully.
   */
   virtual void publishingResponseReceived(bool success, const MAUtil::String &path);

   /*
   * This function is called when a new object was created successfully
   * (Album, Like, Comment, StatusMessage etc).
   */
   virtual void publishingResponseReceived(const MAUtil::String &data, 
   const MAUtil::String &path);

   /*
   * This function is called when a publish/remove request failed
   */
   virtual void queryError(int code, const MAUtil::String &path);

private:

   void requestAlbumsConnection();
   void addComment(const String &id);
   void addLike(const String &id);
   void postLinkOnWall(const String &id);
   void requestFriendListsConnection();
   void deleteFriendList(const String &id);

private:

   FacebookManager *mFacebookManager;

};

MyApplication::MyApplication()
{
   String applicationId = "265721237478169";

   mFacebookManager = new FacebookManager(applicationId);
   mFacebookManager->setAccessToken("476927849827395873465923695798723"); 

   //register as a ConnectionManagerListener so that we retrieve the
   //requested data from FacebookManager
   mFacebookManager->setConnectionRequestListener(this);

   //register as a PublishingListener so that we are informed by the
   //FacebookManager about the result of the publish request
   mFacebookManager->setPublishingListener(this);

   //request the albums of the user. We will add a comment and a like
   //to the first album
   requestAlbumsConnection();

   //request the FriendLists of the user. We will delete the first FriendList
   requestFriendListsConnection();

   //Posting a link on wall for the user
   
   // We post for the current user (that logged in), so we can use "me" as an id.
   postLinkOnWall("me");  
}

void MyApplication::requestAlbumsConnection()
{
   mFacebookManager->requestConnection("albums");
}

void MyApplication::addComment(const String &ID)
{
   mFacebookManager->addComment("Testing adding a comment with MOSYNC SDK", ID); //adding the comment to the object with the id "ID"
}

void MyApplication::Like(const String &ID)
{
   mFacebookManager->Like(ID); //like the Facebook objetc with the id "ID"
}

void MyApplication::postLinkOnWall(const String &id)
{
   mFacebookManager->addLinkOnWall( id, //the id.
   "http://www.mosync.com/", //the link we want to post
   "Testing posting a link on wall with MOSYNC SDK"); //the message to display.
}

void MyApplication::requestFriendListsConnection()
{
   mFacebookManager->requestConnection("friendlists");
}

void MyApplication::deleteFriendList(const String &id)
{
   mFacebookManager->removeFriendList(id);
}

//Retrieving albums

void MyApplication::received(const MAUtil::Vector<Album> &albums,
const MAUtil::String &connType,
const MAUtil::String &objetcId)
{
   Album album = albums[0];

   addComment( album.getId() ); //Adding a comment to an album
   addLike( album.getId() );    //Adding a like to an album
}

void MyApplication::received(const MAUtil::Vector<CategoryData> &friendlists,
const MAUtil::String &connType,
const MAUtil::String &objetcId)
{
   if( connType == "friendlists" )
   {
       deleteFriendList( friendlists[0].getId() ); // f) deleting a FriendList.
   }
}

void MyApplication::received(const MAUtil::Vector<IdNamePair> &friends,
const MAUtil::String &connType,
const MAUtil::String &objetcId)
{
   if( connType == "friends" )
   {
       for (int i=0; i<friends.Size(); i++) //Posting a link for all the user's friends
       postLinkOnWall( friends[i].getId() );
   }
}

FacebookDemo

This example application demonstrates how to log on to Facebook from your application, how to retrieve information, and how to manage posts (publish and delete posts). This application makes use of the MoSync C++ Facebook Library, C++ NativeUI Library, and C Widget API.

This application only works on Android and iOS (iPhone) devices.

Log on screen in the WebView widgetInformation menu with NativeUI controlsRetrieving a photo album

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When the application starts on a device with an Internet connection, the user is shown the log on screen to Facebook displayed in a WebView widget. After the user logs on to Facebook, the main menu is displayed providing the user with a host of options for working with Facebook.

By navigating through the menus, the user can retrieve information about the Facebook objects he or she owns (albums, activities, check-ins, posts, etc.), and also information about the user’s friends. Tapping on a retrieved Facebook object opens a menu with all the connections and publishing options that the Facebook Graph API provides.

For example, to see all the comments related to an album, the user taps albums > display album > album name > comments. Other options for albums include create album, retrieve photos, like, and unlike.

The user can post a link, a video, a picture, or a status message on their own Wall or on their friend’s Wall or on an event’s Wall. The user can create albums, checkins, events, comments, notes, event responses, and friend lists, delete comments, notes, and friend lists, and like/unlike any Facebook object.

In the code

The application uses MoSync Widget API buttons and menus to collect user input. Each button represent a request to Facebook that will be handled by the Facebook Graph API.

For example, when the activities button is clicked, an "activities" connection request for the current user is sent to the Graph API. The JSON data received from server is parsed and converted into a list of objects containing the name, id, category and date created fields.

When the user clicks a "create object " button, a request to create the object is sent to the Graph API. When the request has been completed, a screen with the ID of the new object is displayed. Similarly, when objects are deleted, the "Object removed" message is displayed.

All objects that a user can "like" and "unlike" according to the Graph API documentation can be liked or unliked in the FacebookDemo application.
The project is divided into folders:
 
The FacebookManager files contain the FacebookManager class. This is the class that is used for communicating with Facebook.

GetPermissions.h contains a GetPermissionsFor template class. This is specialized for each Facebook object type. It is used to fill a Set object with the needed permissions for retrieving connections for a Facebook object type or to publish an object of that type.

The /GraphAPI folder contains the implementation of the Graph API. It is divided into three subfolders:

  • /GetConnections contains functions for requesting and retrieving connections.
  • /GetFacebookObjects contains functions for requesting and retrieving Facebook objects. Its subfolder FacebookObjects contains the implementation of the Facebook objects, according to the Facebook Graph API documentation.
  • /Publish contains all the functionality for publishing to and deleting from Facebook.                                      

The /HTTP folder contains all the low-level communication to the server (sending the request, retrieving the JSON data, and so on).


The /JSON_lib folder contains the MoSync JSON library.

Touch responses

Pressing the BACK button closes the application.

WikiSearchNativeUI

This example application searches Wikipedia, based on user input and selected categories. It makes use of functions in the MoSync Widget API.

Home screen on AndroidSummary screen on AndroidWeb screen on Android

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples .

Behaviour

When this application is started, the HomeScreen is presented. From here the user can search Wikipedia articles, filter by category, and specify the number of results to return. Tapping the Next button initiates the search.

While the application engine computes the result in the background, a progress bar indicates progress. When the results are ready, they are displayed as a list of matching article titles. The user can select which Wikipedia entires to show, or select all of them.

In the next screen a snippet of each of the selected articles is displayed, and tapping on a snippet opens the complete article in a WebView widget.

In the Code

The project is divided into several files:

  • Main.cpp is the application's main entry point.
  • WikiMoblet.cpp contains the moblet that manages the application and handles key events.
  • Util.cpp contains utility values such as user messages, methods for managing widgets and performing operations on strings, and methods to convert the HTML content into unicode.
  • BasicScreen.cpp contains the base screen of the application, the base screen constructors, and the creation of a main top layout that is common for some screens.
  • HomeScreen.cpp is the home screen, where user can add or remove tags, and perform a wiki search based on those tags. It contains list boxes, and vertical and horizontal layouts with widgets for user selection.
  • TitlesScreen.cpp displays the available article titles. Results are shown in a list view, and the user can pick only the desired articles. For each selected title, the corresponding article will be displayed in the next screen.
  • SummaryScreen.cpp contains the user selected article snippets. Results are displayed in a list view, and each snippet is followed by an anchor. When the anchor is clicked, the whole article becomes available in a WebView widget.
  • WebScreen.cpp contains a WebView widget with the user selected article, and buttons for Back and New Search actions.
  • WikiEngine.cpp is the application engine. This class is responsible for sending requests to Wikipedia, and handle the XML responses. It manually parses the XML data that is sent back and forth to the Wiki server. The engine communicates with the UI,and constantly sends back notifications on engine status such as chunk of data received, request finished, or error.
  • .h header files contain the forward declarations for the main .cpp code files.

Touch responses

  • The application is exited by pressing the back button on the home screen. Navigation within the application screens is acomplished with the back button.

On the Home Screen:

  • By checking/unchecking the categories boxes the search is filtered.
  • By moving the slider the search will have limited number of results. The default slider value is 10.
  • By clicking on Next button a new search is initiated for all the selected categories. When request is ready TitlesScreen is displayed.    

On the Titles Screen:

  • The user can press Select All to select all the given results, or pick only the desired articles.
  • By pressing Next the SummaryScreen is displayed which contains article snippets only for the selected titles.

On the Summary Screen:

  • Results are displayed in a list view, and each snippet is followed by an anchor. When the anchor is clicked, the whole article becomes available in the WebScreen.

On the Web Screen:

  • The user can navigate in the web view freely, and he can go Back to previous screen or initiate a New Search that gets him to the first screen.

Fonts

Creating New Fonts

The MAUI user interface components use bitmap fonts in its own format. These files are converted from a specific, but common, format for bitmap fonts. The MoSync download comes supplied with a freeware tool called BMFont for creating bitmap fonts from the fonts installed in Windows. MoSync developers can use BMFont to create their own fonts for use in their applications.

Creating a New Bitmap Font with BMFont

If you look in your MoSync installation folder, normally in C:\MoSync, you’ll find a folder called bin\BMFont. In there is the BMFont application by Andreas Jönsson, a freeware application which can create bitmap fonts. Bitmap fonts differ from standard TrueType fonts normally found on Windows. TrueType fonts describe the shape of each letter as vectors, which means that they can be scaled up and down without losing detail. Bitmap fonts are of a fixed size, and scaled bitmap fonts will appear jagged.

 

When you’re using MAUI, it only supports bitmap fonts, so you need to make sure that the size you create is the size you want. You can put as many fonts as you wish into your application, but each widget will only support one font. You cannot change fonts in the middle of a label for instance. If you want bold or italics in your application, you need to create them as separate fonts.

MAUI fonts only support the ASCII character set, so some characters and languages are not currently supported. They will be replaced in your application with spaces.

To create a new font, start BMFont. You can see on the left all of the available characters, and on the right, the character groups supported by this font. As MAUI fonts only support ASCII characters, then stick with the ‘Basic Latin + Supplement’ option. Other characters will take up space in the font file, and won’t render.

If you’re absolutely sure you won’t need some characters in your application, then leave them out. It will save space.

Clicking on the square next to ‘Basic Latin + Supplement’ will select all the characters in the set.

Note: Some fonts only contain ASCII characters. If this is the case, there aren’t any options on the right. This is very important. Most people’s font problems come from not getting these settings correct.

There are several formats for bitmap fonts, and the MoSync conversion tool (mof) will only convert from one of these.

Configure BMFont's settings as follows:

For BMFont v1.12 and MoSync 2.5+:

 

For BMFont v1.9 and MoSync 2.4 and earlier:

1. Change the Charset option from Unicode to OEM. If necessary, change the option in the dropdown box to ANSI.

2. Unless space is at a real premium, then I would say that having the anti-alias font smoothing is always worth it. There are some examples later. Set it to 1 pixel. This will double the size of the final font, but the quality difference is high.

3. At the bottom right of this dialog box, change the Font descriptor to Binary

4. Change the Textures option to PNG.

When you do this, and click OK, you’ll see the changes to font and size reflected in the main window.

Hints and Tips

1. Fonts on your computer screen look bigger than they do on your phone. Compare the size of your phone’s screen to the emulator. Make fonts bigger than you think you need.

2. Small fonts on the emulator will be unreadable on a real phone. Use them with great care.

3. Many fonts are too narrow. They may look wonderful in Photoshop, but they don’t necessarily translate onto a mobile phone. Clear, bold fonts are best.

4. You can get free fonts easily on the web. Google ‘free fonts’ for lots of sources.

5. When you save the fonts, give them clear descriptions like Arial16ptBold.fnt

When you’re happy with the fonts save it with these settings. This will create at least two files, one with a .fnt extension, and one with the name you’ve given to the font with _0.png. If you’ve got more than one png file, then go back to the font settings screen, and increase the size of the png. I don’t think that there is a limit on the size of this, but the font conversion tool will only work with one image.

Converting the Font on the Command Line

To be able to use the font in the MAUI application, you need to convert from the created format to the internal format used by MoSync. There is a command-line tool for that in the bin sub folder of the MoSync root, usually C:\MoSync\bin.

Open a command line. You can do this by selecting Run from the Windows menu, and entering the command cmd.

The first thing you need to do is get to the correct directory. Assuming you’ve done a default installation of MoSync you need to type:

cd c:\MoSync\bin

and press Enter.

The prompt should now confirm your location. If you type ‘mof’ and press enter, you get a small help screen for using the tool.

The format of the commands for mof are

-fontData The name of the font file you’ve created
-fontImage The name of the font image file you’ve created
-outFile The name you want to give it
-fontColor The color you want the font to be in.

We’ll look at coloured fonts later in the tutorial.

If you’ve entered the command correctly, you’ll get a confirmation message, and your new file will be created in the bin folder. You should copy this to the resources folder of your project.

Using Fonts with Widgets

To use the fonts you’ve created, you need to add references to them in your resources file. If you want to know more about this, then there is a separate tutorial, but here is the code I’ve used.

.res STANDARDFONT
.bin
.include "resources/pretty.mof"

.res YAHOOFONT
.bin
.include "resources/Yahoo.mof"

.res YAHOOSMOOTH
.bin
.include "resources/YahooSmooth.mof"

To use a font in your application, you need to add a reference to MAHeaders.h and MAUI/Font.h in your code, and create a new Font in code.

// Create the fonts
Font* yahooFont = new Font(YAHOOFONT);

You can then reference this when creating labels.

// Create a screen title, using the Yahoo font
Label* titleLabel = new Label(0, 0, 240, 32, layout,
"MoSync!", 0x0000C0, yahooFont);

I’ve created a short example application using free fonts I’ve created in BMFont.

// Create the fonts
Font* standardFont = new Font(STANDARDFONT);
Font* yahooFont = new Font(YAHOOFONT);
Font* yahooSmoothed = new Font(YAHOOSMOOTH);

// Create a layout
Layout* layout = new Layout(0, 0, 240, 320, NULL, 1, 4);
//Create a screen title, using the Yahoo font
Label* titleLabel = new Label(0, 0, 240, 32, layout,
"MoSync!", 0x0000C0, yahooFont);
titleLabel->setAutoSizeY(true);
titleLabel->setHorizontalAlignment(Label::HA_CENTER);

// Smoothed label
Label* smoothLabel = new Label(0, 0, 240, 32, layout,
"MoSync!", 0x0000C0, yahooSmoothed);
smoothLabel->setAutoSizeY(true);
smoothLabel->setHorizontalAlignment(Label::HA_CENTER);

//Create some more text, using the standard font
Label* newsLabel = new Label(0, 0, 240, 32, layout,
"Here is some interesting information. In 1964, a gang of eight pigs held their farmer hostage for 14 hours. When questioned, they said 'oink'.", 0xC00000, standardFont);
newsLabel->setAutoSizeY(true);
newsLabel->setMultiLine(true);
this->setMain(layout);

This is the application running:

For comparison, I’ve blown-up the two title bars. One has the smaller but un-smoothed font, and the second is the larger, anti-aliased font. There is a big difference.

Default Fonts

If you have one font, or at least one font you use the most in an application, then you don’t need to specify it whenever you create a label. You can use the Engine object in MAUI to specify the default font.

Engine& eng = Engine::getSingleton();
eng.setDefaultFont(standardFont);

You can then create labels without specifying a font, and it will use the one you set up here.

The label constructor is different though if you don’t specify a font. You can’t pass the label text without supplying a background colour and a font, so if you want to use the default font, you need to call the setCaption(text) method.

 

// Create some more text, using the default font
Label* newsLabel = new Label(0, 0, 240, 32, layout);
newsLabel->setCaption("Here is some interesting information. 
    In 1964, a gang of eight pigs held their farmer hostage for 
    14 hours. When questioned, they said 'oink'.");
newsLabel->setBackgroundColor(0xc00000);
newsLabel->setAutoSizeY(true);
newsLabel->setMultiLine(true);

This will produce exactly the same result as the previous code.

Coloured Fonts

Finally a note on coloured fonts. By default, the mof tool will produce the font in white. However you can produce fonts in whatever colour you want, but in only one colour at a time. You can’t have fonts with coloured borders for instance. You’ll need to produce a mof file for every colour of every font you want to use.

You can only use one font per label as well, so you can’t change colour in the middle of a label, or move to a bold font.

To create a font in a different colour, you need to add –fontColor <colour in hex> at the end of the mof command. For instance:

mof –fontData YahooSmooth.fnt –fontImage YahooSmooth_00.png –outFile 
       YahooSmoothRed.mof –fontColor 0C0000

Note: Font colours are specified like web colours, with a byte for their red, green and blue values. You don’t need to put anything to specify that this is a hex number though, so ‘0c0000’ is right but ‘0xC00000’ won’t work.

Coloured fonts still anti-alias well.

DeviceFonts

This example application demonstrates how to count, load, and manage device fonts for text drawing using maDrawText and maDrawTextW and the Device Fonts API.

Note: This example only works on Android and iOS devices at this time.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When this application is started, it will present a list of the fonts that are installed in the device and the user can access. For devices that include more fonts than the screen can fit, the user is able to scroll through them.

The first column shows the font name, the second show how the font is printed using maDrawText and the third using maDrawTextW. Some fonts might be limited in the way that they can be drawn on the screen using these two functions.

Touch responses

  • The user can scroll through the font list by dragging his finger vertically along the screen
  • The application is exited by pressing the back button.

DeviceFontsNativeUI

This example application demonstrates how to count, load, and manage a device fonts for your NativeUI applications.

  
On IOSOn Android


Note
: This example only works on Android and iOS devices at this time.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When this application is started, it will present a list of the fonts that are installed in the device and the user can access, using a ListView widget. For devices that include more fonts than the screen can fit, the user is able to scroll through them.

In the Code

The project consists of a single file called main.cpp.

Touch Responses

  • The user can scroll through the font list by dragging his finger vertically along the screen
  • The application is exited by pressing the back button.

 

Working with Fonts

The MAUI::Font class draws text using bitmap fonts defined in a mof-file. In this guide we show how to convert a bitmap font to a mof-file, and how to add the file as a resource to a MoSync project. We then create a MAUI::Font instance and draw some text with it.

The BMFont and mof.exe Tools

We will be using two tools in this tutorial:

  • BMFont is a bit map font generator that can render any existing truetype font as a bitmap font. This program is bundled with the MoSync SDK for Windows and can be found in the /bin/BMFont folder along with its documentation. (If you are using the MoSync SDK for OS X you will need to find your own font tool for creating a bitmap font, or use one of the bitmap fonts that are readily available for that platform.)

  • mof.exe is a command line tool that converts bitmap font files to mof format required by MAUI::Font. It can be found in the /bin folder.

Font Settings

First, create a new folder for the project called /fonttest in the MoSync /projects directory.

Now open the BMFont tool and configure its settings as follows:

For BMFont v1.12 and MoSync 2.5+:

 

For BMFont v1.9 and MoSync 2.4 and earlier:

Note that:

  • Charset should be OEM: ANSI (not Unicode)
  • Font descriptor should be Binary
  • Textures should be png

Building the Font

Select all the characters that you want to include in your font. (If you want all characters, use the Edit > (Un)Select All Chars command.)

Select Options > Visualize to see what your bitmap font will look like:



Next, select Options > Save bitmap font as and save your font to our /fonttest folder as a Bitmap font (*.fnt) file:

Converting the Font

Convert the font using the MAUI font generator (mof.exe) from the Windows command line:

The MAUI font generator takes the following parameters:

  • -fontData the bitmap font file to convert (.fnt)
  • -fontImage the font texture file (.png)
  • -outFile the output file (.mof)
  • -fontColor the font colour in hexadecimal. The default colour is white.

Import the newly created .mof file into your project.

Using the Font

The resulting mof-file can be added to your MoSync project as a binary resource and passed to the constructor of MAUI::Font to create a runtime instance of the font.

To draw text using the font, use one of the methods drawString or drawBoundedString. The drawBoundedString method features linebreaking and is therefore slower, so it is recommended to use drawString when possible. We'll show this by making a simple example.

Start Eclipse and create a new project called fonttest. Make it a Moblet project. Also, in the project's build settings, add MAUI.lib to Additional Libraries.

Begin by adding a new resource file called res.lst with the contents:

.res R_FONT
.bin
.include "myfont.mof"

In the source file, add the following inclusions: 

#include <MAUtil/Moblet>
#include <MAUI/Font.h>
#include "MAHeaders.h"

Font.h. is required for bitmap font handling. MAHeaders.h, which will be generated from the compiled resource file, contains the handles to the resources in the resource file.

In the code we will begin by adding an instance of MAUI::Font as a member to the Moblet. We will also make sure it is initialized with a handle to our font resource.

using namespace MAUtil;
class MyMoblet : public Moblet {
private:
    MAUI::Font font;
public:
    MyMoblet() : font(R_FONT) {
    }

Finally we use the font to draw a text whenever the user presses a key.

    void keyPressEvent(int keyCode) {
        font.drawString("Hello world!", 2, 2);
        maUpdateScreen();
    }
    void keyReleaseEvent(int keyCode) {
        // todo: handle key releases
    }
};
extern "C" int MAMain() {
    Moblet::run(new MyMoblet());
    return 0;
};

Working With the Device Fonts API

The font system in MoSync uses a set of functions that create and manage font handles (that behave like any other MAHandle object), which can then be used either in the context of maDrawTextW or NativeUI as needed by the application (Note: MAUI does not support this font system).  

About the Device Fonts API

Note: The Device Fonts API is currently only implemented in the MoSync Android and iOS runtimes.
 
The system works around the concept of font handles. You can obtain a font handle in two ways:

  • By using maFontLoadDefault that gives you a limited range of standard fonts (Sans Serif, Serif and Monospace), and allows you to specify the style, or
  • By using maFontLoadWithName (along with maFontGetCount and maFontGetName ) which gives you acces to the entire range of fonts installed in the device.

Once you acquire a font handle, you can use it either with:

  • maDrawText (and maDrawTextW) by passing it to the maFontSetCurrent function. Any subsequent calls to maDrawText will use that font.
  • NativeUI by passing it as the parameter for the fontHandle property of various widgets that can present text on the screen.

You can have multiple font handles loaded at any time. A font handle can be used by multiple widgets and maFontSetCurrent at the same time. If for some reason you find yourself not in need of a font handle any more, you can delete it using maFontDelete. Never attempt to delete a font that is being used by a NativeUI widget or maFontSetCurrent.

Both maDrawText and NativeUI widgets will work even if a font handle is not specified, in which case they will use a default system font.

A Note on Size and Color

Color is handled by the systems that use the font objects, and as such they are not part of this API. NativeUI Widgets have a font color property that is set independently of the font object. maDrawText uses the color set by maSetColor.

Size is set at font creation, and is part of the font object. However, currently NativeUI objects have a "set font size" property. The behavior when using font handles in conduction with the font size property is undefined. Users are advised to only set the font size during the creation of the font handle if they plan to use fonts other than the default one in their applications.

Constants

The folowing constants are defined in the Device Font API:

  • FONT_TYPE_SANS_SERIF
  • FONT_TYPE_SERIF
  • FONT_TYPE_MONOSPACE
  • FONT_STYLE_NORMAL
  • FONT_STYLE_BOLD
  • FONT_STYLE_ITALIC

Note that you can use both bold and italic styles by using FONT_STYLE_BOLD|FONT_STYLE_ITALIC.

Syscalls

  • maFontLoadDefault loads a font by specifying the type, style, and size, from a predetermined list of standard fonts.
  • maFontGetCount gets a count of the available fonts installed on the device.
  • maFontGetName gets the post-script name of a font that is installed in the device, by providing it's index.
  • maFontLoadWithName loads a font by providing it's post-script name, and size.
  • maFontSetCurrent sets the font of maDrawText and maDrawTextW, and returns the previously used font handle.
  • maFontDelete deletes a font.

Using the Device Font API

Using different fonts and font weights is an easy way to bring attention to the important information in your application. A very simple thing you could do is call maFontLoadDefault three times to create a normal, a bold, and an italic version of the FONT_TYPE_SANS_SERIF font, then use these fonts with maFontSetCurrent (or NativeUI widgets) as they are needed.

Or suppose your application allows the user to enter and edit blocks of text, and you want to allow the users to be able to make use of all the fonts available on the device. You can code a NativeUI widget that lists all the installed fonts, and allows the user to select one. To do this you could first call maGetCount to get the total number of fonts, and then fill the list by subsequently calling maFontGetName for all indices. When the user selects a font from that list, your code calls maFontLoadWithName, with that font's name as a parameter, and passes the handle to the EditBox widget.

As a third example, suppose you want to use Comic-Sans in your application, even though it's the worst font ever seen other that for comics. that font is not available on all devices, so to make sure that every one of your users has to suffer that travesty of typography, you can bundle it with your application, and when the time comes to unleash it upon the unsuspecting eyes of your users, you call maLoadFontWithName("ComicSans",12), and load the handle to your widgets.

Result Codes

  • RES_FONT_OK indicates that the call to a font syscall was successful.
  • RES_FONT_INVALID_HANDLE indicates that an invalid font handle was passed.
  • RES_FONT_INDEX_OUT_OF_BOUNDS indicates that the index was off the list size.
  • RES_FONT_NO_TYPE_STYLE_COMBINATION indicates that a default font with that combination does not exist.
  • RES_FONT_NAME_NONEXISTENT indicates that there is no font with that name on the device.
  • RES_FONT_LIST_NOT_INITIALIZED indicates that maFontGetCount() was not called first.
  • RES_FONT_INSUFFICIENT_BUFFER indicates that the buffer is not big enough to store the font name.
  • RES_FONT_INVALID_SIZE indicates that an invalid size was passed.
  • RES_FONT_DELETE_DENIED indicates that the font cannot be deleted because it is in use. maFontDelete(handle) fails with this return code.

Graphics, Drawing, OpenGL

Draw Targets, Clipping

MoSync graphics operations are always performed to a "draw target". By default, this draw target is the screen, but you can create images that can be drawn to, using maCreateDrawableImage().

These mutable images behave exactly like any other images, and can be used with maDrawImage() or maDrawImageRegion() - the only difference is that they can't be transparent. Also, all drawing operations respect the "clip rectangle", which is a rectangular portion of the screen that is affected by drawing operations. By default, the clipping rectangle is the whole screen.

Draw Targets

To create an image you can draw to, you must have a placeholder resource to use. Either you can put one in the resource file for your application (see tutorial about resources), or you can create one dynamically at run time. We will demostrate how to create one dynamically:

  MAHandle myPlaceholder = maCreatePlaceholder(); 

Then, to create the drawable image:

  maCreateDrawableImage(myPlaceholder, 32, 32); 

This will have created a 32x32 pixels drawable image. Now, to set the draw target to be this image:

  maSetDrawTarget(myPlaceholder); 

This function returns the previously set target, and it might be a good idea to save that information. However, if you know that the previous target was the screen, you can just use the constant HANDLE_SCREEN to set it back:

  maSetDrawTarget(HANDLE_SCREEN); 

Clipping

Clipping allows you to limit the part of the screen that can be drawn to. This might be useful when implementing graphical user interfaces, games or anything involving window-like behavior. Clipping rectangles are per draw target - that is, the screen and every drawable image has one. Consequently, you must take care to set the appropriate draw target using setDrawTarget() before specifying its clipping rectangle:

 maSetDrawTarget(myPlaceholder); 
maSetClipRect(0,0,16,16);

To retrieve the current clip rect:

 MARect myRect; 
maGetClipRect(&myRect);

An Example

#include <MAUtil/Moblet.h>
#include <MAUtil/String.h>
#include <mastdlib.h>
using namespace MAUtil;
#define BORDER 40
class MyMoblet : public Moblet {
public:
MyMoblet() {
MAExtent scrSize = maGetScrSize();
int width = EXTENT_X(scrSize);
int height = EXTENT_Y(scrSize);

MAHandle myPlaceholder = maCreatePlaceholder();
maCreateDrawableImage(myPlaceholder, 32, 32);
maSetDrawTarget(myPlaceholder);
// Fill with blue
maSetColor(0x0000ff);
maFillRect(0, 0, 32, 32);
maSetColor(0xff00);
// draw green triangle
maLine(0,10,10,0);
maLine(0,0,0,10);
maLine(0,0,10,0);

maSetDrawTarget(HANDLE_SCREEN);
maSetClipRect(BORDER,BORDER,width-BORDER*2, height-BORDER*2);
// Draw purple background
maSetColor(0xff00ff);
maFillRect(0,0, 200, 200);
for(int i = 0; i < 10; i++) {
maDrawImage(myPlaceholder, i*40, i*40);
}
// Draw white text
maSetColor(0xffffff);
maDrawText(0,BORDER, "This text is also clipped");
maUpdateScreen();
}
};
extern "C" int MAMain() {
Moblet::run(new MyMoblet());
return 0;
};

Framebuffer API

The Framebuffer API provides an API for overriding the backbuffer of MoSync. In order to use this API, the program should use maFrameBufferGetInfo to fill a structure containing such information as the native pixel format and screensize of the underlying system. These parameters should then in turn be used to allocate memory for a backbuffer.

The custom backbuffer is enabled by using the maFrameBufferInit syscall. By using maFrameBufferClose, the overridden backbuffer is disabled. In order to make a program pixel format and screensize independent, actual drawing should be done using the information retrieved by using the syscall maFrameBufferGetInfo. Increased performance may be gained by using different code paths for different bit depths and creating optimized innerloops.

Note: Whenever a EVENT_TYPE_SCREEN_CHANGED event is sent from the runtime (i.e., when the size of the screen has changed), the native framebuffer gets invalidated and needs to be reinitialized. Just use maFrameBufferClose and maFrameBufferInit in sequence and everything should be just fine.

Example

To illustrate how the API is used we're going to do a simple example. We will implement a function to plot a pixel using a desired color. Be aware that, usually when doing a program using direct pixel access, the backbuffer is traversed linearly, hence a pixel plotting routine may be very inefficent.

First we include the standard MoSync API header file and create a forward reference to the plotPixel function.

#include <ma.h>
void plotPixel(MAFrameBufferInfo *fbi, byte *backbuffer, int x, int y, int color);

Then we begin creating our MAMain. The framebuffer is initialized as previously explained. Information is retrieved by filling an instance of a MAFrameBufferInfo structure, called info with information and then allocate info.sizeInBytes amount of memory for our backbuffer. The backbuffer is then enabled using maFrameBufferInit.

int MAMain()
{
	MAFrameBufferInfo info;
	maFrameBufferGetInfo(&info);
	byte *backbuffer = new byte[info.sizeInBytes];
	maFrameBufferInit(backbuffer);

We then begin doing actual drawing. First we clear the screen to black using memset. As black is usually mapped to 0 on all pixel formats we may do this using a memset on the backbuffer.

memset(backbuffer, 0, info.sizeInBytes);            

Then we plot the actual pixel using our plotPixel routine. We must pass our MAFrameBufferInfo struct, a pointer to the backbuffer, the x and y coordinate and the color on the form 0x00rrggbb (same form as used by maSetColor).

plotPixel(&info, backbuffer, info.width/2, info.height/2, 0xffff00);    

Then we copy the backbuffer to the screen by calling maUpdateScreen and wait for a keypress or close event.

maUpdateScreen();
	while(true) 
	{
		MAEvent e;
	    while(maGetEvent(&e))
	    {
	    	if(e.type == EVENT_TYPE_CLOSE ||
	        e.type == EVENT_TYPE_KEY_PRESSED) 
	    	{
	    		maExit(0);
	        }
	    }
	}

Finally we disable our custom backbuffer by calling the syscall maFrameBufferClose. This isn't necessary as it is done automatically when the system is shut down, but is done just to illustrate how it is used.

maFrameBufferClose();
}

Now we're going to explain how to implement the plotPixel function. A pixel may be stored using different amount of bytes for different bit depths. For instance 16-bit color normally uses 2 bytes per pixel and 24/32-bit color uses 4 bytes per pixel. Each color component is represented by a set of bits of these bytes. For 32-bit color modes each color component is usually a byte or 8 bits, but for 16-bit color modes a common setup is 5 bits for red, 6 bits for green and 5 bits for blue. This information is located in the MAFrameBufferInfo struct. The plotPixel function begins by extracting the color components from the color argument and converts them to the right bit depth.

void plotPixel(MAFrameBufferInfo *fbi, byte *backbuffer, int x, int y, int color) 
{
	int r = ((color>>16)&0xff)>>(8-fbi->redBits);
	int g = ((color>>8)&0xff)>>(8-fbi->greenBits);
	int b = ((color)&0xff)>>(8-fbi->blueBits);

Then we make a switch-statement that depends on the amount of bytes per pixel, so that we may work with the backbuffer using the the correct data type. First we need to find the scan line by multiplying the y-coordinate with the pitch of the screen (the actual width of the backbuffer). Then we type cast the reference pointing at the beginning of the scan line to the correct data type and index it using the x-coordinate to find the correct pixel. Finally encode the color components using the framebuffer information.

switch(fbi->bytesPerPixel) 
	{
		case 2: ((short*)&backbuffer[y*fbi->pitch])[x] = 
				(r<<fbi->redShift)|(g<<fbi->greenShift)|(b<<fbi->blueShift);
		case 4: ((int*)&backbuffer[y*fbi->pitch])[x] = 
				(r<<fbi->redShift)|(g<<fbi->greenShift)|(b<<fbi->blueShift);
	}
}

GLMoblet_OpenGLES1

This example application demonstrates how to use OpenGL ES 1.1 to control a device's graphics hardware.


This example is included in the MoSync SDK installation in the /OpenGLES/examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When this application is started you will see a red rotating square.

In the Code

The code of the project is collected in one single file. It shows how to use the GLMoblet in order to create an OpenGL ES 1.1 based application. It has a set of helper functions for setting up the initial state: gluPerspective, initGL, and setViewport.

The init function first calls setViewport to setup the projection matrix. Next it calls initGL to setup the OpenGL context state.

The draw function first clear the screen to a dark red color and continues by setting up the modelview matrix, by calling the OpenGL functions for manipulating the matrix. Next it sets the color for the square and send the pointer to the structure describing the vertices of the square. Next it fills the structure with the vertex coordinates and draws the primitive as a triangle strip.

Touch Responses

The only button that the program responds to is the back key. If it is pressed the application exits.

GLMoblet_OpenGLES2

This example application demonstrates how to use OpenGL ES 2.0 to control a device's graphics hardware.



This example is included in the MoSync SDK installation in the /OpenGLES/examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When this application is started you will see a trippy pattern moving on the screen.

In the Code

The code of the project is collected in one single file. It shows how to use the GLMoblet in order to create an OpenGL ES 2.0 based application. It has a set of helper functions for loading shaders (‘loadShader’) and checking for errors (‘checkGLError’).

The ‘init’ function first calls initGL which loads the vertex and fragment shader, sets up the default state and maps variables to the uniforms of the shader. In the ‘draw’ function, the uniforms that contain the current time in seconds and the multiplicative inverse for the screen resolution, are updated and a triangle fan that covers the entire screen are drawn.

The shader is set to be used so the shader program will run for each pixel on the screen. The shader generates a pattern by calculating colors as a function of time, x and y coordinates.

Touch Responses

  • Tap buttons to capture image, switch screens, zoom in/out.

 

Graphics Primitives

MoSync provides a number of primitive graphics operations at the syscall level.

 int maSetColor (int rgb); 
 void maSetClipRect (int left, int top, int width, int height); 
 void maGetClipRect (MARect *out); 
 void maPlot (int posX, int posY); 
 void maLine (int startX, int startY, int endX, int endY); 
 void maFillRect (int left, int top, int width, int height); 
 void maFillTriangleStrip (const MAPoint2d *points, int count); 
 void maFillTriangleFan (const MAPoint2d *points, int count); 
 void maDrawText (int left, int top, const char *str); 

The maSetColor() function sets a "current color" which will be used by any subsequent primitive operations. The maSetClipRect() and maGetClipRect() functions are used to either define or query a rectangular area of the screen that is affected by graphics operations. The rest of the functions each draw a primitive. All functions deal with coordinates in one way or another, and these coordinates are pixels coordinates with the origin located at the top-left corner of the screen. The positive x axis goes from left to right, while the positive y axis goes from top to bottom:

Plotting Pixels

The maPlot() function is used to plot individual pixels at given coordinates using the current color:

// Set the current color to a bright green.
maSetColor(0x00ff00); 

// plot a pixel at 10, 10.
maPlot(10, 10); 

// Update the screen.
maUpdateScreen(); 

Drawing lines

The maLine() function is used to draw a line between two pairs of coordinates using the current color:

// set the current color to a bright green.
maSetColor(0x00ff00); 

// draw a line from (10,10) to (50, 50).
maLine(10, 10, 50, 50); 

// update the screen.
maUpdateScreen(); 

Filling Rectangles

The maFillRect() function is used to fill a rectangle between with a given top-left corner, width and height.

// set the current color to a bright green.
maSetColor(0x00ff00); 

// draw a rectangle between (10, 10) and (60, 60).
maFillRect(10, 10, 50, 50);

// update the screen.
maUpdateScreen(); 

Drawing Triangle Strips

Triangle strips are an efficient way of drawing several triangles that share common vertices, since those vertices do not need to be repeated. The following image illustrates such a triangle strip, consisting of triangles 1,2,3,4 and vertices A,B,C,D,E,F:


The maFillTriangleStrip() function is used to fill a rectangle between {10, 10} and {40, 40} using a triangle strip.

// Set the current color to a bright green.
maSetColor(0x00ff00); 
MAPoint2d points[] = 
{ 
    {10, 10}, {40, 10}, {10, 40}, {40, 40} 
};

maFillTriangleStrip(points, 4);

// Update the screen.
maUpdateScreen();

Drawing Triangle Fans

The maFillTriangleFan() function can be used to draw a polygon:

A triangle fan is a primitive in computer graphics that saves on storage and processing time. It describes a set of connected triangles that share one central vertex. If N is the number of triangles in the fan, the number of vertices describing it is N+2. This is a considerable improvement over the 3N vertices that are necessary to describe the triangles separately:

MAPoint2d points[8]; 
points[0].x = points[0].y = 40; 
points[7].x = points[7].y = 40; 
for(int i = 0; i < 7; i++) 
{ 
    float t = 2*M_PI*i / 6.0; 
    points[i+1].x = 40 + 30*cos(t); 
    points[i+1].y = 40 + 30*sin(t); 
} 
maSetColor(0xff00ff); 
maFillTriangleFan(points, 8); 
maUpdateScreen(); 

Drawing Text

The maDrawText() function is used to render text using a device-dependent font. It is useful for things like debug printouts or simplier UI:s. For consistent, flexible fonts, have a look at the MAUtil::Font class - see Font tools.

// set the current color to a bright green.
maSetColor(0x00ff00); 
maDrawText(10, 10, "Hello world!");

// update the screen.
maUpdateScreen(); 

Graphun

Graphun is a 3d graph visualizer built with MoSync. It evaluates a custom expression for every point on a grid and displays the result. This advanced application makes extensive use of NativeUI, and it uses an OpenGL view to display the graphs.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples. The source code is browsable on our Github repository.

This example is also available on various app stores and online marketplaces.

Behaviour

This application is intended for touchscreen devices. It uses NativeUI and OpenGL so it will work only on iOS (iPhone, etc.) and Android devices at this time.

When the application starts up for the first time it displays the graph for the expression sin(x*10.0*y+time)*0.5. As this expression includes time as a variable, the graphical display will continuously change as time changes. The user can change the expression by touching in the expression field and entering a new expression.

The Help page in the application provides some example expressions that the user can select for demonstration purposes. The Help page also includes advice on using the application, information about predefined constants, and so on.

The grid can be rotated to inspect the result from different angles by swiping the screen. Settings are available through the spanner icon in the lower left corner of the screen. The shading of the grid can be turned on and off, and three resolutions can be selected.

Touch responses

  • Touch edit box - opens the on-screen keyboard through which the user can enter expressions. (Return closes the keyboard.)
  • Spanner icon - opens the settings menu.
  • Swipe on the graph screen - rotates the graph.
  • Back/close button - closes the application.

HelloOpenGLES

HelloOpenGLES is a well-commented example application for beginners. It demonstrates how to use OpenGL for Embedded Systems from your MoSync Application.

Note that this example application makes use of NativeUI and therefore only runs on iOS (iPhone) and Android devices. It will not run on MoRE, the MoSync Emulator.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When the application runs, the screen displays a rotating, textured box.

Examine the source code of the application to learn how the program works. The extensive code comments highlight various aspects of working with OpenGL, including:

  • Initializing openGL.
  • Setting projection and camera angle.
  • Handling screens and widgets.
  • Setting texture and rotation parameters.
  • Drawing to the screen.
  • Setting the screen refresh rate.

User Interaction

  • Back key - exits the program.

API Reference

 

 

MAUtil FrameBuffer

MAUtil::FrameBuffer emulates a 4 or 8 bits per pixel, pallettized display using the FrameBuffer API of MoSync. To initialize the library you provide the FrameBuffer_init with information about the size, colour format and orientation of the screen.

You may then set entries in the palette using either FrameBuffer_setPalette or FrameBuffer_setPaletteEntry.

The FrameBuffer_copyRect colour converts, scales and rotates a selected area of the the source, treated as image pixels with the information provided when initializing the library, into the native framebuffer. The function will automatically scale the backbuffer to fit the screen using pixel doubling/halving. When doing pixel halving, the colours of the pixels will be averaged to get a better reproduction of the actual image. It is a very handy API for porting old desktop applications that used the framebuffer of the VGA/EGA display.

There's two important things to keep in mind:

  • FrameBuffer_init allocates memory for the native backbuffer (resolution of the screen x the colour depth), which requires the application to be given a larger heap size (If you get a panic saying 'malloc failed', this is most likely the case).
  • Whenever a EVENT_TYPE_SCREEN_CHANGED event is sent from the runtime, the native framebuffer gets invalidated, so is the MAUtil::FrameBuffer library, and needs to be reinitialized. Just use FrameBuffer_Close and FrameBuffer_init in sequence and everything should be just fine.

Here is a simple example (written in C) showing how it is used:

#include <ma.h>
#include <MAUtil/FrameBuffer.h>

// We allocate a 160*120 pixels large 8 bits per pixel backbuffer.
unsigned char backbuffer[160*120];

int MAMain()
{
	int i;

	// Initialize the FrameBuffer library with no special flags and the default orientation.
	FrameBuffer_init(160, 120, ORIENTATION_0, 0);

	// Create a greyscale palette.
	for(i = 0; i < 256; i++)
	{
		FrameBuffer_setPaletteEntry(i, i, i, i, 0);
	}

	while(1)
	{
		MAEvent event;

		// Retrieve the time and use it to generate a color index for
		// all pixels in the backbuffer.
		int time = maGetMilliSecondCount()/10;
		memset(backbuffer, time, 160*120);

		while(maGetEvent(&event))
		{
			switch(event.type)
			{
				// Make sure we catch the close event and if we do, exit.
				case EVENT_TYPE_CLOSE:
					maExit(0);
					break;
				// Whenever the screen area or orientation of the screen changes,
				// we reinitialize the FrameBuffer library.
				case EVENT_TYPE_SCREEN_CHANGED:
					FrameBuffer_close();
					FrameBuffer_init(160, 120, ORIENTATION_0, 0);
					break;
			}
		}

	// Copy, convert, scale and rotate the backbuffer to the native backbuffer.
	FrameBuffer_copyRect(0, 0, 160, 120, 0, 0, backbuffer, 160);

	// Update the screen (copy the native backbuffer to the actual screen).
	maUpdateScreen();
	}
	return 0;
}

MAUtil Graphics

The graphics API provides a set of functions equivalent to those found in the standard MoSync API, with the addition of a transformation stack. This provides a useful mechanism for keeping transformation state without explicitly managing it on the application side. It is useful for any sort of hierarchical rendering, ranging from UI to advanced graphical effects.

The Transformation Matrix Stack

The transformation matrix stack is manipulated through a number of functions:

  • Gfx_clearMatrix() resets the current translation to (0,0). The initial value of the current translation is undefined, so this function must be called at least once before any subsequent drawing calls are made to ensure a predictable result.
  • Gfx_pushMatrix() pushed the current translation onto the stack, so that it can later be restored using Gfx_popMatrix().
  • Gfx_translate() modifies the current translation.
  • Gfx_getTranslation() returns the current translation.

The Clipping Stack

In many cases, especially when dealing with hierarchical UIs, the ability to save and restore multiple levels of clipping rectangles is desirable. The clipping stack provides such a mechanism. It is manipulated using two simple functions:

  • Gfx_pushRect() pushes the current clipping rectangle onto the stack.
  • Gfx_popRect() restores the previous clipping rectangle.

Drawing Operations

The following functions are provided for drawing operations, each mapping directly to a corrresponding syscall:

  • Gfx_plot() maps to maPlot()
  • Gfx_line() maps to maLine()
  • Gfx_fillRect() maps to maFillRect()
  • Gfx_drawText() maps to maDrawText()
  • Gfx_drawImage() maps to maDrawImage()
  • Gfx_drawRGB() maps to maDrawRGB()
  • Gfx_drawImageRegion() maps to maDrawImageRegion()

Here's a short example program showcasing the use of the transformation stack. For an example of how to use the clipstack, the source code of MAUI::Engine is recommended.

#include <MAUtil/Moblet.h>
#include <MAUtil/Graphics.h>
#include <madmath.h>
#include <mastdlib.h>

using namespace MAUtil;

/**
* Draws a square centered at the origin with respect to the current
* translation with the given size.
*/
void drawBox(int size)
{
	Gfx_fillRect(-size/2, -size/2, size, size);
}

/**
* Draws a blue square at centered at the origin with respect to the current
* translation. Then, recursively, draws four boxes of half the size
* centered at each corner of the original one, and with half the color
* intensity. Then, recurse until the size drops to 2.
*/
void artwork(int size, int color = 0xff)
{
	if(size <= 2) return;

	// Save transformation state.
	Gfx_pushMatrix();
	maSetColor(color);

	// Draw main box
	drawBox(size);

	// Draw the four corner boxes
	Gfx_pushMatrix();
	Gfx_translate(-size/2, -size/2);
	artwork(size/2, color >> 1);
	Gfx_popMatrix();
	Gfx_pushMatrix();
	Gfx_translate(size/2, -size/2);
	artwork(size/2, color >> 1);
	Gfx_popMatrix();
	Gfx_pushMatrix();
	Gfx_translate(size/2, size/2);
	artwork(size/2, color >> 1);
	Gfx_popMatrix();
	Gfx_pushMatrix();
	Gfx_translate(-size/2, size/2);
	artwork(size/2, color >> 1);
	Gfx_popMatrix();

	// Restore transformation state.
	Gfx_popMatrix();
}


class MyMoblet : public Moblet
{
private:
	int exitStartX;
	int exitStartY;
public:
	MyMoblet()
	{
		// Aquire screen dimensions
		MAExtent scrSize = maGetScrSize();
		int width  = EXTENT_X(scrSize);
		int height = EXTENT_Y(scrSize);

		// Clear initial state of transformation matrix.
		Gfx_clearMatrix();

		// ...and save it
		Gfx_pushMatrix();

		// Let's start at the center of the screen...
		Gfx_translate(width/2, height/2);

		// ...and move around in a circle.
		for(int i = 0; i < 8; i++)
		{
			float theta = 2*M_PI*i/8;

			// We need to save and restore the transformation matrixc
			// for each instance of "artwork" we draw along the circle
			// path.
			Gfx_pushMatrix();

			// Position our pseudo-fractals along a circle that
			// scales to fill the screen.
			int scale = height > width ? width : height;
			Gfx_translate((scale/2)*cos(theta), (scale/2)*sin(theta));
			artwork(scale/6);
			Gfx_popMatrix();
		}

		// Restore original transformation matrix
		Gfx_popMatrix();

		// Draw exit "button"
		maSetColor(0xffffff);
		const char* msg = "Exit";
		MAExtent textSize = maGetTextSize(msg);
		exitStartX = width - EXTENT_X(textSize) - 5;
		exitStartY = height - EXTENT_Y(textSize) - 5;
		maDrawText(exitStartX, exitStartY, msg);

		maUpdateScreen();
	}

	void keyPressEvent(int keyCode)
	{
		// Exit using right softkey
		if(keyCode == MAK_SOFTRIGHT)
			maExit(0);
	}

	void keyReleaseEvent(int keyCode)
	{
		// todo: handle key releases
	}

	/*
	 * Simple exit handling for touch/stylus devices, enabling
	 * users to simply press the softkey label saying "Exit"
	 */
	void pointerPressEvent(MAPoint2d p)
	{
		if(p.x > exitStartX && p.y > exitStartY)
		maExit(0);
	}
};

/**
 * Entry point of the program. The MAMain function
 * needs to be declared as extern "C".
 */
extern "C" int MAMain()
{
	Moblet::run(new MyMoblet());
	return 0;
};

  

MoSketch

This application demonstrates simple key input, graphics output, and the permanent storage option.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

This example application requires a device with a keypad and arrow keys or a joystick.

A grey screen welcomes you. Use the keypad's arrow keys or joystick to move the pen and draw a black line.

When you exit the program by pressing the "0" key, the sketch is automatically saved to a permanent store. If a has been saved properly, it will be loaded automatically next time you start the program.

Key Presses

  • Left, right, up, down arrow keys or joystick — moves the pen
  • 9 — clears the screen
  • 2 — raises or lowers the pen
  • 0 or right-softkey — saves your drawing and exits the program

 

Working with OpenGL ES

OpenGL ES 2.0 is the latest standard for accessing the graphics hardware on embedded devices (in our case Android and iOS). It is heavily based on the concept of shader programs, written using the C-like language GLSL.

At several stages in the graphics pipe-line, shader programs are executed in order to provide maximum freedom to the user. The online API documentation is a good start to understand how to use the API:

http://www.khronos.org/opengles/sdk/docs/man/

OpenGL ES 2.0 is not supported by older devices, so when creating an application that utilizes OpenGL ES one should choose the api version wisely. If you want to be able to support a broad range of devices and do less work, choose OpenGL ES 1.1 or if you want to have all the bells and whistles, sacrificing device compatibility, choose OpenGL ES 2.0.

It is of course possible to create an application that supports both, but as the APIs are incompatible, almost entirely different renderers have to be created.

GLMoblet

The GLMoblet is a special version of the Moblet that sets up a fullscreen OpenGL ES view for you and handles all the events that need to be taken care of. It makes it much easier to write pure OpenGL ES applications.

To make use of it you need to inherit the class, implement a constructor, and implement two functions: init and draw.

The GLMoblet constructor takes one parameter: a mask of the GL API versions your application supports. First it will try to load the most recent version specified in the mask. If that fails it will try to load the next most recent version, etc.

If no API can be loaded the program will exit with a panic message stating that OpenGL ES is not available on the platform it is running on. As OpenGL ES 1.1 and 2.0 are incompatible, a program that wants to support both versions needs different code paths for different api versions. Which version was loaded can be queried in runtime using the function getApiVersion.

The init function is invoked when GL has been initialized and the context has been bound. Here you can do any initialization for the program, like uploading textures to the graphics hardware. It is important that you don’t use any GL calls before the init function has been invoked as the GL context may not have been bound.

Directly after the init function has been invoked the GLMoblet begins to call the function draw at regular intervals (the interval is specified using the function setPreferredFramesPerSecond). The GLMoblet automatically stops drawing if the focus is lost and starts drawing when the focus is gained.

When optimizing an application for performance you can use the function getActualFramesPerSecond to measure the amount of frames per second which should be as close to the preferred amount of frames per second, set using setPreferredFramesPerSecond, as possible. The default preferred amount of frames per second is 50.

 

3DLines

3dLines demonstrates basic graphics and key input handling.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When started the mobile/emulator screen appears as graphics labeled "Start Screen" where the logo wireframe rotates

Key Presses

  • Fire or tap the screen — toggles the graphic from the Start Screen to the Fill Screen with its rotating icons.
  • 0 or right-softkey — closes the program
  • All other keys have no effect

AdvGraphics

AdvGraphics tests advanced graphics on the phone and keyboard input.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When started the mobile/emulator screen appears as graphics labeled "Start Screen" where the logo wireframe rotates

Key Presses

  • Fire or tap the screen — a coloured ball appear each time you press the Fire button or tap the screen. The balls also move in a helical path. After pressing Fire or tapping the screen a few times a helix should be visible.
  • 0 or right softkey — closes the program.
  • All other keys — no effect

 

Stylus

Stylus is a simple drawing program, where you draw with your mouse/stylus.

The code for this example can be found in the example folder of the MoSync SDK, typically c:/MoSync/examples on your local installation.

Installation

  1. Choose "Import" from the file menu in MoSync
  2. Select "Old MoSync project" to load into workspace
  3. Locate the project from the examples folder (don't select 'make copy')
  4. Choose your preferred target Device
  5. Press 'Run Project'

Expected Output/Behaviour

The screen is black with a color bar on the top end. Use the pointer to draw, select color by clicking desired color bar.

At the time of this writing, touch support is very limited. In the 2.3 release of MoSync full support can be expected.

Key Presses

Any key exits the application

Location, GPS, Maps

Determining Location

In this tutorial we take a look at how to detect a device's current geographical location and how to use location data in your application. We also describe some basic strategies for updating location data on a regular basis.

Ways of Determining Location

There are two common ways of determining the current location of the user's device:

  • GPS: If the device is equipped with a GPS module that communicates with a satellite network, you can query the GPS module from your MoSync application. 
  • Cell ID: You can also use the mobile phone network to get a good approximation of position by finding out the nearest network cell, and looking up its position in a database.

Most modern smartphones have GPS built in, and many older ones can receive GPS data from a Bluetooth-enabled GPS unit.  GPS will give you a more precise position, but it can be slow and unreliable in built-up areas.  Cell ID works faster, but requires access to a suitable database.

There are also hybrid solutions which work with combinations of network triangulation, cell IDs and GPS, which fall under the collective name of Assisted GPS (A-GPS).

Some platforms are capable of determining location through other means.  In some countries, Android based phones can determine their location by identifying near-by wireless networks.

Accessing GPS Data

The MoSync SDK provides an API for interfacing with GPS modules.  You don't need to do anything special, just start requesting GPS data for your application.  

int res = maLocationStart();      
maLocationStop();

These start and stop methods tell the GPS module to start collecting location data, and deliver it to your application. 

The function maLocationStart() returns an int.  If it returns 1 (MA_LPS_AVAILABLE), then everything is OK.  If it returns a value higher than 1, then it can't start because of network conditions, and you should try later.  If it returns -1, then location data is not available (probably because the handset doesn't have a GPS unit.)

Note: The MoSync IDE's MoRE emulator does not provide GPS data. It will always return -1. 

To receive data, your Moblet needs to respond to system events. You can do this by putting the following code in your Moblet class. This code listens to all events and responds to those that are location messages.

void customEvent(const MAEvent& event)
{
  //Check to see if this is a location event
  if(event.type == EVENT_TYPE_LOCATION)
  {
    MALocation& loc = *(MALocation*)event.data;
    informListeners(loc);
  }
}

In this example, we have a private method for passing the location onto the parts of our application that need it.

The location event returns an MALocation object. This object contains the device's latitude and longitude, as well as data about the accuracy of the reading. GPS locations are not definite. They come with a margin of error You'll have to create a strategy for handling the data depending how confident you are with it.  More on this later.

You can simply display the latitude and longitude data directly on the screen. Or you can pass it to a map with the current location centered on the screen.

Example Application Using GPS

In the following example of a location-handling application, we have a small framework. We have created interfaces for location providers (implemented by Moblet) and for location listeners (our screens which need location data).

Location.h

#ifndef LOCATION_H_
#define LOCATION_H_
 
#include <maapi.h>
#include <MAUtil/Vector.h>
 
using namespace MAUtil;
 
//Provides an interface between Moblet and screens which need location data
 
class ILocationListener
{
  public:
    virtual void locationReceived(MALocation& location);
};
 
class ILocation
{
  public:
    virtual void addLocationListener(ILocationListener* l);
    virtual void removeLocationListener(ILocationListener* l);
 
    virtual void informListeners(MALocation& location);
 
};
 
#endif /* LOCATION_H_ */

Screens can then subscribe to the Moblet class to register for location data. When a location update comes in, the screen is informed.

MapScreen.h

We can use the ILocationListener interface with a map screen to automatically update the map with the user's location.

#ifndef _MAPSCREEN_H_
#define _MAPSCREEN_H_

#include <MAUI/Screen.h>
#include <MAUI/Layout.h>
#include <MAUI/ListBox.h>
#include <MAP/MapWidget.h>
#include <MAP/LonLat.h>
#include <MAP/MapSourceMgr.h>
#include "Location.h"

using namespace MAUI;
using namespace MAP;

class MapScreen : public Screen, public ILocationListener
{
    public:
        MapScreen();
        ~MapScreen();

        void show();
        void locationReceived(MALocation& location);

    private:
        MapWidget* map;
        bool locRec;
};

#endif    //_MAPSCREEN_H_

MapScreen.cpp

#include "MapScreen.h"

MapScreen::MapScreen()
{
  map = NULL;
}

MapScreen::~MapScreen()
{

}

void MapScreen::show()
{
  if(map == NULL)
  {
    MAExtent screenSize = maGetScrSize();
    int scrWidth = EXTENT_X(screenSize);
    int scrHeight = EXTENT_Y(screenSize);

    map = new MapWidget(0, 0, scrWidth, scrHeight, NULL);
    map->setMapSourceKind(MapSourceKind_OpenStreetMap);
    LonLat home;
    home.lat = 51.49663;
    home.lon = 0.00;
    map->setCenterPosition(home);
    map->setMagnification(14);

    this->setMain(map);

  }

  Screen::show();
}

void MapScreen::locationReceived(MALocation& location)
{
  if(map != NULL)
  {
    LonLat myLoc;
    myLoc.lat = location.lat;
    myLoc.lon = location.lon;
    map->setCenterPosition(myLoc);
  }
}

Location Update Strategies

Once you've started to get location data, particularly with GPS, you face a new problem. The GPS system reports to your application everytime it gets a fix - approximately once every second. Each reading is slightly different. If you are trying to plot your position on the map, and you automatically pass the raw location data to the map, the pointer or map center will constantly move around, even if the device is still.

To overcome this, you need an update strategy that works something like this:

  • If the new reading is more accurate (i.e. the accuracy value is lower than the current best reading) use it.
  • If the new reading is outside of a circle created using the reading as the centre point and the accuracy as the radius, assume that the device has moved, and use the new reading.
  • If the new reading is inside the circle of error, and the reading is less accurate, ignore the data and wait for another fix from the satellites.

This keeps the user informed about where they are, without putting a lot of doubt into their minds when it keeps shifting very slightly.

HelloMap

HelloMap is a simple application that displays a slippy (i.e. panning) map.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When started, the application displays a map centered on Stockholm (longitude/latitude 18.07, 59.33) with a medium level of magnification. The initial map source is OpenStreetMap. This application uses the Moblet framework.

Touchscreen responses

Use finger/pointer/stylus.

  • Swipe up, down, left, right — pans the map.
  • Double-tap - closes the application.

Keypad presses

  • Up, down, left, and right keys — pans the map.
  • 2 key — toggles the map source.
  • 3 key — zooms in the map.
  • 1 key — zooms out the map.
  • 0, back button, right-softkey — closes the application.

 

MapDemo

The MapDemo application displays slippy maps and makes use of the MAUI library to provide a simple menu system. The menu system enables the user to switch between map sources and is easy to extend with your own code.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When started, the application displays a map centered on Stockholm (longitude/latitude 18.07, 59.33) with a medium level of magnification. The initial map source is OpenStreetMap. Clicking the left-softkey brings up the menu system. This application uses the Moblet framework.

Key Presses

  • Up, down, left, and right keys or drag on the screen with the pointer or stylus — pan the map.
  • 1 key — zooms out the map
  • 2 key - changes the map source
  • 3 key — zooms in the map
  • Left softkey opens the menu system
  • Right softkey or double click/tap — closes the application.
  • Other keys have no effect

 

Using the MAP Library

The Mosync MAP library is a standard library that provides a MapWidget for displaying geographical maps, sometimes called slippy or panning maps, consisting of tiles provided by a map source. This guide introduces the basics of the MAP library and shows you how to create map applications from scratch.

Important: MoSync 2.5 includes an improved MAP Library. If you want to compile in MoSync 2.5 an existing application that uses the 2.4 MAP Library you will need to update that application. See Migrating to the 2.5 MAP Library

Introduction

At the heart of the MAP Library is MapWidget. You can use it as is, or extend it to overlay information on top of the map. The library contains map classes for using one or more tile-based map servers such as OpenStreetMap, CloudMade, and Google Maps.

Latitude and longitude co-ordinates can be converted to meters and pixels (both global and device). Connection to a location-aware sensor is performed by the client, and is outside of the scope of the library itself.

The primary public map source is OpenStreetMap (openstreetmap.org). There is a working hook into Google Static Maps; however use of Google Static Maps requires a license agreement with Google, so this is more of a code sample.

Example Programs

There are two examples programs you can use to study the use of the MAP Library:  

  • HelloMap is a simple Moblet that displays an OpenStreetMap across the entire device screen. The user can use the phone keys to pan the map and to zoom in and out. The core of the HelloMap sample application is in HelloMapScreen.cpp.
  • MapDemo is a more full-blown example that illustrates the use of the map library in an application.

 These examples are written using a memory tracker to allocate and deallocate memory. This makes the code a little more complex than is strictly necessary, but will prove useful if you want to use a memory tracker in your application.

The MAP Library

The Map Library provides classes for downloading, managing and displaying map tiles. The library includes:

  • MapSource is a base class with implementations for OpenStreetMap, Google Static Maps, CloudMade, as well as an extensible API for other map sources.
  • MapWidget is a class for displaying a slippy map in a Mosync Moblet-based application. MapWidget uses class MapViewport to handle panning and zooming of the map.
  • Tile management and caching classes, notably the MapCache class which is a singleton that provides map tiles.
  • Utility and support classes.

Important MAP classes

MapSourceAbstract base class for provider of map tiles
CloudMadeMapSourceMapSource using CloudMade public map tile server
GoogleMapSourceMapSource using Google Static Maps as map tile server
OpenStreetMapSourceMapSource using OpenStreetMap tile server
MapCacheCaches tiles provided by MapSource
MapTileSingle tile from MapSource
LonLatCoordinate class
PixelCoordinateGlobal map pixel coordinate at a specified magnification
MapTileCoordinateDescribes a tile’s position in global grid of tiles at a certain magnification
MapWidgetWidget displaying slippy map
MapViewportHandles panning and zooming of the map
DateTimeUtility class for date
TimespanUtility class for time span
MemoryMgrUtility class for heap resource tracking for debugging
BroadcasterUtility template class for broadcasting to multiple listeners
QueueUtility template class for a simple object queue
DebugPrintfUtility functions for debug output under MSVC compiler

How to use map sources

Include the header files for the map sources you wish to use, for example:

#include <MAP/OpenStreetMapSource.h>
#include <MAP/GoogleMapSource.h>

Generally, it is a good idea to use instance variables in your class to refer to the map sources, for example:

MapSource* mOpenStreetMapSource;
MapSource* mGoogleStreetMapSource;
MapSource* mGoogleAerialMapSource;
MapSource* mGoogleHybridMapSource;

The map library comes with two map sources that have been set up for you, OpenStreetMap and GoogleMaps. Here is how to create and set up map sources:

mOpenStreetMapSource = new OpenStreetMapSource();
mGoogleStreetMapSource = new GoogleMapSource(GoogleMapKind_StreetMap);
mGoogleAerialMapSource = new GoogleMapSource(GoogleMapKind_Aerial);
mGoogleHybridMapSource = new GoogleMapSource(GoogleMapKind_Hybrid);

Set the current map source like this:

mMapWidget->setMapSource(mGoogleAerialMapSource);

Basic example

This is a bare-bones application with touch interaction -- single touch and drag pans the map; a second touch zooms in a fixed step while pressed, and zooms out when released.

#include <MAUtil/Moblet.h>
#include <MAUI/Screen.h>
#include <MAP/MapWidget.h>
#include <MAP/OpenStreetMapSource.h>

using namespace MAUtil;
using namespace MAUI;
using namespace MAP;

class MapMoblet : public Moblet
{
public:
    MapMoblet()
    {
         mMapWidget = new MapWidget(
            0,
            0,
            EXTENT_X(maGetScrSize()),
            EXTENT_Y(maGetScrSize()),
            NULL);

        // Create a MapViewport for the MapWidget.
        // The MapWidget will deallocate the viewport upon destruction.
        mMapWidget->setViewport(new MapViewport());

        mOpenStreetMapSource = new OpenStreetMapSource();
        mMapWidget->setMapSource(mOpenStreetMapSource);

        mMapWidget->getViewport()->setCenterPosition(
            LonLat(18.07, 59.33), // Position for Stockholm.
            true, // Redraw now.
            false // Not called from pointer event.
            );

        // Magnification level (logarithmic scale).
        mMagnification = 11.0;
        setMagnification(mMagnification);

        mScreen = new Screen();
        mScreen->setMain(mMapWidget);
        mScreen->show();
    }

    virtual ~MapMoblet()
    {
        delete mScreen;
        delete mMapWidget;
        delete mOpenStreetMapSource;
    }

    void keyPressEvent(int keyCode, int nativeCode)
    {
        if (MAK_BACK == keyCode || MAK_0 == keyCode)
        {
            close();
        }
    }

    void multitouchPressEvent(MAPoint2d point, int touchId)
    {
        if (0 == touchId)
        {
            beginPanning(point);
        }
        else if (1 == touchId)
        {
            zoomIn();
        }
    }

    void multitouchMoveEvent(MAPoint2d point, int touchId)
    {
        if (0 == touchId)
        {
            updatePanning(point);
        }
    }

    void multitouchReleaseEvent(MAPoint2d point, int touchId)
    {
        if (0 == touchId)
        {
            endPanning();
        }
        else if (1 == touchId)
        {
            zoomOut();
        }
    }

    void beginPanning(MAPoint2d point)
    {
        mMapWidget->getViewport()->beginPanning(point);
    }

    void updatePanning(MAPoint2d point)
    {
        mMapWidget->getViewport()->updatePanning(point);
    }

    void endPanning()
    {
        mMapWidget->getViewport()->endPanning();
    }

    void zoomIn()
    {
        mMagnification += 3.0;
        setMagnification(mMagnification);
    }

    void zoomOut()
    {
        mMagnification -= 3.0;
        setMagnification(mMagnification);
    }

    void setMagnification(double magnification)
    {
        mMapWidget->getViewport()->setMagnification(
            MagnificationType(magnification));
    }

private:
    Screen* mScreen;
    MapWidget* mMapWidget;
    MapSource* mOpenStreetMapSource;
    double mMagnification;
};

extern "C" int MAMain()
{
    // Optionally, for example if you encounter memory problems, you can
    // experiment with the size of the tile cache. Set the cache size like this:
    //MapCache::get()->setCapacity(30);
 
    Moblet* moblet = new MapMoblet();
    Moblet::run(moblet);
    delete moblet;
    MapCache::shutdown();
    return 0;
}

Migrating to the 2.5 MAP Library

To be able to compile them with MoSync SDK 2.5, existing applications that use an older versions of the MAP Library need to be updated, since the new API is not backwards compatible. Here we explain how to update your existing code.
 
The major change in the API is how map sources are represented. A map source is a map provider, like OpenStreetMap or GoogleMaps. The old API used a predefined enumeration of map providers. The new API, by contrast, uses a more flexible model where the application can define map source providers.

Initialize/shutdown


MoSync 2.4 and earlier:

MapMoblet* moblet = new MapMoblet();
Moblet::run(moblet);
delete moblet;

MoSync 2.5:

// Create and run the moblet in the same way as before.
MapMoblet* moblet = new MapMoblet();
Moblet::run(moblet);
delete moblet;

// Free resources allocated by the Map cache.
MapCache::shutdown();

Header files

The header files for the map source has been changed.

MoSync 2.4 and earlier:

// This header file has been removed from the new map library.
#include <MAP/MapSourceMgr.h>

MoSync 2.5:

#include <MAP/MapSource.h>

// Typically you include only the header files for the map sources you
// wish to use, for example:
#include <MAP/OpenStreetMapSource.h>
#include <MAP/GoogleMapSource.h>

Using map sources

The MapSourceKind enumeration is replaced by map source classes.

MoSync 2.4 and earlier:

MapSourceKind mMapSourceKind;

MoSync 2.5:

MapSource* mOpenStreetMapSource;
MapSource* mGoogleStreetMapSource;
MapSource* mGoogleAerialMapSource;
MapSource* mGoogleHybridMapSource;

Create the MapWidget

MoSync 2.4 and earlier:

mMapWidget = new MapWidget(0, 0, width, height, NULL);

MoSync 2.5:

// You need to create a MapViewport for the MapWidget.
// The MapWidget will deallocate the viewport upon destruction.
mMapWidget = new MapWidget(0, 0, width, height, NULL);
mMapWidget->setViewport(new MapViewport());

How to create/set the map source

MoSync 2.4 and earlier:

// In the old API an enumeration constant was used to
// specify the map source (this was inflexible).
mMapWidget->setMapSourceKind(MapSourceKind_OpenStreetMap);

MoSync 2.5:

// In the new API, the application can create the map source,
// which is a more flexible approach. The map library comes
// with two map sources that have been set up for you,
// OpenStreetMap and GoogleMaps. This is how to create and
// set these map sources:
mOpenStreetMapSource = new OpenStreetMapSource();
mGoogleStreetMapSource = new GoogleMapSource(GoogleMapKind_StreetMap);
mGoogleAerialMapSource = new GoogleMapSource(GoogleMapKind_Aerial);
mGoogleHybridMapSource = new GoogleMapSource(GoogleMapKind_Hybrid);

// Set the map source like this:
mMapWidget->setMapSource(mGoogleAerialMapSource);

Location

This application tests the on-board GPS functionality of the phone, via the MoSync Location API.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

Note: This application will only work successfully on a device with activated GPS capabilities. It will run on the MoSync emulator, but no GPS service will be detected.

When the application starts, instrcutions are displayed. Tap the screen (or press the fire button of left softkey) to start the GPS location detection. A "Start" message and return code will be displayed: if GPS is available the return code 1 will be shown. If the device does not have GPS capabilities (including the MoSync MoRE emulator), or the GPS function is not activated on the device, the return code -1 is displayed.

While location detection is active, the following data is shown, periodically refreshed:

  • The qualification state of the received data (in this example 4 = fully qualified)
  • The latitude and longitude (in this example 59.33, 18.07)
  • The horizontal and vertical accuracy in metres (in this example 200m and 50m)
  • The time taken to retrieve the location data

Key Presses

  • Fire or left-softkey or tapping the screen — starts and stops GPS location detection.
  • 0 or right-softkey — exits the program

 

Memory, Heap, Stack

MAStx

Tests the console, heap management and memory allocation on the device.


This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

The welcome screen (purple) should appear along with some text and a popup saying 'malloc failed'.

Without quoting all the text, it suffices to say it starts with 'Let's do some math...' and ends with 'Expect a panic'.

Key Presses

None of the keys should operate, only OK on the popup button.

PIM, Address Books, Contacts

PIM Access and Control

Our PIM API module is still in the early stage of development, but we have already implemented many vital elements of PIM management, including low-level functions for accessing contact lists. Here we take a quick look at this new interface.

PIM (personal information management) is about accessing the information about the user. PIM data is organized into lists of items, and each item has fields. The device's address book is a typical PIM list, as well as its appointments lists, todo lists, and so on.

Currently we only have functions for working with contact lists. Event lists will be added soon.

We also have a working example application, PIMExample, that demonstrates many aspects of working with the PIM module.

Lists, items, fields, values, and attributes

Each PIM list holds multiple items; for example, the contact list holds multiple contacts.  Each item in the list can have multiple fields of different types, and each field can have multiple values of the same type identified by an index.

Some field values have attributes attached. That makes it possible to define their purpose (home, mobile, work, husband, custom) and to set them as "primary" or "default" in case of multiple-value fields. The PIM structure currently supported by our PIM module looks like this:

Contact List

  • Contact Item
    • Field address
    • Field birthday
    • Field class
    • Field email
    • Field formatted address
    • Field formatted name
    • Field name
    • Field nickname
    • Field note
    • Field organization
    • Field photo
    • Field photo url
    • Field public key
    • Field public key string
    • Field revision
    • Field phone
    • Field title
    • Field UID
    • Field URL
    • Field IM
    • Field relation
    • Field organization info

It is important to note that not all fields are available on all platforms. If a field is not supported on running platform and you try to access it, you will get an error. For details of each field, its type and value, the operations it supports, the possible attributes that can be set on its values, and the platforms on which field is available, see the MoSync API Reference online or in the IDE.

Using the MoSync PIM module

For a list of all functions (syscalls) in the PIM module, see the MoSync API Reference.

Working with lists and items

To open a PIM list, use the function maPimListOpen. If a list of the selected type is available, you will get back a handle to it that you can use in subsequent calls.

MAHandle mContactsListHandle = maPimListOpen(MA_PIM_CONTACTS);

To open the items on the list, use maPimListNext. You will get back a handle to the next item on the list or 0 if there are no more items.

MAHandle pimItemHandle = maPimListNext(mContactsListHandle);

You can create new item in a list using maPimItemCreate.

MAHandle newContactHandle = maPimItemCreate(mContactsListHandle);

The newly created item is empty. The function maPimItemRemove removes an item from the list.

int resultCode = maPimItemRemove(mContactsListHandle, newContactHandle);
if (resultCode < 0)
{
    printf("Remove Item Error: %d", resultCode);
}

Important! To prevent memory leaks, always call maPimItemClose to close the item as soon as you are finished with it before opening the next item. This function also commits changes made by maPimItemAddValue, maPimItemRemoveValue, and maPimItemSetValue to permanent storage. It should also be used after maPimItemCreate.

int resultCode = maPimItemClose(pimItemHandle);
if (resultCode < 0)
{
    printf("Close Item Error: %d", resultCode);
}

To close a list when you have finished with it, use maPimListClose. (This function does not close the list's items, but it does invalidate them, so maPimItemClose will now be the only function you can safely use on items.)

int resultCode = maPimListClose(mContactsListHandle );
if (resultCode < 0)
{
    printf("Close List Error: %d", resultCode);
}

Working with fields and attributes

To count the number of fields an item has, use the function maPimItemCount.

int countFields = maPimItemCount(pimItemHandle);
if (countFields >= 0)
{
    printf("The item has %d fields", countFields);
}
else
{
    printf("Count Item Error: %d", countFields);
}

To count the number of values that exist for the field for the item, use maPimItemFieldCount.

int countValues = maPimItemFieldCount(mItemHandle, MA_PIM_FIELD_CONTACT_ADDR);
if (countValues >= 0)
{
    printf("The field has %d values", countValues);
}
else
{
    printf("Count Item Field Error: %d", countValues);
}

Each field of the item has a type. To get the field's type (Address, Birthday, Name, etc.), use maPimItemGetField passing it the the position of the field and the item handle that was returned by maPimListNext.

for (int index = 0; index < countFields; i++)
{
    int type = maPimItemGetField(pimItemHandle, index);
    if (type > 0)
    {
        printf("Field %d type: %d", index, type);
    }
    else
    {
        printf("Get Field %d Error: %d", index, type);
    }
} 

You can use the maPimFieldType function to get the data type (integer, boolean, string, etc.) of the field. The value returned will be one of the MA_PIM_TYPE_* constants (date, boolean, string array, etc.).

int type = maPimItemGetField(pimItemHandle, 0);
if (type >0)
{ 
	int dataType = maPimFieldType(mContactsListHandle, type);
	if (dataType > 0)
	{
		printf("Field %d data type: %d", type, dataType);
	}
	else
	{
		printf("Get Field Type %d Error: %d", type, dataType);
	}
} 

To get the attribute for a value in a field use maPimItemGetAttributes.

for (int index = 0; index < countValues; i++)
{
	int attribute = maPimItemGetAttributes(pimItemHandle, MA_PIM_FIELD_CONTACT_ADDR, index);
	if (attribute > 0)
	{
		if ((attribute & MA_PIM_ATTRPREFERRED) != 0)
		{
			printf("Primary attribute");
		}
		attribute &= (MA_PIM_ATTRPREFERRED - 1);
		printf("Field %d attribute: %d", index, attribute);
	}
	else
	{
		printf("Get Field Attribute %d Error: %d", index, attribute);
	}
}

You can retrieve the label for a value in a field, if the value has a custom label, using the maPimItemGetLabel function. To set a custom label for a value in a field use the function maPimItemSetLabel. The field must of course be writable.

if (countValues > 0)
{
	int attribute = maPimItemGetAttributes(pimItemHandle, MA_PIM_FIELD_CONTACT_ADDR, 0);
	if (attribute > 0)
	{
		attribute &= (MA_PIM_ATTRPREFERRED - 1);
		if (attribute == MA_PIM_ATTR_ADDR_CUSTOM)
		{
			MA_PIM_ARGS labelArgs;
			labelArgs.item = pimItemHandle;
			labelArgs.field = MA_PIM_ATTR_ADDR_CUSTOM;
			char* buf = NULL;
			int labelSize = 512;
			do
			{
				labelArgs.bufSize = labelSize;
				if (buf != NULL)
				{
					delete[] buf;
				}
				buf = new char[labelArgs.bufSize];
				labelArgs.buf = buf;
				resultCode = maPimItemGetLabel(&labelArgs, 0);
			} while (resultCode > labelArgs.bufSize);
			if (resultCode > 0)
			{
				printf("Label: %S", (wchar*) buf);
			}
			else
			{
				printf("Get Field Label %d Error: %d", 0, resultCode);
			}
		}
	}
	else
	{
		printf("Get Field Attribute %d Error: %d", 0, attribute);
	}
}

Getting and setting values

To read the value of a field and copy it to the argument buffer (args.buf ), call maPimItemGetValue. You will need to specify the index of the value ( 0 <= index < maPimItemFieldCount() ). The argument buffer should look like this:

struct MA_PIM_ARGS {
	MAHandle item; // Opened by maPimListNext().
	int field;     // One of the MA_PIM_FIELD constants.
	MAAddress buf; // The buffer address where a value is stored.
	int bufSize;   // The size of the buffer, in bytes.
}

If the buffer created is too small, the syscall will return the miminum needed size for your buffer

MA_PIM_ARGS args;
args.field = MA_PIM_FIELD_CONTACT_NAME;

// Get value from name field at position 0.
int resultCode = maPimItemGetValue(&mArgs, 0);

if (resultCode < 0)
{
	printf("Get Field %d Value Error %d", 0, resultCode);
}

To add a new value to a field, use maPimItemAddValue; to remove a value from a field, use maPimItemRemoveValue; to change a value in a field and the attributes of the value, use maPimItemSetValue passing an args.buf.

For each of these functions the changes are not actually written to permanent storage until the item is closed with maPimItemClose -- if the program exits before then, the changes we be lost.

Panics on Android and iOS

Usually a MoSync panic will be thrown if the position is invalid, or a field/index combination doesn't exist, and so on. On Android and iOS (iPhone) you can disable panics using the function maSyscallPanicsDisable so that you'll get error codes instead.

Turn panics back on with maSyscallPanicsEnable.

 

PIMExample

This example application demonstrates how to add, modify and remove a new contact from an address book and how to read contacts from address book.



This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When this application is started a new contact will be added to a contacts list. All available contact fields will be filled with values.

At the next step the first address value from the new created contact will be modified. After that the new contact will be removed from the list. At this point user has two options:

  1. Print the next contact from the list.
  2. Close the contact list and start again the whole process(add a new contact, modify address field etc).

In the Code

The project is divided into several files. A brief description of the each file's content is as follows:

  1. Main.cpp: Application's main entry point.
  2. AppMoblet.h and AppMoblet.cpp: These files contain code for adding, modifying and removing a contact from a contact list.
  3. PIMContact.h and PIMContact.cpp: These files contain code for adding and removing values from a contact, printing a contact on the screen and modifying address field.
  4. Util.h and Util.cpp: These files contain field values for the new contact and methods for handling and printing error codes.

Touch responses

  • The application is exited by pressing the Back button.
  • When printing or adding values to a contact user can tap on the screen to go at the next field.
  • Drag on the screen from left to right to print the next contact.
  • Drag on the screen from right to left to close the contacts list.

 

 

Resources, Binaries, Placeholders

Resource Compilation

This topic describes the different types of resources (such as image, audio files, and binary files) that can be included in your application, and how you make MoSync aware of them and ensure type safety. It also gives a brief overview of how MoSync compiles resources during the build process.

For detailed information about resource compilation, the resource compiler, and resource list files and commands, see the Resource Compiler Reference guide. We also have a general tutorial that looks at various aspects of resource handling and provides oodles of good advice: Adding Resources to a Project.

Resource List Files

MoSync uses resource list files (files with the extension .lst) to identify which external resources are to be included in an application. During the build process, the identified resource files are compiled to a single binary file called resources (both the program and resources are combined to a program.comb file also for easy downloading and execution of a program over the internet). This resource file is loaded on application startup and all resources are prepared for use in MoSync API syscalls.

Creating a Resource List File

Open MoSync, then select File > New > Other. The New Wizard window will open:

Expand the MoSync folder and select MoSync Resource File. The New Resource Wizard window opens.

Select a parent folder, then enter a filename for your resource file:

A resource list file contains declarations of resources and instructions. Each resource is identified by a .res declaration, followed by instructions for the resource compiler. Optionally you can add a symbolic name  for the resource which can be used anywhere in the code to identify the resource. These names will be used to generate a header file with preprocessor definitions, mapping the name to a numeric ID, i.e. a MoSync handle. To make use of the resource, it is convenient to include the MAHeaders.h file and use the symbolic names instead of the actual numbers.

Example:

.res R_MYFACE    // resource declaration, with the symbolic name R_MYFACE
.dispose       // instruction to delete the resource after loading
image "myimages/face.png"    // image to load - relative path may be included with forward slashes

Binary Resources

Using this resource type, any binary data can be stored in a resource for access in MoSync. There's two types of binary resources: standard binary resources (.bin) and unloaded binary resources (.ubin). Standard binary resources will be loaded into memory directly at startup while unloaded binary resources will be kept on permanent storage. Standard binary resources can be read from and written to while unloaded binary resources only can be read from. Note that all changes made to the binary resource will be discarded whenever the application is closed as the memory will be discarded. The use of unloaded binary resources saves memory while sacrificing speed, as it usually is slower to do reads from the permanent storage.
Example:

 .res R_MY_DATA
 .bin 
 .include "my_data.bin"

All paths in a resource file are relative to the MoSync project directory. The filename can include a relative path, but always use forward slashes (for example, files/myaudio/dylan.mp3) or escaped backslashes (\\) in the pathname.

Image Resources

Image resources should be used for loading images from standard image formats. At application start the resource loader will automatically decode images and make them ready to use with the image related syscalls. Different phones support different image formats and sizes. Future MoSync versions will solve this by making it possible to customize what resources to bundle with a specific device or a group of devices in a project. This isn't possible right now, so to be sure that the images can be loaded, you should use png images as we've made sure that png images always can be loaded.
Example:

 .res R_PRETTY_IMAGE
 .image "pretty_image.png" 

Media Resources

The media resource is the resource type used for audio. Similar to the binary resource, there's two types of media resources - standard media resources (.media) and unloaded media resources (.umedia). With standard media resources, the audio will be loaded, and sometimes decoded, to memory. With unloaded media resources the sound will be streamed from disk to the extent possible. Some devices cannot stream from disk and will load the whole sound to memory anyway due to limitations in the underlying platform. The media resource statement is followed by a string of the mime-type and then the path to the file that should be included.

Common supported mime-types are:

  1. audio/mpeg - for mp3 audio
  2. audio/x-wav - for Microsoft's wave audio format (note: this is the only format supported on windows mobile devices for now)
  3. audio/basic - for Sun's au format

Example:

 .res R_MUSIC
 .umedia "audio/mpeg", "songs/song.mp3" 

Placeholders

The Placeholder is a type of resource that may be used to create new resources programatically. Several syscalls such as maCreateDrawableImage and maCreateData must be passed a placeholder. This placeholder can then be used to identify the dynamically created resource. Note that there is also support to dynamically create placeholders using the maCreatePlaceholder syscall. Also, any resource that is destroyed at runtime becomes a placeholder.

Resource Compiler Reference

This topic describes how resources (such as image, audio files, and binary files) are compiled by MoSync, and describes the format of the resource list files which contain the commands that are processed by the resource compiler.

Resources

MoSynC applications have three distinct sections: code, data, and resources. Resources are files external to your main code that are compiled during the build process into a form ready to be delivered to the target devices. Resources can include:

  • Images, and sprites cut from images
  • Media files in various formats
  • Binary files
  • Placeholders
  • Tilemaps and tilesets
  • Enums and script values

For more information about resources and how to create them, see the programming guide Compiling Resources. We also have a general tutorial, Adding Resources to a Project, which demonstrates how to make resources available to your applications and discusses many importants aspects of resource handling.

The Resource Compiler

The resource compiler is part of the MoSync Pipe-Tool. It is automatically invoked during the build process but can also be run from the command line using the command:

pipe-tool -R outfile infile1 [infile2 ...]

for example:

pipe-tool -R output/resources audio.lst images.lst other.lst

This command compiles all the resources referenced in the resource list files audio.lst, images.lst, and other.lst and concatenates them into a binary file called resources, ready for deployment to the device.

As well as the binary file, the resource compiler creates the header file MAheaders.h which should be included in your application program. This header file contains the #defines that symbolically link your application to its resources.

Resource List Files

To determine what resources it needs to compile, the resource compiler reads the resource list files (.lst) files in your project. A resource list file is a standard text file.

A resource listfile contains one or more resource definitions, and each resource definition in the file contains one or more commands for the compiler. Each resource definition begins with the .res command.

Example

/* Example of a resource list (.lst) file. A project can contain more than one .lst file */

/* Define an image resource */
.res image1
.dispose  // delete the image after loading has finished in run time
.image "testimage.png", 99, 99

/* Define a file resource */
.res afile
.bin
.include "testfile.dat"

/* Set a label for the beginning of mysprite */
.label mysprite_start

/* Define a sprite resource */
.res mysprite
.sprite image1, /* XY */ 0, 0, 10, 10, 5, 5

/* Define a binary resource */
.res                       // a resource with no name but which can be referenced as e.g. mysprite+1 
.bin
.string "The buck"         // string
.fill 8, '?'               // fill memory
.string "stops here!!!"    // string with esc codes
.byte 1,2,3,4              // bytes
.half 5,6,7,8              // shorts
.word 9,10,11,12           // ints
.include "randomdata.bin"  // include file

Resource IDs and numbering

Resources are numbered sequentially, starting at 1. The resource ID 0 is reserved.

Additionally, the .res command can include a symbolic identifier for the resource, which can be used to reference a previously loaded resource in a later command (see, for example .sprite in the example above).

The .label command can be used to identify the location of resources for the application in run time.

Comments

Both standard C and C++ comments can be used in the resource list file.

Command Reference

This section describe the various commands that can be included within the definitions in a resource list file.

.res [symbol]

Initializes a new resource, optionally with the symbolic name symbol:

.res myimage
.image "myimage.png"

.image "imagefile"

Declares a resource as an image and loads and stores an image file into the resource. The recommend image format is PNG which is supported by almost all devices. (Any type may be declared but whether or not the device supports it is another matter....) The filename can include a relative path, but always use forward slashes or escaped backslashes in the pathname.

.res picture
.image "myfile\\myimage.png"    

.bin

Declares the resource as a binary. A binary resource is created and has 0 length.

.res myfile
.bin 

.ubin

Declares the resource as an unloaded binary. A binary resource is created and has 0 length. At runtime this resource will not be memory resident, but is accessed from the file system directly.

.media "MimeTypeString", "MediaFile"

Declares the resource as a media file with a particular MIME type. (Any type may be declared but whether or not the device supports it is another matter....) The filename can include a relative path, but always use forward slashes or escaped backslashes in the pathname.

.res tune1
.media "audio/mp3", "myfiles/mysound.mp3"

.umedia "MimeTypeString", "MediaFile"

Declares the resource as a unloaded media file with a particular MIME type. (Any type may be declared but whether or not the device supports it is another matter....) At runtime this resource will not be memory resident, but is accessed from the file system directly. The filename can include a relative path, but always use forward slashes or escaped backslashes in the pathname.

.res tune2
.umedia "audio/mp3", "mysound2.mp3"

.sprite imageRef, start_x, start_y, size_x, size_y[, ref_x, ref_y]

Declares the resource as a sprite object. It requires an image reference for a previously loaded image. The sprite is cut out from this image at start_x and start_y. The size of the sprite is defined by size_x and size_y. Optionally, the sprite's reference point is defined by ref_x and ref_y. If ref_x and ref_y are omitted, the reference point is set as top-left (0,0).

.res myimage    // set symbol for base image
.image "myimage.png"   // load base image, the top-left is always 0,0 

.res mysprite1
.sprite myimage,0,0,10,10    // cut sprite from myimage from 0,0 to 10,10, default reference point = 0,0

.res mysprite2
.sprite myimage,10,0,10,10,5,5    // cut sprite from myimage from 10,0 to 10,10, reference point =  5,5

.tileset "imageFile",tilesize_x,tilesize_y

Declares the resource as a tileset image. The image contains tiles of the specified tilesizes. The filename can include a relative path, but always use forward slashes or escaped backslashes in the pathname.

.res
.tileset "mytiles.png",16,16

.tilemap "tilemap.bin",mapsize_x,mapsize_y

Declares a tilemap. The tilemap binary file contains mapsize_x*mapsize_y 16-bit indices that refer to a tileset. The actual connection between tilemap and tileset is created at runtime. The filename can include a relative path, but always use forward slashes or escaped backslashes in the pathname.

.res
.tilemap "mytilemap.bin",64,64

.dispose

Marks a resource as disposable. When the resource loader has finished loading all resources, it deletes all those resources marked for disposal.

.res image1  
.dispose    // dispose of resource after loading
.image "myimage.png"

.placeholder

Creates an empty resource that can be filled with something at runtime.

.res myspace
.placeholder

.skip

Skips a resource when loading. This can be useful when you have duplicated resource list files and wish to skip some resources in some lists.

myreslist1.lst:

.res image1
.image "myfile/myimage1.png"

.res image2
.image "myfile/myimage2.png" 

myreslist2.lst:

.res image1
.image "myfile/myimage1.png"   

.res image2
.image "myfile/myimage2.png" 
.skip

.label "name"

Creates a marker label resource entry, so the application can search for the resource symbolically at runtime. This allows libraries to find their resources.

.label ui_resource_begin

.enum {variable[=expression][, variable[=expression] ...]}

Defines an enumerated set of variables that can be used in expressions.

.res myenum
.enum
{
a = 0,
b,         // assigns to b the value of the next enum (1)
days = 365,
d         // assigns to d the value of the next enum (366)
}

.string "string"

For binary resources only:  inserts an ASCII string. Note: This string has no null terminator.

.res welcome
.bin
.string "hello"    //  write non-null-terminated string to binary resource

.cstring "string"

For binary resources only: inserts an ASCII null-terminated string.

.res
.bin
.cstring "hello"    //  write null-terminated string to binary resource

.pstring "string"

For binary resources only: inserts a Pascal string.

.res
.bin
.pstring "hello"    // write Pascal string to binary resource

.fill size, filler

For binary resources only:  fills the resource with size bytes of the filler. The data will be inserted at the current data position.

.res
.bin
.fill 8, '?'    // insert '?' 8 times

.byte n1[,n2 ...]

For binary resources only: inserts bytes into the resource. The data is inserted at the current data position.

.res
.bin
.byte 1,2,3,4    //  write bytes 1,2,3,4 to binary resource

.half n1[,n2] ...

For binary resources only: inserts half words (16 bits) into the resource. The data is inserted at the current data position.

.res myfile
.bin
.half 1,2,3,4    // write shorts 1,2,3,4 to the binary resource

.word n1[,n2] ...

For binary resources only: inserts (32-bit) words into the resource. The data is inserted at the current data position.

.res myfile
.bin
.word 1,2,3,4    // write ints 1,2,3,4 to the binary resource

.include "file"

For binary resources only: inserts a binary file into the resource. The data is inserted at the current data position. The filename can include a relative path, but always use forward slashes or escaped backslashes in the pathname.

.res myfile
.bin
.include "bin/test.bin"   // write the contents of test.bin to the binary resource

.index symbol

For binary resources only: add an index so that a single resource can contain sub-indices. A resource with indices will contain an index table, which can be read by the user's program code with the resource index reading functions.

.res
.bin
.index "MySym"

.wideindex

For binary resources only: forces a indexed resource to use 32 bit indices's, so an index table may contain data pointers greater than 64K.

.res
.bin
.wideindex

.set variable[=expression]

Sets a script variable with the value of expression.

.res
.set hello = 1

Adding Resources to a Project

This tutorial provides an introduction to adding resources, such as images and sounds, to a MoSync application. These external files need to be added into the project, so that they can be packaged with your code and deployed to the device.

The Resource Compiler

The resource compiler is part of MoSync's Pipe-Tool (pipe-tool.exe). When you build your application in Eclipse, the resource compiler concatenates all the resources your program needs into a file called "resources" and then it creates an index by which your program can access the resources.

When it runs, the resource compiler looks for resource list files in your project. Resource list files are files that end with the .lst extension. Each resource list file references one or more resources. You don’t have to do anything special to make this work, you just need to create a file with a .lst extension and identify in it what resources you want to be included in the final packages.

You can include any file that will be useful for your program. Typically these will be images (in .png format), sounds (in .mp3 format), and MoSync-specific formats such as .mof font files.

If you are going to be creating data at runtime, or want somewhere to store downloaded data, then the resource list file is also useful for that. You can create a placeholder resource which is going to have a handle, but doesn’t yet contain any data.

There is a full description of all of the types of data you can put into a resource file in the Resource Compiler Reference. In this tutorial we will focus on a few of the common types of resource you are likely to use.

Creating a Resource List File

To add external resources to your app, you need to add a resource list file to your project. A resource list file is a text file with the .lst extension: res.lst or similar would be a good name for it.

Create the resource list file by selecting  New > Other from the MoSync IDE's File menu.

Select MoSync Resource File from the list and click Next. Type a filename ending in ".lst" and click Finish. You’ll see the new file appear in the list of files in Project Explorer. It has an icon similar to a Pac-Man ghost. Double-click on the file to open it.

Tool tip: The Eclipse IDE can help with the syntax of writing resource list files with an Intellisense-style option list as you type. Press period (.) and you’ll see it appear.

MAHeaders.h & MAHandle

The resource compiler creates an index of resources of a special type called MAHandle. Under the hood, this is actually an int, but by giving it a special type name it is easy to see where you can use the resources.

To use a resource you’ve compiled, you need to include a reference to file MAHeaders.h which the resource compiler will create in the root of your project:

#include "MAHeaders.h"

This file is a list of C #define directives, mapping the name you’ve given to your resources to the internal index number of the resource in the compiled resources file. You can use these definitions in your code.

As an example, I’ve created a resource file with a font (which we will see later). My res.lst file contains this:

.res MYFONT
.bin
.include "pretty.mof"

and the MAHeaders.h file that the resource compiler creates contains this:

#define MYFONT 1

This means that if you’ve included MAHeaders.h in my source code, you can use the reference directly.

Font* f = new Font(MYFONT);

If you look at the documentation for Font, then the constructor is

Font(MAHandle handle);

MAHandle means the name of the resource in the resource file (MYFONT), and the MAHeaders.h file maps between the name you have given it and its index position in the resources file. Whenever you see MAHandle in the MoSync documentation, it means the name you’ve given to a resource.

Images

One of the most common types of resource you’ll want to include will be an image. This should generally be a PNG file. Some phones can handle JPEG and GIF images, but PNG has the best overall support, as well as supporting an alpha level for transparency.

To include an image, you need to declare it as a resource in the resource list file in the following way:

.res BACKGROUND_IMAGE
.image "images/BG240.png"

Note: the path to the resource cannot be absolute. Furthermore you must use either forward slashes (/) in the path or escaped backslashes (\\).

When this compiles, we will have a reference to BACKGROUND_IMAGE in MAHeaders.h, and this code will create an image:

#include <MAUI/Image.h>
...
Image* i = new Image(0, 0, 100, 100, NULL, true, true,BACKGROUND_IMAGE);

This will create a new image from BG240.png, and automatically set the size correctly for the image.

Alternatively, you can set the resource on an existing Image widget.

i->setResource(BACKGROUND_IMAGE);

This is useful for animation. More on that later.

Sounds

If you want to include a sound file, then there is a specific directive for this. You need to include it as a .media (or a .umedia) file.

You also need to know the MIME type of the audio format you’ve want. You can look this up at http://www.iana.org/assignments/media-types/ but you’ll probably be using MP3 files. Not every phone will play every format (see Feature/Platform Support).

To add an MP3 file, you’ll add this declaration in a resource list file:

.res MUSIC
.media "audio/mpeg", "music.mp3"

To add a MIDI file, you’ll add:

.res MUSIC
.media "audio/midi", "music.mid"

To use the sound in your application:

maSoundPlay(MUSIC, 0, maGetDataSize(MUSIC));

The second parameter here is the offset position to start playing from, and the last parameter is the number of bytes to play. The function maGetDataSize() returns the size of a resource.

Fonts

Fonts are bitmaps fonts which are specific to the MAUI user interface library. MoSync comes supplied with a couple of examples, as well as the software (the BMFonts tool) to make your own. To use them in your application, you need to include them in a resource list file.

.res MYFONT
.bin
.include "pretty.mof"

You can then use them in your own code as follows

Font* f = new Font(MYFONT);

Other tutorials will look at fonts in much more detail.

Placeholders

Placeholders provide a reference to a resource in MAHeaders, but that resource doesn’t exist yet. You’d use this in situations where you are going to either create a resource at runtime (like an image) or download data into a resource, and you want to use that resource in several places.

For instance, if you were writing an app which downloaded an image, you could create a placeholder so you can reference it when it is available:

.res DOWNLOADEDIMAGE
.placeholder

You can then download the image to the placeholder.

ImageDownloader* dl = new ImageDownloader();
dl->beginDownloading("http://austinspad.net/img/front_batman.png", DOWNLOADEDIMAGE);

This is just an excerpt of the code required for downloading. See the Downloading Data from the Internet tutorial for more information and examples.

When this has completed downloading, you can save the image to local storage:

MAHandle imagestore = maCreateStore("IMAGESTORE", MAS_CREATE_IF_NECESSARY);
maWriteStore(imagestore, DOWNLOADEDIMAGE);
maCloseStore(imagestore, 1);

Even if you don’t save it permanently, you can still access the Image you’ve created by referencing the placeholder name you’ve used. You can use this across your whole application, and not just in the scope of the downloader.

You can also create placeholders at runtime. The difference with these is that the reference is not in MAHeaders.h, so it won’t be available to all the classes in your application.

MAHandle TEMPIMAGE = maCreatePlaceholder();

Sequential Files

Not every file has to have its own resource label. You can create a series of resources which are each accessed from the same root MAHandle. For instance, if you wanted to create an animation in a MAUI::Image widget, you can set up four frames for the animation in the .lst file.

.res ANIMATION
.image "frame1.png"
.res
.image "frame2.png"
.res
.image "frame3.png"
.res ENDANIMATION
.image "frame4.png"

Each image has its own .res declaration, which will create an index number for it, but it doesn’t need to be named. As MAHandle is a synonym for ‘int’, you can perform arithmetic operations on it. This excerpt will update an image every time a downloader informs your listener that more data has been downloaded, and you can update an animation on screen to show your user that it is working.

MAHandle h = ANIMATION;
Image* waitingAnim = new Image(0, 0, 100, 100, NULL);
...
void NotifyProgress(Downloader* dl, int downloadedBytes, int totalBytes)
{
    h = h++;
    if(h > ENDANIMATION)
    h = ANIMATION;
    waitingAnim->setResource(h);
}

When the listener’s notifyProgress method is called, the resource on the MAUI::Image widget is updated.

Loaded and Unloaded Resources

There are two variations on some of the examples we’ve seen. Normally, resource files are loaded into memory, and are read directly from there. With some files, this makes perfect sense. You should only include fonts you want to use, and it is going to need access to it. For other resources, such as media files (MP3) and program data, it may be wasteful to have all of them loaded into memory at the same time. You may have three MP3 files, but you can only play one at a time. To handle this more efficiently, you can mark these as being unloaded with either .ubin (for data files) and .umedia (for MP3 files). These will only then be loaded when required.

You can also load other resources on demand. For instance, you can offer the user a choice of skins or background images for the application. Normally, all these resources will be loaded when the application starts, but you can defer it until a time when it is needed. This will reduce the start up time and the memory consumption. Loading resources on demand is slower overall than loading at start up though. The benefits are that you’re not loading resources you won’t need, and that the loading time is spread.

There is no difference in how you use unloaded binary and media resources.

There is another way to work with unloaded resources which are not .ubin or .umedia. The directive .skip means that this resource will not be loaded when the application starts. This is particularly useful if you’ve got large resources which you may not need. They are not taking up loading time or precious memory if you’re not going to use them.

.res BGIMAGE2
.skip
.image "resources/background2.png"

Once you’ve created them, you can use them as you would any .image resource. There is no additional code required. They do take slightly longer to load than they would at application start up, but the user probably won’t even notice.

Strings and Localisation

One easy way to localise your application is to keep all the system text like buttons and labels held externally to your compiled application. The application can then read in the strings that it wants to use. You can create a different version of the resource file for each language.

Reading in strings from the resource file isn’t necessarily obvious however, and I’ve created a few example of how you can do it.

C++ Strings are very strange if you come from a Java or C# background. They are not as ubiquitous as they are in Java and C#, and are quite frankly, of limited use.

They are not inherent objects, and many method calls do not accept Strings but char* or const char* instead. This means that you are constantly converting between String and char*, which can be verbose and laborious.

The MoSync resource compiler handles strings in three different formats

.string

Does not include a null-terminator at the end of the string

.cstring

Does include the null-terminator

.pstring

A Pascal string, which puts a byte at the start of the string containing the length.

To read the value of the string, it isn’t as simple as

String* myString = new String(RESOURCENAME);

Instead, you have to make a more explicit call out to the resources file. There is an object which can help with this called DataHandler. This will let you read from the handle, and keeps track of its own position.

bool GetString(DataHandler* dh, String& output)
{
    char c;
    output.clear();
    if(!dh->read(&c, 1))
    {
        return false;
    }
    while(c) 
    {
        output.append(&c, 1);
        if(!dh->read(&c, 1)) 
        {
            return false;
        }
    }
    return true;
}

In the first example above, you can see that the method takes a DataHandler and a String as parameters. It uses the DataHandler to read from the resource file one character at a time, and builds the string. 

This on its own should be enough to convince you to always use .pstring whenever possible.

You can avoid a String object and create the string in a faster way using this function.

char* GetString(MAHandle stringResource)
{
    // Get the length of the string data.
    int length = maGetDataSize(stringResource);

    // Allocate space for the string data plus the
    // null termination character.
    char* buffer = new char[length + 1];

    // Read data.
    maReadData(stringResource, buffer, 0, length);

    // Null terminate the string.
    buffer[length] = '\0';

    // Return the string.
    return buffer;
}

This returns a char* to your string, but it doesn’t delete it. You will need to call

delete[] buffer;

when you’ve finished with it. It uses the function maGetDataSize() to determine the length of the string and then reads it in one go. It will add the null terminator on, so this would be suitable with resource string of the .string type.

The next example read Pascal strings into a String object without doing it character by character:

bool GetPascalString(MAHandle stringResource, String& output)
{
    bool success = false;
    output.clear();
    DataHandler* dataHandler = new DataHandler(stringResource);
    byte length;
    if(dataHandler->read(&length, 1))
    {
        char* buffer = new char[length];
        dataHandler ->read(buffer, length);
        output.setData(new StringData<char>(buffer));
        delete[] buffer;
        success = true;
    }
    delete dataHandler ;
    return success;
}

It uses the DataHandler as well, and it creates a new StringData object with the string in. This would be a good way to read .pstrings from the resources file.

Finally, the fourth way doesn’t make use of DataHandler, and will probably be the fastest and sleekest way to read strings:

bool GetPascalStringWithoutDataHandler(MAHandle stringResource, String& output)
{
    output.clear();
    byte length;
    
    // Check that there is at least one byte.
    if(maGetDataSize(stringResource) == 0)
    return false;
    
    // Read byte size.
    maReadData(stringResource, &length, 0, 1);
    char* buffer = new char[length];
    maReadData(stringResource, buffer, 1, length);
    output.setData(new StringData(buffer));
    delete[] buffer;
    return true;
}

It uses the underlying maReadData() function, which is wrapped by DataHandler to read the string from the resource. It also creates a new StringData object, and it deletes its temporary char array.

Binary Data

You can create or load binary data to use in your application. This may be a proprietary data format, or it may be a way of creating screens dynamically using localised resource files. The directive .bin indicates that this should be included as raw binary data.

There are two ways to do this. Firstly, if you’ve already got some binary data you want to use

.res BINARYDEMO
.bin
.include "resources/data.bin"

This will include your data for you to process as you see fit.

A second way is for you to describe it in the resource file itself.

.res BINARYDEMO
.bin
.byte 4 //The number of widgets
.byte 1 //A label widget
.pstring "This is the first item. It is a label"
.byte 2 //A button widget
.pstring "This is the second item. It looks like a button"
.byte 1 //A label widget
.pstring "The next item is an image"
.byte 3 //An Image widget
.word 14422 //The size of the image
.include "resources/test.png"

I’ve created some content I want to put on to the screen, using some of the resource techniques we’ve already looked at. I can write a simple parser to create screen dynamically at runtime.

(Creating screens at runtime is the subject of the tutorial Creating New Screens.)

// Read the first byte. This determines the number of widgets
maReadData(BINARYDEMO, &length, 0, 1);
while(completed < length)
{
    // Read the next byte. This is the type of widget
    maReadData(BINARYDEMO, &widgetType, position, 1);
    position++;
    switch(widgetType)
    {
    case 1: // Label
        position += getPString(BINARYDEMO, *text, position);
        listbox->add(createLabel(text->c_str()));
        break;
    case 2: // Button
        position += getPString(BINARYDEMO, *text, position);
        listbox->add(createButton(text->c_str()));
        break;
    case 3: // Image
        lprintfln("Creating image");

        // Get the length of the image
        maReadData(BINARYDEMO, &imageLen, position, 4);
        position += 4;
        lprintfln("Image is %d bytes", imageLen);

        // Create a temporary placeholder
        MAHandle imagePlaceholder = maCreatePlaceholder();
	
        // Create an image from binary data
        maCreateImageFromData(imagePlaceholder, BINARYDEMO, position, imageLen);

        // Add the image to the listbox
        listbox->add(new Image(0, 0, 100, 100, NULL, true, true, imagePlaceholder));
        position += imageLen;
        lprintfln("Image created");
        break;
    }
    completed++;
}

This is an extract from the accompanying source code. It reads through the binary data we’ve put into the resource file, and creates the appropriate widgets at runtime. You can change the content of you application by just changing the resource file. You can share the resource file with clients and translators without giving away all of your code. This is the screen it produces:

Sensors, Orientation, NFC

The MoSync Sensor API lets your applications communicate with the device's accelerometer and gyroscope, and its magnetic field, orientation, and proximity sensors. (Note that not all devices have hardware sensors. Before you run the application on the device, check which sensors it has using our SensorTest application.). We've a great user guide too.

The MoSync NFC API makes it easy to create applications that read and write standard near field communication tags on the latest Android devices. You can communicate according to the latest standards (NDEF, IsoDep, Mifare Classic/Ultralight), opening a whole range of possibilities for payment, personal identification, and similar close-proximity applications. Try out our NFCExample.

User guides

Reference pages

Example applications

Sensor Control

Modern smartphones have a variety of in-built sensors to detect, for example, movement, orientation, rotation, proximity, and magnetic fields. This tutorial explains how to start and stop sensors, and to receive sensor data in your application through the MoSync Sensor API.

Only the MoSync runtimes for Android and iPhone have sensor support at the moment, and sensor simulation is not yet available in the MoSync Emulator.

Not all devices have hardware sensors. Before you run the application on the device, check which sensors it has using our SensorTest application.

Accelerometer

An accelerometer is a sensor that measures the acceleration forces. On most modern mobile devices there is a 3-way axis device that determines the phone’s physical position. By measuring the static acceleration due to gravity you can find the angle at which device is tilted and by measuring the dynamic acceleration due to movement you can analyze the direction in which the device is moving.

This sensor is available for both the Android and iPhone runtimes.

Starting the Accelerometer

To start the accelerometer, call the MoSync Sensor API function maSensorStart(sensor, interval). The first parameter should be  SENSOR_TYPE_ACCELEROMETER. The second parameter should be the desired update interval in milliseconds. Note that each device has a minimum value for this interval and setting a smaller value could stop the sensor.

The maSensorStart function returns SENSOR_ERROR_NONE if the sensor is started successfully, or a SENSOR_ERROR_* in case of error.

Receiving Event Data

After the sensor starts, your application will receive EVENT_TYPE_SENSOR events. The event contains a struct with the following values:

  • type - SENSOR_TYPE_ACCELEROMETER.
  • values[0] - the acceleration value (in Gs) for the x-axis of the device.
  • values[1] - the acceleration value (in Gs) for the y-axis of the device.
  • values[2] - the acceleration value (in Gs) for the z-axis of the device.

Stopping the Accelerometer

To stop the accelerometer,  call maSensorStop(sensor) with SENSOR_TYPE_ACCELEROMETER as the parameter.

The function returns SENSOR_ERROR_NONE, if the accelerometer stopped successfully, or SENSOR_ERROR_* in case of error.

Example

#include <MAUtil/Moblet.h>
#include <MAUtil/util.h>
#include <maapi.h>
#include <conprint.h>

extern "C" int MAMain()
{
    maSetColor(0xFF0000);
    maUpdateScreen();

    // start the accelerometer
    maSensorStart(SENSOR_TYPE_ACCELEROMETER, 400);

    while(true)
    {
        MAEvent event;

        // listen for events
        while(maGetEvent(&event))
        {
            // check if it’s a sensor type event
            if(EVENT_TYPE_SENSOR == event.type)
            {
                // check if it’s a accelerometer sensor type event
                if (SENSOR_TYPE_ACCELEROMETER == event.sensor.type)
                {
                    char buffer[50];
                    maSetColor(0xFF0000);
                    sprintf(buffer, "x=%f, y=%f, z=%f",
                            event.sensor.values[0],
                            event.sensor.values[1], 
                            event.sensor.values[2]);

                    maSetColor(0);
                    maFillRect(0, 0, 1000, 100);
                    maSetColor(0xFF0000);
                    maDrawText(10, 10, buffer);
                    maUpdateScreen();
                }
            }
        }
    }
};

Gyroscope

A gyroscope is a device for measuring or maintaining orientation. It can measure the rate of rotation around a particular axis. Unlike the accelerometer, the gyroscope is not affected by gravity.

This sensor is available for Android and iPhone runtimes.

Starting the Gyroscope

To start the gyroscope, call maSensorStart(sensor, interval). The first parameter should be  SENSOR_TYPE_GYROSCOPE. The second parameter should be the desired update interval in milliseconds. Note that each device has a minimum value for this interval and setting a smaller value could stop the sensor.

The maSensorStart function returns SENSOR_ERROR_NONE if the sensor has started successfully, or a SENSOR_ERROR_* in case of error.

Receiving Event Data

After the sensor starts, your application will receive EVENT_TYPE_SENSOR events. The event contains a struct with the following values:

  • type - SENSOR_TYPE_GYROSCOPE.
  • values[0] - the x-axis rotation rate in radians per second.
  • values[1] - the y-axis rotation rate in radians per second.
  • values[2] - the z-axis rotation rate in radians per second.

Stopping the Gyroscope

To stop the gyroscope, call maSensorStop(sensor) with SENSOR_TYPE_GYROSCOPE as the parameter.

The function returns SENSOR_ERROR_NONE if the sensor stopped successfully, or SENSOR_ERROR_* in case of error.

Example

#include <MAUtil/Moblet.h>
#include <MAUtil/util.h>
#include <maapi.h>
#include <conprint.h>

extern "C" int MAMain()
{
    maSetColor(0xFF0000);
    maUpdateScreen();

    // start the gyroscope sensor
    maSensorStart(SENSOR_TYPE_GYROSCOPE, 400);

    while (true)
    {
        MAEvent event;
        // listen for events
        while(maGetEvent(&event))
        {
            // check if it’s a sensor type event
            if(EVENT_TYPE_SENSOR == event.type)
            {
                // check if it’s a gyroscope sensor type event
                if (SENSOR_TYPE_GYROSCOPE == event.sensor.type)
                {
                    char buffer[50];
                    maSetColor(0xFF0000);
                    sprintf(buffer, "x=%f, y=%f, z=%f", 
                            event.sensor.values[0],
                            event.sensor.values[1],
                            event.sensor.values[2]);

                    maSetColor(0);
                    maFillRect(0, 0, 1000, 100);
                    maSetColor(0xFF0000);
                    maDrawText(10, 10, buffer);
                    maUpdateScreen();
                 }
            }
        }
    }
};

Magnetometer

A magnetometer is a instrument used to measure the strength or direction of magnetic fields.

This sensor is available for Android and iPhone runtimes.

Starting the Magnetometer

To start the magnetometer, call maSensorStart(sensor, interval). The first parameter should be SENSOR_TYPE_MAGNETIC_FIELD. As the magnetometer does not require an update interval, the second parameter can be any random number.

The function returns SENSOR_ERROR_NONE if the sensor has started successfully, or a SENSOR_ERROR_* in case of error.

Receiving Event Data

After the sensor starts, your application will receive EVENT_TYPE_SENSOR events. The event contains a struct with the following values:

  • type - SENSOR_TYPE_MAGNETIC_FIELD .
  • values[0] - the geomagnetic data for the x-axis.
  • values[1] - the geomagnetic data for the y-axis.
  • values[2] - the geomagnetic data for the z-axis.

The values are in microteslas. You can calculate the magnetic north using those values.

Stopping the Magnetometer

To stop the magnetometer, call maSensorStop(sensor) with SENSOR_TYPE_MAGNETIC_FIELD as the parameter.

The function returns SENSOR_ERROR_NONE if the sensor stopped successfully, or SENSOR_ERROR_* in case of error.

Example

#include <MAUtil/Moblet.h>
#include <MAUtil/util.h>
#include <maapi.h>
#include <conprint.h>
 
extern "C" int MAMain()
{
    maSetColor(0xFF0000);
    maUpdateScreen();
 
    // start the magnetometer sensor
    maSensorStart(SENSOR_TYPE_MAGNETIC_FIELD, 1234);
 
    while (true)
    {
        MAEvent event;
        // listen for events
        while(maGetEvent(&event))
        {
            // check if it's a sensor type event
            if(EVENT_TYPE_SENSOR == event.type)
             {
                // check if it's a magnetic field sensor type event
                if (SENSOR_TYPE_MAGNETIC_FIELD == event.sensor.type)
                {
                    char buffer[50];
                    maSetColor(0xFF0000);
                    sprintf(buffer, "x = %f, y = %f, z = %f",
                            event.sensor.values[0],
                            event.sensor.values[1],
                            event.sensor.values[2]);
                    
                    maSetColor(0);
                    maFillRect(0, 0, 1000, 100);
                    maSetColor(0xFF0000);
                    maDrawText(10, 10, buffer);
                    maUpdateScreen();
                }
            }
        }
    }
};

Proximity Sensor

A proximity sensor detects the presence of nearby objects.

This sensor is available for Android and iPhone runtimes.

Starting the Proximity Sensor

To start the proximity sensor, call maSensorStart(sensor, interval). The first parameter should be SENSOR_TYPE_PROXIMITY. As the proximity sensor does not require an update interval, the second parameter can be any random number.

The function returns SENSOR_ERROR_NONE if the sensor started successfully, or a SENSOR_ERROR_* in case of error.

Receiving Event Data

After the sensor starts, your application will receive EVENT_TYPE_SENSOR events. The event contains a struct with the following values:

  • type - SENSOR_TYPE_PROXIMITY.
  • values[0] - SENSOR_PROXIMITY_VALUE_NEAR or SENSOR_PROXIMITY_VALUE_FAR

Note: the device’s OS establishes the meaning of “near” and “far”.

Stopping the Proximity Sensor

To stop the proximity sensor, call maSensorStop(sensor) with SENSOR_TYPE_PROXIMITY as parameter.

The function returns SENSOR_ERROR_NONE if the sensor stopped successfully, or SENSOR_ERROR_* in case of error.

Example

#include <MAUtil/Moblet.h>
#include <MAUtil/util.h>
#include <maapi.h>
#include <conprint.h>

extern "C" int MAMain()
{
    maSetColor(0xFF0000);
    maUpdateScreen();

    // start the proximity sensor
    maSensorStart(SENSOR_TYPE_PROXIMITY, 123);

    while (true)
    {
        MAEvent event;
        // listen for events
        while(maGetEvent(&event))
        {
            // check if it's a sensor type event
            if(EVENT_TYPE_SENSOR == event.type)
            {
                // check if it's a proximity sensor type event
                if (SENSOR_TYPE_PROXIMITY == event.sensor.type)
                {
                    char buffer[50];
                    maSetColor(0xFF0000);
                    sprintf(buffer, "proximity sensor value = %f",
                            event.sensor.values[0]);

                    maSetColor(0);
                    maFillRect(0, 0, 1000, 100);
                    maSetColor(0xFF0000);
                    maDrawText(10, 10, buffer);
                    maUpdateScreen();
                }
            }
        }
    }
};

Orientation Sensor

The device’s operating system uses the accelerometer to determine the current orientation of the device and can therefore behave as an orientation sensor.

This sensor is available for Android and iPhone runtimes.

Starting the Orientation Sensor

To start the orientation sensor, call maSensorStart(sensor, interval). The first parameter should be  SENSOR_TYPE_ORIENTATION. As the orientation sensor does not require an update interval, the second parameter can be any random number.

The function returns SENSOR_ERROR_NONE if the sensor started successfully, or a SENSOR_ERROR_* in case of error.

Receiving Event Data

After the sensor starts, your application will receive EVENT_TYPE_SENSOR events. The event contains a struct with the following values:

type
- SENSOR_TYPE_ORIENTATION
values[0]
- UIDEVICE_ORIENTATION_UNKNOWN              
- UIDEVICE_ORIENTATION_PORTRAIT                           
- UIDEVICE_ORIENTATION_PORTRAIT_UPSIDE_DOWN                           
- UIDEVICE_ORIENTATION_LANDSCAPE_LEFT                           
- UIDEVICE_ORIENTATION_LANDSCAPE_RIGHT                           
- UIDEVICE_ORIENTATION_FACE_UP                           
- UIDEVICE_ORIENTATION_FACE_DOWN                                                   

Stopping the Orientation Sensor

To stop the orientation sensor, call maSensorStop(sensor) with SENSOR_TYPE_ORIENTATION as the parameter.

The function returns SENSOR_ERROR_NONE if the sensor stopped successfully, or SENSOR_ERROR_* in case of error.

Example

#include <MAUtil/Moblet.h>
#include <MAUtil/util.h>
#include <maapi.h>
#include <conprint.h>

extern "C" int MAMain()
{
    maSetColor(0xFF0000);
    maUpdateScreen();

    // start the orientation sensor
    maSensorStart(SENSOR_TYPE_ORIENTATION, 123);

    while (true)
    {
        MAEvent event;
        // listen for events
        while(maGetEvent(&event))
        {
            // check if it's a sensor type event
            if(EVENT_TYPE_SENSOR == event.type)
            {
                // check if it's a orientation sensor type event
                if (SENSOR_TYPE_ORIENTATION == event.sensor.type)
                {
                    char buffer[50];
                    maSetColor(0xFF0000);
                    sprintf(buffer, "orientation sensor value = %f",
                            event.sensor.values[0]);

                    maSetColor(0);
                    maFillRect(0, 0, 1000, 100);
                    maSetColor(0xFF0000);
                    maDrawText(10, 10, buffer);
                    maUpdateScreen();
                }
            }
        }
    }
};

SensorTest Example Application

Our SensorTest example application implements all of the sensor types supported by the MoSync Sensor API. When it is run on a device, it checks for the presence of each type of sensor and shows you the current event data being received from those that it finds.

AccelerometerOpenGLES

This example application makes use of the MoSync Sensor API and OpenGL API to graphically display the orientation of the device.

Note: This example only works properly on a physical device with an accelerometer. Currently only Android and iOS devices are supported, and no emulators.

This example is included in the MoSync SDK installation in the /OpenGLES/examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When it is started on a suitable device, this application displays a quad (finite plane) tangential to the ground. The rectangle remains stationary regardless of changes to the orientation of the device. Use the Back button to close the application.

In the Code

This example makes use of the Sensor API to collect data from the devices accelerometer, and makes use of the Open GL API to display graphically that data. The 3D rendering is done in a seperate module so if you dare you can try and plug in your own 3D renderer and see how your 3D world looks from different angles using the accelerometer. To do this you just implement the Renderer interface and the two functions init and render. Look at the default renderer implementation called SimpleRenderer to see how it is done.

NFCExample

This example application shows how to use the MoSync NFC API which provides C syscall functions to read and write near field communication tags. This example works on the platforms supported by the MoSync Wormhole JavaScript Library and NFC C++ Library (see Feature/Platform Support).


Main screen (Android Emulator)

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behavior

  • To read information from a tag, hold it close to the device. The lower text box will provide some information about the tag.
  • To write sample information to a tag, click one of the two “Write...” buttons and then hold a tag close to the device.

In the Code

The following technologies are used in this application:

  • The MoSync NFC API to read from and write to NFC tags.
  • HTML5 & Javascript, for the design the main user-interface elements and handling of UI events.
  • The MoSync Bridge Library (bridge.js) to connect to Wormhole Library.
  • MoSync Wormhole Library for bi-directional communication between MoSync C++ code and HTML5/JavaScript pages.

ScreenOrientation

ScreenOrientation is a well-commented example application for beginners. It demonstrates how to detect and respond to changes in screen orientation, and also how to set the orientation mode on platforms that support it. This application is based on the MoSync Moblet framework.

 

 

 

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When the application runs, the screen initially displays the message "Started". Turn the device. The screen will repaint showing the new orientation and dimensions.

Examine the source code of the application to learn how the program works.

Note the use of the maScreenSetOrientation syscall to set the screen orientation mode. For platforms that support it (for example, Android), this sycall enables you to lock the orientation mode to just portrait or landscape mode, or to enable switching between them (dynamic mode).

Key Presses

  • Any key - exits the program.

SensorTest

SensorTest is a simple application that demonstrates how to use the functions of the MoSync Sensor API to control a device's sensors, and to receive current measurements.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

This example application implements all of the sensor types supported by the MoSync Sensor API. When it is run on a device, it checks for the presence of each type of sensor and shows you the current event data being received from those that it finds.

In the Code

The project is divided into 2 files:

  • main.cpp contains the main application logic, checking for sensors, listening for sensor events, and displaying current sensor data.
  • consts.h contains the defines.

Touchscreen and key control

Use the Back button to close the application.

Standard C/C++ Libraries

The MoSync SDK comes with several useful standard C and C++ code libraries to ease code development.

The C++ Standard Template Library (STL) is a feature-packed code library that will be familiar to many programmers thanks to its all-round usefulness to computer science and industry.It provides many powerful algorithms, containers, associative arrays, and iterators to simplify data handling in advanced applications. We have an example application that demonstrates many of the functions of this library: HelloSTL.

The C Newlib Library is particularly suited for mobile devices and embedded systems. It is a conglomeration of several library parts, all under free software licenses. It provides subroutines for handling files, environment variables, processes, output streams, signals, and memory. (Note: MoSync does not implement the signals and locale sections of the library).

MoSync mastdlib Library is our cut down version of the Standard C Library, libc. It contains the basic string conversions and random number generators. Great when your applications need a small footprint.

User guides

Example applications

Reference pages

 

 

C++ Standard Template Library (STL)

The C++ Standard Template Library provides a set of common classes and interfaces that greatly extend the core C++ language. It provides a set of classes such as containers, associative arrays, iterators, functions objects, and strings. It also provides algorithms for counting, sorting,  finding, coping, and replacing elements in a container.

STL is almost entirely written in the form of C++ template classes that provide common programming data structures and functions. These template classes are all members of the std namespace.

 

STL code examples

We provide detailed code examples in the use of STL with MoSync, HelloSTL , that you can download from our GitHub repository. It provides many examples of how to use the containers, algorithms, strings, functors, auto_ptr and other components of STL. 

Iterators

An iterator is an object that can iterate through a range, pointing to the elements in the range. All iterators in STL provide at least the operator++, to iterate forward in the range, and the derreference operator(*), which provides the way to access the object to which the iterator points.

All containers define their own iterators, which have different abilities. There are five types of iterator in STL:

Input iterators

  • Allow only reading elements from a sequence and moving forward, one step, using operator++. The elements are read with operator*. Input iterators also provide operator== and operator!=.

Output iterators

  • Allow only writing elements from a sequence and only moving forward, one step, using operator++.
  • The elements are written with operator*.
  • These iterators also provide operator== and operator!=.

Forward iterators

  • Allow reading and writing elements from a sequence.
  • Only moving forward, one step, using the operator++ is possible.
  • The elements are read and written with operator*.
  • These iterators also provide operator== and operator!=.

Bidirectional iterators

  • Allow reading and writing elements from a sequence.
  • Allow moving forward and backward, one step, using operator++ and operator--.
  • Elements are read and written with operator*.
  • Bidirectional iterators also provide operator== and operator!=.
  • Examples of bidirectional iterator implemented in STL are the ones provided by the containers list, multiset, map, and multimap.

Random access iterators

  • Allow all operations a normal pointer does: add and subtract integral values, move forward and backward, one or more steps, use the operator[ ], subtraction of one iterator from another, and so on.
  • Such iterators overload operator<, operator>, operator==, operator!=.
  • Examples of random access iterators implemented in STL are the ones provided by the containers vector, deque, and for string.

Containers

STL provides sequence and associative containers. For an associative container, the index does not have to be an integer; it can be any type.

bitset

A special container class that is designed to store bits. It is optimized for space allocation: each element occupies only one bit.

Bitset is defined in the <bitset> header.

deque

A container that holds it's elements in multiple blocks of memory and keeps track of them, making it fast on insertions at the end and also at the beginning. It doesn't need to copy and destroy objects when it needs to allocate more memory. Deques invalidate iterators, pointers, and references when the size is changed.

Deques are the right choice:

  • for random access to elements
  • for iterating through it (from beginning to end or backwards)
  • for add or remove an element from the beginning and end
  • if it is important that the amount of memory used decreases as elements are removed from the deque.

Deque is defined in the <deque> header.

list

List is implemented as a doubly linked list of elements. std::list doesn't have random access, it provides only bidirectional iterators. For accessing an element you have to iterate through list, until you reach the element. Compared with vector or deque, list is not efficient for  random access.

List is the right choice for:

  • inserting and removing of elements anywhere in the container
  • moving elements (or block of elements) within the container (or between different containers)
  • iterating through it (backwards or forward).

List doesn’t invalidate an iterator that refers to an element as long as the element is not erased from the container.

map

The map container is an associative container. An entry in the map is a combination of key - value (std::pair). The key is used to identify an element in the map (the index).

  • the key/value pair must be assignable and copyable (the operator= and == must be defined for std::pair<key, value>).
  • the key must be comparable with the sorting criterion (the operator== must be defined for the pair<key, value>).

Map sorts its elements automatically by key ( from lower to higher ). The default comparison criterion is std::less, and it's provided as a default template parameter. Another ordering criterion, can be provided as the third template parameter. It has  to be a class that defines an operator() taking two arguments of the key type and returns a bool. It can be also just a function (with the same prototype as operator()).

std::map allows only unique entries. That means that you can't have in a map two entries with the same key. If you try to insert in map a entry with a certain key, and inside the map exits already an element with that key, the element will be overridden.

Map is the right choice:

  • if we need unique key values
  • element are pairs of key-value.
  • elements are sorted at all times.

Map provides bidirectional iterators.

Map is defined in the <map> header.

multiset

std::multiset sorts automatically it's elements from lower to higher and accepts multiple copies of an object.

Multiset sorts automatically it's elements from lower to higher and it is implemented usually as a binary search tree. The ordering criterion is, by default, std:less. Another one can supplied as the second template parameter. It can be a function, taking two arguments, of the key type and returning a bool or a class defining operator()  with the same prototype.

Multiset is the right choice:

  • if we need multiple elements with equal key.
  • if we need the elements are sorted at all times.
  • if we need to search for elements according to a certain criterion.       

Multiset is defined in the <set> header.

priority_queue

Priority_queue is implemented as a container adaptor. Containers adapters are classes that use an encapsulated container and provide a restricted interface to that container. The underlying container can be a STL container or some other  container type and it has to provide the  following public member functions: back(), push_back(), pop_back().

Priority_queue  is defined so that the first element is always the one with the highest value.

The default comparison criterion used is std::less. We can provide another comparison class, a functor, with it's operator() taking two  arguments, of the same type as the container elements, and returning a bool.

priority_queue is defined in the <queue> header.

queue

queue is implemented as a container adaptor. Containers adapters are classes that use an encapsulated container and provide a restricted interface to that container. The underlying container can be a STL container or some other  container type and it has to provide the  following public member functions:  front(), back(), push_back(), pop_front().

Queue is designed to operate in a FIFO mode (first-in first-out).

Queue is defined in the <queue> header.

set

std::set is an associative container, that stores unique elements. If you try to insert an object that is equivalent to one that is already in the set, the set won't make the insertion.
 
Set sorts automatically it's elements from lower to higher and it is implemented usually as a binary search tree. The ordering criterion can be supplied as the second template parameter and  is by default std::less.
 
The ordering criterion can be a function taking two arguments of the key type and returning a bool. A class defining operator() with the same parameters and returning value as the function can be also used as an ordering criterion.
 
Set is the right choice:

  • if we need unique elements
  • if we need the elements are sorted at all times.
  • if we need to search for elements according to a certain criterion. 

Set is defined in the <set> header.

stack

std::stack is implemented as a container adaptor. Containers adapters are classes that use an encapsulated container and provide a restricted interface to that container.
 
The underlying container can be a STL container or some other  container type and it has to provide the  following public member functions: back(), push_back(), pop_back().
 
Stack is a container that operates as in a LIFO (last in first out) mode.
 
The elements are inserted and extracted only from the end of the container.
 
std::stack is defined in the <stack> header.

vector

Vector keeps its elements in a dynamic array and maintains its storage as a single contiguous array of objects (in order to have efficient indexing and iteration).  That means that we can have random access to its elements, not only though iterators but also with pointers to elements, just like with regular arrays.
 
Accessing the elements of a vector provides almost the same performance as the regular arrays do. Compared with the other STL containers, vector has the fastest random access to its elements.
 
A dynamic array is allocated first time the vector is constructed. When vector needs a bigger array, to hold its elements, it will allocate a new, bigger, chunk of memory and will copy all the elements to this new chunk, deleting the old chunk and destroying the objects contained in it.
 
Vectors invalidate their iterators on insertions and deletions. Also when the capacity is exceeded, and new memory has to be allocated.
 
Vectors are the right choice for:

  • random access to its elements
  • iterating through it (from beginning to end or backwards)
  • add or remove an element from the end 

Vector is defined in the <vector> header.

Strings

String is a template class designed to manipulate sequences of characters. It is a special type of container holding characters. The string class is defined in the <string> header.

Functors 

A functor is a class that can act like a function. It has the advantage that, unlike functions, it can store data.
 
A functor is a class/struct that overloads the function call operator so that an instance of that class acts just like a function ( and can be supplied were a function is expected ).
 
For example:

class MyFunctor
{
         public:
         int operator()()
         {
               //some implementation
               //returns an integer value
         }
         private:
              //some data
 };

MyFunctor getNumber;
int someNumber = getNumber();        // MyFunctor::operator()() is called

The function call operator can be defined to take any number of parameters, or no parameters at all.

Algorithms

STL algorithms are function templates,implementing algorithms for sorting, filling, searching containers,comparing ranges, copying ranges, etc.

They can be used with any type of container (STL container or not) that provides the proper iterator and holds elements of types that overload the operators required by the algorithm.
For example the std::count algorithm, compares every element in a container with a value we provide. For comparison it uses operator==, so we have to have inside the container a type that has an operator== defined.

STL algorithms are defined in the <algorithm> header.

Utilities

auto_ptr

A smart pointer that takes ownership of the pointer it stores. When the auto_ptr object is destroyed, the pointer will be deleted.

auto_ptr is defined in the <memory> header.

pair

The pair structure is provided to treat two values as a single unit. It is defined in <utility> header.

min, max template functions

If both values are equal, usually the first element is returned. Both functions are provided with an additional argument, that is the comparison criterion.

The default comparison criterion is operator<.

These functions are defined in the <algorithm> header.

swap

This function is provided to swap the values of two objects. The swap is possible only if the copy constructor and the assignment operators are defined.

The function is defined in the <algorithm> header.

Missing STL headers

Due to platform limitiations some of the STL headers is not included in this package. They will be added later.

  • fstream
  • iomanip
  • ios
  • iostream
  • sstream
  • queue

User Interfaces, NativeUI, MAUI

Creating User Interfaces With MoSync

For some mobile applications, the user interface elements can take up more than half of the source code. Getting every little detail right can add hours of frustration for the programmer. That’s why we have developed multiple solutions for cross-platform UI development: the NativeUI Library, the Widget API and MAUI. Each, in their own way, ensures UI consistency across multiple platforms from a single source base. Let’s take a look.


MAUI screen with MAUI widgets (MoRE) Widget API screen with native widgets (Android) An HTML UI, designed using jQTouch (Android)

HTML5/JavaScript

With the introduction of Wormhole technology in MoSync 2.7 Pyramid, now developers can design their UI in HTML and JavaScript and then use the available device functionality of MoSync using the available libraries. You can browse our documentation for creating HTML based projects in MoSync or communicating between JavaScript and C++

To create HTML based user interfaces you do not need to use special calls and any normal HTML file would work in MoSync as a UI. It is also possible to use the JavaScript UI frameworks designed specifically for mobile websites (e.g. jQTouchJQuery mobile, or Sencha Touch) in your MoSync apps.

Widget API

Widget API uses the device’s own widgets, allowing you to create applications that are exactly like the ones you create with the platform’s own SDK. It also brings you the power to integrate a  web browser and utilize OpenGL in your applications. The NativeUI Widget API is primarily aimed at leading-edge platforms and currently supports both Android and iOS (iPhone, iPad, iTouch). We’ll be adding more platforms in the near future.

Using the Widget API gives distinct advantages when you need:

  • Your application to look and feel like other applications on the platform.
  • Users to intuitively understand how the app is controlled and behaves.
  • The high-performance gained from Widget API’s tight integration with the OS.
  • To embed OpenGL or a web browser in your application.
  • To use the experimental open-source MobileLuaEditor to design your UI.

(Note that Widget API doesn’t work of course on MoRE, the MoSync emulator, so you must test and develop your application using the Android emulator, iPhone simulator, or a real device. To read more about testing apps in native emulators, see Emulating a Device.)

NativeUI Library

The NativeUI Library is a high-level wrapper for the Widget API. It supports all the widgets in the Widget API with their specific properties and events.A base Widget class that takes care of instantiated objects and their destruction and provides methods for managing widgets, like addChild, insertChild, and removeChild, and methods for manipulating widgets like setHeight, setVisible, and fillSpaceVertically.

The NativeUI Library includes a background widget manager that handles widget events. You do not have to use the CustomEventListener to catch widget events: you can receive only widget specific events by setting listeners to widget objects. Basic widget functions (setPropertyInt, getPropertyString) can be called at any time instead of the specific functions. And support is provided for fonts.

MAUI

MAUI uses custom MoSync screens and widgets, enabling you to create application user interfaces that look exactly the same across all platforms. It runs not only on Android and iOS, but also on Windows Mobile, JavaME, Symbian, Moblin, MoRE ( the MoSync emulator ) and all the other platforms MoSync supports. This is the user interface solution to choose if you simply must have an application that looks the same whatever the device it runs on.

Using MAUI gives distinct advantages when you need:

  • Your application to look and behave the same on all platforms.
  • Support across all platforms and devices, including older, more primitive ones.
  • Full open-source code that’s easy to modify so you can add your own custom widgets.

NativeUI Library, Widget API, and MAUI Together

The Widget API and MAUI are very similar to other user interface solutions that you will find in proprietary SDKs. The user interface is structured as a hierarchy of components consisting of containers called screens and controls called widgets. Different widgets have different functions and properties which can be set in order to customize their appearance or behaviour. Special types of widgets called “layouts” give you control over the positioning of other widgets on the screen. As the user of the application interacts with your application’s widgets, events are sent from the MoSync runtime on the device to your code for action.

You can of course use both Widget API (or NatriveUI Library) and MAUI together in the same application, switching between Widget API screens and widgets and MAUI screens and widgets as needed, giving you absolute control over the user interface. Use Widget API to present the standard application screens that the user expects to always look the same as others on their device, then provide highly-customized screens and controls using MAUI. You get the best of both worlds.

API Reference

All our user interface solutions are documented in the MoSync API Reference available in both in the download package and on our website: see the entries for NativeUI Library,  Widget API and MAUI.

Example Applications

We provide example applications that use MAUI and Widget API  in the /examples folder in the download package. In particular, check out our HelloNativeUI and HelloMAUI examples, well-commented beginners applications that show how to define, position and control screens and widgets using Widget API and MAUI, and MAUIex and NativeUIDemo, which demonstrate the variety of widgets available in both libraries. (Read our guide called Importing the Examples to understand how to load and view the examples.)

Tutorials

Our Introduction to MAUI tutorial covers a lot of the basics you need to get started both with MAUI and user interfaces in general.

Using the NativeUI Library

The MoSync NativeUI Library is built on our Widget API. It provides classes and functions for high-level control of native user-interface elements.

Platform and Emulator Support

The NativeUI Library and the underlying Widget API are designed to work with native user-interface controls. Currently the library supports the iPhone/iOS and Android platforms.Some widgets specific to the iPad and Android tablets are not yet available through the library.

You can test your NativeUI Library application directly from the MoSync IDE as long as you have installed at least one native emulator. (NativeUI applications will not, of course, work in the MoSync MoRE emulator.)

The Widget API

The NativeUI Library is built on our Widget API which provides a comprehensive range of low-level functions (syscalls), event types, and constants for creating and working with native user-interface widgets like screens, layouts, buttons, and many more.

Note: not all of the widget types in the API are supported on all platforms -- they are, after all, native widgets! Full documentation of the Widget API can be found in the MoSync API Reference in the IDE and online.

Example Applications

We provide many example applications that make use of the Widget API or the NativeUI Library (which is itself a high-level wrapper for the Widget API). The examples and the API/Library they use include:

  • NativeUIDemo -- NativeUI Library
  • VideoNativeUIExample -- NativeUI Library
  • DeviceFontsNativeUI -- NativeUI Library
  • CameraDemo -- NativeUI Library
  • WikiSearchNativeUI -- Widget API
  • HelloNativeUI -- NativeUI Library
  • RockPaperScissors -- Widget API

The NativeUI Library

The library is a high-level wrapper that supports all the widgets in the Widget API. It includes:

  • Support for all types of widgets, along with their specific properties and events.
  • A base Widget class that takes care of instantiated objects and their destruction. This class also provides methods for managing widgets, like addChild, insertChild, and removeChild, and methods for manipulating widgets like setHeight, setVisiblefillSpaceHorizontally and fillSpaceVertically.
  • A background widget manager that handles widget events. You do not have to use the CustomEventListener to catch widget events: you can receive only widget specific events by setting listeners to widget objects, for example: buttonObject->addButtonListener(this).
  • A set of base widget functions (setPropertyInt, getPropertyString) that can be called at any time instead of the specific functions.
  • Support for adding modal dialogs via the Dialog class. A dialog object acts like a container for any kind of widgets. Remember that a Dialog modal widget cannot have parents, so adding a dialog to a layout will have no effect.
  • Support for Fonts:
    • Access all fonts stored on the device.
    • Setting the font based on font type and font style.

Widget Support

 

Activity IndicatoriOS & Android
ButtoniOS & Android
CameraPreviewiOS & Android
Check boxiOS & Android
Date PickeriOS & Android
Edit boxiOS & Android
GL ViewiOS & Android
Horizontal layoutiOS & Android
ImageiOS & Android
Image BrowseriOS & Android
Image ButtoniOS & Android
LabeliOS & Android
List ViewiOS & Android
List View ItemiOS & Android
Navigation BariOS & Android
Number PickeriOS only
Progress BariOS & Android
Relative LayoutiOS & Android
Screen  iOS & Android
Search BariOS & Android
SlideriOS & Android
Stack ScreeniOS & Android
Tab ScreeniOS & Android
Time PickeriOS & Android
ToggleButtonAndroid only
Vertical layoutiOS & Android
Video ViewiOS & Android
Web ViewiOS & Android

 

Widget Creation

For each type of widget there is a corresponding class, so the routine in which a widget is instantiated is very simple:

WidgetType* myNewWidget = new WidgetType().

Do not forget to include the header file for all widgets, Widgets.h, or each widget class one by one.

Widget Properties

The shared methods implemented across all widgets include:

  • setProperty and getProperty for true property types.
  • getWidth(), getHeight()
  • setWidth(width), setHeight(height)
  • setSize(width, height)
  • wrapContent and fillAvailableSpace for a certain dimension.
  • setBackgroundColor
  • setVisible(visible)
  • setEnabled(enabled)
  • setBackgroundColor(color)
  • setBackgroundGradient(color1, color2)
  • getLastError: get details about the last error that has occurred, in a structure. Causes might be any of the following:
    • A widget was instantiated but it's type is not yet available on the target platform.
    • A property was set/get on a widget that is not yet available on the target platform.
    • A setter/getter method received an invalid property name.
    • A syscall has failed due to invalid parameters.
    • NOTE: In order to have this structure empty, make sure you read carefully the documentation before you use the NativeUI library.

For all properties, we have endeavoured to implement them as separate methods, rather than as setProperty/getProperty function calls.

Widget Events

The library implements separate listeners for each widget that can receive events. Take, for instance the EditBoxListener. The EditBox widget might send 4 types of events:

  • EDITING_DID_BEGIN
  • EDITING_DID_END
  • TEXT_CHANGED
  • RETURN

Those events will notify the user with respectively the callbacks:

  • editBoxEditingDidBegin(editBox)
  • editBoxEditingDidEnd(editBox)
  • editBoxTextChanged(editBox,text)
  • editBoxReturn(editBox)

More than just one listener can be attached to a widget. Attaching a listener to a widget can be done in this way:

EditBox* myEditBox = new EditBox();
myEditBox->addEditBoxListener(this);

Dialogs

A Dialog is a modal view that can look different depending on the platform:

  • On Android it is a modal alert dialog
  • On iPad it is a PopoverController
  • On the iPhone it is a modal view.

When a Dialog widget is created it is empty, it has no content. Use setMainWidget(mainLayout) to set the main widget of the dialog.

A Dialog gets visible only after calling show() method.

Usage example:

Dialog* myDialog = new Dialog();
myDialog->setTitle("My Form");
// Set specific iPad property for position, so that
// the dialog arrow points up.
myDialog->setArrowPosition(MAW_CONSTANT_ARROW_UP);
// Set its content.
myDialog->setMainWidget(mainLayout);
...
// Display the dialog at the right time.
myDialog->show();

Fonts Support

Access to device fonts can be acomplished via font syscalls (see maFontGetCount, maFontLoadDefault, maFontGetName, maFontLoadWithName, etc).

After getting the desired font using maFontLoadDefault or maFontLoadWithName, the handle to the font can be applied via the method setFont to the widgets:

  • Label
  • Button
  • ImageButton
  • ListViewItem
  • Navigation bar's title.

Example:

// Get the first available font of the device.
if ( maFontGetCount() > 0 )
{
	char buf[256];
	maFontGetName(0, buf, 256);
	// Load the font with size 10 and get it's handle.
	int fontHandle = maFontLoadWithName(buf, 10);
        myLabel->setFont( fontHandle );
}

At any time the font color can be set by calling the method setFontColor:

enum Colors
{
	INTENSE_BLUE = 0x104E8B ,
	LIGHT_BLUE = 0xB2DFEE
};
myLabel->setFontColor(LIGHT_BLUE);

Also, the font size can be set for the widget in use, without having to actually modify the font resource:

#define FONT_SIZE_REGULAR 15.3

myLabel->setFontSize(FONT_SIZE_REGULAR);

Note: When building applications that use Native UI,  remember to select an iOS or Android target in the right-hand device profile panel.

HelloNativeUI

HelloNativeUI is a well-commented example application for beginners. It consists of a very simple graphical user interface application that uses the NativeUI library and Moblet framework. It illustrates how to create NativeUI screens, and how to position widgets and handle events.

 

These screens show the look of the application on Android: its appearance is, of course, different on different platforms because it uses each platform's native GUI controls and settings. Note also that this example will not work on the MoSync emulator (MoRE) because it has no Native UI support yet.

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When the application runs, the user is presented with a simple NativeUI screen with four widgets:

  • a label with the instructions "Enter a password:"
  • an edit box operating in password mode
  • a button for clearing the password field 
  • a button for submitting the password

On clicking the Submit button, the password is validated (just validates the length of the password for demonstration purposes).

Examine the source code of the application to learn how the program works. The code commenting highlights various aspects of working with NativeUI screens and widgets, including:

  • How to define a screen.
  • How to identify the root of a screen's widget hierarchy.
  • How to use a layout widget to arrange other widgets.
  • How to define widgets and use their methods.
  • How to detect events and respond to them.

To understand how NativeUI and MAUI (an alternative GUI solution that use's MoSync's own screens and widgets to ensure graphical consistency across all platforms), compare this example application with the HelloMAUI example application.

User Interaction

  • Tapping the screen's Clear button - clears the edit box.
  • Tapping the screen's Submit button - validates the contents of the edit box.
  • Back key - exits the program.

NativeUIDemo

This example application demonstrates many of the important features of MoSync's NativeUI Library. The library provides a set of C++ classes for handling native user interface components (buttons, toolbars, list boxes, etc.). Under the hood, the library makes use of the C functions in MoSync's Widget API. In the latest version of this example we also demonstrate banner ads.

This example works on all platforms supported by the NativeUI Library (see Feature/Platfom Support).

Colors screen on AndroidImages screen on AndroidWeb View screen on Android
   
 Colors screen on iOS (iPhone) Images screen on iOS Web View screen on iOS

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

This application example shows how to use various native widgets (UI components) in a MoSync application. This includes a tab screen, a list view, a web view, and a custom view for an image swiper composed of several widgets.

In the code

The example also shows how to use placeholders and strings in the resource definition file. In the /UIWrapper folder is a class library that wraps the MoSync widget system for NativeUI on Android and iPhone.You can use this library in your own applications, and extend it as needed, but note that this is an example library, and is not yet fully documented and does not fully support all features of the NativeUI Widget API.

For the full documentation of the MoSync NativeUI, see the documentation in the package for the include file IX_WIDGET.h and the overview for the Widget API.

The main UI widget in the application is a TabScreen, which has three screens (tabs):

  • Colors screen (ScreenColorList.h/cpp) - demonstrates a ListView and a StackScreen.
  • Images screen (ScreenImageSwiper.h/cpp) - demonstrates a custom view for an image swiper composed of several widgets.
  • Web View screen (ScreenWebView.h/cpp) - demonstrates a Web widget.

Some techniques demonstrated by the example include:

  • Use of a single code base with slightly different UIs to target different platforms and form factors - the application is designed to work on both iOS and Android, but also to work on both tablets and phones. See for example method WidgetManager::isAndroid() and its use in method ScreenColorList::createUI().
  • How to manage events generated by the Widget API. See method WidgetManager::customEvent().
  • Using the stack screen mechanism to manage navigation between different screens within the application. See classes ScreenColorList and StackScreen.
  • How to specify image resources for different screen resolutions as unloaded binary resources (.ubin). These are only loaded into memory when used. See file Resources/Resources.lst. (Note that resource files can be placed anywhere in the project's file system, and be named anything that has an .lst extension.)
  • How to use placeholders in the resource file (.placeholder) to be able to loop over a range of resources. See file Resources/Resources.lst and the method ScreenImageSwiper::loadImages().
  • How to specify string data as part of resource data. See the use of .pstring in file Resources/Resources.lst and method ScreenImageSwiper::readStringFromResource().

Advertisements

In the MoSync 3.0 version of this example application, we include a banner advertisment created using the MoSync Advertising Library and API:

Advertising banner on iOSAdvertising banner on Android

 

 

RockPaperScissors

This application demonstrates the use of NativeUI screens and widgets to play the traditional game Rock-Paper-Scissors.

 

  

Note: These screens show the look of the application on Android. The application's appearance will of course be different on other platforms because it uses each platform's native GUI controls and settings. Note also that this example will not yet work on the MoSync emulator (MoRE) because it has no Native UI support. The platforms that support NativeUI are: Android, iOS and WP7.

This example is included in the MoSync SDK's /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When the application starts the user sees the main application screen with the message "Press PLAY to start game". On tapping "Play" the user is instructed to choose a weapon: rock, paper, or scissors. (The displayed weapons images can be found in the project's /Resources/Images folder.) Tapping a weapon plays the game against the computer opponent. The winner is computed by Game.cpp, and the result of the contest is shown to the user. The application keeps track of wins, ties, and losses, displaying these totals after each round.

Code

The application's project is split into several code files, linked together through header files. In Moblet.cpp we manage the application. In PlayScreen.cpp we define the UI elements. And in Game.cpp we handle the game logic (scoring, etc.). The entry point for the entire application is in main.cpp.

There is one main application screen, Playscreen. All other displays are created from it (for example, by switching backgrounds and showing different sub-sets of widgets) according to the stage of the game that has been reached. PlayScreen.cpp handles the display of the screen, including its layout and the behaviour and positioning of the individual widgets:

To minimize the use of string conversions (using itoa functions) and code duplication, the method setWidgetProperty is used to set the value of an integer property for widgets (properties like MAW_WIDGET_BACKGROUND_COLOR).

A timer is used to switch the label for "Choose you weapon!" (mFlickrLabel) from visible to invisible and back to notify the user that input is required.

Key Presses and Screen Taps

  • Play — starts a game.
  • Weapon icons — selects a weapon and challenges the opponent.
  • Back key — closes the application.

 

VideoNativeUIExample

This example application makes use of the MoSync NativeUI Library and demonstrates how to use the VideoView widget to embed video in your application.

On Android, playing a .3gp video from a URLOn iOS, playing a .m3u8 video from a URLOn Android, playing a .3gp video from a local path

 
This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

On iOS and Android:

When it starts up, this application displays a main screen with two tabs, Video and Settings.

On the default Video tab the user can enter the URL of a video file -- of any Android or iPhone/iOS supported formats . (A default URL is provided at start-up, based on the application settings.)

Tapping Load url fetches the file. Tapping Play starts the video.

The settings on the Settings tab enables the user to show or hide the label that displays the video duration, and also the default URL that is loaded when the application starts-up. Pressing Reload returns to the video screen and starts loading the default video.

While a video is playing the user is granted control and the Pause and Stop buttons can be used. After tapping on the video, the platform-specific media controller is displayed.

When loading a new video, any playing video is stopped automatically before the new video plays.

On Android only:
The user can play a local file by entering a local path in the edit box and clicking the Load path button.

In the Code

This example application makes use of the MoSync NativeUI Library, and through that the VideoView widget in the MoSync Widget API.

The project is divided into several files:

  • Main.cpp is the application's main entry point.
  • Moblet.cpp is the moblet that manages the application and handles key events.
  • Util.cpp contains utility functions and values for the application.
  • MainScreen.cpp is the TabScreen of the application. Here the application screens are created and connected to the main tab screen.
  • VideoScreen.cpp is the first screen of the application, containing widget controls for the Video: Play, Pause, and Stop, and a edit box widget for user input with the source link/path for the video.
  • SettingsScreen.cpp is the second screen of the application and it consists of a ListView with a check box for enabling/disabling the duration label of the VideoScreen, a label with the default source url loaded at application startup, and a Reload button for reloading the default source in the VideoScreen.
  • .h header files contain the forward declarations for the main .cpp code files.

Touch responses

  • The application is exited by pressing the back button.
  • The current video source starts playback by pressing Play button.
  • The current video source pauses playback by pressing Pause button.
  • The current video source stops playback by pressing Stop button.
  • The SettingsScreen is shown by pressing on the Settings tab.
  • The VideoScreen is shown by pressing on the Video tab.
  • By clicking on the edit box from VideoScreen a virtual keyboard is displayed and the user can input a video url, or a video path.
  • By clicking LoadUrl button the video url is taken from the edit box and it starts loading.
  • By clicking the LoadPath button the video path is taken from the edit box and it starts loading.
  • By clicking the "Reload default source" from SettingsScreen, the default video starts loading in the VideoScreen.

Introduction to MAUI

This tutorial introduces the MoSync API for User Interfaces (MAUI). It describes how to use the MAUI library classes to build screens and widgets and how to extend the MAUI model with your own custom components.

What is MAUI?

MAUI is the collection of user interface components that you can use in your own applications. It is quite possible to create an entire mobile application in MoSync without them, but they provide a lot of basic functionality, and if you make use of them you can save a lot of time. The MAUI model is extendible, and you can create your own components in it.

MAUI is a general-purpose UI framework that helps you develop usable, attractive, responsive user interfaces with a consistent look and feel across all the platforms and devices that MoSync supports. It draws inspiration from popular modern user interface frameworks like Windows Forms, Java SWING and QT, and is designed with the peculiarities and limitations of mobile devices very much in mind.

MoSync comes with an example application called MAUIex which has a lot of useful code that you can copy into your own application.

Screens

The fundamental building blocks of MAUI interfaces are screens and widgets.

Screens represent a particular instances of the application's user interface, a whole screen display. A screen is typically associated with a specific activity — like logging in, or browsing a list of albums, or viewing the description of a shopping item. Screens are essentially different logical locations in your application that the user can navigate between to perform different tasks.

To use the MAUI Screen class in your application you need to include the header file:

#include <MAUI/Screen.h>

There is no inherent hierarchy or other relationship between individual screens, although one can be implied or even enforced at the application level. Some screens may be considered side-by-side "siblings" of other screens, others have a hierarchical parent-child relationship.

To show a particular screen (and send all other screens to the background) you use the class's show method. There is also a corresponding hide method.

Screens are containers for widgets. There is always a main widget, which is resized to match the screen resolution of the device. To set the main widget, you use the setMain method, and to get it you use getMain.

The MAUI Screen class implements a number of useful event listeners so that you can detect UI events including key presses and pointer movements.

Widgets

Every user interface element in MAUI is a widget. Each widget performs a common GUI task. Examples include:

  • buttons
  • list boxes
  • labels
  • images

All widgets are direct or indirect subclasses of the base class MAUI::Widget . To use a MAUI widget class in your application you need to include the appropriate header file, for example, if you want to use labels:

#include <MAUI/Label.h>

A widget can be a container for other widgets. For instance, if you want an image with text overlaid, you can create an Image widget, and then set the Image to be the parent of a Label widget. When a parent widget is destroyed, all of its children are destroyed as well.

Arranging Widgets

The UI of a screen is built by creating a hierarchy of widgets.

There is a widget called Layout that can be used to define a grid in which to arrange other widgets, and a widget called ListBox that can be used to make a horizontal or vertical list of widgets. Both ListBox and Layout are designed solely to be containers of other widgets. Others widgets, like Label will be more or less successful containers depending on how you employ them.

A good example of how the Label and ListBox widgets work together can be found in the MAUIex example application that comes with MoSync. Here we can see a typical hierarchy of  Layout and ListBox widgets arranging one of that application's screens:

Each screen has a main widget which covers the entire physical screen — in this case it is a Layout widget with one column and two rows: the top row is the main application area, the bottom row is being used to display navigation buttons ("Back"). Inside the main layout widget is a ListBox widget, which allows for vertical scrolling of the main application area.

You can define the size and background colours of widgets, and they are skinnable. When you create a widget, you specify its size, its position and its parent:

Widget* widget = new Widget(0, 0, 240, 320, NULL);

The standard constructor for a widget is new Widget(x position, y position, width, height, parent widget).

On a mobile device, the x and y positions are often less useful than you might imagine. In reality you may find that you do almost all of your positioning through Layout and ListBox widgets, and by setting the padding properties of the widgets with methods like w->setPaddingBottom(5); which will put a margin against the bottom of that widget.

Some standard methods of widgets set the background colour of the widget, and whether it should draw a background at all.

 

// Set the widget background colour to a dark green
widget->setBackgroundColor(0x30A030);

// Ensure the widget is drawing a background colour
widget ->setDrawBackground(true);

Sometimes you want it to draw a background, and sometimes you don’t.  With the widgets here, a dark green background is added to them rather than a skin. However, if you put a layout over an image you want to use as the background, then you need to set setDrawBackground(false) to ensure the layout doesn’t paint over my image where you don’t want it to.

Screen Size and Orientation

There are a lot of different screen sizes around now. It used to be that there were two or three common sizes which virtually all phones shared. Now, with so many different aspect ratios it is almost impossible to predict what the screen size and shape will be.

However, it is easy to query a device's screen size from within your application using the maGetScrSize() function:

 

// Returns size of the screen to screenSize.
MAExtent screenSize = maGetScrSize();

// Returns width of the screen to scrWidth.
int scrWidth = EXTENT_X(screenSize);

// Returns height of the screen to scrHeight.
int scrHeight = EXTENT_Y(screenSize);

This will give you the height and width of the screen in pixels.  To know whether the screen is in landscape or portrait mode, you need to test the sizes:

 

// If Width is greater than Height of the screen, the screen 
// is in Landscape mode otherwise in portrait mode.
if(scrWidth > scrHeight)
{
    // Landscape mode
}
else
{
    // Portrait mode
}

If you want a widget to be a quarter of the screen size, then you can specify it as:

Widget* alertLabel = new Label(0, 0, scrWidth / 2, scrHeight / 2, NULL);

Moblets

At the heart of the MAUI model is the Moblet. This is the event-based platform which all MAUI applications are based on. When your application starts, it is the moblet which runs first. The moblet will create (at least) the first screen of your application and show it. The moblet is also responsible for closing your application down, and managing system events. Some events it passes to screens automatically, for instance key presses or screen touches. Other events occur and the moblet may need code to inform your screens of the event, like getting a new location from GPS.

If you create a new MAUI project (see the tutorial on starting a new MAUI project to do this), then you get some standard code. We’ve added some additional comments to it below.

 

#include <MAUtil/Moblet.h>
#include <MAUI/Screen.h>
#include <MAUI/Label.h>
using namespace MAUtil;
using namespace MAUI;
// This is the Screen class. This is what you'll see displayed on your phone. It inherits 
// from MAUI::Screen base class. To put content on the screen, you have to add widgets to it.
class MyScreen : public Screen 
{
public:
    MyScreen()
    {
/*
 * todo: initialize the widget hierarchy of this screen and possibly show it
 * example:
 * Label* l = new Label(0, 0, 50, 50, NULL, "", 0x1f7f1f, NULL);
 * setMain(l);
 */
    }
    virtual ~MyScreen() 
    {
        // todo: delete main widget of this screen
    }
private:
};

// This is the Moblet class. This manages the events your application will need, like getting
// key presses and screen touches.
// It also creates an instance of the MAUI::Screen class 'MyScreen', and shows it on screen.
class MAUIMoblet : public Moblet 
{
public:
    MAUIMoblet() 
    {
        // initialize
        screen = new MyScreen();
        screen->show();
    }
    void keyPressEvent(int keyCode) 
    {
        // todo: handle key presses
    }
    void keyReleaseEvent(int keyCode) 
    {
        // todo: handle key releases
    }
    MyScreen* screen;
    virtual ~MAUIMoblet() 
    {
        delete screen;
    }
};

/* This is where your application starts. It creates a new instance of MAUIMoblet (see above)
 * and runs it.
 */
extern "C" int MAMain() 
{
    Moblet::run(new MAUIMoblet());
    return 0;
}

You may want to make changes to this when you start a new project. Firstly, you may want screen definitions in their own files, so delete the MyScreen class here, and create it elsewhere.  Secondly, Moblet automatically passes key and screen events to my screens, so you don’t need the additional methods here.  They would be useful if you wanted to capture screen presses when you’ve not got a screen displayed, or for a button which you can use on every screen, but for your applications you may prefer to handle it screen by screen.

Some developers also don’t start with a Screen, but with their own class of ScreenController. ( This will be the topic of another tutorial. In this example, we're going to show a screen instead.)

 

#include <MAUtil/Moblet.h>
#include "Screens/Menu.h"
using namespace MAUtil;
using namespace MAUI;
// This is the Moblet class. This manages the events your application will need, like getting
// key presses and screen touches.
// It also creates an instance of the MAUI::Screen class 'Menu', and shows it on screen.
class MAUIMoblet : public Moblet 
{
public:
    MAUIMoblet()
    {
        // initialize
        menu = new Menu();
        menu->show();
    }
    Menu* menu;
    virtual ~MAUIMoblet()
    {
        delete menu;
    }
};

/* This is where your application starts. It creates a new instance of MAUIMoblet (see above)
 * and runs it.
 */
extern "C" int MAMain()
{
    Moblet::run(new MAUIMoblet());
    return 0;
}

Much smaller, this is the basic code that you need to start your application.

Screens

The Screen is the main unit of the application. A screen is a container for Widgets, and contains the code to process interaction with the user. The different types of Widget are explained below, but here is some boiler plate code that can be used as templates when you're creating new Screens.

We’re going to look at creating Widgets shortly, but you can use code similar to that you’ll find in the MAUIex example supplied with MoSync. That has some small factory methods which return you the widgets, rather than duplicating code in every screen.

Menu.h

 

/*
 * @file: Menu.h
 * @Author: Naveed Asif, Niklas Nummelin
 *
 * Description:
 *
 * In this tutorial we will learn how to create a
 * menu using Screen, Main Layout, and Labels.
 *
 * (Util.h, Util.cpp) files and (background_tile_sel.png,
 * background_tile_unsel.png) images, and (pretty.mof) font
 * can be found in MAUIEx example.
 */
#ifndef _Menu_H_
#define _Menu_H_

#include <MAUI/Screen.h>
#include <MAUI/Layout.h>
#include <MAUI/ListBox.h>
#include <MAUI/Label.h>
#include <MAUI/Engine.h>
#include <MAUI/Font.h>
#include <MAUtil/Moblet.h>

#include "MAHeaders.h"

using namespace MAUI;
using namespace MAUtil;

Widget* createSoftKeyBar(int height, char *left, char *right);

/*
 * MainScreen Class definition.
 */
class MainScreen : public Screen
{
public:
	// Constructor for the MainScreen class
	MainScreen();

	// The destructor.
	virtual ~MainScreen();

	void keyPressEvent(int keyCode, int nativeCode);
	void pointerPressEvent(MAPoint2d point);
	void pointerReleaseEvent(MAPoint2d point);

private:
	ListBox* listBox;
	Layout* layout;
	Widget *softKeys;
};

/*
 * MyMoblet class definition.
 */
class MyMoblet : public Moblet
{
public:
	MyMoblet();
	void keyPressEvent(int keyCode, int nativeCode);
	void keyReleaseEvent(int keyCode, int nativeCode);
	void closeEvent();

private:
	Screen* mainScreen;
};

#endif /* _Menu_H_ */

This header file defines a new class called ‘Menu’, inheriting from ‘Screen’.  For the screen to capture button presses and respond, the method keyPressEvent must be implemented, and there is reference to a ListBox widget called mContentBox.

Menu.cpp

 

/*
 * @File: Menu.cpp
 * @Author: Naveed Asif, Niklas Nummelin
 *
 * Here we include the header file Menu.h
 * The Util.h header file contains many useful
 * functions for working with widgets.
 */
#include "Menu.h"
#include "Util.h"

/*
 * An object of MyMoblet class is created.
 */
MyMoblet* moblet;

/*
 * The MainScreen class is defined here.
 */
MainScreen::MainScreen()
{
	// The main layout
	layout = createMainLayout("", "Exit");

	// This will create the list box widget that has
	// 3 Labels as its children.
	listBox = (ListBox*) layout->getChildren()[0];
	listBox->add(createLabel("Label"));
	listBox->add(createLabel("Another Label"));
	listBox->add(createLabel("Yet another Label"));

	// Softkeys will generate soft key bar Exit button
	// using create main layout.
	softKeys = layout->getChildren()[1];

	// Sets the layout as main widget for this screen. The widget will
	// be resized to match the screen resolution of the device.
	this->setMain(layout);
}

/*
 * The desctutor for MainScreen
 */
MainScreen::~MainScreen()
{
	delete layout;
}

/*
 * This will handle key press events.
 */
void MainScreen::keyPressEvent(int keyCode, int nativeCode)
{
	switch(keyCode)
	{
		case MAK_UP:
			listBox->selectPreviousItem();
			break;
		case MAK_DOWN:
			listBox->selectNextItem();
			break;
		case MAK_SOFTRIGHT:
			moblet->closeEvent();
			moblet->close();
			break;
	}
}

/*
 * Pointer press events.
 */
void MainScreen::pointerPressEvent(MAPoint2d point)
{
	Point p;
	p.set(point.x, point.y);
	if(listBox->contains(p))
	{
		for(int i = 0; i < listBox->getChildren().size(); i++)
		{
			if(listBox->getChildren()[i]->contains(p))
			{
				int index = listBox->getSelectedIndex();
				if(index == i)
				{
					keyPressEvent(MAK_FIRE, 0);
				}
				else
				{
					listBox->setSelectedIndex(i);
				}
				break;
			}
		}
	}
	else if(softKeys->contains(p))
	{
		if(softKeys->getChildren()[0]->contains(p))
		{
			keyPressEvent(MAK_SOFTLEFT, 0);
		}
		else if(softKeys->getChildren()[1]->contains(p))
		{
			keyPressEvent(MAK_SOFTRIGHT, 0);
		}
	}
}
/*
 * Todo: Pointer release event for Screen.
 *
 */
void MainScreen::pointerReleaseEvent(MAPoint2d point)
{
}

/*
 * Todo: Key press event for moblet.
 */
void MyMoblet::keyPressEvent(int keyCode, int nativeCode)
{
}

/*
 * Key release event for moblet.
 */
void MyMoblet::keyReleaseEvent(int keyCode, int nativeCode)
{
}
/*
 * This event is fired at application exit.
 */
void MyMoblet::closeEvent()
{
	// do destruction here
	delete mainScreen;
}

/*
 * MyMoblet class declaration.
 */
MyMoblet::MyMoblet()
{
	// The default font and skins are declared.
	gFont = new MAUI::Font(RES_FONT);
	gSkin = new WidgetSkin(RES_SELECTED, RES_UNSELECTED, 16, 32, 16, 32, true, true);

	// Returns a reference to the single instance of
	// Engine class, using lazy initialization.
	Engine& engine = Engine::getSingleton();
	engine.setDefaultFont(gFont);
	engine.setDefaultSkin(gSkin);

	// This returns screen coordinates.
	MAExtent screenSize = maGetScrSize();
	scrWidth = EXTENT_X(screenSize);
	scrHeight = EXTENT_Y(screenSize);

	// Creates a new instance of the main screen.
	mainScreen = new MainScreen();
	mainScreen->show();

}

/**
 * Main function that starts the program.
 */
extern "C" int MAMain()
{
	moblet = new MyMoblet();
	MyMoblet::run(moblet);
	return 0;
}

The constructor calls the factory method to get a basic screen layout. It then creates Label widgets with the menu options.

(Every screen must have one, and only one, main widget. This is considered to be the main widget, and it will be resized to the whole of the screen. Screens cannot have areas which do not have widgets.)

You don’t have to explicitly destroy the widgets you’ve created for this screen. They will be deleted when the screen is disposed if they have been added as children to the screen.

The moblet traps key presses and screen touches, and passes them automatically to the screen which is being displayed. The only thing you need to do as a programmer to get these is to implement the virtual functions keyPressEvent, keyReleaseEvent, pointerPressEvent, pointerMoveEvent and pointerReleaseEvent. In this example, we want to navigate the menu when a button is pressed, so we can capture the keyPressEvent. The method is passed an int, which represents a key. These are mapped to a series of constants.

Labels

Probably the most basic widget, the Label will put some text on screen.

Label* myLabel = new Label(0, 0, scrWidth, scrHeight, NULL, "My Label", 0xFFC0C0, myFont);

 

Label has the standard Widget constructor as described above, but you can also call it a caption to display, the background colour as hex and a font. To use the default font, you have to do it the long way.

Label* myLabel = new Label(0, 0, scrWidth, scrHeight, NULL);
myLabel->setCaption("My label");

By default, labels will only display the first line of the caption. It won’t wrap around onto the next line automatically. You can set it to do this with:

myLabel->setMultiLine(true);

You can also let the label decide how much space it needs. You can specify exactly how big you want the label to be, but you can also let it stretch automatically vertically and horizontally. Generally, you may find that you don’t want to let it resize horizontally, but you do vertically.

 

// Allow the label to resize vertically automatically.
label->setAutoSizeY(true);
// Prevent the label from resizing horizontally automatically.
label->setAutoSizeX(false);

Images

An image will let you put a picture in your application.  These are used frequently as buttons. Creating them is very simple. In addition to the default Widget constructor, there is

 

// Create a new Image object based on the PNG file in the resources.
Image* image = new Image(0, 0, 100, 100, listbox, true, true, IMAGE);

Where the last three arguments are to automatically resize automatically horizontally, resize automatically vertically and the MAHandle of the image resource. By setting the resize arguments as above, you can use this command to create any image you want and it will be the correct size for the image. If you set the resize arguments to false, it does not resize the image. If the sizes you’ve specified are too small, it will crop from the centre of the image. If the sizes are too large, it will show the background colour, if it has been set. This image has been cropped.

If you want to show a border on the image, you can use the padding as described earlier. If you’ve got it automatically resizing, you won’t see the entire image. This is because the widget has sized to dimensions of the image, but then the padding will eat some of that space.

 

// Create a new Image object based on the PNG file in the resources.
Image* image = new Image(0, 0, 150, 150, listbox, true, true, IMAGE);
image->setPaddingTop(10);
image->setPaddingLeft(10);

This image has padding set.

You can animate images by listening for timer events, and changing the MAHandle of the resource you are using. See the tutorial on resource files for more on this.

Layouts

The layout widget does not display much in its own right, but it will organise your widgets on screen for you. You can specify a grid into which widgets will be placed. They will be added from top left to bottom right. Don’t confuse this too much with an HTML table. There are some similarities, but the Layout does not give you all the options an HTML table will.

When you create a layout, you specify the number of rows and columns you want.

Layout *mainLayout = new Layout(0, 0, scrWidth, scrHeight, NULL, 1, 2);

In this example, we’ve got one column and two rows, so we can have two widgets, arranged vertically. You cannot add more widgets than you’ve specified space for. It will not automatically grow as you add more. Use a ListBox if you want to do that. If you have more than one column, then horizontal space is allocated evenly between them. You cannot have one column which is narrower than another. Each widget can be given the right amount of space vertically though, but you do have to have just one column to do it. If you have a 2x2 grid, then each cell must be the same size as its neighbour on the same row.

Any specific spacing you’ve set for widgets in a layout will be ignored. You can’t offset a widget within its own cell. You have to make that widget larger if you want it to appear with a border, so the first two arguments when creating a child for a widget can be ignored.

ListBox

The ListBox is probably the single most useful widget in the standard collection. It is like a Layout, but with two key differences. Firstly, it only works in one dimension. You can have a vertical list or a horizontal list, but you can’t create a grid with it. Secondly, it expands as you put more widgets into it, so you don’t need to know how many widgets it will end up containing.

Note: Don't be tempted by the basic constructor with a ListBox as it won't be size you expect.

If you build a ListBox with the basic parameters, it won't scroll. You'll probably be looking at it wondering why it only has one item in it.

infobox = new ListBox(0, 0, scrWidth, scrHeight, layout);

You need to add all of the arguments specific to ListBox.

 

infobox = new ListBox(0, 0, scrWidth, scrHeight, layout, 
          ListBox::LBO_VERTICAL, ListBox::LBA_LINEAR, true);

You can decide whether it is going to grow vertically or horizontally, whether it is going to be animated, and whether it will wrap to the beginning when you get to the end. Typically, an app screen will contain a Layout with a child ListBox. In the diagram below, there is a Layout in a 1x3 grid. The widgets in the Layout have been marked.

The ListBox will not break out of its space in the layout. Should it grow longer than its space, it will scroll as items are selected in it. When a specific widget in the ListBox is selected, it will scroll so that widget is on screen. You can scroll a widget using selectNextItem() andselectPreviousItem() methods. In this example, key press on the screen has been captured, and the application is going to scroll up and down the ListBox (contentBox):

 

void Menu::keyPressEvent(int keycode)
{
    // A full list of the key constants is available at 
    // http://www.mosync.com/docs/2.0b1/html/maapi_8h.html
    switch(keycode)
    {
    case MAK_SOFTLEFT:
        // Quit the application
        maExit(0); // Calling the moblet to exit is the proper way
        break;
    case MAK_8:
    case MAK_DOWN:
        contentBox->selectNextItem(true); // Select the next item on the menu
        break;
    case MAK_2:
    case MAK_UP:
        contentBox->selectPreviousItem(true); // Select the previous item on the menu
        break;
    case MAK_5:
    case MAK_FIRE:
        // Decide on the action you want to perform when an option is selected.
        // You can get the selected option with
        // contentBox->getSelectedIndex();
        break;
    }
}

You can also decide whether to scroll back to the top once you go past the bottom with the option setWrapping().

EditBox

An EditBox allows for text capture, so users can use their phone keypad to enter text into the application.

 

EditBox* editBox = new EditBox(
                   0, 0, scrWidth, 60, NULL, “”, 
                   0xFFFFFF, aFont, true, true, 256, 
                   EditBox::IM_STANDARD
                   );

EditBox is probably the most complicated of the standard widgets, as there are different options for capturing key presses. You don’t want navigation keys to start working when someone is trying to type their name in, so you can set the EditBox to manage its own navigation.

When it is activated, it becomes the key press listener and will get the keys entered as well as the screen. This gives you a headache when managing navigation, and you have to create a sensible approach in your application between navigating the screen, and moving the cursor around the text box.

The edit box has a cursor, but its default colour is white, so if you’ve got a white background to your edit box, then you’ll need to change the colour of the cursor with

editBox->setCursorColor(0x000000);

There are two text entry modes. You can use it capture letters, or you can set it so it only takes in numbers. The hash key (#) acts as a temporary shift, so if the user presses # and the 4, they will get an upper case G.

WidgetSkins

Most widgets can have a skin applied. This is a standard image which will be applied to the border and background of the widgets to give them a uniform look and feel. Here is a skin which is supplied in the MAUIex examples. The skin is split into a 3x3 grid, and lines have been drawn to show where the grid is defined. When it is applied to a widget, the corners stay as they are, and the other five sections are tiled to fill the required space. Whatever is in box 5 is tiled over the background of the widget. You do have to be careful with this, as nice looking gradients won’t look so nice when tiled.

To define the skin, you don’t specify the top left and bottom right corners of the center box as you might expect, but instead you specify the horizontal positions of the vertical cuts, and then the vertical positions of the horizontal cuts.

 

gSkin = new WidgetSkin(
        RES_SELECTED, RES_UNSELECTED, 
        16, 32, 16, 32, true, true
        );

The first two arguments are the MAHandles of the images you want to use as the skin. The first one is the skin to apply when the widget has focus (is selected), and one to use when it doesn’t have focus. You then specify the offsets of the vertical cuts, the offsets of the horizontal cuts, and whether the images you want to use contain any transparent elements (which they probably do).

Very importantly, both the selected and unselected skins must be exactly the same size. You can see here the skins being applied to EditBoxes.

Creating your own Widgets

The best thing about the Widget model is that it is easily extendible. There are many widgets to handle common features like softkey bars, progress bars, buttons, calendars and sliders. There will be another tutorial which will go into detail on creating your own widgets. In short, you need to inherit from Widget, and implement the drawWidget() method.

This is from the ProgressBar widget:

 

#ifndef _PROGRESSBAR_H_
#define _PROGRESSBAR_H_

#include <MAUI/Widget.h>
#include <MAUI/Image.h>

using namespace MAUI;
namespace DatiloUI
{
    class ProgressBar : public Widget
    {
    public:
        ProgressBar(int x, int y, int width, int height,Widget* parent = NULL);
        virtual ~ProgressBar();
        int getPercentage();
        void setPercentage(int percentage);
        void drawWidget();
        void setFullImage(Handle image);
        void setEmptyImage(Handle image);
    private:
        Handle mTileFullImage;
        Handle mTileEmptyImage;
        int mPercent;
        void plotSection(MAPoint2d* offset, int width, Handle image);
    };
};
#endif // _PROGRESSBAR_H_

The drawWidget() method is the essential one. It will be called when the screen is painted, and it must plot its contents in the confines of the widget.

Here is an extract from the drawWidget() method:

 

void ProgressBar::drawWidget()
{
    int w = this->getWidth() - this->getPaddingLeft() - this->getPaddingRight();
    int amountRemaining = (w * percent) / 100;
    MAPoint2d* offset = new MAPoint2d();
    Point p = this->getPaddedPosition();
    Rect r = paddedBounds;
    offset->x = r.x;
    offset->y = r.y;
    // Plot the full section
    if(mTileFullImage != NULL)
    {
        plotSection(offset, amountRemaining, mTileFullImage);
    }
    else
    {
        // Plot a progress bar
        plotBar(offset, amountRemaining);
    }
    if(mTileEmptyImage != NULL)
    {
        offset->x += amountRemaining;
        plotSection(offset, w - amountRemaining, mTileFullImage);
    }
    delete offset;
}

You can see that in the drawWidget method, you can get the PaddedPosition of the widget. This tells you where the Widget is on screen. In the plotSection() method, you get the space you're allowed to use

 

void ProgressBar::plotSection(MAPoint2d* offset, int  width, Handle h)
{
    if(mDrawBorder)
    {
        plotBorder();
    }
    MARect* clippedImage = new MARect();
    Rect r = this->getPaddedBounds();
    Extent imageSize = maGetImageSize(h);
    while(width > 0)
    {
        clippedImage->left = 0;
        clippedImage->width = width > EXTENT_X(imageSize) ? EXTENT_X(imageSize) : width;
        clippedImage->top = 0;
        clippedImage->height = r.height > EXTENT_Y(imageSize) ? EXTENT_Y(imageSize)
        : r.height;
        // lprintfln("Drawing image at x:%d, y:%d", offset->x, offset->y);
        Gfx_drawImageRegion(h, clippedImage, offset, 0);
        offset->x += clippedImage->width;
        width -= clippedImage->width;
    }
    delete clippedImage;
    if(writePercentage)
        plotPercentage();
}

The method getPaddedBounds() tells me exactly what space you're allowed to use without drawing over another widget.

The Engine

Behind all of these widgets is the MAUI Engine. This is the part which you rarely have to use, but provides the functionality for getting your widgets on to the screen.

The Engine is a singleton, and you can get a reference with

Engine& eng = Engine::getSingleton();

The Engine can let you specify default fonts and WidgetSkins, so you don’t need to specify them each time you use them. They will be automatically applied to your widgets. How cool is that?

eng.setDefaultFont(standardFont);
eng.setDefaultSkin(standardSkin);

Set up the font and the skins, and apply them to the default methods above.

Creating New Screens (MAUI)

Screens are the skeleton of your application. They contain the content widgets which are the heart and muscles of your program, but the Screens provide the navigation and structure you will want. You can create screens with their widgets at design time, when you are writing your code, or you can interpret some data to create screens at runtime. We’ll be looking at both of these in this tutorial.

Instantiating a New Screen

The Screen is the container for the widgets you want to display. It contains functionality so screens know which one is visible, and they can be shown with one command.

When you are creating your application, you will probably want to create screens with different information. To create a new screen at design time, you need to inherit from MAUI::Screen. When you create a new MAUI application in MoSync, then you get some example code to do this. I’ve built a new screen here to show a menu.

 

#ifndef MENU_H_
#define MENU_H_
#include <MAUI/Screen.h>
#include <MAUI/Layout.h>
#include <MAUI/Image.h>
#include <MAUI/ListBox.h>
#include <MAUI/Label.h>
#include "../Util.h"    // From MAUIex
#include "../MAHeaders.h"
using namespace MAUI;
using namespace MAUtil;
class Menu : public Screen
{
public:
	Menu();
	virtual ~Menu();
	void keyPressEvent(int keyCode);
private:
	ListBox* contentBox;
};
#endif /* MENU_H_ */

We can see that class Menu inherits from Screen.

In the C++ code we can see what a Screen might do. (This is not a complete listing.)

 

#include "Menu.h"
// Constructor for Menu. Takes a pointer to the Moblet object created in main.cpp
Menu::Menu(Moblet* moblet) : mlet(moblet)
{
	// Build the menu options
	buildMenu();
}

/*
 * Destructor
 */
Menu::~Menu()
{
}

/*
 * Add the menu options to the listbox
 */
void Menu::buildMenu()
{
	// Get a basic screen set up from the UI Builder
	Widget* w = createScreen(EXITBUTTON, "Menu");
	
	// Get the layout from the created screen. Cast from Widget* to Layout*
	Layout* layout = (Layout*)w->getChildren()[0];
	
	// Get the listbox from the layout. Cast from Widget* to ListBox*
	listbox = (ListBox*)layout->getChildren()[1];
	
	// Create a button for each menu option, and add it to the listbox.
	listbox->add(createButton("Music Demo"));
	listbox->add(createButton("Font Demo"));
	listbox->add(createButton("Image Demo"));
	listbox->add(createButton("Animation Demo"));
	listbox->add(createButton("Strings Demo"));
	listbox->add(createButton("Skipped Resource Demo"));
	listbox->add(createButton("Placeholder Demo"));
	listbox->add(createButton("Binary Demo"));
	
	// Set the widget from the UIBuilder as the main widget for the screen
	this->setMain(w);
}

A screen contains Widgets (for more information, see the tutorial Introduction to MAUI). In our example, we can see that in the constructor we call buildMenu which calls out to an external function to create Widgets on our behalf. These widgets will make up the content of our screen.

The final command here is setMain(w); Each screen has one main widget, which is the parent widget of this screen. When this screen is painted, it calls the drawWidget method on the main widget to paint it. Each of the children of the main widget will be painted as well.

Note: Screens cannot have any sections which don’t have a widget on. The main widget will be resized to the size of the screen.

To create the screen, you need to create a new instance of it. In the most simple example, the controlling Moblet (see the tutorial Starting a New Moblet Project) will call an initial screen to start.

 

#include <MAUtil/Moblet.h>
#include "Screens/Menu.h"

using namespace MAUtil;
using namespace MAUI;

/*
 * This is the Moblet class. This manages the events your
 * application will need, like getting key presses and
 * screen touches. It also creates an instance of the
 * MAUI::Screen class 'Menu', and shows it on screen.
 */
class MAUIMoblet : public Moblet {
public:
	MAUIMoblet()
	{
		// Initialize menu.
		menu = new Menu();
		menu->show();
	}
	
	/*
	 * The destructor.
	 */
	virtual ~MAUIMoblet()
	{
		delete menu;
	}
	
private:
	Menu* menu;
};

/*
 * This is where your application starts. It creates a new
 * instance of MAUIMoblet (see above) and runs it. 
 */
extern "C" int MAMain() {
	Moblet::run(new MAUIMoblet());
	return 0;
};

This moblet code has a reference to Menu.h, and when it is constructed, it creates a new instance of Menu. It then calls the method show().

Only one screen can be shown at a time. By calling the show() method on a screen you are indicating that this is the screen which should now be shown. There is also a hide() method, which isn't implemented in Screen directly. You can create Screens though which use it. If you've got screens which have some sort of processing capability, you can pause it while the Screen isn't shown if you want to.

The screen which is being shown will automatically be passed key and screen events.

Capturing Keys and Screen Touches

Screen already contains the functionality to automatically receive events raised from the keyboard and the screen (on touchscreen devices), and we can react to these events by providing functionality for the following methods.

void keyPressEvent(int keyCode);
void keyReleaseEvent(int keyCode);
void pointerPressEvent(MAPoint2d point);
void pointerMoveEvent(MAPoint2d point);
void pointerReleaseEvent(MAPoint2d point);

Typically, we might want to capture these to control navigation on screen. In our menu, we’ve got a ListBox with the menu options. We want to capture key presses to navigate the menu.

 

/*
 * This is inherited from Screen, and captures key presses.
 */
void Menu::keyPressEvent(int keyCode)
{
	// Select the key which has been pressed
	switch(keyCode)
	{
	// Right Soft Key - ask the moblet to close the application.
	case MAK_SOFTRIGHT:
		mlet->closeEvent();
		mlet->close();
		break;
		
	// Move up the menu when the navigation up button, the navigation
	// left button the number 2 or the number 4 buttons are pressed.
	case MAK_UP:
	case MAK_LEFT:
	case MAK_2:
	case MAK_4:
		listbox->selectPreviousItem(true);
		break;
		
	// Move down the menu when the navigation down, the navigation right,
	// the number 8 or the number 6 buttons are pressed.
	case MAK_DOWN:
	case MAK_RIGHT:
	case MAK_8:
	case MAK_6:
		listbox->selectNextItem(true);
		break;
		
	// When the fire button or the number 5 are pressed, show the 
	// selected screen from the vector.
	case MAK_FIRE:
	case MAK_5:
		lprintfln("Showing screen %d", listbox->getSelectedIndex());
		screens[listbox->getSelectedIndex()]->show();
		break;
	}
}

The KeyPress and KeyRelease events pass an int, which is an index of the button pressed. These are constants in MoSync, and are in the format MAK_. There is a full list available in the MoSync API Reference Guide. In this example, we’ve a simple switch statement. If the right soft key button is pressed, we end the application. If the up, left, 2 or 4 key is pressed, we tell the ListBox to go to the previous item. On the down, right, 6 or 8 buttons, we scroll down. When fire or 5 is pressed, we perform an action based on the selected item in the ListBox. We can see that we’ve created a navigation system just by simply plugging into the functions already available.

When we capture a screen touch, we get a location on screen which has been touched.

 

void TouchListBox::pointerPressEvent(MAPoint2d point)
{
	locateItem(point);
}

We can use this point to identify a widget which has been pressed, or perform any other action you may want to with the screen. I prefer for touch sensitive widgets to handle their own touch events, and I’ll be presenting my TouchListBox in the tutorial Creating New Screens.

When you start writing MoSync apps, you’ll start by creating a couple of screens in the way described above, and then get stuck wondering how to navigate between screens. How does this screen go back to the one before? Or that one over there? In the MAUI example supplied with MoSync, there is a simple method for managing this.

 

LayoutScreen::LayoutScreen(Screen *previous) : previous(previous) 
{
	mainLayout = createMainLayout("", "back");
	this->setMain(mainLayout);
	ListBox* listBox = (ListBox*) mainLayout->getChildren()[0];
	Layout *layout = new Layout(0, 0, scrWidth, 
			scrHeight-mainLayout->getChildren()[1]->getHeight(), 
			listBox, 3, 4);
	layout->setMarginX(5);
	layout->setMarginY(5);
	layout->setPaddingLeft(5);
	layout->setPaddingRight(5);
	for(int i = RES_ICONS_START; i != RES_ICONS_END+1; i++) 
	{
		new Image(0, 0, 0, 0, layout, true, true, i);
	}
}

When you create a LayoutScreen, you have to pass it the pointer to the previous screen. In my menu example, to create a LayoutScreen, enter

Screen layoutScreen = new LayoutScreen(this));

where this is the menu screen. This means that the LayoutScreen has an anchor to another screen, and when it wants to show the menu again, it can call the show() method on it.

 

/*
 * Pressing soft right key will display previous screen.
 */
void LayoutScreen::keyPressEvent(int keyCode) 
{
	if(keyCode == MAK_SOFTRIGHT) 
	{
		previous->show();
	}
}

This screen doesn’t have to worry about hiding itself. Calling show() on another screen will hide this one.

Creating Screens Dynamically

As well as these fixed screens, you can create Screens at runtime. There may be many occasions where you don’t know what you want on screen yet. It may depend on something the user does, or it may be that it is based on data downloaded from the internet. To manage these, I’ve used a screen which can parse input data and create widgets based on it. This means I can theoretically have any number of screens in my application without running out of memory. I keep a small pool of maybe five screens alive at any one time, and when I need the sixth new screen, I delete the first one. Should I need the first one again, I can recreate it.

The data for your dynamic screen may be included as a binary file in your resources, which is good for default data, explicitly build into your resource files, or downloaded and put into a data store.

If you’ve already got some binary data you want to use, you can include it in the resources file.

.res BINARYDEMO
.bin
.include "resources/data.bin"

A second way is for you to describe it in the resource file itself.

 

.res DYNAMICSCREENDEMO
.bin
.byte 4 // The number of widgets
.byte 1 // A label widget
.pstring "This is the first item. It is a label"
.byte 2 // A button widget
.pstring "This is the second item. It looks like a button"
.byte 1 // A label widget
.pstring "The next item is an image"
.byte 3 // An Image widget
.word 14422 // The size of the image

I use my own format. I’ve a server which compiles data for my applications and sends it to the phone.

I can build a screen at runtime based on the values in the resource file:

 

#ifndef BINARYSCREEN_H_
#define BINARYSCREEN_H_
#include "UIBuilder.h"
#include <MAUI/Screen.h>
#include <MAUI/Image.h>
#include <conprint.h>
#include "MAHeaders.h"
using namespace MAUI;
using namespace MAUtil;
class BinaryScreen : public Screen
{
public:
	BinaryScreen(Screen* returnTo);
	virtual ~BinaryScreen();
	void keyPressEvent(int keyCode);
private:
	void buildScreen();
	Screen* homeScreen;
	ListBox* listbox;
	int getPString(MAHandle stringResource, String& output, int startPos);
};
#endif /* BINARYSCREEN_H_ */

This BinaryScreen has a reference to the previous screen, so it can return. It also has a method for reading strings from the resource file.  This code reads sequentially through the data in the resource file and determines the type of widget it wants to create. It then builds that widget and set the data in it accordingly.

 

#include "BinaryScreen.h"
BinaryScreen::BinaryScreen(Screen* returnTo) : homeScreen(returnTo)
{
	buildScreen();
}

/*
 * The destructor.
 */
BinaryScreen::~BinaryScreen()
{
}

/*
 * Pressing soft right key shall display home screen.
 */
void BinaryScreen::keyPressEvent(int keyCode)
{
	switch(keyCode)
	{
	case MAK_SOFTRIGHT:
		homeScreen->show();
		break;
	}
}

/*
 * This will build screen dynamically at runtime with 
 * layout and list box.
 */
void BinaryScreen::buildScreen()
{
	// Get a basic screen set up from the UI Builder
	Widget* w = createScreen(BACKBUTTON, "Menu");
	
	// Get the layout from the created screen. Cast from Widget* to Layout*
	Layout* layout = (Layout*)w->getChildren()[0];
	
	// Get the listbox from the layout. Cast from Widget* to ListBox*
	listbox = (ListBox*)layout->getChildren()[1];
	
	// This is going to read through a binary resource to create widgets 
	// dynamically. These are variables required for decoding widgets.
	byte len;
	int position = 1; // Skip the first byte
	byte widgetType;
	byte completed = 0;
	String* text = new String();
	short imageLen;
	
	// Read the first byte. This determines the number of widgets.
	maReadData(BINARYDEMO, &len, 0, 1); 
	while(completed < len)
	{
		// Read the next byte. This is the type of widget
		maReadData(BINARYDEMO, &widgetType, position, 1);
		position++;
		switch(widgetType)
		{
		case 1: // Label
			position += getPString(BINARYDEMO, *text, position);
			listbox->add(createLabel(text->c_str()));
			break;
		case 2: // Button
			position += getPString(BINARYDEMO, *text, position);
			listbox->add(createButton(text->c_str()));
			break;
		case 3: // Image
			lprintfln("Creating image");
			
			// Get the length of the image.
			maReadData(BINARYDEMO, &imageLen, position, 4);
			position += 4;
			lprintfln("Image is %d bytes", imageLen);
			
			// Create a temporary placeholder
			MAHandle imagePlaceholder = maCreatePlaceholder();
			
			// Create an image from binary data
			maCreateImageFromData(imagePlaceholder, BINARYDEMO, position, imageLen);
			
			// Add the image to the listbox
			listbox->add(new Image(0, 0, 100, 100, NULL, true, true, imagePlaceholder));
			position += imageLen;
			lprintfln("Image created");
			break;
		}
		completed++;
	}
	this->setMain(w);
	delete text;
}

/*
 * This will read a Pascal string from the resources.
 */
int BinaryScreen::getPString(MAHandle stringResource, String& output, intstartPos)
{
	output.clear();
	byte len;
	
	// Check that there is at least one byte
	if(maGetDataSize(stringResource) == 0)
	return false;
	maReadData(stringResource, &len, startPos, 1);
	char* buffer = new char[len + 1];
	maReadData(stringResource, buffer, startPos + 1, len);
	buffer[len] = '\0';
	output.setData(new StringData(buffer));
	delete[] buffer;
	return len + 1;
}

The BinaryScreen has a handle helper which will create widgets for it, but you can see that it processes the resource data, looks at the widget type byte and extracts the necessary data to build the screen.

Building screens at runtime is much slower than having them built at compile-time, but it does make localisation easier, and potentially means that you only have to write one screen class, if this can handle all the content you may want.

GUI-Based Applications with MAUI

Using the MAUI library helps you develop powerful graphical user interface applications. You get access to a variety of ready-made user interface widgets and you can register different types of listeners with the widgets, thus responding to higher-level events.

MAUI development is based on the Moblet system, so to create an application with it you first need to make a moblet as shown in the previous example.

The fundamental building block of a MAUI application is called MAUI::Screen. Each screen has their own root widget. Each time show() is called on a screen, that screen is shown and all the others are hidden. When a screen is shown, it means that the root widget of that screen is made active. It can often be very useful to group your application into several screens in order to give your program a logical structure. When a screen is shown, key events are sent to that screen only. When a screen is hidden, each widget that belongs to it is disabled. This normally means that any timer events are unregistered.

Example: A Simple, Well-Behaved Application

Note: if you build this application in the IDE, make sure that you base it on the MAUI template or make sure that the Additional Libraries listed in the build configuration (Project > Properties > MoSync Project > Build Settings > Additional Libraries) include MAUtil.lib and MAUI.lib. You will also need to create and declare your font resources: see Working With Fonts.

#include <MAUtil/Moblet.h>
#include <MAUI/Screen.h>
#include <MAUI/Label.h>
#include "MAHeaders.h"
using namespace MAUtil;
using namespace MAUI;
class MyScreen : public Screen {
public:
	MyScreen() {
		label = new Label(
		0, // set the top-left corner
		0, // to be in the top-left corner of the lcd display.
		0, // the width and height are set to the width and height of the lcd display
		0, // automatically for the root widget in a screen.
		NULL, // this widget has no parent widget
		"Hello World!", // the caption of the label
		0, // use background color 0 (black)
		new Font(MY_FONT) // create a new instance of the font stored as the
		// resource MY_FONT and set the font of the label to that font.
		);
		setMain(label); // set the root widget to be the label we just created
	}
	void keyPressEvent(int key) {
		if(key == MAK_0) {
			maExit(0); // exit the application when key 0 has been pressed
		}
	}

private:
	Label *label;
};
class MyMoblet : public Moblet {
public:
	MyMoblet() {
		myScreen = new MyScreen();
		myScreen->show();
	}
private:
	MyScreen *myScreen;
};
// Since this is a C++ program, the main function
// needs to be declared extern "C"
extern "C" int MAMain() {
	Moblet::run(new MyMoblet());
}

Using Layouts (MAUI)

Layouts format your widgets on screen. They provide a grid into which all of your widgets can be aligned. Almost every screen you’ll create will have a Layout widget on it, and whilst they can’t do everything you can imagine, they can help you create some attractive and useful screens.

Basics of Layouts

The layout widget does display much in its own right, but it will organise your widgets on screen for you. You can specify a grid into which widgets will be placed. They will be added from top left to bottom right. Don’t confuse this too much with an HTML table. There are some similarities, but the Layout does not give you all the options an HTML table will.

When you create a layout, you specify the number of rows and columns you want.

Layout *mainLayout = new Layout(0, 0, 240, 320, NULL, 1, 2);

You cannot add more widgets than you’ve specified space for. It will not automatically grow as you add more. Use a ListBox if you want to do that.

If you have more than one column, then horizontal space is allocated evenly between them. You cannot have one column which is narrower than another.

Each widget can be given the right amount of space vertically though, but you do have to have just one column to do it. If you have a 2x2 grid, then each cell must be the same size as its neighbour on the same row.

Any specific spacing you’ve set for widgets in a layout will be ignored. You can’t offset a widget within its own cell. You have to make that widget larger if you want it to appear with a border, so the first two arguments when creating a child for a widget can be ignored.

You add widgets into the layout with the add(Widget* w) method.

 

/*
 * This program will generate a basic menu.
 */
Layout* createMainLayout(const char *left, const  char *right)
{
	Layout *mainLayout = new Layout(0, 0, scrWidth, scrHeight, NULL, 1, 2);
	Widget *softKeys = createSoftKeyBar(30, left, right);
	ListBox* listBox = new ListBox(0, 0, scrWidth, scrHeight-softKeys->getHeight(),
              mainLayout, ListBox::LBO_VERTICAL, ListBox::LBA_LINEAR, true);
	listBox->setSkin(gSkin);
	listBox->setPaddingLeft(5);
	listBox->setPaddingRight(5);
	listBox->setPaddingTop(15);
	listBox->setPaddingBottom(15);
	mainLayout->add(softKeys);
	return mainLayout;
}

This example comes from the MAUIex project supplied with MoSync. Widgets are added into the Layout from left to right and top to bottom. If you have a Layout with a 2x2 grid, the first widget you add will be placed top left, the second top right, the third bottom left and the fourth in the bottom right. If you want to skip a cell, then you have to add a blank widget.

Advanced Layouts

I’ve created a new Widget, which is a grid-based menu where each cell contains an icon and label. You may have seen these on some phones.

To do this, the Widget has inherited from Layout instead of Widget.  By doing this you can control some of the placements within your control, and make it really easy to reuse it.

 

#ifndef _GRID_MENU_H_
#define _GRID_MENU_H_
#include <MAUI/Layout.h>
#include <MAUI/Widget.h>
#include <MAUtil/Environment.h>
#include <MAUtil/Set.h>
using namespace MAUI;

/*
 * Interface class to call back to grid menu listener.
 */
class IGridMenuListener
{
public:
	virtual void GridMenuItemSelected(int item, Widget* selectedItem);
};

/*
 * This class will show the list of member variables and functions
 * that are required to form an advanced layout.
 * This is not a complete tutorial, some of the member functions which
 * can be quite helpful in creating advanced layouts, are
 * declared in the following sections.
 */
class GridMenu : public Layout, public KeyListener, public PointerListener
{
public:
	GridMenu(int x, int y, int width, int height, Widget* parent = NULL,
			 int rows = 3, int columns = 3);
	virtual ~GridMenu();
	void pointerPressEvent(MAPoint2d point);
	void pointerMoveEvent(MAPoint2d point);
	void pointerReleaseEvent(MAPoint2d point);
	void keyPressEvent(int keyCode);
	void keyReleaseEvent(int keyCode);
	bool handleEvents;
	void add(Widget* button);
	void addListener(IGridMenuListener* l);
	void setSelectedIndex(int index);
	int getSelectedIndex();
	void selectNext();
	void selectPrevious();
	void selectNextRow();
	void selectPreviousRow();
	void setEnabled(bool e);
	void drawWidget();
	void bindKeyLeft(int keyCode);
	void bindKeyRight(int keyCode);
	void bindKeyUp(int keyCode);
	void bindKeyDown(int keyCode);
	void bindKeySelect(int keyCode);
	void setPanelActive(int panelID, bool active = true);

private:
	IGridMenuListener* listener;
	Widget* selectedWidget;
	Widget* previouslySelectedWidget;
	int _rows;
	int _cols;
	int itemCount;
	int selectedIndex;
	void selectButton(int index);
	void informListener();
	void locateWidget(MAPoint2d, bool inform = false);
	bool setContains(Vector<int> s, int value);
	Vector<int> moveUpKeys;
	Vector<int> moveDownKeys;
	Vector<int> moveLeftKeys;
	Vector<int> moveRightKeys;
	Vector<int> selectKeys;
	int origW;
	Vector<int> activePanels;
};
#endif

At the top, there is a class IGridMenuListener, for a Screen to be able to attach to the Widget so it can be informed of changes to the menu, such as items being selected. This will appear again in another tutorial on programming Widgets for touch screens.

This class inherits from Layout, has code to capture screen and key presses and for navigation within the menu. It also has the essential drawWidget() method which is required when you create new widgets, although because we’ve inherited an existing Widget and we're not changing how it works, it isn’t essential.

There is a lot of other code to examine in the tutorials about creating your own widgets.

Below is some of the code for managing this advanced Layout. This is not a complete code example.

/*
 * Some of the member functions of GridMenu class are declared here.
 */
#include "GridMenu.h"
#include <conprint.h>
GridMenu::GridMenu(int x, int y, int width, int height, Widget *parent,
int rows, int columns): Layout(x, y, width, height, parent, rows, columns)
{
	// lprintfln("Inside menu constructor");
	origW = width;
	itemCount = 0;
	selectedIndex = -1;
	selectedWidget = NULL;
	listener = NULL;
	enabled = false;
	_rows = rows;
	_cols = columns;
	// set all the panels to active
	for(int i = 0; i < _rows * _cols; i++)
	{
		activePanels.add(1);
	}
	this->setPaddingLeft((width - this->getWidth()) / 2);
	this->setPaddingRight(5);
	this->setBackgroundColor(0xFF0000);
	this->setDrawBackground(true);
	// lprintfln("Menu constructed");
}

/*
 * The destructor for GridMenu class
 */
GridMenu::~GridMenu()
{
}

/*
 * The drawWidget member function.
 */
void GridMenu::drawWidget()
{
}

/*
 * The add function is used to add widgets to the layout.
 */
void GridMenu::add(Widget *button)
{
	if(button != NULL && itemCount < _rows * _cols)
	{
		Layout::add(button);
		itemCount++;
	}
	// lprintfln("button added");
}

/*
 * This function is used to select a widget.
 */
void GridMenu::selectButton(int index)
{
	// lprintfln("Selecting button %d", index);
	if(index <= itemCount && index > -1)
	{
		// lprintfln("Testing previously selected widget");
		if(selectedWidget != NULL)
		{
			selectedWidget->setSelected(false);
			previouslySelectedWidget = selectedWidget;
		}
		selectedWidget = getChildren()[index];
		// lprintfln("Selecting new button");
		selectedWidget->setSelected(true);
		// lprintfln("Updating index");
		selectedIndex = index;
	}
	requestRepaint();
}

/*
 * To navigate next item or widget in the index.
 */
void GridMenu::selectNext()
{
	// lprintfln("Current index: %d", selectedIndex);
	int nextItem = (selectedIndex + 1) % itemCount;
	// lprintfln("First option: %d", nextItem);
	while(activePanels[nextItem] == 0)
	{
		nextItem = (nextItem + 1) % itemCount;
		// lprintfln("Hide the next one and try %d", nextItem);
	}
	selectButton(nextItem);
}

/*
 * This will navigate to previous widget in the index.
 */
void GridMenu::selectPrevious()
{
	// lprintfln("Selecting previous");
	// lprintfln("selectedIndex: %d", selectedIndex);
	// lprintfln("itemCount: %d", itemCount);
	int nextItem = selectedIndex - 1;
	// lprintfln("nextItem: %d", selectedIndex);
	while(activePanels[nextItem] == 0)
	{
		nextItem--;
		if(nextItem < 0) nextItem = itemCount - 1;
		// lprintfln("Don't select nextItem: %d", nextItem);
	}
	selectButton(nextItem);
}

/*
 * This will navigate to next row in the grid.
 */
void GridMenu::selectNextRow()
{
	int nextItem = (selectedIndex + _cols) % itemCount;
	while(activePanels[nextItem] == 0)
	nextItem = (nextItem + 1) % itemCount;
	selectButton(nextItem);
}

/*
 * To navigate to previous row in the grid.
 */
void GridMenu::selectPreviousRow()
{
	int nextItem = (selectedIndex + itemCount - _cols) % itemCount;
	while(activePanels[nextItem] == 0)
	nextItem = (nextItem + itemCount - 1) % itemCount;
	selectButton(nextItem);
}

/*
 * The setSelectedIndex function sets the value for selected index.
 */
void GridMenu::setSelectedIndex(int index)
{
	selectButton(index);
}

/*
 * Returns the selected index value.
 */
int GridMenu::getSelectedIndex()
{
	return selectedIndex;
}

/*
 * The setPanelActive function can set a widget to be active or inactive.
 */
void GridMenu::setPanelActive(int panelID, bool active)
{
	activePanels[panelID] = active == true ? 1 : 0;
}

Unlike standard layouts, we don’t want every cell to be active. We don’t want all the cells to have a Widget in, and when you navigate you want it to skip the empty cells. To do this, there is a Vector<int> (although, looking at it now, this could be Vector<byte>), where it can mark cells as active or not.

Vector<int> activePanels;

When the menu is created, the panels can be set to active.

// set all the panels to active
for(int i = 0; i < _rows * _cols; i++)
{
    activePanels.add(1);
}

When a widget is added to the menu, it is added to the base Layout and to keep a track of the number of items which have been added.

void GridMenu::add(Widget *button)
{
	if(button != NULL && itemCount < _rows * _cols)
	{
		Layout::add(button);
		activePanels.add(1);
		itemCount++;
	}
	// lprintfln("button added");
}

When the user navigates the Layout, we can check to see if the panel is active or not, and decide whether it should be skipped.

void GridMenu::selectNext()
{
    // lprintfln("Current index: %d", selectedIndex);
    int nextItem = (selectedIndex + 1) % itemCount;
    // lprintfln("First option: %d", nextItem);
    while(activePanels[nextItem] == 0)
    {
    	nextItem = (nextItem + 1) % itemCount;
	// lprintfln("Hide the next one and try %d", nextItem);
    }
    selectButton(nextItem);
}

Keep moving onto the next panel (going back to the start if necessary) until we find one which is active.

When we move up or down a row, we have to do the same thing:

void GridMenu::selectNextRow()
{
    int nextItem = (selectedIndex + _cols) % itemCount;
    while(activePanels[nextItem] == 0)
    nextItem = (nextItem + 1) % itemCount;
    selectButton(nextItem);
}

So, when creating the menu Layout, you can set which panels you want to use.

// Creates an iPhone-style grid menu
Widget* createGridMenu(IGridMenuListener* listener)
{
    GridMenu* gridMenu = new GridMenu(0, 0, scrWidth, scrHeight, NULL);
    gridMenu->setBackgroundColor(0xFFFFFF);
    gridMenu->setDrawBackground(false);
    setLabelPadding(gridMenu);
    gridMenu->addListener(listener);
    Font* f = new Font(FONT);
    gridMenu->add(createImage(BLANK));
    gridMenu->add(createImage(BLANK));
    gridMenu->add(createMenuImage(THEMESICON, "Themes", f, aSkin));
    gridMenu->add(createImage(BLANK));
    gridMenu->add(createMenuImage(MAPICON, "Map", f, aSkin));
    gridMenu->add(createMenuImage(FRIENDSICON, "Friends", f, aSkin));
    gridMenu->add(createMenuImage(EVENTSICON, "Events", f, aSkin));
    gridMenu->add(createMenuImage(SETTINGSICON, "Settings", f, aSkin));
    gridMenu->add(createMenuImage(MESSAGESICON, "Messages", f, aSkin));
    gridMenu->setPanelActive(0, false);
    gridMenu->setPanelActive(1, false);
    gridMenu->setPanelActive(3, false);
    gridMenu->setSelectedIndex(2);
    return gridMenu;
}

At the end of this, we are setting three panels to be inactive, as you can see from the picture. There is a 3x3 layout, but now it acts like a pyramid.

HelloMAUI

HelloMAUI is a well-commented example application for beginners. It consists of a very simple graphical user interface application that uses the MAUI library and Moblet framework. It illustrates how to create MAUI screens, and how to position and control widgets.

  
  

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When run, the user is presented with a simple MAUI screen with the following widgets:

  • a label widget with the instructions "Enter a password:"
  • an edit box operating in password mode,
  • two label widgets skinned as buttons.

When the user types using the keypad keys the characters are echoed in the edit box. On clicking the Submit button, the password is validated. Examine the source code of the application (in the file hellomaui.cpp) to learn how the program works. The code commenting highlights various aspects of working with MAUI screens and widgets, including:

  • How to define a screen,
  • How to identify the root of a screen's widget hierarchy,
  • How to use a layout widget to arrange other widgets,
  • How to define widgets and use their methods,
  • How to use skin and font resources,
  • How to shift focus and highlight widgets,
  • How to detect events and respond to them.

For more advanced uses of MAUI, see our MAUIEx application example.

To understand how MAUI and the NatuveUI Widget API (an alternative GUI solution that makes use of each platform's user interface controls), compare this example application with HelloNativeUI.

Key Presses

  • Tapping the screen's Clear button - clears the edit box.
  • Tapping the screen's Submit button - validates the contents of the edit box.
  • 2-9 keypad keys - types text in multi-tap mode.
  • Up/down arrow keys - moves between buttons/edit box.
  • 0 or right-softkey - exits the program.

MAUIEx

This application demonstrates the variety of widgets available in the MAUI library and how to use the ListBox and Layout widgets to arrange widgets on a screen.

 

This example is included in the MoSync SDK's /examples/MAUI folder. For information on importing the examples into your workspace, see Importing the Examples. 

Behaviour

When the application has started the user is presented with a simple menu. Following the menu items takes you to other screens that demonstrate different widgets.

The layout of each screen is controlled by a combination of Layout and ListBox widgets. For example, for the Layout screen:

It is worth taking some time to examine the code of the MAUIex example to understand how the Layout and ListBox widgets work together for each screen.

The following screens are available:

  • "Label / ListBox" — Shows three Label widgets in a vertical ListBox widget. The ListBox enables navigation between the Labels.
  • "Image" — Shows an simple Image widget.
  • "EditBox" — Shows three EditBox widgets in different typing modes: Single line, multiline and number input.
  • "Layout" — Shows 9 Image widgets arranged in a layout in a Layout widget.
  • "Custom" — Shows a digital/analog clock created as a custom widget.

For a comprehensive description of MAUI, see our tutorial called Introduction to MAUI.

Key Presses

  • Left Ctrl, Left softkey or the Fire key — opens the highlighted menu item.
  • Up and Down — moves up and down in the menu.
  • Click pointer on an unselected menu item — highlights it.
  • Click pointer on a highlighted menu item — opens it.
  • 0, Right softkey, or Exit — closes the application.

 

XML, Web Services

MTXml Parser

MoSync's Tiny XML parser, MTXml, provides an efficient, callback-based way of parsing XML files. MTXml has a SAX-like interface, and is re-entrant: it can start with just the beginning of an XML document and request additional data when needed.

MTXml can handle most XML 1.0 and 1.1 documents. However, in the interests of performance, MTXml is not a conforming XML processor, as defined by the W3C Recommendation. It does not validate documents, and it only checks a few of the well-formed-ness criteria. It even ignores some "fatal errors". Still, it should properly parse a well-formed document.

There are two modes of parsing. One, mtxFeedProcess(), is slower, and converts standard entities and UTF-8 characters to Latin-1 before passing them to the application. The other, mtxFeed(), is faster, and passes such characters without conversion. If you know which parts of your document need conversion and which ones don't, you can do the conversion manually, using mtxProcess().

Example

Here's a minimal C program that uses MTXml.

#include <MTXml/MTXml.h>
#include <conprint.h>
#include <maassert.h>

// For each callback, print its type and parameters.

static void tagStart(MTXContext* context, const void* name, int len)
{
	printf("s %i: \"%s\"\n", len, (char*)name);
}

static void tagAttr(MTXContext* context, const void* attrName, const void* attrValue)
{
	printf("a \"%s\": \"%s\"\n", (char*)attrName, (char*)attrValue);
}

static void tagData(MTXContext* context, const void* data, int len)
{
	printf("d %i: \"%s\"\n", len, (char*)data);
}

static void tagEnd(MTXContext* context, const void* name, int len)
{
	printf("e %i: \"%s\"\n", len, (char*)name);
}

static void dataRemains(MTXContext* context, const char* data, int len)
{
	printf("r %i: \"%s\"\n", len, data);
}

static void parseError(MTXContext* context, int offset)
{
	printf("parseError %i\n", offset);
}

static void emptyTagEnd(MTXContext* context)
{
	printf("emptyTagEnd\n");
}

// The XML document is declared here, but not defined.
// Fetching the document is beyond the scope of this example.
extern char gDocument[];

int MAMain(void) GCCATTRIB(noreturn);
int MAMain(void)
{
	MTXContext c;
	printf("Hello World!\n");

	// Set up the context.
	c.tagStart = tagStart;
	c.tagAttr = tagAttr;
	c.tagData = tagData;
	c.tagEnd = tagEnd;
	c.dataRemains = dataRemains;
	c.parseError = parseError;
	c.emptyTagEnd = emptyTagEnd;
	c.unicodeCharacter = mtxBasicUnicodeConvert;
	mtxStart(&c);

	// Perform the parsing.
	mtxFeed(&c, gDocument);

	// Wait for the user to exit.
	FREEZE;
}


tagStart() is called when an XML tag starts. tagAttr() is called for each attribute of a tag. tagData() may be called multiple times, to handle pieces of character data inside a tag. tagEnd() is called at the end of each tag. However, if the tag is empty (looks like this: <someTag/>), emptyTagEnd() is called instead of tagEnd().

parseError() is called if the parser encounters something it cannot parse. When that happens, parsing stops. It should not be restarted without resetting the context with mtxStart().

dataRemains() is called if the parser encounters a partial object at the end of the buffer supplied to it. The application should copy that part to the beginning of the buffer and fill the buffer with more data, so that the object may be fully parsed later. In this example, we have a complete XML document available (the external variable gDocument), so dataRemains() is not called.

Please note that this program, as written, will not build, because gDocument is not defined. If you want to compile it, you'll have to supply an XML document to parse.

void

MTXml Wrapper

There is a C++ wrapper for the C-based MTXml parser. It can be used in many ways, but one of the most useful ways is by coupling it to a Connection. This allows the CPU-intensive parsing to be split up into more than one call, spreading the load and improving the application UI's response times. Here we provide an example of how to implement such an XmlConnection.

Example

xmlconnection.h

#ifndef XMLCONNECTION_H
#define XMLCONNECTION_H
 
#include <MAUtil/Connection.h>
#include <MTXml/MTXml.h>
 
class XCListener 
{
public:
    // Called when there is a connection error. Parsing stops.
    virtual void xcConnError(int code) = 0;
};
 
class XmlConnection : private MAUtil::ConnectionListener, Mtx::MtxListener 
{
public:
    XmlConnection();
 
    // Inits an Mtx::Context, sets itself as ConnectionListener,
    // starts recieving data, which is passed on to the XML parser.
    // Callbacks from the parser are passed on to the XmlListener.
    //
    // The connection must be ready to recieve data, which means that you
    // must have recieved either of two callbacks:
    // MAUtil::ConnectionListener::connectFinished() or
    // MAUtil::HttpConnectionListener::httpFinished().
    //
    // You also must not have a read() or recv() operation active.
    void parse(MAUtil::Connection* conn, XCListener* xc, Mtx::XmlListener* xml);
 
    // Stops parsing and closes the connection.
    // \see mtxStop().
    void stop();
 
    // see mtxProcess().
    int process(char* data);
 
private:
    Mtx::Context mContext;
    MAUtil::Connection* mConn;
    XCListener* mXc;
    char mBuffer[1024];
    char* mPtr;
 
    void mtxDataRemains(const char* data, int len);
    void connRecvFinished(MAUtil::Connection* conn, int result);
};
 
#endif  //XMLCONNECTION_H

xmlconnection.cpp

#include "XmlConnection.h"
 
XmlConnection::XmlConnection() : mConn(NULL) 
{
}
 
void XmlConnection::parse(MAUtil::Connection* conn, XCListener* xc, Mtx::XmlListener* xml) 
{
    mConn = conn;
    mXc = xc;
    mContext.init(this, xml);
    mConn->setListener(this);
 
    mPtr = mBuffer;
    mConn->recv(mBuffer, sizeof(mBuffer)-1);
}
 
void XmlConnection::connRecvFinished(MAUtil::Connection* conn, int result) 
{
    MAASSERT(conn == mConn);
    if(result < 0) 
    {
        mXc->xcConnError(result);
        return;
    }
 
    mPtr[result] = 0;
    mPtr = mBuffer;
    bool stopped = mContext.feed(mBuffer);
    if(!stopped) 
    {  
        //parsing may have been interrupted by stop().
        mConn->recv(mPtr, sizeof(mBuffer) - 1 - (mPtr - mBuffer));
    }
}
 
void XmlConnection::mtxDataRemains(const char* data, int len) 
{
    if(mBuffer != data) 
    {
        memcpy(mBuffer, data, len);
    }
    mPtr = mBuffer + len;
}
 
void XmlConnection::stop() 
{
    if(mConn != NULL)
        mConn->close();
    mContext.stop();
}
 
int XmlConnection::process(char* data) 
{
    return mContext.process(data);
}

The trick here is using a static buffer to recieve data, pass it to the parser, have the parser pass the standard XML events to the application, and handle mtxDataRemains(). Any remaining data is copied to the front of the buffer, and the next recv() starts writing where the remaining data ends.

This is streaming XML. :)

Processing XML

If you are writing cross-platform mobile apps in the MoSync® SDK, you will probably want at some point to connect to the Internet to get data. Typically, this data will be in XML or in JSON format. If you are getting XML data, the MoSync SDK has a built in XML parser (MTXml) you can use to parse it. There are also string functions in the MoSync MAUtil library which can be used to read XML in some simple cases. In this tutorial we take a look at retrieving XML data and the various ways of reading the data from it.

Ways of Getting XML Data

There are several ways to get XML data into your application:

  • Read it from a store saved on the device.
  • Create it within your application using the techniques described in our blog post Creating XML Documents. 
  • Download it from the Internet using the MoSync Connection class. Part of this tutorial will look at how you can do this, and even start processing XML before it has finished downloading.

 Note that this tutorial covers XML only. If you are working with JSON data, we have a blog post called Working with JSON Data that describes how to handle it.

Reading XML Using String Functions

If you've got simple requirements for XML, then you can read it very easily. Take this example from the Yahoo! XML format for weather data.

<title>Conditions for Carcassonne, FR at 10:00 am CEST</title> <geo:lat>43.22</geo:lat> <geo:long>2.35</geo:long> 

If we just want to get the latitude out of this data, we don't need to parse the complete XML.

String xml = readXml();//Build a string with the xml in it
int start = xml.findFirstOf("<geo:lat>") + 9;
int end = xml.findFirstOf("</geo:lat>", start);
String lat = xml.substr(start, end - start);

We can use the standard String functions to get the values out of the XML. The start of the data can be found by getting the position of <geo:lat>. 9 is added to this value, which is the length of the string we're looking for. The value of start is now the number of characters from the start to the beginning of the latitude value. The end of the value is found in a similar way, but we don't need to add any additional characters. Finally, the value of latitude can be extracted using the substring function (substr), passing it the start location and the length of the data to extract (end - start).

SAX Parser and DOM Parsers

Broadly speaking, there are two ways to handle XML documents: via the DOM or via SAX.

If you load the XML into an object model that represents the XML structure, you can then look at the properties of each XML element in the model and navigate the model with an XPATH. This is very common approach for manipulating XML and is called using a Document Object Model or DOM. Java and C# developers will be very used to dealing with XML like this. 
It is easy to use this approach, but can use a lot of memory. If you have a burning need for DOM processing, read the blog post on our website which details a lightweight DOM approach to XML with the MoSync SDK.

<myTag>

it will raise an event to say that it has found an open tag. The program listening to events, or receiving the callback can then decide whether or not to do something with this knowledge.

The MTXml Parser

The MoSync SDK comes with our open source SAX parser called MTXml (the MoSync Tiny XML Parser). 

To use the MTXml parser, you need to include the MTXml.lib file in your project dependencies. To include the MTXml.lib file:

  1. Right-click on your project in Project Explorer in the MoSync IDE and select Properties.
  2. Select MoSync Project and then Build Settings
  3. In the box Additional Libraries you will see the list of libraries you are already using. Add MTXml.lib to this list. If you've got any other .lib files in the Additional Libraries box, then put a comma between each of them. You will probably already have MAUtil.lib and MAUI.lib.
  4. Click OK.

You can parse XML from a variety of sources. Most likely this will be from an Internet connection, processing XML as it is being downloaded.  The MoSync XML API can start parsing XML from a connection before it has completely downloaded.

To process XML, the minimum you need to do is to create a C++ class which inherits from MTXml/MTXml.h and implements XmlListener. This will contain the callback classes which the XML processor will call when it finds the appropriate XML. 

You must implement all of the methods in XmlListener. If you leave any methods unimplemented, the program may crash if a call to an unimplemented method is made. Just create an empty method, or in the case of mtxunicodeCharacter, use the standard implementation.

This is an example from a class which is designed to process XML data. It implements the XmlListener interface, which allows it to process XML. 

class XMLProcessor : public XmlListener
{
  public:
  XMLProcessor();
  ~XMLProcessor();

  //XmlListener
  void mtxEncoding(const char* value);
  void mtxTagStart(const char* name, int len);
  void mtxTagAttr(const char* attrName, const char* attrValue);
  void mtxTagStartEnd();
  void mtxTagData(const char* data, int len);
  void mtxTagEnd(const char* name, int len);
  void mtxParseError();
  void mtxEmptyTagEnd();
  unsigned char mtxUnicodeCharacter(int unicode);
}

Managing XML State

When you are processing XML you need to keep track of where you are in the file. If your code is processing a complex XML file, you may want to create some sort of state handling. The calls between the callbacks are essentially stateless, and your code has to be robust enough for them to be called in any order -- even in an unexpected order.

The critical parts of the code are the mtxTag... methods. These are called when the processor gets the XML elements. Generally, you are passed the value which has been processed.

We can process the XML we looked at earlier with these mtxTag... methods. Here is the XML again:

<title>Conditions for Carcassonne, FR at 10:00 am CEST</title> <geo:lat>43.22</geo:lat> <geo:long>2.35</geo:long> 

The mtxTagStart method is passed the name of the tag. For instance, if the XML contained <title> the mtxTagStart would be called with 'title'. We can examine these values at runtime.

void XMLProcessor::mtxTagStart(const char* name, int len)
{
  lprintfln("%s", name);
  lprintfln("%d", len);
}

When this code is called for the <title> tag, it will display 'title' and 5 in the console.

To be able to read the <title> tag, we need to maintain the state of the XML. When the mtxTagData method is called, it needs to know which tag the data is for. A good way of doing this is by keeping a variable with different values you can test for.

One way of doing this is to create an enum of the different tags you want to process. This code shows an enum representing the tags in the XML, and a private int variable which will keep state:

enum XmlTags
{
  TITLE,
  LATITUDE,
  LONGITUDE
};
int mState; 

When you receive a tag, you can then set the mState variable.

void XMLProcessor::mtxTagStart(const char* name, int len)
{
   if(strcmp(name, "title", 5) == 0) //Compare the char* with the word 'title'
   {
      mState = TITLE;
   }
}

In the next section, we will see how we can use this state information when reading data.

Reading Data from XML

When data inside an XML tag is received, the mtxTagData method in your XmlListener will be called.  As an example, assume you have this data:

<title>Conditions for Carcassonne, FR at 10:00 am CEST</title> <geo:lat>43.22</geo:lat> <geo:long>2.35</geo:long> 

If we imagine that this is the complete feed, then the mtxTagStart method will be called three times, with 'title', 'geo:lat' and 'geo:lon'. 

The mtxTagData method will be called at least three times, once with 'Conditions for Carcassonne, FR at 10:00 am CEST', once with '43.22' and once with '2.35'.

mtxTagData may actually be called more than this. If the data source is coming from the Internet, then it may be called before then end of the data. Imagine that the packet we get from the connection contains this data.

<title>Conditions for Carcassonne, FR at 10:00 am CEST</title> <geo:lat>43.2

This is processed, and the XML callbacks are made. The call to mtxTagData for the 'geo:lat' tag will contain '43.2' at this point, and not '43.22'

We then get some more data:

2</geo:lat> <geo:long>2.35</geo:long> 

mtxTagData will now have been called four times. In fact, you'll never want to start processing in the mtxTagData method. Save it for mtxTagEnd. A good implementation for mtxTagData would be:

void XmlProcessor::mtxTagData(const char* data, int len)
{
  //Add the data to the buffer, but don't process until we've got the end tag
  temp.append(data, len);
}

Where 'temp' is a String. This will continue to fill up until the end tag is reached.

Because we've been using the mState variable to hold state information, we know how to deal with this data. In mtxTagEnd we can use the data we collected in mtxTagData, and the state information we've set up in mtxTagStart to do something useful with the data.

void XmlProcessor::mtxTagEnd(const char* name, int len)
{
    switch(mState)
    {
    case TITLE:
    // At this point, we know we are in the <title> tag, and that we've 
    // received all the data it contains.
    mMyClass->title = temp;
    // Use the contents of the string 'temp' we've populated in mtxTagData.
    break;
    case LATITUDE:
    break;
    case LONGITUDE:
    break;
  }
}

You can now perform different operations on the data you have got with the state handling you've created.

Soap

This application demonstrates connecting to a Web Service using SOAP. It contains a class called SoapRequest which formats an easily modifiable XML Soap request, and a Moblet to test the SOAP communication using a currency converter as an example.

 

This example is included in the MoSync SDK installation in the /examples folder. For information on importing the examples into your workspace, see Importing the Examples.

Behaviour

When started, the application makes a call to Joel Hainley's Rot13 SOAP service, passing it the string "Hello World!" Rot13 is a simple substitution cypher, and therefore the service returns the string "Uryyb Jbeyq!"

The application demonstrates how to correctly format a SOAP request, and process a response from the web server.  It can be easily modified to work with any web service. The screenshot above shows details of the commands being sent over SOAP and the responses, ending with a normal soapError of -6, indicating that the underlying HTTP connection has been closed.

Key Presses

  • 0 or right softkey  — closes the application.
  • Other keys have no effect