MoSync Documentation

Welcome to the MoSync Mobile Developer Center. The documentation for MoSync includes many guides and tutorials to give mobile developers a better start, we also have reference manuals, glossaries, and app examples. Here we provide an overview of all the documentation available for our latest feature releases, including how to install MoSync, how to start up and use the MoSync IDE, how to create mobile apps with it and how to build your mobile applications for multiple devices. Our mobile developer documentation is divided into the following  sections.

Quick Links: API Reference Manual | Panics Reference | Example Applications

What is MoSync?

The MoSync SDK and its main components, what you can do with it, and who uses it

Getting Started with MoSync

How to get and install MoSync, start it, and create your first application

Using the Mosync IDE

How to use the IDE and device profiles, how to build your mobile applications

Programming with MoSync

How to use MoSync libraries to create applications in C and C++

Lessons and Tutorials

Step-by-step walkthroughs of common MoSync tasks and programming activities

MoSync API Reference Manuals

Detailed information about the MoSync function libraries and panics

 

Developing MoSync

How to build MoSync from source, coding conventions, writing extensions, and contributing to MoSync

More Resources

More information about common issues, using MoSync, and programming with MoSync

As you move through the documentation you will find full navigation menu in the right sidebar. You can leave comments on all pages. Please feel free to tell us about your mobile development experiences, pass on tips and code examples, and to let us know of any errors you find in the documentation.

What is MoSync?

Today's mobile device market is more fragmented than ever. New platforms are introduced every year. Dozens of new devices arrive from manufacturers every month. Sometimes it can seem that writing your application is the easy part: the real headache is tailoring it for all those different platforms and devices, and trying to keep up with the ever-changing marketplace.

One Solution For All Platforms
 We think that mobile application development is hard enough without having to worry about porting issues. That's why we've created the MoSync® Mobile SDK — a truly open-source solution for today's fragmented mobile market.

The MoSync SDK is a fully-featured software development kit helps you develop anything from simple programs for basic mobile phones to advanced applications that exploit the full potential of the latest intelligent smart phones and mobile devices.

And with MoSync you can adapt, build, and package your application for hundreds of different mobile devices in a few clicks - all from the same code base! That means huge savings in development costs, faster time-to-market, and wider distribution and revenue possibilities.
Develop for multiple mobile platforms simultaneously 
Build and package for hundreds of mobile devices 
Gain huge savings in porting and testing costs 
Faster time-to-market and a quicker return on investment 
And it's open-source! 

Fully-Featured SDK

Mosync Roadmap

Comprehensive Libraries

Extensive Mobile Platform Support

Highly Active Development Community

MoSync is developed by a dedicated and enthusiastic team of developers who are committed to making the development of mobile applications faster, easier, and more cost-efficient. MoSync development is very active — check our home page at www.mosync.com and see how often we commit! We'll are adding new mobile platforms, device profiles, and library functions all the time.

And It's Open Source!

MoSync is distributed under the standard GNU General Public License (GPL) version 2. That means that it is completely free for private use. You can even sell your applications commercially, as long as you make your source code available to others.

For individuals, businesses, and organizations who wish to develop MoSync-based applications but who do not wish to publish their source code, we offer a range of commercial subscriptions.

Feature/Platform Support

Major platforms features supported by MoSync 2.7.Pyramid

Key

Available in the featured release, tested Available in the latest nightly build, may or may not be fully tested
Coming soon but not yet available Not possible due to platform limitations

Platform-Dependent Features

Feature/Platform MoRE (MoSync emulator)
Java ME Windows Mobile
Symbian S60 2nd, 3rd, 5th
Android 1.5-2.3.3
iOS  (iPhone, iPad, iPod Touch)Moblin/
MeeGo
Blackberry
Basic graphics
Basic audio
Permanent storage
Numeric keypad input
n/a
Native text box input
Qwerty keypad input
Stylus/touch input 5th:
Socket communication
HTTP communication
SSL Socket communication
HTTPS communication
Bluetooth discovery 2.x:
Bluetooth communication 2.x:
Bluetooth server2.x:
Location 3rd/5th:
PIM 3rd/5th:

File system access
Camera access 2.x: 

Locale (639-1 or 639-2)Win32:
IMSI/IMEI
Sending SMS

Dynamic program loading
Framebuffer drawing
Vibration
Streaming Video/Audio
Sensors
    
 - Accelerometer     
 - Proxomity     
 - Gyroscope     
 - Orientation     
 - Magnetic field     

Audiobuffer

OpenGL ES 1.0

 
 
 
 
 

OpenGL ES 2.0


NFC

Device Fonts

Native UI (Widget API)

 
 
 
 
 

HTML5 (Wormhole)

Sound codecs (some devices may have more codecs preinstalled) Wav, MP3, AMR Wav, MP3, AMR Wav Wav, MP3, AMR AAC, AMR, Wav, MP3, OGG, MIDI AAC, AIFF, MP3, Audible, Apple Lossless, WAV Wav, MP3, AMR MP3, AAC, MIDI, AMR, WAV, WMA

Platform-Independent Features

Feature
Eclipse-based IDE
Advanced device emulator
Resource management
Device profiles database
Extensive documentation
Fully integrated debugger
Selected standard C libraries
Custom UI (MAUI)
OOP TCP Communications
OOP HTTP Communications
SOAP Communications
JSON/REST layers
Twitter API
Collection library
XML parsing libraries
Font libraries/converters
Map rendering API
On-device error reporting
Batch building (finalizer)
Command line packager
Standard C Library
STL (Standard C++ Library)

Feature Support and Nightly Builds

We are constantly adding support for new platforms and new features in MoSync. Features denoted as are to be found in the nightly builds, rather than in the current featured download.

Our nightly builds are Windows binaries and corresponding source, generated by an automatic timer-based build process. If the build fails, no files are generated. Therefore, a feature can be developed and committed to the repository several days before it shows up in the nightly builds, and longer in a featured build.

If you're really up to it, you can always pick up your MySync directly from the source, but then you need to compile it yourself. To do so you need to get the source code from our public repository:

git clone https://github.com/MoSync/MoSync.git 

You can also find instructions for building MoSync on Windows here and on Mac OS X here.

The MoSync SDK

The MoSync® SDK is a complete software development kit for mobile application development. It includes many tightly integrated components — compilers, runtimes, libraries, device profiles, tools, and utilities — all carefully integrated to form a most powerful open-source cross-platform mobile development SDK ever.

Mobile development with unfamiliar tools, languages, and APIs are a thing of the past. MoSync gives you standard C/C++, easy-to-use well-documented APIs, and a full-featured Eclipse-based IDE.

You'll hardly notice you're developing for mobile devices!

The MoSync SDK simplifies the development of mobile applications across multiple platforms. Our unique transformation engine, Pipe-Tool, allows your single code base to be efficiently executed on an endless variety of existing and emerging platforms.

We've developed our own GCC backend that produces MoSync Intermediate Language, which is fed into Pipe-Tool and converted to a number of platform-specific executable forms. The profile database guides the process, ensuring that the application is precisely adapted to each device. The various runtimes provide consistent behavior regarding graphics, audio, communications, input and other device features.

MoSync platform creates magic for most phones

The MoSync IDE is based on Eclipse, allowing you to leverage its powerful editing capabilities. From main development using the PC runtime environment, through testing on a few phones to finalizing the build for hundreds of devices, the MoSync IDE provides support at every stage. Read more

MoSync platform creates magic for most phones

MoRE is the reference implementation of MoSync. It's a PC application that executes MoSync bytecode and looks and feels like a phone. However, it is not an emulator for any particular phone. It's rather the phones that emulate MoRE (MoSync Reference Environment) - not the other way around! Read more

MoSync platform creates magic for most phones

The device profile database contains information about hundreds of mobile devices - everything from screen sizes and memory amounts to obscure bugs and undocumented quirks. It can be used to tailor your application to different devices or create fallbacks for unsupported features. Read more

MoSync Architecture Thumbnail

MoSync uses a custom GCC backend that outputs MoSync Intermediate Language. This is fed into Pipe-tool, our transformation engine, which builds code trees, analyzes, optimizes and finally outputs either MoSync bytecode or a generated runtime core. Read more

MoSync platform creates magic for most phones

MoSync runtimes are libraries or programs that execute MoSync programs on a given target device. They also provide a uniform interface to low-level system APIs, including graphics, audio, event handling and communications. Read more

The Eclipse IDE

The MoSync IDE is based on Eclipse, allowing you to leverage its powerful editing capabilities. From main development using the PC runtime environment, through testing on a few phones to finalizing the build for hundreds of devices, the MoSync IDE provides support at every stage.

Build System

A MoSync application is built, targeting a device profile, using gcc and pipe-tool. First, pipe-tool is used to compile any resources included in the application. Subsequently gcc is called, using the settings in the application project. The path to the target device's profile is passed to gcc, which uses it to preprocess the application being built. Finally, Pipe-Tool is called to link and assemble the program.

The Finalizer

The finalizer is a batch build system that allows you to automatically build your application for hundreds of devices. Once you've applied a number of criteria to your project, such as requiring a minimum screen resolution or the availability of a certain set of features, the finalizer automatically generates a build script for the devices that fulfil these criteria. It organizes the builds into neat, hierarchical structures that requires very little processing in order to comply with the required deployment structures.

MoSync Finalizer

The Toolchain

MoSync uses a custom GCC backend that outputs MoSync Intermediate Language. This is fed into Pipe-tool, our transformation engine, which builds code trees, analyzes, optimizes and outputs bytecode or java ready for packaging with the appropriate runtime.

The MoSync Build Process

When you finalize your application, the MoSync IDE manages the entire build process for you, activating all the tools in the MoSync toolchain with the appropriate options and settings. The IDE uses GCC to compile your code and Pipe-Tool to link it with the MoSync libraries and your resources. The IDE then combines the resulting bytecode or java source code with the appropriate platform runtime to create the executable packages for each target device.

If needed, control over the options and settings for compilation, linking, and packaging is available through the build configurations for your project.  It is also possible of course to run GCC and Pipe-Tool individually from the command line.

GCC

MoSync's implementation of GCC (the GNU Compiler Collection) compiles your program into an assembly-like format called MoSync Intermediate Language (MoSync IL).

The GCC backend defines a simple machine with a variable number of 32-bit registers and a flat memory model. Currently we are using GCC 3.4.6. For more information about GCC, see http://gcc.gnu.org/onlinedocs.

MoSync IL contains detailed metadata about your program, including symbol mappings and control flow information. MoSync IL is extremely rich, so much so in fact that you could pretty well convert it back to its source form if you wanted to.

Pipe-Tool

Pipe-Tool is our transformation engine. It combines the functions of a resource compiler, linker, and code optimizer.

Pipe-Tool combines the MoSync IL output from GCC with the pre-compiled MoSync libraries into various target formats, including MoSync bytecode, Java bytecode, and C/C++ source code, ready for packaging. In the process, it also performs code verification, optimization, and dead code elimination to produce highly optimized outputs.

Pipe-Tool also compiles and indexes the external resources (images, audio files, binary files, etc.) that your application needs. For more information, see our Resource Compiler Reference.

The MoSync Build Process

When you finalize your application, the MoSync IDE manages the entire build process for you, activating all the tools in the MoSync toolchain with the appropriate options and settings. The IDE uses GCC to compile your code and Pipe-Tool to link it with the MoSync libraries and your resources. The IDE then combines the resulting bytecode or java source code with the appropriate platform runtime to create the executable packages for each target device.

If needed, control over the options and settings for compilation, linking, and packaging is available through the build configurations for your project.  It is also possible of course to run GCC and Pipe-Tool individually from the command line.

GCC

MoSync's implementation of GCC (the GNU Compiler Collection) compiles your program into an assembly-like format called MoSync Intermediate Language (MoSync IL).

The GCC backend defines a simple machine with a variable number of 32-bit registers and a flat memory model. Currently we are using GCC 3.4.6. For more information about GCC, see http://gcc.gnu.org/onlinedocs.

MoSync IL contains detailed metadata about your program, including symbol mappings and control flow information. MoSync IL is extremely rich, so much so in fact that you could pretty well convert it back to its source form if you wanted to.

Pipe-Tool

Pipe-Tool is our transformation engine. It combines the functions of a resource compiler, linker, and code optimizer.

Pipe-Tool combines the MoSync IL output from GCC with the pre-compiled MoSync libraries into various target formats, including MoSync bytecode, Java bytecode, and C/C++ source code, ready for packaging. In the process, it also performs code verification, optimization, and dead code elimination to produce highly optimized outputs.

Pipe-Tool also compiles and indexes the external resources (images, audio files, binary files, etc.) that your application needs. For more information, see our Resource Compiler Reference.

The Device Profile Database

The device profile database contains information about hundreds of mobile devices - everything from screen sizes and memory amounts to obscure bugs and undocumented quirks. It can be used to tailor your application to different devices or create fallbacks for unsupported features.

Profile Database

Our device profile database is stored as a hierarchical filesystem structure - one directory per device vendor, each containing per-device subdirectories. Each device directory contains a C header file with information about the device formatted as C preprocessor definitions, and a textual reference to the associated runtime. The profile header file can be used in MoSync programs to automatically modify their functionality depending on the target device: Common information in profiles include vendor and device names, screen size and API availability, for example Bluetooth or Location.


if-defs sample

The MoSync Emulator (MoRE)

The MoSync Runtime Environment (MoRE) is the reference implementation of MoSync. It’s an application that executes MoSync bytecode and looks and behaves like a phone. But it is not an emulator of any particular phone - it’s the phones that emulate MoRE, not the other way around!

Using For full instructions on using the MoSync Emulator, and for installing and using native emulators on iOS and Android, see Emulating a Device.

A New Angle

In traditional mobile development, being able to access an emulator that faithfully mimics  the behavior of an actual devices is crucial: being able to catch bugs early before moving to testing on real physical devices significantly reduces the amount time spent porting to each device, and speeds up application development.

But at MoSync AB we don't think that's good enough. Development time shouldn't be proportional to the number of devices targeted at all. Instead, it should be related to the number of differences between the devices that really matter for your application - things like screen size, memory capacity, input methods, or API availability. You should be able to adapt your project to these variations independently, not based on arbitrary combinations of properties exhibited by actual devices.

MoRE is designed to meet these requirements. It can be configured with any combination of properties independently. Set it to any screen size you desire. Let it have Bluetooth support and 16 Mb of memory. Toggle stylus support. If you like, tell it to have the same  properties as a Nokia N95 or Sony Ericsson k700i. Whatever properties you set, the MoSync SDK makes sure that the behavior you observe in MoRE regarding those properties is consistent with that of any device that exhibits them.

A Reference for Devices

We're approaching the problem from the opposite direction; instead of making an emulator that faithfully reproduces the behaviour of every single device, we make sure that each MoSync implementation reproduces the behaviour of MoRE. We've developed advanced testing mechanisms that let us verify, to a very high degree, that two MoSync implementations behave the same. At any stage of an application's execution, we can accurately compare the abstract MoSync machine state between them.

Each time we develop a new MoSync runtime or update an existing one, we run comparative tests between that runtime and MoRE. We do it on the actual phones so that you do not have to. When we're done, we have a runtime that for all intents and purposes behaves just like MoRE and executes MoSync applications flawlessly. So if it runs in MoRE it will run on the device.

For more about using the MoSync Emulator, see Emulating a Device.

The Runtimes

The MoSync runtimes consist of libraries and programs that execute your code on a given target device. They provide a uniform interface to low-level system APIs, including graphics, audio, event handling and communications.

While MoSync runtimes are tiny and efficient, they are still very sophisiticated pieces of engineering. Learn more about the architecture here.

Software as a contract

When people hear “runtime”, they usually think of one of two things; either a lightweight support system like the C runtime library or a Java-style virtual machine. Our definition is broader.

We think of MoSync as a contract between the generators — the things that produce MoSync IL — and the runtimes, which execute it on a set of devices. In some cases, this is indeed a virtual machine, but not always. For lower-end Java phones, we don’t even have to use pre-generated runtimes — each application your write can be transformed directly to Java bytecode. We currently have a prototype implementation of this transformation. However, we still call the result a "runtime" because it essentially makes the MoSync IL executable. In the case of iPhone we convert the MoSync IL into C++ and output it in an Xcode project which includes the iPhone syscall libraries.

Not your father's VM

The MoSync runtimes that are virtual machines are not the kind of VM most people would think of. They’re register-based, generally smaller than 100k and very, very fast. Where possible, they recompile your entire program to native ARM machine code before executing it. With the tiny footprint and native recompilation, there’s never any need to worry about performance.

The Runtime Architecture

MoSync has two primary runtime architectures - one implemented in C++ and one in Java. However, the design is very similar in both cases.

Service Layer

A runtime includes a service layer providing file I/O, threading, networking, memory managment and other supporting functions.

Application framework

This module holds the runtime's entrypoint. Its size and responsibilities vary across platforms. However, it typically handles platform-specific event management, initialization and destruction.

Syscalls

This module is responsible for implementing the basic features required for all platforms - graphics, audio, networking etc. It interprets MoSync resource files and also provides event managment, initialization and destruction.

Resource system

This module provides facilities for managing resource objects, such as images, sounds and data blobs. It supports dynamic creation and destruction of resources.

Internal extensions

Some features may not be available on all platforms. Such features are implemented as numbered functions called through a single Syscall. That Syscall returns a specific error value if the function being called is not present. This allows developers to determine whether such a non-universal API is accessible in runtime.

Core

This module is responsible for the execution of MoSync programs. It interoperates with the syscalls and resource system. Cores come in a number of different flavors, all of which share a common interface to the above mentioned modules. These are the three main types:

VM CoreRecompiler CoreGenerated Core
A VM core is a virtual machine that loads, interprets and executes MoSync bytecode directly. The execution is implemented in a single, small function, allowing for efficient JIT optimization. This is the type of core we use for Java ME.
A recompiler core loads MoSync bytecode and recompiles it into native code for the platform concerned, typically ARM machine code. The generated machine code is then executed. This is the type of core we use for Windows Mobile and Symbian.
This type of core neither inteprets nor recompiles MoSync bytecode - instead, it contains native code generated ahead of time from MoSync IL, and exposes an interface consistent with other core variants. This is the type of core we use for iPhone.

The reason we have three types of cores is that each has its distinct advantages. VM cores are great for debugging and conformance verification and are fully dynamic, making it possible to load new code at runtime, which is useful for many types of applications. Recompiler cores are more efficient, but provide less debugging support. They are also dynamic, albeit with some recompilation overhead when loading new code. Finally, generated cores provide zero overhead for low end devices, while sacrificing dynamicite. They are not able to load code at runtime.

All three types of core share a common interface - a generated or recompiler core works as a drop-in replacement for a VM core. This allows all the other modules to be shared across runtimes without modification.

MoSync SDK Roadmap

This is the roadmap for MoSync until December 2011. Some of you who may have been following the previous roadmap will notice that we've dropped the 2.8 release and moved things into the 3.0 release, but we've also added in some previously unannounced goodies!

All these things will be available in experimental form a lot earlier in our nightly builds. You can also check out our Github repository too see what we are working on at the moment.

MoSync 3.0 - December 19th 2011

  • Windows Phone 7 support
  • Module system (Extensions for Android, iOS and Windows Phone 7)
  • Updated resource compiler with support for conditional resource loading
  • New deployment model based on platforms, not devices
  • Multichannel Sound APIAds (iOS and Android)
  • Notifications (iOS and Android)
  • Improved development workflow (quicker turnaround)
  • Automatic updates
  • Local SQL Database support
  • JavaScript libraries to access to native features, including native UI

Who's Using MoSync?

The people who know the most about the challenges of mobile application development are our customers. Without them and the feedback they provide to us, development of the MoSync SDK would have been nearly impossible. We thank them all! Many of our cross-platform mobile development projects are for customers who insist on strict non-disclosure for commercial or security reasons, so of course we can't tell you about them, but here are a few of our customers who we can tell you about, and what they are doing with MoSync.

Astando AB and E-Adept

A guidance system for visually impaired pedestrians

The e-Adept system from Astando is based on a digitalized road network for pedestrians and cyclists. Combined with a precise and reliable handheld positioning system based on GPS and inertia navigation, e-Adept provides an extremely detailed route indication for its pedestrian users — much more detailed than the normal road maps for  drivers. The pedestrian route network makes it possible for the user to be guided along sidewalks, pathways and pedestrian crossings.

Astando uses MoSync to build e-Adept on standard phones. In this project we supported the development of a mobile navigation service for the visually impaired involving intriguing challenges in areas such as high performance real time positioning, UI design for the visually impaired, speech synthesis, design of Web Services suited for mobile communication and more. Mosync's role in the project involves supporting development of the client software and the client-server communication solution.

A common problem with GPS positioning is that it doesn't work well indoors or when there is no clear view of the positioning satellites. To solve this problem, we first developed an indoor positioning solution based on WLAN beacons working in combination with a sophisticated triangulation software residing on the mobile phone.

The final positioning solution made use of a dead reckoning technology with circuit boards provided by Honeywell Inc — a kind of advanced step counter with compass and 3D accelerometers on-board. The goal for the project is to create seamless outdoor/indoor positioning and navigation for various groups such as the visually impaired, the elderly, tourists and other services requiring reliable, high-performance positioning. The system has been tested in full-scale in three cities, and is already in use in lesser scale, by user groups and a selected trial population. The delegates of the 2008 ITS conference in Stockholm had the opportunity to try the system during the tour day of the international conference on intelligent transport systems.

Further reading, and an introductionary movie can be found at the customer's project website at www.eadept.se (in Swedish and English).

 

Bloo AB and BlooFlirt

A social networked mobile system

You’re in a pub with your friends, having a drink and chatting. You see a good looking guy with his friends a couple of tables away. Suddenly your eyes meet, and a mutual smile is exchanged. You’ld like to talk to him but you’re to shy too approach him at this time. Instead, you pick up your mobile phone and start your BlooFlirt application. Within a few seconds you see a picture list of other BlooFlirt members in the same pub, including the good looking guy who you can’t take your eyes off.

Bloo use MoSync to achieve consistent cross-plattform performance from a single code source. Instead of spending time on porting to various phone models, they can concentrate on development of the actual application.

The application uses a lot of connections, both local using bluetooth and via the cell network. This allows a user to "connect" even though a client is not in use, as long as their bluetooth connection is active another peer in the system can identify another nearby handset and register bluetooth MAC adresses in its proximity. This together with smart algoritms for roaming makes BlooFlirt as a social media system very flexible and robust. For more information on BlooFlirt and other networked social media applications, feel free to contact tommy@bloo.com.

 

Dalarnas Tidningar and MoPub LBS

A daily paper's local lunch guide

Dalarns Tidningar is a local daily in the mid-west of Sweden, found at www.dt.se. They run a community hub where a lot of information in the municipalty can be found, along with reviews, tips and debate on sociatal matters. All the local restaurants publish their weekly menus at the daily's website, and as an introductary step into the mobile landscape they wanted to publish information into mobile channels as well - hence they created a mab-based lunchguide for their users using MoSync.

The application is developed for the project by Lars Åke Vinberg, who normally develops software for the Windows platform and is skilled in c/c++ development. His work is then contributed to MoSync where this application is generalised into a standard map library, with the possibility to support several map formats such as Open Street Map, CloudMade and Google Maps (static).

The software is layed out in a layered structure, with a tile-based slippy map as a base layer, and with graphical elements on top which are fed into the application over HTTP using an XML-based format called geoRSS. There is also a browser supporting a small subset of HTML for viewing details about each point location on the map.

The main components are today available as libraries of the MoSync SDK, which through an open source GPL licensing is available for download at the mosync.com website.

Read more about the Maps API here

What's New

What's New in MoSync 2.7

Our 2.7 release is packed with new features for cross-platform mobile application developers. We've enhanced MoSync's HTML5 and JavaScript support, created a near field communications API, and ported the C++ Standard Template Library and the C Newlib library to MoSync. There are many new code templates and example applications in the IDE, and tutorials and user guides online. Let's take a look.

Wormhole - HTML5, CSS & JavaScript Support

iOS and Android

We're introducing Wormhole - a technology which makes it possible to build apps which seamslessly blend HTML5/JavaScript with C++ code. We've also greatly enhanced support for HTML5 applications in the IDE. That means you can bundle HTML/CSS/JavaScript/media files with your application, and edit HTML and JavaScript files right in the IDE, with syntax support. You can create native applications with rich web content, and develop advanced JavaScript applications that communicate with all the services provided by the MoSync API. You'll be up and running with a slick JavaScript app that talks to all those powerful MoSync C syscalls and C++ libraries in no time. More...

Near Field Communication API

Android 2.3.3+

Our NFC API is packed with functions that allow your application to talk to the near field communication (NFC)  features on the latest Android devices. That means that you can write applications that communicate with NFC tags according to the latest protocols and standards (NDEF, IsoDep, Mifare Classic/Ultralight), opening a whole range of possibilities for payment, personal identification, and similar close-proximity applications. More...

STL ported to MoSync

All platforms

We have ported the C++ Standard Template Library (STL) to MoSync. This feature-packed code library will already 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 within advanced applications. More...

Newlib ported to MoSync

All platforms

We have also ported the Newlib standard C library to MoSync. Newlib 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 and memory. More...

New IDE Templates

  • HTML5 Project
  • HTML5/C++ Project
  • MoSync C Newlib Project
  • MoSync C++ STL Project
  • MoSync NativeUI C++ Project

These templates are available when creating new projects in the MoSync IDE. For an overview of all the available templates, see Creating Projects from Templates.

New Example Applications

  • NFCExample — a new example application that demonstrates how to use a new near field communication API to read and send tags to NFC-aware devices.
  • WebViewGeolocation — a new example application that displays your current location and demonstrates the basic mechanisms for communicating between JavaScript and C++.
  • WebViewTwitter — a simple Twitter client that shows tweets by selected users. It makes use of the jQuery JavaScript library and its jQtouch plug-in for touchscreen devices, together with the MoSync Wormhole Library.
  • WebViewLoveSMS — a fun app that makes use of JavaScript to send text messages with smileys to friends and loved ones. 

Documentation for all examples is available at http://www.mosync.com/content/demo-examples.

New User Guides and Tutorials

All documentation is available online at http://www.mosync.com/documentation.

Known Issues with MoSync 2.7

What's New in MoSync 2.6

Please note: MoSync 2.7 is available http://www.mosync.com/#features.

The 2.6 Pyramid release introduces many new device APIs, new libraries for Facebook and NativeUI, and many new application examples. We also now support native emulators and application profiling in the IDE.

 

Release Highlights

On iPhone (iOS) and Android:

  • Widget API enhancements — many new NativeUI widgets, enhancements to existing widgets, support for native fonts and custom font loading (requires native emulators).
  • NativeUI Library — classes and functions for high-level NativeUI control (requires native emulators).
  • Sensor API — control a device's accelerometer, magnetic field, orientation, gyroscope, and proximity sensors.
  • Camera API — advanced control of the device's cameras, embedding previews in your application, zooming, etc.
  • OpenGL ES API — access to the devices graphics hardware through OpenGL ES 2.0 (requires native emulators).
  • Facebook Library — classes and functions for logging on to Facebook, retrieving information, making posts, creating albums, creating check-ins and events, and a whole lot more.
  • PIM API — add to, read from, modify, and delete contacts in a device's address book.
  • Device Fonts API — new syscalls and constants for discovering a device's fonts and loading them for text drawing and Native UI Widgets.

In the IDE:

Other:

Important Changes Since MoSync 2.5

The maLogWrite syscall, and also the lprintfln/printf functions, only print to the device console when the application is built in Debug mode. For example, if you use lprintfln to output log messages to the logcat console on Android, the application must be built in Debug mode for the output to appear. No output is shown in Release mode.

New Library Classes and Functions

On iPhone (iOS) and Android:

  • NativeUI Library — classes and functions for high-level NativeUI control. This high-level wrapper API supports all available widgets and is easy to update as new widgets are added. It provides support for specific widget functionalities : properties and events.

On all platforms:

  • GLMoblet— a Moblet that creates a fullscreen OpenGL ES application.
  • Facebook Library — classes and functions for interfacing with Facebook.

New APIs

On iPhone (iOS) and Android:

  • Camera API — new syscalls, constants, and properties for advanced camera control.
  • Device Fonts API — new syscalls and constants for discovering a device's fonts and loading them for text drawing and Native UI Widgets.
  • Sensor API — new syscalls, constants, properties, and event types for controling a devices sensors.
  • OpenGL ES 2.0 API — new syscalls and constants for advanced graphics hardware control.
  • PIM API — new syscalls and constants for contact management in address books.

Updated APIs

On iPhone (iOS) and Android:

  • Widget APIenhancements:
    • New widgets: progress bar, activity indicator, slider, date picker, time picker, number picker, video view, image browser, toggle button.
    • Improved widgets: check box, navigation bar colour, widget gradient, label max lines, web view scroll bars, edit box and button events.
    • Native font support: san-serif, serif, monospace, normal, bold, italic, custom font loading.

New Syscalls

On iPhone (iOS) and Android:

  • All OpenGL|ES 2.0 functions (see OpenGL ES 2.0 Reference Pages) are implemented as syscalls, e.g. glEnable, glShaderSource.
  • maCameraStart — start a camera
  • maCameraStop — stop a camera
  • maCameraSnapshot — take a picture
  • maCameraSelect — select a camera
  • maCameraNumber — find out how many cameras teh device supports
  • maCameraSetPreview — embed in your application a camera preview (via NativeUI)
  • maCamerSetProperty — zoom in/out, turn on flash, focus mode, image format
  • maCameraGetProperty — get a current camera setting.
  • maSensorStart — enable a sensor, set time intervals
  • maSensorStop — disable a sensor
  • maImagePickerOpen — display dialog enabling user to scroll through images and pick one.

New syscalls are documented in the reference documentation included in the package. Documentation for the OpenGL ES 1.1 and 2.0 syscalls can be found in the Open GL Reference Pages.

Updated Syscalls

On Android:

  • maOpenGLInitFullscreen— now implemented on Android
  • maOpenGLCloseFullscreen— now implemented on Android

New Event Types

  • MAW_EVENT_* — many new event types for NativeUI widgets.
  • MAW_VIDEO_VIEW_STATE_* — new event types for video play status.
  • EVENT_TYPE_SENSOR — event sent by a sensor; the struct includes the sensor type and sensor values.

All new event types are documented in the reference documentation included in the package.

New IDE Features and Tools

New Example Applications

On iPhone (iOS) and Android:

  • SensorTest — an application which checks which sensors (accelerometer, gyroscope, etc) the device has and shows their current readings.
  • AccelerometerOpenGL — an application that graphically displays the device's orientation using OpenGL.
  • FacebookDemo — an application for iOS and Android devices, showing how to interface with Facebook. Uses NativeUI.
  • CameraDemo — an application for iOS and Android devices, showing how to control a device's camera.
  • GLMoblet_OpenGLES1— an application in the OpenGLES folder, showing how to use OpenGL ES 1.1 in applications for iOS and Android devices.
  • GLMoblet_OpenGLES2 — an application in the OpenGLES folder, showing how to use OpenGL ES 2.0 in applications for iOS and Android devices.
  • WikiSearchNativeUI — an application that uses Native UI to text search Wikipedia, based on user input and selected categories.
  • VideoNativeUIExample — an application that shows how to use the NativeUI wrapper API video widget to embed video in your application.
  • PIMDemo — an application demonstrating how to add a new contact to an address book and how to read contacts from address book.
  • DeviceFonts — an application demonstrating how to count, load, and manage device fonts for text drawing.
  • DeviceFontsNativeUI — a new example demonstrating how to count, load, and manage a device fonts for your Native UI applications.

Documentation for all examples is available at http://www.mosync.com/content/demo-examples.

Updated Example Applications

On iPhone (iOS) and Android:

  • NativeUIDemo - updated to make use of the new NativeUI library.

New User Guides and Tutorials

All documentation is available online at http://www.mosync.com/documentation.

Known Issues with MoSync 2.6


What's New in MoSync 2.5

Our 2.5.Pyramid release introduces new APIs, runtime features, IDE improvements, application examples, and user guides. Here is a quick overview of what is new in this release.

Release Highlights

  • Widget API (Native UI) for iPhone (iOS) and Android devices* — access each platform's own user interface widgets using our new maWidget syscalls.
  • Support for OpenGL ES 1.0 on iPhone (iOS) and Android devices* — use OpenGL for Embedded Systems 1.0 to create a manipulate graphics.
  • Multitouch support for touchscreen devices — keep track of successive screen touches and respond to movement.
  • Program loading on Android devices — load another program from your MoSync Android application.
  • File API on Android devices — manage files from your MoSync Android application.
  • New example applications for beginners — heavily commented so that you can see how to build MoSync apps line-by-line.

* Not available on the MoSync emulator.

Important Change Since MoSync 2.4

  • In MAUI's WidgetSkin.h file we have moved the using namespace MAUtil declaration into the namespace MAUI declaration. This means that the MAUtil namespace is not explicitly visible to any program elements that include WidgetSkin.h. Some existing code may therefore fail to compile if it doesn't already explicitly declare the MAUtil namespace. An example of when this might occur is when you have used Moblet::run in your code, rather than the more correct MAUtil::Moblet::run. A simple solution is to put the declaration using namespace MAUtil; at the start of any file that fails to compile.
  • The MoSync slippy map library has been updated. Existing applications will need to be migrated if you want to compile them with MoSync 2.5. For a complete description of the new library see Using the MAP Library.

New Syscalls

We have implemented dozens of new syscalls. A brief summary is given below. All new syscalls are documented in the reference documentation included in the package.

On iPhone (iOS) and Android:

  • maWidget* — create and destroy Native UI widgets; manage layout, screen stacks, and widget properties. The available widgets include Screen,TabScreen, StackScreen, Button, Image, ImageButton, Label, EditBox, ListView, ListViewItem, CheckBox, HorizontalLayout, VerticalLayout, RelativeLayout, SearchBar, GLView,and WebView. GLView gives access to OpenGL.
  • maMessageBox — pop up a message box with information for the user.

On Android:

  • maHomeScreen* — listen for home screen events; add a shortcut to the home screen on the desktop.
  • maNotification* -- show your application on the notification bar.
  • maScreen* — set/lock screen orientation, display app fullscreen, listen for screen state events.

Support for all new syscalls will be added to the MoSync emulator in the near future.

New Event Types

All new event types are documented in the reference documentation included in the package.

  • HOMESCREEN_SHOWN
  • HOMESCREEN_HIDDEN
  • SCREEN_STATE_ON
  • SCREEN_STATE_OFF
  • WIDGET (parameters in the event give more information)
  • BLUETOOTH_TURNED_OFF
  • BLUETOOTH_TURNED_ON

Updated Syscalls and Event Types

On all platforms:

  • maHttpCreate — HTTP PUT and DELETE methods added.

On iPhone (iOS) and Android:

  • All EVENT_TYPE_POINTER* events now have touchID to indicate successive touches (multitouch). 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, LG Optimus 2X = 10, MoRE emulator = 2.

On Android:

  • File API implemented - manage files from your application.
  • maLoadProgram implemented - load another MoSync program from your application.

Updated Library Functions

  • MAUtil::BluetoothConnection::connect (url) — new connection method that accepts a Bluetooth URL.

Updated IDE Features and Tools

In MoSync SDK for Windows and MoSync SDK for OS X:

  • Support for path parameters (like %mosync-home%) so that full paths no longer need to be specified in each project's Build Settings.

In MoSync SDK for Windows:

New project templates, available in both versions of MoSync:

  • MoSync NativeUI Template
  • MoSync OpenGL Template

All project templates now contain a small sample of code so that they are immediately runable on an appropriate device. All templates have been revised in line with our Coding Conventions.

Eclipse have changed the way the hover function works. See: http://www.mosync.com/content/show-source-hover.

New and Updated Example Applications

  • HelloNativeUI — a new example for iOS and Android devices, showing how to work with the platform's own screens and widgets.
  • HelloOpenGLES — a new example for iOS and Android devices, demonstrating the enormous power of OpenGL for Embedded Systems.
  • HelloWorld — our simplest app, now fully commented from head to toe.
  • HelloMoblet — our basic Moblet example, re-commented for beginners.
  • ScreenOrientation — a new example showing how to set, detect, respond to changes in the orientation of a device.
  • MultiTouch — a new example showing how to handle multitouch events like pinch, swipe and rotate.
  • HelloMAUI — a new example, introducing screen and widget handling using MoSync's MAUI library.
  • RockPaperScissors — a full application playing the traditional playground game using NativeUI.
  • Graphun — this complex application is a 3d graphical visualizer built with MoSync NativeUI and OpenGL.
  • BluetoothServer — a replacement example, showing how to make your application behave as a Bluetooth server.
  • BluetoothClient — a replacement example, showing how to make your application behave as a Bluetooth client.
  • NativeUIDemo — a full demostration of the capabilities of the MoSync NativeUI based on the Widget API.

Documentation for all examples is available at http://www.mosync.com/content/demo-examples.

New User Guides and Tutorials

We have an entirely new Developer Centre at our website from which you can access a huge range of tutorials, code examples, and resources.

Many of our existing user guides and tutorials have been updated and expanded for this release, including:

All documentation is available online at http://www.mosync.com/content/documentation.

Known Issues with MoSync 2.5

Issues Resolved Since MoSync 2.4 (r2425)

Details about every resolved issue can be found in our issue tracker.

What's New in MoSync 2.4

Our 2.4 release introduces support for Apple's popular iPhone platform (including iPad and iPod) and Android 2.2, plus many many new libraries, runtime features, IDE improvements, application examples, user guides, and tutorials. Here is a quick overview of what is new in this release.

New Development Environment

  • MoSync SDK for OS X

New Platforms and Features

  • Support for Apple iOS devices (iPhone, iPad and iPod)
  • iPhone, iPad, and iPod device profiles
  • Android NDK implementation
  • Bluetooth server and client syscall library
  • ARM recompiler for Symbian 3rd and 5th edition
  • Retrieve ISO 693-1 or 693-2 language with maGetSystemProperty
  • Support for secure connections using HTTPS and SSL

SDK Improvements and Enhancements

  • Testify test framework
  • Improved dead-code elimination
  • Improved application icon support
  • New version, publisher, app name packager settings
  • New registration and auto-update process
  • Improved Welcome screen with automated examples import
  • Improved API Reference Manual with full index

New Example Applications

  • btServer -- a simple Bluetooth example application, including server and clients.
  • MapDemo -- a slippy map demonstration with maps from various sources.
  • MAUIex -- a demonstration of various screen widgets created using our MAUI library.
  • MDLBenchmark -- a performance benchmark test.

Many of the existing example applications in the SDK package have been updated for this release, and now include touch support. Documentation for all examples is available at http://www.mosync.com/content/demo-examples.

New User Guides and Tutorials

Getting Started Guides:

User Guides:

Programmer's Guides:

Tutorials:

Developer's Guides

All of our existing user guides and tutorials have been updated and expanded for this release.
All documentation is now available online at http://www.mosync.com/content/documentation.

Important Changes in MoSync 2.4

We have changed the default background drawing behaviour of several widgets to make them more sensible for each widget. For example, the label widget now draws its background by default. Use setDrawBackground(false) to disable background drawing.

Known Issues with MoSync 2.4

Issues Resolved Since MoSync 2.3 (r767)

Details about each issue can be found in our issue tracker.

Using the MoSync IDE

The guides and tutorials in this section of the documentation cover various aspects of the MoSync integrated development environment and associated tools, including how to work with mobile device profiles, how to use the emulator, different ways to build and finalize your application, and how to send your application to a mobile device. We also provide advice here about using alternative development environments such as Visual Studio.

Installing the MoSync SDK for Windows

Here we describe the system requirements of the MoSync SDK for Windows, and how to download and install it. We also describe how to upgrade from a previous release of the MoSync SDK for Windows.

Downloading MoSync

You can download the last featured release of the MoSync SDK for Windows (and its latest nightly builds) from the Download page at our website: http://www.mosync.com/download.

Important note: The MoSync SDK for Windows enables you to directly build application packages for Java ME, Symbian, Android, Windows Mobile, Smartphone, Pocket PC, and Moblin/Meego devices. You can also use it to build Xcode projects for iOS applications (i.e., for iPhone, iPad, and iPod Touch), however to finalize an Xcode project you will need to transfer it to an Apple Mac running OS X and Xcode and create the application package there. If you already have a Mac available, we recommend that you download and install our OS X development environmant, the MoSync SDK for OS X, which is available from our Download page. 

System Requirements

MoSync is designed to run on any PC that meets the following requirements:

  • Microsoft Windows XP, Vista or Windows 7 operating system
  • Java SE Runtime Environment (JRE) 6
  • 300MB free hard drive space

Install or update your Java SE Runtime Environment before installing MoSync. It can be downloaded here.

Note: we recommend the Oracle/Sun version of JRE. If you wish to use another vendor's JRE and you have set the permanent memory perm gen size to less than the default size of 82Mb, you will get warning message requesting that you increase it.

To use MoSync's device discovery and transfer features, you will also need a Bluetooth-enabled PC, a mobile device and one of the following software stacks:

  • Microsoft (included in Windows XP Service Pack 2)
  • WIDCOMM (aka Broadcom)
  • BlueSoleil

To use the emulator's Bluetooth support when testing programs, you will need the Microsoft stack.

Running the Installer

After you have downloaded the MoSync installer package, double-click the file to begin the installation. The installer will unpack the files and begin the installation process.



When the files have been unpacked, the MoSync splash-screen will appear.



The splach screen will fade and our license agreement will appear. Please take the time to read the MoSync License Agreement — it contains important information about the terms under which we supply the MoSync SDK. Agree to it if you would like to proceed with the installation.



Select the components you wish to install. Note that there isn't any options for the MoSync package, everything needs to be installed.



Select where you would like MoSync to be installed. Please note that MoSync needs to be installed to a directory which is not containing any spaces in its path.



If you have a previously uninstalled copy of MoSync you will get the following notice. Please follow the instruction and make a copy of your project files if they are inside the MoSync installation folder. Even if they shouldn't be removed it's better to be safe than sorry.



If you had a previous installation it will first be removed before the new version of the MoSync SDK is installed on your computer. Please wait for the installation to complete. The installation process may take a few minutes.

As part of the installation process, the Microsoft Visual C++ 2005 Redistributable will also be installed and some changes will be made to environment variables.

MoSync is now installed. Press Finish to end the installer and open the MoSync IDE.

Installing Native Emulators

MoSync includes its own powerful, cross-platform emulator, MoRE. You can also install native emulators -- particularly useful for testing native user interfaces like our Widget API and NativeUI Library, and for testing APIs and libraries that are only currently supported on a limited number of platforms. Instructions for installing native emulators can be found in our user guide Emulating a Device.

Installing the MoSync SDK for OS X

The OS X version of the MoSync SDK enables you to build applications for iOS devices (including Apple iPhones and iPads) as well as for Windows Mobile, Java ME, Symbian, Android, Smartphone, Pocket PC, and Moblin devices. Here we describe the system requirements of the MoSync SDK for OS X, and how to download and install it. We also describe how to upgrade from a previous release of the SDK.

Downloading MoSync

You can download the last stable featured release of the MoSync SDK (and the latest nightly builds) from the Download page: http://www.mosync.com/download.

System requirements

MoSync SDK for OS X has the following system requirements:

  • Mac OS X Snow Leopard (10.6.6), 64-bit
  • Java SE Runtime Environment 6 or later, 64-bit (usually pre-installed on the Mac)
  • 250 MB free hard disk space

Apple imposes strict rules and guidelines on how applications for its App Store are to be built. To build an iOS application with the MoSync SDK, you will need to install Xcode 3.2.5 with the iOS SDK. You will also need to join Apple's Developer Program.

To use MoSync's device discovery and transfer features, you will also need a Bluetooth-enabled Mac, a mobile device and one of the following software stacks:

  • Microsoft (included in Windows XP Service Pack 2)
  • WIDCOMM (aka Broadcom)
  • BlueSoleil

To use the emulator's Bluetooth support when testing programs, you will need the Microsoft stack.

Installing the MoSync SDK

  1. Launch the MoSync for OS X Installer.



  2. Click Continue.
  3. Accept the license agreement.
  4. The Installation Type window will show you the standard install details:



  5. To install on your local hard drive, click Install.
    Alternatively, if you want to install MoSync on another drive, click Change Install Location, select the appropriate drive, make sure there is enough free space, click Continue, then Install.

    Note: you cannot select where on the hard disk to install the MoSync SDK, it will always be installed in /Applications/MoSync/.
  6. The final installation prompt appears:



  7. Click Continue Installation to install the MoSync SDK.



  8. To update several important environment variables used by the MoSync SDK, you must log out when the installation completes. Click Log Out to log out and update the environment variables.

Xcode and the iPhone OS SDK

Xcode can be downloaded from the Apple iOS Dev Center.  Make sure that the version you download includes the iPhone OS SDK.

Advice on building iPhone applications can be found in our programmer's guide Developing iPhone Applications.

The iOS Provisioning Portal is designed to take you through the necessary steps to test your applications on iOS devices and prepare them for distribution.

Installing Native Emulators

MoSync includes its own powerful, cross-platform emulator, MoRE. You can also install native emulators -- particularly useful for testing native user interfaces like our Widget API and NativeUI Library, and for testing APIs and libraries that are only currently supported on a limited number of platforms. Instructions for installing native emulators can be found in our user guide Emulating a Device.

 

Creating Projects from Templates

This guide shows you how to create a new application project in the MoSync IDE and describes the project templates that are available to you. It also introduces the concepts of projects and workspaces. We have templates for various types of C/C++ projects, and templates for hybrid HTML/Javascript/C/C++ projects.

Projects and Workspaces

The MoSync IDE is based on Eclipse. Eclipse organizes your source code using projects and workspaces. A workspace is a collection of projects. A project is a collection of a set of source files. Projects can be individual applications or modules, depending on how you choose to organize your workspaces. Each project maps to a corresponding directory in the file system.

The different projects in a workspace may map to different file system directories or drives, although, by default, all projects map to subdirectories of a single workspace directory. All files in the workspace are directly accessible to the standard programs and tools of the underlying operating system.

The Project Explorer view in the IDE shows you all the projects in the current workspace and their source files.

Creating a New Project

In the MoSync IDE, you create a new project by doing one of the following:

  • From the File menu, choose New > Project, or
  • Right-click in the Project Explorer, then choose 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:



In the folder MoSync you will find wizards for creating MoSync projects.

Select the one called MoSync Project.

Click Next.

The project name and location screen appears:



Enter the Project name "HelloWorld". (Note: 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:

Select a template and click Finish.

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

MoSync Project Templates

We provide you with many different project templates, adapted to meet the needs of various different application types.

All templates have:

  • Default compiler flags for Release configuration: -O2
  • Default compiler flags for Debug configuration: O0
  • Dead code elimination: Off
  • Default library: mastd.lib, if enabled.

Template

Languages

Description

Libraries

Heap size

Stack size

Data size

HTML5 Project

 HTML5, JavaScriptAn HTML5 template that includes the WebAppletMoblet for pure web applications based on HTML5, CSS,and JavaScript.mastd.lib, MAUtil.lib, NativeUI.lib, MAFS.lib, Wormhole.lib30725124096

HTML5/C++ Project

 HTML5, JavaScript, C/C++

A hybrid HTML5/JavaScript and C/C++ template. Build your user interfaces in JavaScript. Do the heavy lifting in C/C++.mastd.lib, MAUtil.lib, NativeUI.lib, MAFS.lib, Wormhole.lib30725124096

MoSync C++ Project

C/C++

A standard ANSI C++ application template consisting of a main file with a small code example.

mastd.lib, MAUtil.lib

512

128

1024

MoSync C++ STL Project

C/C++

A standard ANSI C++ application template consisting of a main file with a small code example and  includes the C++ Standard Template Library.

newlib.lib, stlport.lib

1536

256

2048

MoSync Moblet Project

C/C++

A standard ANSI C++ application template that includes our Moblet event-handling framework and some simple example code.

mastd.lib, MAUtil.lib

512

128

1024

MoSync MAUI Project

C/C++

A standard ANSI C++ application template that includes the Moblet framework, MoSync’s graphical user interface library (MAUI), and some simple example code.

mastd.lib, MAUtil.lib, MAUI.lib

1536

256

2048

MoSync NativeUI Project

C/C++

A standard ANSI C application template that includes our Moblet event-handling framework and uses the Widget API.

mastd.lib, MAUtil.lib

3072

512

4096

MoSync NativeUI C++ Project

C/C++

A standard ANSI C++ application template that includes our Moblet event-handling framework and uses the NativeUI Library.

mastd.lib, MAUtil.lib, NativeUI.lib

3072

512

4096

MoSync OpenGL Project

C/C++

A standard ANSI C++ application template that includes our Moblet event-handling framework and includes some basic OpenGL code.

mastd.lib, MAUtil.lib

3072

512

4096

Empty MoSync Project

C

No main file, roll your own.

mastd.lib

512

128

1024

MoSync C Project

C

A standard ANSI C project template consisting of a main file with a small code example.

mastd.lib

512

128

1024

MoSync C Newlib Project

C

A standard ANSI C application template using newlib.

newlib.lib

1536

256

2048

Launching and Registering MoSync

The first time you start the MoSync IDE you will be offered the chance to register the product and you will also see the Welcome screen which provides helpful links to get you started. Here we look at some of the options you have when starting MoSync.

Starting MoSync

Starting the MoSync IDE on Windows:

  • If you created a desktop shortcut during installation you can use that to launch the MoSync IDE.
  • Alternatively, you can use the MoSync IDE shortcut icon under Start > MoSync on your Windows Desktop.
  • If you choose to install without shortcuts, the path to MoSync is \eclipse\mosync.exe in the root of the installation (default: C:\MoSync).

Starting the MoSync IDE on OS X:

  • Click on the Applications folder in the Dock. Click MoSync > eclipse > mosync to launch the IDE.

The MoSync Splash Screen

When you start MoSync the first thing you will see is its splash-screen:

The splash screen shows you the target platforms that this version of MoSync can build packages for. It is worth checking this screen whenever you update MoSync because we regularly add new platforms.

The splash screen also shows the version number of the MoSync SDK in the form:

<major>.<minor> <status> (<build>)

Selecting a Workspace

Next, the MoSync IDE will ask you which workspace you want to start working in this session:

The MoSync IDE is based on the popular development environment Eclipse, and workspaces are Eclipse's way of organizing projects. You can have several workspaces, with different projects in each. (Workspaces and projects are normal folders that you can see in the file system.)

If this is the first time you are launching MoSync, you can just accept the default workspace by clicking OK. The MoSync IDE will finish loading.

(Later you can create new workspaces and use them to orgainse your different projects. Note that workspace names cannot include spaces — only alphanumeric characters (a-z, A-Z, 0-9) and underscore (_)).

Registering MoSync

When you start the MoSync IDE, it will prompt you to register the program if you have not already done so.

MoSync registration is completely free, and it means that you can get automatic device profile updates whenever you start MoSync and full access to your website including our Developer's Forum. You also have the option of subscribing to our mailing list for news about new features.

If you have not registered with us before, simply enter your desired username and e-mail address and follow the instructions. Your e-mail address must be valid because we will send a confirmation e-mail to that address.

If you have already registered with us, follow the Already registered? Click here link and enter you username and password.

When you have completed the registration process, click Continue.

Troubleshooting Registration

If you do not receive a confirmation email within a few minutes, it may be because your e-mail provider's spam filter has blocked it. Check in your spam or junk email folder to see if it has been put there.

If you are using Gmail and some similar email systems, and you have requested the MoSync IDE to resend your confirmation email, you may get an apparently empty email. Simply click on the "show quoted text" in this email to see the confirmation text.

If you have disabled the registration screen and want to display it later, you can find it under Help > Register in the IDE.

The Welcome Screen

When you successfully registered the MoSync IDE will next display its Welcome screen:

The Welcome screen provides you with many useful links to get you started with MoSync, including links to our guides and examples:

*If the examples already exist in the current workspace they are not replaced. If you make changes to the examples, you can still import the originals at a later time (see Importing the Examples). The examples are stored in the /examples folder in your MoSync installation directory.

If you choose not to show the Welcome screen at start-up, you can always find it again under Help > Welcome in the IDE.

When you are ready to leave this screen, click the Close the welcome screen and start using MoSync link. You will now see the normal Eclipse views in the IDE.

Views and Toolbars in the MoSync IDE

The main screen of the MoSync IDE consists of several "views" and toolbars. If you are familiar with Eclipse, many aspects of the IDE will be familiar to you, but we have added a lot of many features to support the MoSync IDE. Some of the more important features are labelled in the diagram below.


The main views in the MoSync IDE include:

  • Project Explorer View - this view shows the projects that you have created or imported into the MoSync IDE. This is your main file manager.
  • Editor View - this view is where you create and edit your application code. It features all the standard features of Eclipse, such as auto code highlighting and indenting. If you hover over an identifier of a class, method, etc. its documentation will pop-up. Type part of an identifier and press Crtl-Spacebar for auto-complete. It is worthwhile taking a little time to explore the comprehensive Eclipse workbench and C/C++ documentation on the Help menu to learn more about the editing features of the IDE.
  • Device Profiles View - this view lists the list of available devices for which you can build you current projects. You can set filters so that only devices which match certain criteria are included in the list. The current device profile that will be used if you run your application in the MoRE emulator is highlighted in the list, and also shown at the top of the view.
  • Finalizer View - this view shows the current finalizer script that will be used to finalize your application for all the devices listed in the Device Profiles view.
  • Problems View - lists any problems encountered during a build.
  • Console View - this view shows the output of the execution of your application and enables you to enter input. You can customize this view using the toolbar displayed in the view header bar.
  • MoRE Emulator - The MoSync Runtime Environment (MoRE) starts up when you click the Run button on the main toolbar. The most visible feature of MoRE is its emulator which looks like a generic mobile device and is matched in various ways to the current target device profile.

If you close a MoSync-specific view (for example the Device Profiles view or the Finalizer view you can reopen it again by selecting Window > Show View > Other > MoSync.

Keyword Help

You can jump directly to the documentation for MoSync syscalls and other keywords by highlighting the keyword in the Editor View and pressing the F1 key. The Help View will open where you can find a link to the keyword's documentation:

 

 

Importing the Examples

Here we show you how to import the example applications in the /examples folder into MoSync. For a description of each example application, see the Example Applications overview.

There are two ways to import the example applications:

  • On the MoSync Welcome screen, click the Example applications link. (To see the Welcome screen if it is closed, select Help > Welcome). A new workspace will open with the examples loaded into it. (If the examples already exist in the current workspace they are not replaced.)
  • Use the Import wizard. This method allows you to choose the workspace into which the example applications will be imported and to set various import options. See Importing Projects and Files for information about the Import wizard.

After import, the example application projects will be visible in the Project Explorer:

To build and run an example application in the MoRE emulator, open one of the examples in Project Explorer, then click the Run button on the toolbar, or press Ctrl+F11.

Note that examples that use NativeUI and OpenGLES will not run in the MoRE emulator. They need to be transfered to a device (see Sending to a Device).

Working with MoSync Libraries

MoSync's libraries provide you with thousands of pre-written classes and functions that you can use within your application. Here we show how to connect to the libraries from your applications, and give you a brief overview of each of the main libraries.

Using the MoSync Libraries

MoSync's libraries contain both code files and matching header files. To get be able to use the syscalls, classes, and functions in the code files you need to reference the appropriate header files in your application using #include statements:

Here we are including the header file for Moblet (MoSync's event handling framework) and some header files for MAUI. That will enable us to access to the Moblet code and the MAUI screens and widgets later in our application.

You can get a list of all the header files that you can include in your application by browsing the Includes folder in your project:

As well as referencing the header files in your application code, you also need to specify the actual libraries that you want to use in the project's Build Settings (Project > Properties > MoSync Project > Build Settings):

Here we are including the mautil.lib library that contains the moblet code and the maui.lib library that contains the screen and widget code.

Note that the mastd.lib library is always included in the build — you don't need to specify it in your list of additional libraries.

When you build your application, MoSync's Pipe-Tool links the libraries that are listed in the Additional Libraries field and mastd.lib with your code:

 

The Libraries

The MoSync libraries are stored in the /lib directory in your MoSync installation. A complete description of the libraries and their contents can be found in the MoSync IDE's help system (Help > MoSync API Reference) and also in our online API Reference Guide.

LibraryContent
mastd.libThe main library of MoSync system calls (syscalls). The syscalls give you access to many basic platform features, including permanent storage, graphics, sound, communications, camera, and so on.
mafs.lib Virtual file system classes and functions
map.lib Slippy map classes and functions
matest.lib MATest test framework
maui.lib MAUI graphical user interface classes and functions
mautil.lib General utilities, including Moblets, event handling, framebuffer, Bluetooth discovery and connections, containers, and geometric helper structures.
mtxml.lib XML SAX parser classes and functions
testify.lib Testify test framework

Using Device Profiles

MoSync comes with a comprehensive set of device profiles to help you deploy your applications to the widest possible range of mobile devices. Each device profile defines the characteristics and capabilities of a single mobile device. Device profiles are used by MoSync's MoRE emulator and during the build process to dynamically tailor the application to a particular device.

About Device Profiles

MoSync comes with hundreds of device profiles, categorized by vendor.  Each device profile holds important information about the characteristics of target mobile device, including information such as:

  • Screen dimensions
  • Memory capacity
  • Supported features (for example, Bluetooth)

The available device profiles are shown on the Device Profiles tab in the IDE:

When you start a new project, the active device profile is always the profile for the MoSync emulator.  (You can find this profile in the Device Profile view under Mobile Sorcery > Emulator.)

Changing the Current Device Profile

The current device profile is highlighted with an orange background to its icon.

To change the current device profile, right-click on the name of a device in the Device Profile view and select Set Target Phone.

Viewing Device Profile Information

If you want to see more information about a particular device, select Show Profile Info from the right-click menu. You will then see the header file that MoSync will use when it builds the application for the emulator:

Filtering Device Profiles

You can filter the list of device profiles so that only a subset of the full device profile list is shown in the Device Profiles view so that, when you build your application, packages will only be built for the devices listed.

You can set more than one filter: setting the right combination of filters enables you to build only for the devices that meet the exact requirements of your application.

To set a filter, click the Add button beneath the Device Profiles view. The Select Filter Type window will open:

The first option is Vendor/Device. Here it's possible to choose which vendors and devices you want to include or exclude from your profiles. Use the checkboxes to select the combination of vendors and devices you require.



The second option is Feature/Bug. Here it's possible to include and exclude devices according to the features and bugs that we know exist on the various devices. It's also possible to select device operating systems through this option:



The third option is Device Constant Condition. These are constant values set in all devices. They include the screen sizes and the available heap sizes of the various devices:



By using multiple filters you can select the devices that satisfy the requirements for your application. Mutliple filters are combined using the AND operator: a device is listed only if it satisfies the conditions of all filters.

Editing and Removing Filters

To remove or edit a currently applied filter, highlight the filter beneath the Device Profiles view, then click Remove or Edit.

Accessing Device Profiles in your Code

To access device profile definitions in your C/C++ code, include the maprofile header file:

 #include <maprofile.h> 

Useful profile definitions include MA_PROF_STRING_VENDOR and MA_PROF_STRING_DEVICE, which are the names of the device and its vendor.

For writing vendor-specific or device-specific code, MA_PROF_VENDOR_* and MA_PROF_DEVICE_* definitions are also available. For example MA_PROF_VENDOR_NOKIA and MA_PROF_DEVICE_6630.

MA_PROF_SUPPORT_JAVAPACKAGE_BLUETOOTH is defined if and only if the device supports Bluetooth.

MA_PROF_CONST_SCREENSIZE_X, MA_PROF_CONST_SCREENSIZE_Y are defined as integers that describe the screen size in pixels. MA_PROF_CONST_BITSPERPIXEL describes the color depth.

Showing the Device Profiles View

If you close the Device Profiles view you can reopen it again by selecting Window > Show View > Other > MoSync > Device Profiles.

 

Emulating a Device

So that you can rapidly test your application from within the IDE, MoSync includes an emulator called the MoSync Runtime Environment (MoRE). MoRE can emulate any device in MoSync's device profile database. The device profile information provides all the configuration settings it needs. You can also install the native Android Emulator and iPhone/iOS Simulator and run your application in those too -- useful if you are working with OpenGL ES, NativeUI, or other APIs that are only available for Android or iOS.

Available Emulators

All MoSync releases:

  • The MoSync MoRE emulator is a virtual machine that directly executes MoSync bytecode. It's not just a compilant MoSync implementation - it's the actual reference implementation that all the real device platforms are required to conform to. It is the default emulator in the IDE.

From MoSync 2.6:

  • The Android Emulator and iPhone/iOS Simulator can be installed and specified as the target emulator for Android or iOS applications so that you can test OpenGL ES, NativeUI Library, and Widget API-based applications. Once installed you just need to select an Android or iOS device profile before you run your application in the IDE. The appropriate native emulator will start up authomatically. For installation instructions, see Installing and Working with Native Emulators.

Starting the MoSync Emulator

To start the MoSync MoRE emulator, click the Run button on the toolbar (or press Ctrl+F11). The current project is be saved, built using the "Release" build configuration, and loaded into the emulator. A graphical phone is displayed.

(You can also start the emulator in debug mode by clicking the  Debug button on the toolbar. In this case the current project is built using the "Debug" build configuration, before being loaded into the emulator.)

Controlling the MoSync Emulator

The emulator is controlled either by clicking the keys on the graphical phone, or using a number of keyboard shortcuts:

The emulator communicates with the MoSync IDE, sending call stacks, panics, and other useful data to the console window.

Screen orientation

The F4 key on the keyboard toggles the screen orientation of the emulator between portrait and landscape modes (and causes an EVENT_TYPE_SCREEN_CHANGED to be sent to the application).

Multitouch emulation

MoSync 2.5+

Right-click with the mouse pointer on the emulator screen to engage multitouch simulation. Two filled circles appear, equidistant and opposite from the screen's centre point:

To emulate pinch-in, pinch-out, and rotational guestures, move the pointer in different directions while holding down the right mouse button.

Closing the emulator

The Esc key closes the emulator.

The Emulator Device Profile

When it executes a MoSync application, the emulator uses the current device profile. For instance, the screen dimensions of the graphical phone varies according to settings in the current device profile.

When you create a new MoSync project, MoSync defaults to a generic device profile.  You can find this profile in the Device Profile view under Mobile Sorcery > Emulator. The default device profile has the following characteristics:

  • Platform: JavaME
  • Bluetooth: yes
  • MMAPI: yes
  • Screen size: 240 x 320

You can use the default profile when you start to explore MoSync, and for many of our basic tutorials, but sooner or later you will want to test your application by setting up the emulator to use another device profile.

Changing the Emulator's Device Profile

The device profile that the emulator currently uses is highlighted in the Device Profile list with an orange background. To make the emulator emulate another device, right-click on a device in the Device Profile view and select Set Target Phone.



If you want to see more information about the device, select Show Profile Info from the right-click menu. You will then see the header file that MoSync will use when it builds the application for the emulator.

Configuring the MoSync Emulator

Currently, the only property you can change is screen size. You can manually override the screen size setting by clicking the small down-arrow next to the Run button in the toolbar, and selecting Run Configurations:

If you debugging your application, there is a similar setting for debug mode: click the small down-arrow next to the Debug button in the toolbar, and selecting Debug Configurations to see the settings.

Debugging in the MoSync Emulator

When a MoSync application terminates because of a panic, a call stack is transmitted to the output console in the IDE:


The call stack shows you all the stack frames that were active when the application terminated, and you can click on each line to go to the corresponding source file and line. This is the format of such lines:

IP:<instruction pointer>: <path to source file>:<line number in source file>

If the panic involved MoSync libraries that you do not have the source code to, those lines will not be clickable.

Displaying Build Results in the Console

When you build your project for the emulator, the progress of the build will be displayed in the Console and includes information about the success of the invokations of GCC and Pipe-Tool, the build paths, and so on. If you build your project successfully, these results will be replaced at the end of the build process by a new Console view showing the output from your application. If you want to examine the build results, you can switch the Console view using the Display Selected Console selector on the Console toolbar:

Installing and Working with Native Emulators

MoSync 2.6 onwards:
If you would like to run your application directly in the Android Emulator or iPhone/iOS Simulator you will need to install and configure them. Once you have done this, when you select an Android or iPhone/iOS device profile and start the emulator, your application will run in the appropriate emulator.

  • To be able to use the iPhone Simulator from the IDE, you need to be using the MoSync SDK for OS X on an Apple Mac.
  • To be able to use the Android Emulator from the IDE, you can use either the MoSync SDK for OS X on an Apple Mac, or the MoSync SDK for Windows.
  • On both Windows and Mac OS X you can, of course, still use MoSync's MoRE Emulator.

Installing the iPhone simulator

  1. Install the MoSync SDK for OS X, including Xcode 4 with iPhone SDK support. For full instructions, see Installing the MoSync SDK for OS X.

Installing the Android emulator

  1. Install the MoSync SDK for Windows (instructions: Installing the MoSync SDK for Windows) or the MoSync SDK for OS X.
  2. Install the Android SDK and use the AVD manager to install at least one platform to emulate (see Google's instructions for Installing the SDK).
  3. Start the MoSync IDE.
  4. Specify the location of the Android SDK. On Windows, open Window > Preferences > MoSync Tool > Android SDK. On OS X, MoSync IDE > Preferences > MoSync Tool > Android SDK.
  5. In the SDK location field, enter the absolute path of where you installed Android SDK. (This directory usually contain directories such as "tools", "platforms", etc).

Launching a MoSync application on a native emulator

  1. Make sure you have prepared your environment as described above.
  2. In the MoSync IDE device profiles list, select either an Android or an iOS device.
  3. Click the Run button.

Important! Those of you who are already familiar with the Android Emulator will know that it can take a long time -- and we mean a really long time -- to start. Time enough, indeed for you to make lunch, or even dinner. Nothing to do with us. Talk to Mr Google.

Running the Android emulator directly from the MoSync IDE

Sorry, you need to install flash to see this content.

In this MoSync tutorial, Miles shows you how to configure and run the Android emulator directly from the MoSync IDE.

Scanning for a Device

MoSync makes it easy to scan for nearby devices and connect to them using Bluetooth. Of course, both your computer and the device must have Bluetooth installed and enabled, the device must be configured as "discoverable" so that the computer can find it. The device must also support the OBEX communications protocol. Once you have connected to your device, you will be able to send your application to it over Bluetooth.

Scanning for a Device

Click the Select Target Device icon (or select Scan for Device from that icon's drop-down menu):

The transport selection dialog will appear.

Select Bluetooth. If you have correctly configured your Bluetooth stack, MoSync will start a search for all discoverable Bluetooth devices in range and display the ones that it finds:

Highlight the device that you want to send your application to and click OK.

MoSync will now check that the selected device has the OBEX service available for binary transfers:

The Device Profile-Target Device Association

In MoSync 2.3, when the OBEX service is detected, the Select Preferred Profile window will appear:

Select the MoSync device profile you want to associate with the target device. Usually, this will be the profile for the target device, e.g., the N95 profile for the N95 phone. However, if there is no suitable profile for your device, you can choose another compatible profile. Click OK to associate the chosen device profile with the target device.

When the OBEX service is detected, MoSync automatically completes the device selection by associating the device profile currently selected in the project with the target device. For more about this association, including how to check its current setting and how to change it, see Sending to a Device.

Windows Mobile Bluetooth Issues

We have noted some issues with the pairing of Windows Mobile devices to Windows computers.

The first issue seems to happen if the device has been previously paired with the computer outside of MoSync. If you are having problems sending application packages files from the MoSync SDK to a Windows Mobile bluetooth device, try unpairing the device in the Windows Bluetooth Device Manager or its equivalent, and then searching for it again in the MoSync SDK.

The second issue relates to Windows Active Sync and/or HTC Active Sync. These applications can cause  problems when trying to connect Windows Mobile devices to the computer via MoSync and Bluetooth. Try disabling Active Sync if you are experiencing connection or file transfer issues.

Documentation






Inbox

X





Reply
|

Abi Waqas

 to me
show details 28 Aug (3 days ago)  


Hi Chris..
I just have found a problem and we need to document it somewhere.... its about connecting windows mobile (the crappy one :D ) with mosync. if the windows active sync/HTC active sync for windows mobile is installed on the computer, one might face soooo many problems to connect it to the computer via bluetooth in MoSync. One can find the device, associate it with the preferred profile but can't send the application to the device.. I also have tried to send the cab file through windows bluetooth file wizard but it didn't work.. tried it on a couple of fone.. then I forcefully exit the the active sync and it started working fine... one may think its mosync problem but its not....
--
Med vänlig hälsning / Best regards,
 Abi Waqas Asmat (Ghuman)
Test and Verification Engineer
MoSync AB
Cell: +46(0)760064164
Email: abi@mosync.com
Reply

Forward

Reply by chat to Abi


Sending to a Device

Once you have scanned for a device and selected it (see Scanning for a Device) you can send your application package to it via Bluetooth. Alternatively, if your target device is not Bluetooth-enabled or cannot be detected, you can transfer your package to it via a cable or viaa  web server. Here we describe all these methods.

Checking the Target Device and Device Profile Association

Before you send your application to a device, it is always a good idea to check what target device is currently selected and which device profile is associated with it. That's because MoSync will send the package for the device profile associated with the target device.

The association is set up when you first scan for and select a target device:

  • In MoSync 2.3, you can chose the device profile that should be associated with the target device,
  • In MoSync 2.2, MoSync automatically attaches the device profile active in the project.

Here's how to see the current associations and how to change them if necessary.

Click the small down arrow next to the Select Target Device icon. A drop-down menu will appear:

At the top of the menu you will see the names of the devices followed [in brackets] by the device profiles currently associated with them. In the example above, the phone called "Nokia 6630 demo2" is associated with MoSync "6630" device profile, while the phone called "W715" is associated with the closest match in MoSync's database, the "W710" device profile.

The current target device has a tick against it (here it's the Nokia phone).

Note: The device name is a setting in the mobile device. This name is usually set by the manufacturer, but it is often configurable, which can be helpful if you are testing your application on two otherwise identical devices.

Changing the Target Device and Device Profile Association

You can change the device profile associated with a target device by selecting Edit Device List from the Select Target Device icon's drop-down menu. The Select Preferred Profile window will appear:

Select the target device you want to edit from the top list box. Double-click on a device profile in the bottom list, then click OK. The new device profile will be associated with the chosen target device.

Sending Your Application to the Target Device

Click the Send to Target Device icon to send the package that matches the device profile to the currently selected device:



How the actual installation on the device works is platform- and device-specific. If you are not sure how this is done or where your application is located after installation, consult the user manual for your device.

File transfer over Bluetooth (OBEX) doesn't work?

If your target device does not have Bluetooth file transfer (OBEX), you will need to transfer your package using one of the following methods:

  • USB connection/Android device:
    Install the device vendor's drivers on your machine. Connect your device using the vendor's USB cable, then select Scan for Android USB Device from the Select Target Device drop-down menu.
  • USB connection or other cable/any device:
    Connect the device to your PC using the cable provided by the device vendor and use the vendor's own file transfer solution.
  • No cable connection:
    Upload the package to a web server, then download it to the device using the device's web browser; or copy the application to a memory card and transfer that to the device.

Finding the Package to Transfer

Open MoSync, select your device profile, and build your project. You will find the executable package that MoSync has built for your application in the folder:

C:\<install-path>\<workspace>\<project>\FinalOutput\<build-config>\<vendor>\<model>\<package>

where <install-path> is the place you installed MoSync.

To install your application, copy the executable package to your device using the vendor's own file transfer solution and run it.

Build Configurations and Settings

The MoSync IDE provides you with several ways to build your application (i.e. to compile it into a package ready for installation on a mobile device). Builds can be set to start automatically or can be started manually. The project's build configuration specifies the compiler, packager, and linker settings to use when building the project. In this guide we describe the build options available to you, and the effect of various build settings on the output packages.

Automatic Builds

When the automatic build feature is on, your application will be built whenever any file in the project is saved. By default, the automatic build feature is turned off. You can enable it by selecting Build Automatically from the Project menu.

Manual Builds

When the automatic building feature is turned off, there are several ways to build your application manually:

  • Select Build Project from the Project menu, or
  • Right-click on a project in the Project Explorer view, then select Build Project from the pop-up menu, or
  • Click the Run button to run the application in the emulator, or
  • Double-click on a new device in the Device Profile view.

In the case of both automatic and final builds done this way, the package is only built for the currently selected device profile. To build the application for all devices in the Device Profile view, you will need to Finalize Your Application.

Output Location

The location where the newly built package is stored can be seen in the IDE's Console view.

Cleaning a Project

If something goes wrong during the build process (or you find inconsistencies in your application when it runs), you should clean up your project files. Cleaning removes the intermediate files created during the build process.

To clean up your project files, do one of the following:

  • Select Clean Project from the Project menu, or
  • Right-click on a project in the Project Explorer view, then select Clean Project from the pop-up menu.

Release and Debug Configurations

All new MoSync projects have two build configurations available by default: Release and Debug. These are the build configurations for creating standard release packages and for building a debug version of the application.

You can create more build configurations if you need them. For example, if your target platforms have different screen sizes and want to use different image files, you can create new build configurations to make sure the right images are included in each package. (Excluding files is done on a per-configuration basis, so in the above case it would be easy to have one resource list file for, say, 320x240 screens, and one for 220x176 screens, and just exclude/include the desired files.)

Selecting a Build Configuration

To see the currently available build configurations for your project, and to see which one is active:

  1. Highlight your project in Project Explorer.
  2. Select Properties from the Project menu (or right-click on the project and select Properties).
  3. Open the MoSync Project element and select Build Configurations.
  4. Tick the Activate Configurations box.

All launch configurations as well as the Finalizer have settings that control which build configuration to use.

For example, when you press the Debug toolbar button (or select Debug from the Run menu), the build configuration is automatically set to "Debug" prior to launching your application in the MoRE emulator.

Note: if you do debug your application, the Debug profile will be set to [Active]; you will need to make the Release build configuration [Active] to launch a standard (non-debug) version of your application in the MoRE emulator.

Like the Debugger, the Finalizer has a similar setting: To see these settings, right-click the project and select Properties... > Mosync Project > Finalizer:

Note: All new MoSync projects come with build configurations activated: the Activate Configurations box is ticked by default. (The ability to inactivate build configurations is there for historical reasons as this is a fairly recent addition to the IDE.)

Adding a New Build Configuration

To add a new build configuration to a project:

  1. Highlight your project in Project Explorer.
  2. Select Project > Properties > MoSync Project > Build Configurations.
  3. Make sure that the Activate Configurations box is ticked. You will now see the existing build configurations for the project. The currently active build configuration is marked with the label [Active].
  4. Click Add to add a new build configuration (or highlight an existing one and click Duplicate). A new build configuration will appear in the list.
  5. If you want to rename the new build configuration, highlight it and click Edit.
  6. Select the Build Settings element from the properties tree.
  7. Adjust the build settings as required (see table below).

Build Settings

Project type A project can be a stand-alone application or a library. Note: when building a library, make sure to check the Ignore default checkbox to the right of the Additional Libraries text box, otherwise the default set of libraries will be included during the build and you will get errors.
Incremental Build Strategy The default incremental build strategy is to compile all C files with the GCC -MF switch which generates dependency files consumed by the IDE and used for determining which files to rebuild. The other strategy is to always perform a full build.
Configuration The configuration selected in this box is the one being edited.
Paths and Files
Additional Include Paths / Library Paths / Libraries These three options allows the user to set additional C/C++ include paths, library paths and libraries to be used during compilation and linking. The Ignore Default checkboxes is an advanced option to exclude the MoSync system libraries from the build. Note that, from MoSync 2.5 onward, there are several Path Variables you can use in paths.
Exclude file pattern A space-separated list of files to not include in the build. An example could be test*.c which will exclude all C files that start with test. There is an option to explicitly include files as well, which can be useful for example if all except one file in a directory should be included. To include a file, prefix the file with a + character. So the line /images +player1.png will exclude all files in the images directory except the player1.png file. Files can also be excluded/included from a build by right clicking a file in the project explorer and selecting the Exclude From Build and Do Not Exclude From Build menu items respectively.
Output File (libraries only) The output file of the built library, relative to the project's Output directory.
Output Directory (applications only) The output directory of the built application, relative to the project's Output directory.
Compiler Flags
Activate Dead Code Elimination This option instructs the linker to remove all redundant code from the resulting binary. (Only applicable for applications; we cannot tell which parts of a library is dead code before it has been incorporated in an application)
Additional GCC Switches Allows the user to set additional GCC switches. By default an optimization level switch is set for each configuration; -O0 for the debug configuration and -O2 for other configurations. Note: that no other optimization level than -O0 should be set for debug configurations as this will confuse the debugger and trigger errors during debugging.
Additional Resource Compiler Switches / Additional Linker Switches Allows the user to set additional compiler and linker switches. To get an overview of the available options, run Pipe-Tool from the Windows command line with the -h switch  (pipe-tool -h).
GCC Warnings Compiler warning levels, corresponding to the GCC -Wall, -Wextra and -Werror switches.
Memory Settings > Heap size / Stack size Sets the amount of memory (in kilobytes) to allocate to the heap and the stack respectively. If your application runs our of memory (e.g. you get a "malloc failed" error) you can increase the amount of memory allocated for the heap. But see Memory Settings > Data size below for some limitations.
Memory Settings > Data size Sets the total amount of memory (in kilobytes) to allocate, including the heap and stack. The size will be rounded up to the nearest power of two. Memory settings must be chosen so they conform to the following rule:

Data Size > Heap Size + Stack Size + Application's static data section

Since there's no easy way of knowing the exact size of the statically allocated memory, a good rule of thumb is to use the following relationship:
Data Size = n
Heap Size = n/2

Different devices have different amounts of memory available. If you specify a Data Size greater than the amount of available memory on the device, your program will fail to execute.
Packaging
 
Use Debug Runtimes This option instructs the packager to create packages with debug runtimes. Debug runtimes carry more information to aid the developer during application development. In particular more descriptive error messages will be shown if the debug runtimes are included. [And in the future, this is where on-device debugging support will be put.]
VersionYour application's version number (major.minor), used as a label in the application packages. For example "2.4". Note that the minor version number may not be supported on all platforms. Default: "1.0".
PublisherYour publisher or vendor name, used as a label in the application packages. For example: "Joe Smith Software". Default: "Built with MoSync SDK". Note that some platforms (for example Android) have no publisher identification within the package. For Windows Mobile, this value will be used for both the "provider" and "manufacturer" label within the package.
Application NameThe name to be used for the application's installation package. For example, if Application Name is to "MyCoolApp_setup", the installation package created for Android devices will be MyCoolApp_setup.apk. If not set, the project's name will be used instead. Note: the name of the installed application will always be the project name, which can be changed by right-clicking on the project in Project Explorer and selecting Rename.

Path Variables

MoSync 2.5+

In Build Settings you can use the following variables in paths:

%app-name%Name of the application defined in "Build Settings" -> Packaging.
%app-vendor%Application vendor defined in "Build Settings" -> Packaging.
%app-version%Version of the application, as defined in "Build Settings" -> Packaging.
%compile-output-dir%Path to the directory where the compiled source files will be stored.
%mosync-bin%Path to the MoSync bin directory.
%mosync-home%Path to the MoSync home defined by environment variable MOSYNCDIR.
%package-output-dir%Path to the directory where the resulting package will be stored.
%platform%The name of the platform in the profile being built for, e.g. android_7, wm6 etc.
%profile%The device name in the profile being built for, e.g. Desire.
%programcomb-output%Output directory of program.comb (combined program and resource files).
%program-output%Path to the directory where the program file will be stored.
%project:<project>%Path to the root of another project in the workspace, e.g. %project:3dlines%.
%project-name%Name of the project.
%runtime-dir%Path to the runtime directory.
%vendor%The name of the vendor in the profile being built for, e.g. HTC.
%version-major%Major part of the version number (see %app-version% above).
%version-micro%Minor part of the version number (see %app-version% above).

Note: If you open in MoSync 2.5 a workspace created with an earlier version of MoSync, MoSync 2.5 will automatically add the %mosync-home% variable to the Additional Include Paths and Additional Library Paths in the project's Build Settings. If you then want to go back to an earlier version of MoSync you will have to remove those variables.

Building an Example and Testing on a Device

This screencast shows how to build a demo application (MoTris), how to run it in the emulator, how to select a device, and how to create a package for it.

Setting Application Permissions

Some devices (particularly Symbian and JavaME devices) require that your application must have permission to access functions and services like Bluetooth, Calendar, Camera, Contacts, file storage, Internet, location, power management, SMS, and vibration. The MoSync SDK makes it easy to grant access to such services and functions.

An error on a device such as the Symbian OS error code "-46 KErrPermissionDenied" is a sure sign that you are trying to access a function or service without the necessary permissions. You can set the access permissions you need from the Properties menu for your project. Highlight your project in Project Explorer, then select Project > Properties > MoSync Project > Application Permissions.

Use the checkboxes to grant access to the device services that you application uses when it executes.

Click Apply to apply the changes, then rebuild your project.

You can use the Restore Defaults button to reset access permissions to their original settings.

We strongly recommend that you grant your application just enough permissions to run properly, and no more. This is not only for the sake of security, but also to avoid annoying end-users who might decide that an application that has unnecessary permissions is suspicious and is doing stuff that it shouldn't.

Using Pipe-Tool

Pipe-Tool is MoSync's code transformation engine. It combines the functions of a resource compiler, code linker and assembler, and performs code verification, optimization and dead code elimination to produce highly optimized outputs for each of the target platforms.

Invoking Pipe-Tool

Pipe-Tool is automatically invoked by the MoSync IDE during the normal build process, so in the normal case you don't need to do anything to use Pipe-Tool, just build (or rebuild) your project.

For specialist uses, Pipe-Tool can also be run on demand from the command line (see below).

What Pipe-Tool Does

When it is invoked, Pipe-Tool makes several passes through your code. It is very fast: it takes only around one second to make six passes through a 25000 line application.

Resource Compilation

Pipe-Tool compiles and indexes the external resources that your application uses when it runs. These resources include image and audio files, binary files, and namespaces.

Pipe-Tool's resource compilation function is automatically invoked as the first stage of the build process, prior to the compilation of your code by GCC. Resource compilation can also be invoked from the command line using the command:

pipe-tool -R outfile infile1 [infile2 ...]

for example:

pipe-tool -R resources audio.lst images.lst other.lst 

You identify the resources that your application needs by listing them in a resource list file. In the example above, Pipe-Tool compiles all the resources that are referenced in the three resource list files called audio.lst, images.lst, and other.lst and then concatenates them into a single binary file called resources, ready for deployment to the device.

As well as outputting the binary file, Pipe-Tool also creates a header file called MAheaders.h which you need to include in your application program. This header file contains the #defines that symbolically link your application to its resources.

For more information about resource list files and their syntax, see our Resource Compiler Reference.

Code Compilation

Pipe-Tool invokes the GCC compiler to compile your application code.

Code Verification and Optimization

A little later in the build process, Pipe-Tool takes the output from GCC and performs code verification, code optimization, and dead code elimination. Code verification includes among other things:

  • Bounds checking
  • Division error detection
  • Stack abuse detection
  • Function tracing

Depending partly on the output required by the target platform, Pipe-Tool's code optimization features help create faster, smaller programs by:

  • Converting immediate values to constant register values
  • Eliminating jump-to-jump occurrences
  • Rewriting functions to perform constant value inlining
  • Analyzing and optimizing program flow
  • Analyzing and optimizing register life
  • Performing memory index reduction and adaptive index scaling

During the dead code elimination phase, Pipe-Tool looks through the application tree, marking any nodes that are unreachable. Unlike a traditional linker that marks only whole objects or functions for elimination, Pipe-Tool eliminates dead code down to the lowest level of granularity: right down to individual instructions and bytes.

Linking

After optimization, Pipe-Tool links the MoSync IL from GCC with the pre-compiled MoSync libraries, and the compiled resource binary to create the MoSync bytecode, Java bytecode, or C/C++ source code that will be packaged with the platform runtimes.

Assembly

When a program has been optimized and linked, Pipe-Tool sends the internal representation of the program to the compiler backend. The backend transforms the application into a representation that can be understood by the target architecture. Most commonly the program gets assembled into MoSync bytecode which in turn may be interpreted or recompiled by the MoSync runtimes. We also have experimental support for producing a Java bytecode representation of the application that can be executed on Java virtual machines.

Using Pipe-Tool from the Command Line

Compiling Resources

pipe-tool -R options outfile infile1 [infile2 ...]

Building a Library

pipe-tool -L options outfile infile1 [infile2 ...]

In -L mode, Pipe-Tool combines the specifed infiles (.s files) into a single library .lib file.

Building an Application

pipe-tool -B options outfile infile1 [infile2 ...]

In -B mode, Pipe-Tool combines the specifed infiles to create a single output file.

Manual Page

The options are described in the Pipe-Tool manual page, which can displayed using:

pipe-tool -h 

General options:
  -h | --help            show this usage info
  -error-paths         show file paths in errors
        -xerr                extra information in case of errors
        -master-dump         also dump the input into a single text file
        -s<dir>              search <dir> for input libraries

Build application (-B) options:
  -entry=sym           set code entry point (default 'crt0_startup')
  -datasize=size       set data size (default 65536)
  -stacksize=size      set stack size (default 2048)
  -heapsize=size       set heap size (default 16384)
  -appcode=value       set application code (default 'TEST'(0x54455354))
  -p=vendor/model      link with device profile
  -dump-syms           dump symbol tables
  -dump-unref          dump unreferenced symbols
  -sld=file            output source/line translation
  -stabs=file          output debug information
  -elim                eliminate unreferenced code/data
  -no-verify           prevent code verification
        -java                build a Java class file
  -gcj=flags           for -java option: set flags for GCJ

Resource compiler (-R) options:
  -depend=file         output dependencies in makefile syntax

Known Issues with Pipe-Tool

  • Dead code elimination sometimes gives unstable results.

Finalizing Applications

MoSync's great strength is that you can use it to build your one application into the different packages you need for hundreds of mobile devices. MoSync's Finalizer will build all the executable packages, ready to be transferred to the devices you've propogated from your device profiles list.

Preparing to Finalize

Device profiles list

Before you finalize your application, it is worthwhile checking that the set of devices listed on the Device Profiles tab includes all the devices you want to build the final packages for. If it doesn't, adjust the device profile filters until it does. (But don't worry if you can't get exactly the subset you want, you can adjust things later.)

Finalizer build configuration

When you finalize your application, the build process will use the build configuration set in the properties for your project. To see which build configuration will be used, right-click on your project in the Project Explorer view and select Properties > MoSync Project > Finalizer.

Generating and Editing the Finalizer Script

When you are ready to finalize your application, click the Finalizer tab next to the Device Profiles tab. The Finalizer view appears:



Click the Propagate button. MoSync will generate a build script based on the devices listed in the Device Profiles tab:



The script is editable so you can make changes such as adding device to it or removing devices from it.

Running the Finalizer Script

To run the script and build all your application packages, press the Finalize button.

If you have selected many devices and you have a large project, this operation can of course take quite some time. A progress bar will indicate progress:



When you run the Finalizer, the built packages are placed in a directory called \FinalOutput in your project folder. Each device package is placed in a directory hierarchy sorted by vendor and model.

 

Organising Your Workspaces

Workspaces are useful for organizing projects. For example you can use them to working with different versions of projects, or as sandboxes. Here we describe how to create workspaces and switch between them.

Creating a New Workspace

From the File menu, select Switch Workspace > Other.

The Workspace Launcher will open:

In the Workspace box, enter a name for your new workspace. Workspace names cannot include spaces — only alphanumeric characters (a-z, A-Z, 0-9) and the underscore character (_).

Click OK.

MoSync will restart with the new workspace loaded. (If the Welcome page opens, just close it.)

To add projects to your new workspace, see Importing Projects and Files.

Switching Workspaces

To switch between your workspaces, select Switch Workspace from the File menu.

If you have already used the other workspace in this session, you can choose it directly from the pop-up menu. Otherwise, select Other to open the Workspace Launcher and browse for you workspace from there.

In the Workspace Launcher you have the option to copy the current workbench layout and working sets from the current workspace to the new one.

Importing Projects and Files

Here we show you how to import projects and files into MoSync. We give detailed instructions on how to import existing MoSync projects, and some information about importing other resources like archives, files systems, preference settings, breakpoints, launch configurations, and team project sets.

Importing Existing MoSync Projects

To see an existing MoSync project in your current workspace, you need to import it.

The Import wizard guides you through all the necesary steps of the import process and allows you to set various options.

Note: You can follow these steps to import our example applications (which of course are just another type of project). However, there is a much quicker way of importing them, which we describe in Importing the Examples.

Open the Import wizard in one of the following ways:

  • Right-click inside the Project Explorer view and choose Import from the pop-up menu, or
  • Select Import from the File menu.

Choose the import source by opening the MoSync group, selecting Existing MoSync project into workspace, and clicking Next. The Import Projects dialog will open.

Select the Select root directory option and click its Browse button.

Browse for the folder where your projects are stored, highlight it, and click OK.

A list of the projects available for import will be displayed (in this case it is example application projects in our \examples folder):

You have the choice of copying the projects into the workspace or leaving them in their current location.

  • If you copy the projects (by ticking the Copy projects into workspace option), MoSync will put its builds in the new project folders that it has copied into the workspace.
  • If you do not copy the projects, MoSync will put its builds in to the original project folders.

Make sure that the checkboxes of the projects you want to import are ticked, then click Finish to import the projects into your workspace.

Importing Other Projects and Files

The Import wizard can also be used to import other types of projects and files. Many of these import options will be familiar to Eclipse users. A brief overview of the available options is given in the table below.

General > Archive File Standard Eclipse import of a previously exported archive file
General > Existing MoSync project into workspace see Importing Existing MoSync Projects above
General > Existing Project into Workspace Standard Eclipse project import
General > File System Standard Eclipse file/folder import. This option is useful for bringing legacy C/C++ files into the workspace. Create a project first, then import your files/folders into it
General > Preferences Standard Eclipse import of previous exported preferences (i.e. as set in Windows > Preferences). Useful for transferring preference between Eclipse instances
MoSync > Existing MoSync project into workspace see Importing Existing MoSync Projects above
Run/Debug > Breakpoints Standard Eclipse import of a previously exported breakpoints file
Run/Debug > Launch configurations
Standard Eclipse import of a previously exported launch configurations file. This can be very useful. When you select Run As... in MoSync, a launch configuration is created. This launch configuration contains settings like screen size, which project to run, which build configuration to use, and so on.
Team > Team Project Set Standard Eclipse import of a previously exported Team Project Set.

Building Projects from the Command Line

Use the following syntax to build MoSync projects from the Windows command line:

mosyncc.exe -application com.mobilesorcery.sdk.builder.headless  -data workspacelocation -project
   [-f finalizerscript] projectname

Replace workspacelocation with the location of your workspace, and projectname with the name of the project to build. This will actually launch a Finalizer build with the currently set device filters.

If you wish to produce binaries for a different set of devices you will need to create a Finalizer script and use the -f switch. A finalizer script is just a text file containing lines like the ones produced when you click the Propagate button in the Finalize view in the IDE.

MoSync with Visual Studio 2005

You can use MoSync with Microsoft Visual Studio although, of course, much of the functionality concerning building for mobile devices will be lost this way. The gain is that you will be able to use the fully-featured debugger in Visual Studio. Here we describe how to set up Visual Studio, and how to use it to build and debug an application.

It is only possible to use Microsoft® Visual Studio C++ 2005 Express Edition at this time. You will also need the Microsoft® Windows Server 2003 SP1 Platform SDK. You can download these products here:

Our win32 library for Visual Studio 2005 implements the MoSync syscall APIs. This enables developers to build MoSync projects using Visual C++ and use all of Visual Studio's features, including its debugging facilities.

You should be aware that Visual Studio produces a native windows application, which is fundamentally different from a real MoSync application. For starters, a different compiler is used, which has a number of implications, including a different layout of code and data. Furthermore, if you use the native Win32 libraries (including the Win32 API itself) the resulting source code will not work in MoSync.

(Our win32 library was originally provided because MoSync lacked a proper debugger, and although the debugging experience would be inaccurate with respect to when the application is built for the MoSync architecture, it was considered better than nothing. Today, we would encourage you to use the debugger provided in the MoSync IDE. It is still not perfect, but we are working hard to improve it and are grateful for any feedback or bug reports.)

Setting up Visual Studio

If you haven't installed Visual Studio 2005 you will need to install it first.
Also check so that you have installed the Platform SDK.



First we need to add the correct search paths to Visual Studio. Choose Options from the Tools menu.



Expand Projects and Solutions and choose VC++ Directories. Add the search path for the MoSync library files as shown.



Choose 'Include Files' and add the path for MoSync include files.

If you use the Visual Studio 2005 Express edition you will need to follow these steps before closing the dialog

To be able to build you will also need to add the following search paths for the Windows Platform SDK:

  • To executable files
    C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Bin
  • To include files
    C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include
  • To library files
    C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib

Now you have added all the correct search paths so that Visual Studio will be able to build your applications.

Close the Options window by clicking OK.

Now you just have to make one more adjustment before you will be able to create new projects. Inside the directory C:\Program Files\Microsoft Visual Studio 8\VC\VCProjectDefaults you will need to edit the file corewin_express.vsprops. Find the following string:

AdditionalDependencies="kernel32.lib"

You will need to change that line to:

AdditionalDependencies="kernel32.lib user32.lib gdi32.lib winspool.lib 
comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib"

More information about this can be found at http://msdn.microsoft.com/en-us/vstudio/aa700755.aspx

Configuring a MoSync project in Visual C++

Start with a Win32 project.

In the Application Wizard, in Application Settings, select Empty Project.

  • Add a C/C++ file. This allows you to access C/C++ project properties.

In Project Properties:

  • Select All Configurations.

In General:

  • Change Output Directory from $(SolutionDir) to $(ProjectDir).
  • Change Character Set to Not Set

In C/C++, General:

  • Set Detect 64-bit Portability Issues to No
  • Optional but Recommended: Set Treat Warnings As Errors to Yes.

In Linker, add the libraries needed (mosynclib.lib and MAStd.lib are required) Input:

  • Set Additional Dependencies to mosynclib.lib MAStd.lib MAUtil.lib MAUI.lib

In Linker, System:

  • Set SubSystem to Windows

Building and debugging an application

If you build and run your project, the MoSync emulator will start with your project running in it. You will also be able to debug your application using the Visual Studio debugger so you are able to set breakpoints and single step through your code. Note that the program is compiled as pure x86 code, so the execution envionment may be more forgiving to badly behaved code.

MoSync with Visual Studio 2010

You can use MoSync with Microsoft Visual Studio although much of the functionality concerning building for mobile devices will be lost this way. The gain is that you will be able to use the fully-featured debugger in Visual Studio. Here we describe how to set up Visual Studio, and how to use it to build and debug an application.

MoSync supports Microsoft® Visual Studio C++ 2010 at this time. You can download it here:

Our library for Visual Studio implements the MoSync syscall APIs. This enables developers to build MoSync projects using Visual C++ and use all of Visual Studio's features, including its debugging facilities.

You should be aware that Visual Studio produces a native windows application, which is fundamentally different from a real MoSync application. For starters, a different compiler is used, which has a number of implications, including a different layout of code and data. Furthermore, if you use any other native Win32 libraries (including the Win32 API itself) the resulting source code will not work in MoSync.

(Our win32 library was originally provided because MoSync lacked a proper debugger, and although the debugging experience would be inaccurate with respect to when the application is built for the MoSync architecture, it was considered better than nothing. Today, we would encourage you to use the debugger provided in the MoSync IDE. It is still not perfect, but we are working hard to improve it and are grateful for any feedback or bug reports.)

Configuring a MoSync project in Visual C++

Start with a Win32 project.

In the Application Wizard, in Application Settings, select Empty Project.

  • Add a C/C++ file. This allows you to access C/C++ project properties.

In Project Properties:

  • Select All Configurations.

In General:

  • Change Output Directory from $(SolutionDir) to $(ProjectDir).
  • Change Character Set to Not Set

In VC++ Directories:

  • Add MoSync\include to Include Directories.
  • Add MoSync\libs\vs2010 to Library Directories.

In C/C++, General:

  • Optional but Recommended: Set Treat Warnings As Errors to Yes.

In Linker, add the libraries needed (mosynclib.lib and MAStd.lib are required) Input:

  • Set Additional Dependencies to mosynclib.lib mastd.lib mautil.lib

Building and debugging an application

If you build and run your project, the MoSync emulator will start with your project running in it. You will also be able to debug your application using the Visual Studio debugger so you are able to set breakpoints and single step through your code. Note that the program is compiled as pure x86 code, so the execution envionment may be more forgiving to badly behaved code.

Creating HTML5/JavaScript Apps

With the MoSync SDK you can develop applications in HTML5/JavaScript ... or in C/C++ ... or in HTML5/JavaScript and C/C++! Yes, that's right: you can build apps based on the latest web standards with industrial-strength foundations.

It is easy to create pure HTML5 and JavaScript applications in MoSync. We have templates in the MoSync IDE that let you define your pages, style them with CSS3, incorporate JavaScript widgets and functions, embed streaming media, and use all the other great features of HTML5.

Include the Wormhole JavaScript Library in your code and you can extend your HTML5 apps with functions that access and manage device features like sensors, contact lists, cameras, native user interface widgets, databases, and file systems, and which can react to events and notifications.

You can even also create flexible HTML5/C++ hybrid applications that combine HTML5/JavaScript with C/C++. Hybrid applications make it possible to create great-looking application interfaces and intuitive controls and make use of MoSync's extensive C++ class libraries (for example the Facebook Library and the Slippy Map Library) and C syscall functions (like the Advertising API and Database API) for fine control over all the important features supported by the mobile device.

Screencasts

Guides/tutorials

Example applications

Forum

Blogs

Developing Apps in HTML5/JavaScript

With the MoSync Mobile SDK you can build pure applications in HTML5 and JavaScript without needing to write a single line of C/C++ code! This tutorial shows you how to create a project where you can author mobile applications using just HTML5 and JavaScript.

MoSync support for HTML5 applications

MoSync has a native widget system, and one of the widget types is the WebView widget. This means you can use web technologies to create your application.

New in MoSync 2.7 is full support for HTML5 applications. This means that you can create a native application with HTML, CSS, and JavaScript, and deploy it to app stores as a standalone appliction. You can use established web standards and JavaScript libraries, such as jQuery and jQTouch, and reuse your current web-based code.

You can code your entire application using HTML, CSS, and JavaScript, or you can mix web technologies with C++, using MoSync C/C++ to access device functionality not available in HTML5. Depending on which langauges and libraries you wish to use, you can write all or most of the application logic in JavaScript, or you can write the application logic in C/C++ and use HTML and CSS for the user interface of the application.

HTML5/JavaScript Templates

To get started, open the MoSync IDE, select File > New > Project, create an HTML5/JavaScript project, and select the HTML5 Project template as presented in the following screenshots:

This will give you an HTML5 project which is ready to build and run.

(Note: If you want to use your JavaScript to access your own C++ code or MoSync's C++ class libraries or C function APIs, use the template called HTML5/C++ Project. Unlike the HTML5 Project template, the HTML5/C++ Project template is already set up to support communication between JavaScript and C++.)

MoSync's HTML5 templates are real working applications. Here is how the HTML5 Project template application looks when you run it. In both applications the text will change when you touch the screen. (In the HTML5/C++ Project template application, the device will also vibrate.)

Note that you can manually add the code neeed for JavaScript/C++ interaction to a plain HTML5 Project, in case you wish to add functionality that requires you to code in C++ (for example, to access native device functionality not implemented by the WebView browser widget).

File system layout

An HTML5 project has a directory called /LocalFiles. This is where you should put your HTML, CSS, and JavaScript media files.

Here are the technical details. The content of /LocalFiles is packaged by a build step called "Bundle", to a file called LocalFiles.bin in the /Resources directory. In /Resources, there is a file called Resources.lst, which includes LocalFiles.bin. At runtime, when the application is launched, this file is unpacked and the contents are copied to the application's local file system on the device.

This is the file system layout you will get when creating a template project (note that LocalFiles.bin won't be created until you actually build the project):

The difference between the two template projects, is that the HTML5/C++ Project template contains a file called bridge.js, which provides support for communicating between JavaScript and C++.

The main.cpp file contains code that creates a WebAppMoblet. A moblet is a class that handles events in an application. The WebAppMoblet is a moblet designed to be used for web applications. It features one WebView widget (i.e., a full-screen browser). 

In the case of a plain HTML5 project, you don't need to touch anything in main.cpp.

If you want to provide functionality that invokes C++ code from JavaScript, you can edit main.cpp to add that functionality. The sample template application has code that makes the device vibrate when the screen is touched. How this is done is explained in the guide Communicating Between JavaScript and C++ in MoSync.

How to build and run your app on Android or iPhone/iPad

To run the app you need to have an Android device, or have the Android SDK or Xcode for iOS installed.

To run on an Android device, start by selecting the target device profile. This is done in Eclipse in the right-hand page labeled "Device Profiles". Double-click on the profile you wish to select. For Android, select the "Android 2.x" profile:

Then select the target device and send the app to the device. This is done using the two icons "Select Target Device" and "Send To Taget Device" in Eclipse:

Alternatively, if you have the Android SDK installed, you can select the "Run" command from the menu bar, or press the green "Run" icon, to run the app in the Android emulator. Note that you will be presented with a dialog that asks the location of the Android SDK the first time you do this. You can also use the "Run" command to run the app in the iOS simulator if you have Xcode installed.

Another option is to build the application and install the resulting package on the device. This is an alternative if the "Select/Send To Target Device" icons should not work (if you for example do not have Bluetooth).

Right-click the project in the left-hand Project Explorer and select the "Rebuild Project" command. This will produce an output package:

If the Android target profile is selected, an apk-file will be produced. The location of the file is shown in the Console output window. You can transfer this file to the device using the adb tool in the Android SDK, or publish it on a web site for download:

When building for iPhone/iPad an Xcode project file will be generated. Double-click that file to launch Xcode and run the application from Xcode. The document Developing iPhone Applications explains this in further detail.

For further details regarding Android development with MoSync, see the document Developing Android Applications.

Adding an application icon

To make your app have an nice icon, you need to have two files in your project. One XML-file with a .icon extension, for example App.icon, and one SVG file with the actual icon image. If your SVG file is named Icon.svg, this is the content of App.icon:

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

When you rebuild the program in Eclipse, it will be added to the resulting application package.

There are several programs available for creating SVG-images, for example the open-source drawing program Inkscape.

More details can be found in the Application Icons guide.

An HTML5 Application for Android and iOS in 90 Seconds

Sorry, you need to install flash to see this content.

In this MoSync tutorial, Mikael discusses how to import an example project into MoSync, and how to send it to a mobile phone.

The Wormhole JavaScript Library

Through MoSync's Wormhole Library you can access a wide range of device features and native user interface components from your JavaScript code. This gives you access to features that are normally not avaiable to an HTML5 application.

MoSync supports the standard smartphone implementations of HTML5. That means it is possible to create rich web applications using JavaScript and CSS over the powerful MoSync core. You can even mix HTML5 UI components and Native UI widgets.

You can edit HTML and JavaScript files directly in the MoSync IDE and get full syntax support. We provide easy-to-use project templates to get you started.

If you are a C++ developer, you can benefit from creating cool looking user interfaces in HTML and CSS. And you don't need to master all the dirty details of CCS, as you can choose from a rich selection of  high-level JavaScript libraries available, such as jQTouch.

WebViewLoveSMS

Android and iOS

It is fun to send text messages with smileys to friends and loved ones. But it is time consuming to type in lots smileys on a phone. WebViewLoveSMS is an app you can use to easily send messages with lots of heart or kiss smileys to your loved one.

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.

You will find the latest version of the source code of the app at the MoSync GitHub repository.

Behaviour

When this application starts up, the user is shown the main screen where the telephone number of the loved one can be entered.

There are just two buttons. Tapping either button send a 140-character SMS to the loved one. The Send Eternal Love button sends a repeated string of "<3" characters. The Send Warm Kisses button sends a string of ":-*" characters.

The phone number is stored on the device and will be displayed next time the application is started. (Just one phone number saved — because, after all, this is an application to be used with your loved one...)

In the Code

This example application is implemented in HTML and JavaScript, and uses the MoSync Wormhole Library to access device services from JavaScript via C++. The Wormhole Library is used for sending text messages and storing data on the device, services that you normally cannot access from JavaScript.

Here is the JavaScript code used in the app for sending a text message:

bridge.messagehandler.send( { messageName: 'SendSMS', phoneNo: GetPhoneNo(), message: textMessage }, null); 

bridge is a JavaScript library used in the application, which enables sending messages to C++. In the above example, the message consist of a dictionary with the message names, and a callback function, which we are not using, and therefore set to null.

Here is the C++ code in file WebViewLoveSMS.cpp for SendSMS message:

void handleWebViewMessage(WebView* webView, MAHandle urlData) { WebViewMessage message(webView, urlData); if (message.is("SendSMS")) { savePhoneNoAndSendSMS( message.getParam("phoneNo"), message.getParam("message")); } ... }

In file WebViewLoveSMS.cpp you find the class LoveSMSMoblet, which inherits WebAppMoblet, a central class in the Wormhole Library. The WebAppMoblet class automatically creates and configures a WebView widget (a widget in the MoSync Widget API) for the application. It listens for events from the web view and calls method handleWebViewMessage when messages are sent from JavaScript.

The file /LocalFiles/js/bridge.js is the JavaScript end of the Wormhole, and contains functions for communicating with C++. Note that WebAppMoblet is not depending on bridge.js, there could be other JavaScript libraries created to communicate with the moblet.

Class WebViewMessage is also part of the Wormhole Library and parses the data recieved from the web view. Currently the message format is based on RESTful URLs.

Support for parsing other formats, like JSON, could be added, either to class WebViewMessage, or to a subclass of it, or to an entirely new class. The idea is to provide flexibility in how to commuicate between JavaScript and C++.

Fun things to do

A great way to learn how to author HTML5 applications in MoSync is to modify the LoveSMS app in various ways. You can easily change the background image and the color of user interface elements, to create your own personal unique look for the app. Check out this blog post for further details. 

WebViewTwitter

This example application is a simple Twitter client that shows tweets by selected users. Users can be added to a Favorites List stored on the device. The app uses the jQuery JavaScript library and its jQtouch plug-in, and the MoSync Wormhole C++ Library.

  
 Tweet Reader screen (Android) User tweets 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.

Behavior

When started, this example application displays the main application screen headed "Tweet Reader". On this screen the user can enter the username of a Twitter user. On tapping View Tweets by this user, a second screen is displayed showing that Twitter user's recent tweets.

On the main application screen the user can also add the Twitter user's username to a Favourites List, or tap on an entry in the Favourites List to go straight to that person's tweets.

In the Code

The following technologies are used in this application:

  • HTML5, Javascript, and jQtouch for the design the main user-interface elements and handling of UI events.
  • The JQuery getJSON function to call the Twitter API and parse the JSON document that is returned as a result.
  • 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.
  • MoSync File API (C++) to write to and read from the device's file system.

Included in the example is a set of wrapper functions for accessing the MoSync File API from Javascript (file.js).

WebViewTwitter.cpp is the main file. It includes an implementation of WebAppMoblet. The example application uses the MessageHandler class to process and respond to messages coming from JavaScript.

All of the HTML5 and JavaScript-related files and libraries are located in the /LocalFiles directory, are included in the application when it is built in the MoSync IDE.

The /Resources.lst file has a default entry for the bundled files that should not be removed unless the user wants to remove all the HTML5 functionality.



Extending HTML5 Apps with C++

Kindly note that this document applies to the upcoming MoSync 3.0 release.

This document outlines how to use the Wormhole communication bridge between JavaScript and C++, which is useful when you want to extend your HTML5/JS application with custom code written in C++. If you are not familiar with how to create an HTML5 project, please read the tutorial Creating an HTML5 project in MoSync.

Invoke C++ Code from JavaScript

The following example code is based on the HTML5/JS/C++ Hybrid Project template. That template program illustrates how to invoke code you have written in C++ from JavaScript, and has buttons in the HTML user interface for making the device vibrate and beep. Create an app in the Eclipse IDE, using the HTML5/JS/C++ Hybrid Project template, to get the full source code discussed below.

The example uses classes the Wormhole C++ library (WebAppMoblet, MessageStream, MessageStreamJSON) and the JavaScript libraries included in the file wormhole.js (included with the application template).

Two ways for communicating are supported by Wormhole: JSON messages and string stream messages. We will discuss both of them here.

Sending JSON Messages

In JavaScript, the function mosync.bridge.sendJSON() is used to send JSON messages to C++:

mosync.bridge.sendJSON(message, callbackFunction)

This function takes two parameters. The parameter "message" is a dictionary, which contains the message name and optionally other message parameters.

The parameter "callbackFunction" is an optional function that you can call from C++ to return a value to JavaScript. The communication mechanism is asynchronous, which is why a callback function is used to pass the result back from C++ to JavaScript.

Here is some JavaScript code from the file index.html that invokes C++ to make the device vibrate (here we do not need to use any callback function):

/**
 * Vibrate for 1 second.
 */
function vibrate()
{
    // Send message to C++ to make device vibrate.
    // We use the JSON format. The message will
    // be delivered to C++ in a JSON array.
    mosync.bridge.sendJSON(
    {
        "messageName":"Custom",
        "command":"vibrate",
        "duration":1000
    });
}

And here is the C++ code in main.cpp that handles incoming JSON messages:

void handleMessageStreamJSON(WebView* webView, MAHandle data)
{
    // Create the message object. This parses the message data.
    // The message object contains one or more messages.
    JSONMessage message(webView, data);

    // Loop through messages.
    while (message.next())
    {
        // This detects the PhoneGap protocol.
        if (message.is("PhoneGap"))
        {
            mPhoneGapMessageHandler.handlePhoneGapMessage(message);
        }
        // Here we add our own messages. See index.html for
        // the JavaScript code used to send the message.
        else if (message.is("Custom"))
        {
            String command = message.getParam("command");
            if (command == "vibrate")
            {
                int duration = message.getParamInt("duration");
                maVibrate(duration);
            }
        }
    }
}

Wormhole comes pre-package with PhoneGap compative libraries. The messageName parameter is used to distinguish the "protocol" of the messsage. In the example, we have used "Custom" as the protocol for our own code.

To improve efficiency, the JavaScript side can send several messages to C++ in one shot. We therefore need a while-loop in the code to be able to process all incoming messages.

In the C++ code, you get the value of message parameters with the help of the methods getParam (returns a String) and getParamInt (returns an int):

String command = message.getParam("command");
if (command == "vibrate")
{
    int duration = message.getParamInt("duration");
    maVibrate(duration);
}

Sending String Messages

String streams are an alternative way of sending messages from JavaScript to C++, which was developed to make JavaScript Native UI perform well. This method is generally faster than JSON messages (on some platforms 20x faster). Parsing on the C++ side is very efficient, and streams of string messages are concatenated before passed to C++. This makes sending large number of small messages efficient.

The JavaScript function mosync.bridge.send() is used to send string messages to C++:

mosync.bridge.send(stringArray, callbackFunction)

This function takes two parameters. The parameter "messageArray" is an array of strings, and "callbackFunction" is an optional parameter that works in the same way as with mosync.bridge.sendJSON().

Here is JavaScript code from index.html that invokes C++ to make the device beep, using a string message (again, we do not need to use a callback function):

/**
 * Play one beep sound.
 */
function beep()
{
    // Send to custom message beep to C++.
    // Here we used the string stream format.
    mosync.bridge.send(["Custom", "beep"]);
}

Here is the C++ code in main.cpp that handles incoming string messages:

void handleMessageStream(WebView* webView, MAHandle data)
{
    // Create a message stream object. This parses the message data.
    // The message object contains one or more strings.
    MessageStream stream(webView, data);

    // Pointer to a string in the message stream.
    const char* p;

    // Process messages while there are strings left in the stream.
    while (p = stream.getNext())
    {
        if (0 == strcmp(p, "NativeUI"))
        {
            //Forward NativeUI messages to the respective message handler
            mNativeUIMessageHandler.handleMessage(stream);
        }
        else if (0 == strcmp(p, "Resource"))
        {
            //Forward Resource messages to the respective message handler
            mResourceMessageHandler.handleMessage(stream);
        }
        else if (0 == strcmp(p, "close"))
        {
            // Close the application (calls method in class Moblet).
            close();
        }
        // Here we add your own messages. See index.html for
        // the JavaScript code used to send the message.
        else if (0 == strcmp(p, "Custom"))
        {
            const char* command = stream.getNext();
            if (NULL != command && (0 == strcmp(command, "beep")))
            {
                // This is how to play the sound in the resource BEEP_WAV.
                maSoundPlay(BEEP_WAV, 0, maGetDataSize(BEEP_WAV));
            }
        }
    }
}

As with JSON messages, we use a "protocol parameter" to determine which module the message should go to. Again we use the token "Custom" to designate our own protocol.

String streams are processed by obtaining pointers to charater sequences. As long as the method stream.getNext() returns non-NULL we get a pointer to the next string parameter.

You can use standard C library functions such as strcmp to operate on string parameters, as illustrated by this code snippet:

const char* command = stream.getNext();
if (NULL != command && (0 == strcmp(command, "beep")))
{
    // This is how to play the sound in the resource BEEP_WAV.
    maSoundPlay(BEEP_WAV, 0, maGetDataSize(BEEP_WAV));
}

How to Call JavaScript Code from C++

Note that the code below is not part of the code you get with the template application. You can however type it in yourself, to further explore how the code works.

To evaluate JavaScript code in the WebView from C++, you can use the method WebAppMoblet::callJS(). Here is an example:

callJS("alert('Hello World')");

The following snippets illustrate how to invoke C++ from JavaScript and call a JavaScript callback function.

Here is the JavaScript part:

mosync.bridge.sendJSON(
    { "messageName":"Custom", "command":"GetScreenSize" }, 
    function(width, height) {
        alert("Screen size: " + width + " " + height);
    });

And here is the C++ part:

String command = message.getParam("command");
if (command == "GetScreenSize")
{
    // Call JavaScript reply handler for this message.
    int callbackId = message.getParamInt("callbackId");
    char script[512];
    sprintf(
        script,
        "mosync.bridge.reply(%d, %d, %d)",
        callbackId,
        EXTENT_X(maGetScrSize()),
        EXTENT_Y(maGetScrSize()));
    getWebView()->callJS(script);
}

The JavaScript function mosync.bridge.reply always takes the callbackId as the first argument. You can then supply a variable number of arguments (zero to many) that will be passed to the callback function in JavaScript.

Under The Hood

If you wish to dig deeper, you can explore the MoSync source code at GitHub, and you can also take a look at the example program WebViewGeoLocation, which is a low-level example that does not use the Wormhole library. Note that wormhole.js is a generated file, made up of several JavaScript source files (which means it is not found in the GitHub repository).

Items of interest:

Documentation:

Related example programs:

  • WebViewLoveSMS (shows how to invoke custom C++ code, does not use wormhole.js and the PhoneGap libraries, uses only the basic mosync-bridge.js file, uses the Wormhole C++ library)
  • WebViewTwitter (same as above, uses only the basic mosync-bridge.js file and the Wormhole C++ library)
  • WebViewGeoLocation (uses no JavaScript library, index.html contains the JavScript code you need to communicate with C++, and does not use the Wormhole C++ library)

 

WebViewGeoLocation

The WebViewGeoLocation application displays your current location, as returned by EVENT_TYPE_LOCATION events. This example is useful for understanding the basic mechanisms for communicating between JavaScript and C++.

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.

You will find the latest version of the source code of the app at the MoSync GitHub repository.

Behaviour

When this application starts up on an Android of iOS device, the main screen is displayed. Touch the Start button to start tracking the current latitude and longitude of the device. Touch the Stop button to stop tracking.

In the Code

The purpose of the example is to demonstrate how to communicate between JavaScript in a WebView widget and C++ code, by using basic low-level syscalls and events.

(Other WebView examples, such as WebViewLoveSMS, demonstrate how to use the high-level MoSync Wormhole Library for such communications.)

The application is implemented in C++, HTML and JavaScript. It has an explicit event loop, rather than using a Moblet, to let you see how the basic event mechanism that handle messages from JavaScript works.

The user interface for this application is created in HTML: to keep the example code to a minimum, the example uses only very basic HTML tags and no CSS.

When the program starts, a single WebView widget is created. The HTML and JavaScript code is stored in a text file and packaged with the application as a binary resource. That resource is extracted when the program starts, and the web view is set to display that data. This is done in C++ code, in file WebViewGeoLocation.cpp.

The communication mechanism for invoking C++ code from JavaScript is based on a URL hook. In file WebViewGeoLocation.cpp, the following URL hook is installed when the web view is created:

maWidgetSetProperty(
    webView,
    MAW_WEB_VIEW_HARD_HOOK,
    "mosync://.*");

This will cause all urls with the scheme "mosync://" to be detected by the WebView, and rather than loading a new page, a widget event of type MAW_EVENT_WEB_VIEW_HOOK_INVOKED will be sent to the MoSync event queue.

In file /Resources/GeoLocationPage.html, the following JavaScript code is evaluated when clicking the Start button:

document.location = "mosync://StartTrackingGeoLocation";

The result will be that the url is "hooked", and the following C++ code in WebViewGeoLocation.cpp is called:

case EVENT_TYPE_WIDGET:
    handleWidgetEvent((MAWidgetEventData*) event.data);
    break;

In the handleWidgetEvent method the following code gets called, which causes location tracking to start, and location events to be sent to the event queue:

if (0 == message.find("mosync://StartTrackingGeoLocation"))
{
    maLocationStart();
}

When a location event is sent, the following code is used to communicate the location back to JavaScript to update the web page:

case EVENT_TYPE_LOCATION:
    handleGeoLocationEvent((MALocation*) event.data);
    break;

void handleGeoLocationEvent(MALocation* location)
{
    char script[512];
    sprintf(
        script,
        "javascript:GeoLocationUpdated('%f','%f');",
        location->lat,
        location->lon);
    maWidgetSetProperty(mWebView, "url", script);
}

Calling maWidgetSetProperty on a WebView widget, with the property "url" set to a string that has the "javascript:" scheme will cause that JagScript code to be evaluated in the web view.

In this way, by hooking URLs in a web view and setting the "url" property of a web view, you can communicate asynchronously between JavaScript and C++ code.

It should be noted that the above code snippets use low-level mechanisms that may not be avaiable on all future platforms MoSync will run on. While they work file on Android and iOS, the facilities provided by the WebView widget varies between platforms, and some platforms may not provide url hook capabilities, for instance. If this is the case, you cannot use "document.location" to set a url with data that is hooked by the web view. You then need to use other techniques to send data between JavaScript and C++. The higher-level Wormhole library will encapsulate and abstract away these low-level details and differences between current and future platforms supported by MoSync.

Related documentation

 

 

Extending HTML5 Apps with Lua

MobileLua is a port of Lua to MoSync. There are two ways to author MobleLua apps:

  • The easiest way is by using the MoSync Mobile IDE, and this is the way you need to use when developing a stand-alone app that can be deployed to the app stores.

  • The alternative way is to use LuaLive, an editor and client app that supports live editing and interactive development of Lua apps. This is a great way to experiment and learn Lua. It is also a very productive way of developing MobileLua apps as it dramatically cuts down the edit/build/deploy cycle.
 

In our screencast The JavaScript Lua Bridge Explained, Iraklis and Micki take a look at the basic architecture of JavaScript/Lua apps and show how easy it is to create them.

There is also a dedicated site for MobileLua on GitHub. It provides all the information you need to get working with Lua in MoSync or LuaLive.

The JavaScript/Lua Bridge Explained

Sorry, you need to install flash to see this content.

In this video, Iraklis and Mikael demonstrate the JavaScript/Lua bridge. Lua, like JavaScript, is a dynamic language, which means that we can evaluate code on-the-fly. We evaluate Lua from JavaScript by sending messages with Lua scripts to the underlying Lua application hosting the WebView. View the full tutorial.

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

 

Lessons and Tutorials

Reference Manuals

The MoSync API Reference Manuals provide a detailed description of the MoSync sycalls, function libraries, panics, and resource files. Here we describe the reference documents are available for the latest featured release of MoSync.

MoSync API Reference (Doxygen)

  • MoSync syscalls (low-level graphics, sound, communication, user input, and permanent storage functions)
  • MAStd standard libraries (malloc/free, string manipulation, rudimentary text output console)
  • MAUtil C++ utility libraries (templates Vector, Set and Map, class String, base class for Reference Counting, text input system, Moblet wrapper for the MoSync event system)
  • Widget API and MAUI GUI library (labels, listboxes, edit boxes, layouts...)
  • MTXml (a SAX-like, streaming XML parser with both C and C++ interfaces)
  • MAFS (a standard C file stream implementation)
  • MAP library.

Panics Reference

  • A Panic message (with an maPanic code) appears when an unrecoverable error occurs in the MoSync runtime. This guide shows what they mean.

Resource Compiler Reference

  • This topic describes the format of MoSync resource list (.lst) files and the resource definitions and commands that they contain. It also describes how Pipe-Tool's resource compiler compiles resources.

If you are looking for the very latest reference documentation for one of the nightly builds, look under the Help menu in the product itself.

Helping Develop MoSync

We love open source software and by opening up the Mosync SDK under the GPL license, we aim to give a large group of developers the opportunity to go mobile in a short time on both the Windows and OS X environments. We look forward to working together with you to create new, great applications in mobile, as well as receiving your contributions and suggestions on how to make Mosync even better!

Participation from our community is very important in the success of the MoSync Open Source project. 
We invite you to participate on many levels. These activities require a certain level of technical skill. As a MoSync community member, you as a user can contribute in many other ways; i.e. by participating in the forum or by blogging about MoSync on the Internet.

Share your MoSync application

We'd really like to see what people have done with the MoSync SDK, and cordially welcome everyone to share their applications. You can find some of our own application on the Example Applications page, and some of our customer's applications on the Who's Using MoSync page.

Contribute code

Before you can contribute code to the MoSync SDK, we need you to do a couple of things:

Send your core patches for review to tony@mosync.com.


Raise an issue

How to Report an Issue

Report issues at our Jira Issue Tracker.

What follows are some additional tips on ways to make your bug report better so that someone will be able to help you.

The basics: what you did, what you wanted to happen, and what actually happened.

There are the three basic elements of a bug report, this may be only to basic but bear with us. You need to tell us exactly what you did:
"I instructed the program to draw a polygon on the canvas",
what you expected to have happen:
"I expected a red polygon to appear on the screen", and lastly what actually happened
"I got a blue circle, flickering on screen".

Always search the bug database first.

The odds are good that if you've found a problem, someone else has found it, too. If you spend a few minutes of your time making sure that you're not filing a duplicate bug, that's a few more minutes someone can spend helping to fix that bug rather than sorting out duplicate bug reports.

If you don't understand an error message, ask for help.

Don't report an error message you don't understand as a bug. There are a lot of places you can ask for help in understanding what is going on before you can claim that an error message you do not understand is a bug.

Be brief, but don't leave any important details out

This is a fine line to walk. But there are some general guidelines:

Use English

Yes, the MoSync developer community is global and include a great many people who can speak a great many languages. But if you were to report a bug in a language other than English, many (if not most) of the people who would otherwise help you won't be able to.

Don't report bugs about old versions

Every time a new version of a MoSync product is released, many bugs are fixed. If you're using a version of a product that is more than two revisions older than the latest version, you should upgrade to the latest version to make sure the bug you are experiencing still exists. (And it's not a bad idea to try upgrading even if your version is only a version behind the most current one.)

Only report one problem in each bug report

If you have encountered two bugs that don't appear to be related, create a new bug report for each one. This makes it easier for different people to help with the different bugs.

Suggest a feature

While commercial customers and partners can influence the direction and feature set of the MoSync Mobile SDK, you can too. Submit your feature request in the issue tracker.

Write a usability report

Usability reports are feedback from the community about usage of (new) features in the real world. A usability report is a short account of the user's first impact on a newly tried feature, recording his/her expectations and the level of satisfaction achieved.

An usability report should have the following components.

These reports are like any other contribution very important to us.

Send your usability reports to miles.midgley@mosync.com.

The Issue Tracker

The MoSync Issue Tracker is based on Jira and can be found here: jira.mosync.com. Jira allow users to see more than just one project at a time and ongoing work for up-and-coming releases and is integral in our development.

Note: Our oldIssue Tracker is still visible for archive purposes. We have imported all the issues from the old Google issue tracker. We are manually adding attachments, an ongoing task. If you were following an issue on the Goggle issue tracker you will have to resubscribe to that issue in Jira.

How to Add an Issue

Note: To post or follow issues you will need to create a user ID on Jira.

Follow the instructions and log in.

Next you will be presented with a dashboard. This gives an overview of the MoSync project.

At the top right there is a button to create an issue. This screen allows you to define if this issue is a Bug or New Feature. These are currently the only two issue types open to users.

The following screen shows what we would like you to fill in as a minimum.

Issue Resolution

Issues are usually resolved in 7 stages:

OpenA newly reported issue, no action taken yet
AcceptedA developer has accepted the issue and will soon start working on it
In ProgressA developer is working on the issue
ResolvedThe developer feels that the issue has been dealt with.
Code ReviewThe code is read is ready for code review.
Waiting QAThe code has been merged and QA can build and verify the issue is resolved. In most cases the fix will be in the next nightly build.
VerifiedThe QA department has checked the solution and has agreed that it is fixed

Reporting Issues

Some things to keep in mind before reporting an issue:

  • Please search the Issue Tracker to see if this issue has already been posted. It helps reduce our workload if there are no duplicate issues!
  • Please provide test cases, or sample code, or screen snapshots as attachments to your issue, if they are relevant.
  • If you know the exact area of the product where a bug is, use the issue labels to select the appropriate part of the product. The more accurately you label your issue, the faster we can process it.
  • Sometimes our developers and testers might want more information, the easiest way to communicate is via comments on the issue.
  • The Issue Tracker notifies you of comments posted and status updates to your issues. (You can turn this feature off — but if you do, remember to check back periodically to see what is happening with your issues.
  • If you find other issues that are of interest to you and you would like to keep track of them, simply click on the watch next to the issue. You will be notified by email about all changes to your starred issues.

Feature Requests

If you have suggestions for improving MoSync, enter it as an issue into the Jira issue tracker. Provide as much information as you like. We love feedback, and we are more than happy to let our user base help us determine the direction of development.

If you are missing a feature in MoSync, why not write it up? The development team keeps a close watch on the Issue Tracker so your queries and posts will usually be attended to within a few days. We are committed to providing the best possible user experience. Help us make MoSync the most powerful mobile development solution on the planet!

Building the MoSync SDK for Windows

These instructions describe how to build a Windows installation package for the MoSync SDK. The completed installation package is an .exe file, just like our featured releases and our nightly builds.

To build the full installation package, you will need a Windows machine, a Mac OS X machine, and a Linux machine. If you omit the iPhone and Moblin runtimes, you can build a partial package on just the Windows machine.

(Instructions for building the MoSync SDK from source on OS X are also available.)

Hardware and OS Requirements

To build the MoSync Eclipse IDE, the MoSync libraries, and the MoSync tools, you will need a computer running Windows (Windows XP, Windows Vista, or Windows 7) and capable of running Visual Studio C++ 2005.

To build the MoSync runtimes you will need:

  • For the Java ME, Android, Symbian, and Windows Mobile runtimes, your Windows computer.
  • For the iPhone runtime, a Mac running Snow Leopard (OS X v. 10.6).
  • For the Moblin runtime, a Linux computer running the i386 version of Ubuntu.

If you don’t need the iPhone and Moblin runtimes, everything can be built on the Windows machine.

Getting the MoSync Source Code

1. Download the source tarball (.tar.bz2) of our current feature release from our website or from our open-source project repository:

Alternatively, get the source tarball for our latest nightly build from our website:

or through TortoiseGit:

git clone https://github.com/MoSync/MoSync.git c:\mb\MoSync-trunk
git clone https://github.com/MoSync/Eclipse.git c:\mb\Eclipse

2. Unpack the source tarball into (or, if you have installed TortoiseGit, check it out to):

C:\mb

Getting Eclipse

3. Download target-platform.zip which contains everything needed to build the Eclipse-based IDE. Put the zip-file in:

C:\mb\eclipse\com.mobilesorcery.sdk.product\build

Installing the Dependencies

Important! Make sure you install and configure the dependencies in the order listed below. If you do not, terrible things may happen!

Visual Studio C++ 2005

4. Download and install Microsoft® Visual Studio® 2005 and apply Service Pack 1. Make sure that you install the runtime MT (Multi Threaded) DLL.

5. Add the following paths to Visual Studio:

  • To the end of the list at Tools > Options > Projects and Solutions > VC ++ Directories > Include Files add:

\mb\mosync-trunk\tools\ReleasePackageBuild\build_package_tools\include
\mb\mosync-trunk\libs
\mb\mosync-trunk\libs\MAStd

  • To Tools > Options > Projects and Solutions > VC ++ Directories > Library Files add:

\mb\mosync-trunk\tools\ReleasePackageBuild\build_package_tools\lib

6. Find the file:

C:\mb\mosync-trunk\tools\ReleasePackageBuild\build_package_tools\include\msvc\stdint.h

Copy it to the directory:

C:\Program Files\Microsoft Visual Studio 8\VC\include\

Visual Studio SDKs

7. Download and install the following Visual Studio SDKs:

8. Find the files:

C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Smartphone SDK\Include\Armv4i\gdiplus*.h 

Copy them to:

 

C:\Program Files\Microsoft Visual Studio 8\SmartDevices\SDK\Smartphone2003\Include\

 

9. Download and install the Ruby 1.8 (or later).

10. Download Sqlite 3 for Ruby 1.8 and put it in your ruby\bin directory. (If you have installed a later version of Ruby you'll have to find another version of the Sqlite 3 dll.)

11. Install Sqlite 3 Ruby Gem by opening a command (cmd) window and entering:

gem install sqlite3-ruby

12. Accept the installation of dependencies. These are the ones you need:

  • hoe
  • rake-compiler
  • mocha
  • rake
  • rubyforge

Java SE Development Kit (JDK) 1.6

13. Download and install Java SE Development Kit (JDK) 1.6.

14. Make sure the JDK’s \bin directory has been added to Window’s list of path environment variables, otherwise add it.

SonyEricsson J2ME SDK

15. Download and install the latest version of the SonyEricsson SDK for the Java ME Platform.

16. Download and install the ProGuard 3.7 Java obfuscator.

17. Put  the file proguard.jar in the \OnDeviceDebug\bin directory of the J2ME SDK.

Symbian Series 60 SDK

18.  Download and install ActiverPerl 5.8 Community Edition .

19. Download and install S60 3rd Edition SDK for Symbian OS FP1.

20. Download and install the Extensions plug-in for S60 3rd Edition SDK for Symbian OS, for C++, MR.
 
21. Download and install S60 2nd Edition SDK for Symbian OS FP3. If the installer asks you which network adapter to use, pick any.
 
22. Download and install S60 5th Edition SDK for Symbian OS. Make sure you install it in the folder \Symbian\s60v5\, as that is the location that is assumed by our build scripts.
 
23. Download the Extensions Plug-in for S60 3rd Edition SDK for Symbian OS, for C++, supporting Feature Pack 1, v2.5. Install it in both 3rd and 5th edition SDKs.
 
24. Download and unzip symbian-94-fixes.zip. Install it over the s60v5 SDK.
 
25. Download and unzip symbian-81-extras.zip. Install it over the s60v2 SDK, located in the folder:

\Symbian\8.1a\S60_2nd_FP3\

26. For both 3rd and 5th edition SDKs, run CSL Toolchain (GCCE) Install, found on the Start menu, under:

\S60 Developer Tools\* Edition SDK\v1.0\Tools

27. For each Symbian SDK, make sure that the following folder exists:

\Epoc32\Data\z\resource\apps\

Sometimes, this folder is a file, or does not exist at all, and in both cases this would cause the build to fail.

 28. For both 3rd and 5th edition SDKs, find line 31 in the file \epoc32\tools\e32env.pm:

die "EPOCROOT environment variable must be capitalised\n" if (!$found);

29. Disable this line by adding a hash sign (#) in front of it.

GNU binutils

30. Download and install CoreUtils for Windows.

31. Download and install Grep for Windows.

32. Download and install Sed for Windows.

33. Make sure the \bin directory of GNU binutils has been added to Window’s list of path environment variables, otherwise add it.

Building the SDK

34. If required, build the Moblin runtimes on Linux.

35. If required, build the iPhone runtime on Mac OS X.

(You do not need to build the Windows Mobile,  Java ME, Android, and Symbian runtimes, they are automatically built by the build script).

36. Locate the file run.bat.example in \mb\mosync-trunk\trunk\tools\ReleasePackageBuild, copy it, and name the copy run.bat.

37. Replace the SDK path in run.bat with one appropriate for your system:

start /B /BELOWNORMAL build_package.bat c:\SonyEricsson\JavaME_SDK_CLDC\OnDeviceDebug\ 2>&1 | tee log.txt

38. Start run_commercial_recompiler.bat. It will set up an environment, then call your run.bat (found in the \mb\mosync-trunk\tools\ReleasePackageBuild folder). You can use one of the other run_*.bat files to build the package in different ways.

If all goes well, an installer package should appear in \mb\MoSyncReleasePackage\.
It will be called something like MoSyncSetup_XXX.exe.

39. Done.

 

Building Runtimes for the MoSync SDK on OS X

If you want to create the Windows Mobile,  Java ME, Android, and Symbian runtimes to use with the OS X version of the MoSync SDK (see building the MoSync SDK from source on OS X), here’s how:

1. Follow the instructions above for Getting the MoSync Source Code and Installing the Dependencies.  (You do not need to get Eclipse or build the SDK.)

2. Run the following command from the command line to set the target MoSync Directory:

set MOSYNCDIR=\mb\MoSyncReleasePackage

3. Go to  the folder

\mb\mosync-trunk

and run the following command:

ruby workfile.rb base

4. Go to

\mb\mosync-trunk\tools\ConcurrentBuild

and run the following command:

ruby ConcurrentBuild.rb \mb\MoSyncReleasePackage \mb\MoSync-trunk android javame wm s60

The script will build your runtimes and put them in the \profiles folder under \mb\MoSyncReleasePackage .

5. Copy the runtimes to your mac installation folder and you can start using them on your Mac OS X version.

6. Done.

Building the MoSync SDK for OS X

These instructions describe how to build an OS X installation package for the MoSync SDK. The completed installation package is an .dmg file, just like our featured releases and our nightly builds.

To build a full installation package that includes all the MoSync runtimes, you will need a Windows, a Mac OS X, and a Linux machine. If you want an installation package that just has the iPhone runtime, you can build the whole thing on a single OS X machine.

To be able to follow these instructions you will need to be registered as an Apple Developer. Instructions for building the MoSync SDK for Windows are also available.

Hardware Requirements

To build the MoSync Eclipse IDE, the MoSync libraries, and the MoSync tools, you will need a Mac OS X Snow Leopard (OS X v. 10.6) computer capable of running Xcode.

To build the MoSync runtimes you will need:

  • For the Java ME, Android, Symbian, and Windows Mobile runtimes, Windows (Windows XP, Windows Vista or Windows 7) computer capable of running Visual Studio C++ 2005.
  • For the iPhone runtime, you can use your Mac OS X computer.
  • For the Moblin runtimes, you need a Linux computer running the i386 version of Ubuntu.

If you only want the iPhone runtime, everything can be built on OS X.

Preparing the Mac

1. Download and install Xcode 3.2.5 toolset from the iPhone Dev Center, including the iPhone SDK.

2. Download and install the 32-bit version of Fink for Snow Leopard.

3. Download and install MacPorts.

4. Download and install IceBerg for Mac OS X.

5. From the Terminal, run the following commands:

sudo fink -b install sdl
sudo fink -b install sdl-image
sudo fink -b install sdl-ttf
sudo fink -b install sdl-sound
sudo port install freeimage +universal
sudo port install ImageMagick +universal
sudo port install doxygen

Getting the MoSync Source Code

6. Get the MoSync source code from github repository:

git clone https://github.com/MoSync/MoSync.git MoSync

Getting Eclipse

7. Clone the MoSync Eclipse Repository into the same path as MoSync:

git clone https://github.com/MoSync/Eclipse.git Eclipse

8. Download the zip-package here that contains everything needed to build the Eclipse-based IDE and put it in the folder:

/MoSync/tools/ReleasePackageBuild/

Making the MoSync Package

9. In your checked out version of the MoSync SDK trunk, go to the folder:

/MoSync/tools/ReleasePackageBuild/

then run:

./build_mac_package.sh

10. If everything goes as it should, the script will build a .dmg installation package in the folder called /results.

11. Install the .dmg installation package on an OS X machine.

12. If required, build the Moblin runtimes on Linux.

13. If required, build the Windows Mobile,  Java ME, Android, and Symbian runtimes on Windows (see Building the MoSync SDK on Windows).

14. Add the runtimes to the folder:

/Applications/MoSync/profiles

15. Done.

Building an iPhone Runtime for the MoSync SDK on Windows

If you want to create an iPhone runtime to use with the Windows version of the MoSync SDK (see building the MoSync SDK for Windows ), here’s how:

1. Follow the instructions above for Preparing the Mac and Getting the MoSync Source Code. (You do not need to get Eclipse or make the MoSync package.)

2. Give your user the correct permissions to the /MoSync folder by entering the following command at the Terminal (replace user with you actual user name):

sudo chown -R user  /Applications/MoSync

3. To build the MoSync base prerequisites, go to your cloned MoSync source directory and run the command:

ruby workfile.rb base

4. Now you are ready to build the iPhone runtime from source. In your checked out version of the MoSync SDK trunk do:

cd runtimes/cpp/platforms/iphone
cp Classes/impl/config_platform.h.example Classes/impl/config_platform.h
ruby buildLibraries.rb
cp -R template $MOSYNCDIR/profiles/runtimes/iphoneos/1/

5. Find the runtime in the folder:

/Applications/MoSync/profiles/runtimes/iphoneos/1/

6. Copy it to your MoSync installation directory on your Windows computer in the folder:

\MoSync\profiles\runtimes\iphoneos\1\

7. Done.

Building the Moblin Runtimes on Linux

These instructions describe how to build the MoSync runtimes for Moblin. The runtimes can then be included when you build the MoSync SDK for Windows or build the MoSync SDK for OS X.

To build the Moblin runtimes you will need a machine running Ubuntu (9.10).

1. First install the runtime dependencies in the Linux terminal:

sudo apt-get install gcc g++ ruby rake subversion rpm
sudo apt-get install libgtk2.0-dev libexpat1-dev
sudo apt-get install libbluetooth3-dev libsdl1.2-dev
sudo apt-get install libsdl-image1.2-dev libsdl-ttf2.0-dev
sudo apt-get install libfreeimage-dev libssl-dev

Important! Do not install libsdl-sound -- Moblin does not support this library.

2. Download the source tarball (.tar.bz2) of our current feature release from our website or from our open-source project repository:

Alternatively, get the source tarball for our latest nightly build from our website:

or through TortoiseGit:

git clone https://github.com/MoSync/MoSync.git MoSync
git clone https://github.com/MoSync/Eclipse.git Eclipse

Important! You must download the same version as you downloaded to build the MoSync SDK for Windows or OS X. If you do not, when you try to use the SDK you may get an “IDL Version Mismatch” error.

3. Run the Linux runtime builder script in the MoSync source directory::

/tools/RuntimeBuilder/linux/build-linux-runtime-package.sh

4. You will now have two files in the current directory:

runtime.ubuntu.r*.i386.dbg.tar.gz
runtime.ubuntu.r*.i386.rel.tar.gz

Rename them to:

runtime.dbg.tar.gz
runtime.rel.tar.gz

5. If you are building the MoSync SDK for Windows, move the files to the following folder on the Windows build machine,

c:\mb\MoSyncReleasePackage\profiles\runtimes\moblin\1\

You are then ready to run the SDK build script.

If you are building the MoSync SDK for OS X, copy the runtimes to the machine on which you have installed the MoSync SDK, in the directory:

/Applications/MoSync/profiles/runtimes/moblin/1/

6. Done.

Building the MoSync SDK from Source on Linux

This guide explains how to build the MoSync SDK from source on Linux. It shows you how to set up your build environment and how to build GCC, Pipe-Tool, and the MoRE Emulator.

Please remember that support for Linux is limited and the build system was made for Windows. At the moment, it is only possible to build the MoRE Emulator on Linux, and to run some example programs in the Emulator. It is not possible to build the entire MoSync package (with its IDE) on Linux.

The distributions that we ourselves run at MoSync AB are Ubuntu 8.10/9.04/9.10 and Xubuntu 9.10. This description may work with those distributions. If you're using any other distribution, you may need to adapt these instructions.

Prerequisites

There are a few prerequisites to building the MoSync SDK. In Ubuntu, they can be installed by:

sudo apt-get install gcc g++ bison flex ruby rake subversion rpm libgtk2.0-dev libexpat1-dev
sudo apt-get install libbluetooth3-dev libsdl1.2-dev libsdl-image1.2-dev libsdl-ttf2.0-dev
sudo apt-get install libfreeimage-dev gperf

Note that, since the MoSync SDK is continuously evolving, there may be additional dependencies.

Setting up your Build Environment

The first step is to set up a directory where the compiled binaries, libraries and SDK header files will reside. From now on we will refer to this directory as the installation directory. The installation directory, which resides wherever you want, should contain the following directory structure:

 /<installation-directory>
        /bin
        /include
        /lib
        /libexec/gcc/mapip/3.4.6

The /include and /lib directories will be created automatically during compilation, but the other directories should be created manually because we need to compile GCC before the MoSync SDK can be compiled.

Once you have created the directory structure, the environment variable MOSYNCDIR should point to the installation directory. If you use bash then this is accomplished by adding the following line to your .bashrc:

export MOSYNCDIR=<installation-directory>

or in csh by adding the following line to .cshrc:

setenv MOSYNCDIR  <installation-directory>

When the lines have been added to your .bashrc or .cshrc the shell must be restarted, you can accomplish this by closing the terminal window and opening it again.

Building GCC

MoSync uses its own GCC backend that generates source code for the MoSync virtual machine. In order to build the MoSync GCC we start by downloading the source code from Google Code, the source code can be placed wherever you want, in this example we are using a directory called gcc_trunk. To download the code run svn:

git clone git://github.com/MoSync/gcc.git gcc_trunk

Now go into gcc_trunk and build the compiler, the compilation will fail when trying to compile libgcc since we do not use the standard linker. But the compiler will already have been built and will be placed in gcc_trunk/build/gcc/gcc/.

We build the compiler by performing the following steps:

cd gcc_trunk
./configure-linux.sh
cd build/gcc
make

Remember that the compilation will fail when trying to build libgcc, on my Ubuntu machine the error output begins with:

libgcc2.s: Assembler messages:
libgcc2.s:1: Error: unknown pseudo-op: `.model'
libgcc2.s:2: Error: unknown pseudo-op: `.code'
...

In order for the build scripts to find MoSync GCC it has to be moved to the installation directory:

cp gcc/xgcc gcc/cpp $MOSYNCDIR/bin
cp gcc/cc1 gcc/cc1plus $MOSYNCDIR/libexec/gcc/mapip/3.4.6/

Building Pipe-Tool and the Emulator

The source code for the MoSync SDK also resides on Google Code and is downloaded by:

git clone git://github.com/MoSync/MoSync.git mosync_trunk

MoSync uses its own build system coined 'work'. The build system consists of a set of Ruby files that compiles the MoSync SDK. In each directory that contains source files there is a file called workfile.rb, which instructs the build system how compile the source files. To build the fundamental tools of the MoSync SDK, move to the root of mosync_trunk and invoke:

./workfile.rb CONFIG="debug"

The above command builds the tools and libraries in debug mode but you should also build everything in release mode:

./workfile.rb CONFIG=""

You will then have to copy the font used in the emulator to the bin directory, stand in the root of mosync_trunk and invoke:

cp tools/ReleasePackageBuild/build_package_tools/mosync_bin/unifont-5.1.20080907.ttf $MOSYNCDIR/bin/

When this script is finished make sure that $MOSYNCDIR/bin contains cc1cc1plusxgccpipe-tool and moemu. You may then try the MAUIex example by performing:

cd examples/MAUI/MAUIex/
./workfile.rb CONFIG=""
cd build/pipe_release/
$MOSYNCDIR/bin/moemu -program program -resource ../resources

Coding Conventions

These coding conventions are guidelines for developers creating MoSync code. These conventions are largely inspired by and derived from existing ones in well-known programming environments such as Java and SDL. As such, they shouldn't come across as exotic to any moderately experienced programmer.

Header Files 

The #define Guard

All header files should have #define guards to prevent multiple inclusion. The format of the symbol name should be <PROJECT>_<PATH>_<FILE>_H_.

Header File Dependencies

Don't use an #include when a forward declaration would suffice.

Inline Functions

Define functions inline only when they are small, say, 10 lines or less.

Function Parameter Ordering

When defining a function, parameter order is: inputs, then outputs.

Names and Order of Includes

Use standard order for readability and to avoid hidden dependencies: C library, C++ library, other libraries' .h, your project's .h.

Scoping 

Nonmember, Static Member, and Global Functions

Prefer nonmember functions within a namespace or static member functions to global functions; use completely global functions rarely.

Local Variables

Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.

Static and Global Variables

Static or global variables of class type are forbidden: they cause hard-to-find bugs due to indeterminate order of construction and destruction.

Classes 

Doing Work in Constructors

In general, constructors should merely set member variables to their initial values. Any complex initialization should go in an explicit Init() method.

Default Constructors

You must define a default constructor if your class defines member variables and has no other constructors. Otherwise the compiler will do it for you, badly.

Structs vs. Classes

Use a struct only for passive objects that carry data; everything else is a class.

Inheritance

Composition is often more appropriate than inheritance. When using inheritance, make it public.

Multiple Inheritance

Only very rarely is multiple implementation inheritance actually useful. We allow multiple inheritance only when at most one of the base classes has an implementation; all other base classes must be pure interface classes tagged with the Interface suffix.

Interfaces

Interfaces should be named using an I- prefix. Example: IMySexyInterface

Access Control

Make data members private, and provide access to them through accessor functions as needed. Typically a variable would be called mFoo and the accessor function getFoo(). You may also want a mutator function setFoo().

Declaration Order

Use the specified order of declarations within a class: public: before private:, methods before data members (variables), etc.

Write Short Functions

Prefer small and focused functions.

Other C++ Features

Reference Arguments

All parameters passed by reference must be labeled const.

Use of const

We strongly recommend that you use const whenever it makes sense to do so.

64-bit Portability

Code should be 64-bit and 32-bit friendly. Bear in mind problems of printing, comparisons, and structure alignment.

Preprocessor Macros

Be very cautious with macros. Prefer inline functions, enums, and const variables to macros.

Naming 

General Naming Rules

Function names, variable names, and filenames should be descriptive; eschew abbreviation. Types and variables should be nouns, while functions should be "command" verbs.

Type and Namespace Names

Type names start with a capital letter and have a capital letter for each new word, with no underscores: MyExcitingClass, MyExcitingEnum.

Variable Names

They start with a capital letter and have a capital letter for each new word, with no underscores. A m- prefix is used for member variables. A s- prefix is used for static variables. For instance: myExcitingLocalVariable, mMyExcitingMemberVariable, sMyExcitingMemberVariable

Constant Names

Use capitals and underscore as a separator: DAYS_IN_A_WEEK.

Function Names

Regular functions have mixed case; accessors and mutators match the name of the variable: myExcitingFunction(), myExcitingMethod(), getMyExcitingMemberVariable(), setMyExcitingMemberVariable().

Enumerator Names

Enumerators should be named either like constants or like macros: ENUM_NAME.

Macro Names

You're not really going to define a macro, are you? If you do, they're like this:  MY_MACRO_THAT_SCARES_SMALL_CHILDREN. 

Comments 

File Comments

Start each file with a copyright notice, followed by a description of the contents of the file.

Class Comments

Every class definition should have an /** accompanying comment */ that describes what it is for and how it should be used.

Function Comments

Every function should have a /** declaration comment */ which describes use of the function, using @param and @return syntax.

Variable Comments

In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. In certain cases, more comments are required.

Implementation Comments

In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code.

Punctuation, Spelling and Grammar

Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.

Formatting

Line Length

Each line of text in your code should be at most 80 characters long.

Preprocessor Directives

Preprocessor directives should not be indented but should instead start at the beginning of the line.

Class Format

Sections in public, protected and private order, each indented one space.

Indents and spaces

Inconsistent use of indents, tabs and spaces is a problem in much the same way as inconsistent naming is. Most text editors have the necessary options. For example, in Visual Studio, go Tools > Options > Text Editor > C/C++ > Tabs.
 
The following should apply to all MoSync code: one level of indention equals one tab. A tab is defined as the ASCII character \t. The actual screen size of the indents can thus be set by and for each user with no ill effects for others.

Syscalls

Syscalls are named using a ma- prefix. Only actual Syscalls and IOCTLs are named this way. Examples:
 
void maDisposeLayer(Handle layer);
void maSetMap(Handle layer, Handle srcMapResource, int destX, int destY);

Library functions

Library functions that are directly associated with the manipulation of some complex datatype (such as a struct) should be prefixed with the name of the type followed by an underscore, followed by the function name in camel case. Examples:

MAVec3_normalize(MAVec3* v);
MARect_containsPoint(const MAVec3* v, const MAPoint2d* p); 

Library functions that are not directly and strongly related to a datatype should be prefixed with an identifying name for the library, which should always contain an initial 'MA'. Examples:

MAMath_solveLinearSystem();
MAInet_sendMail();

Design Patterns

Syscalls and extensions

Syscalls and extensions constitute the lowest-level APIs available in MoSync. They are typically directly mapped to underlying platform features and are also subject to a number of restrictions:
 
The number of parameters is limited to four. If more information must be passed, the stack or struct pointers can be used.
Parameters cannot be function pointers. If a notification mechanism is required, one is required to make use of (custom) events.
The only allowed parameter types are primitive IDL types (see IDL documentation) and IDL structs that actually are declared in the relevant IDL file.
The only allowed return types are primitive IDL types.
MoSync memory cannot be allocated within syscalls, other than in the specific case of built-in or custom events.

Syscalls that manipulate objects/resources

Allocation, manipulation and deallocation of native objects (resources) is typically implemented by means of handles.
Typically, there is an maCreateX() function, which takes a placeholder handle to represent the allocated native object. Subsequent access to this object is provided through other syscalls that accept handles as their first parameter.
Finally, all objects are destroyed using maDestroyObject(). As an example, these are some functions that create and manipulate images:
 

void maCreateImageFromData(
in Handle placeholder,
in Handle data,
in int offset,
in int size
);
void maCreateImageRaw(
in Handle placeholder,
in MAAddress src,
in Extent size,
in int alpha
);
void maDrawImage(
in Handle image,
in int left,
in int top
);
void maDrawImageRegion(
in Handle image,
in MARect srcRect,
in MAPoint2d dstPoint,
in int transformMode
);

Asynchronous operations

Since MoSync doesn't support user-created threads, it is important to provide asynchronous interfaces to time-consuming native functionality such as network communication. Since syscalls can't accept function pointers, the central MoSync event system is used to provide progress notification. A few built-in event types are provided in the EVENT struct, but there's also a void* provided that can be used for custom events.

Writing Extensions for MoSync

 

We encourage everyone to help us develop MoSync, and writing new extensions for MoSync is probably the most common thing you will want to do. In this topic we introduce you to some important core concepts that you need to understand and lead you step-by-step through creating an API extension.

This topic assumes that you have a good understanding of how to use MoSync, that you understand the build process, and that you can build the MoRE runtime in Visual Studio. It also assumes that you have some basic familiarity with the MoSync code.

 

Core Concepts

In this section we introduce some core concepts that you will need to understand when you write extensions.

Runtimes and user code

Interaction of user code and runtime through syscalls

User code includes all the code that you write when you create an application with MoSync along with some of the MoSync libraries, such as the MAUI library.

A runtime is the thing that executes the user code on the device. For each platform that MoSync supports, there is a different runtime. Some runtimes are written in C++, others like the J2ME and Android runtimes are written in Java. Each runtime has two parts: one that executes the user code and one that interfaces to it to provide low-level services.

Syscalls and events

Communication between the user code and a runtime is through syscalls and events. Syscalls are like the traditional calls a user application makes to an OS kernel. Whenever the user code needs to do something it cannot do on its own, such as creating a socket connection, it performs a syscall to the runtime. To create a socket connection, for example, the syscall is maConnect.

A syscall is an actual instruction which the runtime traps during code execution. The instruction only has one argument, and that is the syscall number. A syscall, like any ordinary function call, can return a value. A syscall can do pretty much any thing you'd like it to do, but what it can never do is to block. If the syscall cannot finish immediately, such as when waiting for socket data, it needs to return and have the blocking call run in a thread in the background. Once it has finished, it will return the result through events which the user fetches through yet another syscall called maGetEvent.

The maIOCtl syscall

The maIOCtl syscall is a special syscall which is meant for extensions which aren't supported or fully supported on all the MoSync runtimes. What this means in practice is that anything that isn't part of the standard API, that is anything that isn't the least common denominator on all platforms,  will be implemented through an maIOCtl. Implementing an MaIOCtl is simpler than implementing an entirely new syscall, and is what you will be using to implement your extensions. The only limitation that the maIOCtl syscall has is that it only takes three parameters. But on the bright side, any or all the three can be user defined structures, which means that you can always pass all the parameters you like in a structure.

maapi.idl and Pipe-Tool

Any syscall starts its life in a file called maapi.idl. Here all syscalls, events, and event types (among other things) are defined. To minimize the amount of code redundancy, we have created a tool called idl2 which takes the maapi.idl file and generates several important files:

 

  • $MOSYNCDIR/bin/asm_config.lst — the file used by Pipe-Tool to recognise syscalls
  • runtimes/java/shared/invoke_syscall_java.h the inner part of switch case which handles different syscalls for the java runtimes
  • runtimes/cpp/core/invoke_syscall_cpp.h the inner part of switch case which handles different syscalls for the c++ runtimes
  • runtimes/java/shared/generated/core_consts.h opcodes and such for java runtimes
  • runtimes/java/shared/generated/maapi_consts.h constants in the maapi.idl file
  • intlibs/helpers/cpp_defs.h constants and structs in the maapi.idl file, for the C++ runtimes
  • intlibs/helpers/cpp_maapi.h all the syscall declarations for the C++ runtimes
The idl2 tool also generates a set of files with the string "_IX_" in their filename. These are extensions that have been implemented through the maIOCtl syscall. To the MoSync GCC, a syscall is the same as just a normal function call. It is completely unaware of the concept syscall. Recognising syscalls and patching the binary code with the syscall opcode is the job of pipetool. The way it recognises the syscalls is quite simple, it looks at the generated asm_config file to see if a function call is actually a syscall or not. If it is, then its call opcode is replaced with a syscall opcode.

The maapi.idl format is quite simple. It contains declarations for syscalls in a C-like syntax:
RETURN_TYPE IDENTIFIER ( (in|out) TYPE IDENTIFIER, ...);
where:
  • RETURN_TYPEis 
    • void [*]
    • [unsigned] char [*]
    • [unsigned] short [*]
    • [unsigned] int [*]
    • [unsigned] long [*]
    • [unsigned] float [*]
    • [unsigned] double [*]
  • IDENTIFIER is a normal C identifier (for example, maConnect )
  • in|out - Tells the idl2 tool whether the parameter goes in or out.
  • TYPE is any primitive or user defined type
A syscall declaration looks like this:
int maFrameBufferGetInfo ( out MAFrameBufferInfo info );
Note that the declarations are written in between:
#if IX_SOMETHING . . . #end 

For maIOCtl declarations the block ends up in a separate file with a similar name. It is in this block that you will be writing API extensions. The idl2 tool also produces wrapper code for any extensions that have been written through maIOCtl. This is another one of the advantages of the idl2 tool. It takes care of all the little details and updates every where that needs to be updated. The wrapper code might look something like this (from libs/MAStd/IX_CELLID.h):

static inline int maGetCellInfo ( MACellInfo* pInfo ) {     return maIOCtl( 56, (int)pInfo, 0, 0 ); } 

Creating an Extension Step-by-Step

Now that the concepts are in place, it's time to get our hands dirty. There are five steps to writing an API extension:
  1. Edit tools/idl2/maapi.idl
  2. Run the idl2 tool
  3. Add your implementation to the runtimes you need
  4. Rebuild all the user libraries
  5. Try it out

In this example we will write a simple extension as an maIOCtl, we will pass a structure with 16 chars in it, and will return the same chars through an event.

Step 1 — Editing tools/idl2/maapi.idl

We need to make four changes in this file:

  • Add an extension define
  • Add a new event type
  • Add a new inner structure to the event structure
  • Add a new maIOCtl

 

Adding the extension define

Edit tools/idl2/extensions.h file and add to it "#define IX_TUTORIAL". This is needed to process anything that is declared between '#if ...' and '#endif' in the maapi.idl file. Here is the start of our example:

#define IX_RESOURCE_TYPES
#define IX_GUIDO
#define IX_WLAN
#define IX_FILE
#define IX_RECORD
#define IX_CELLID
#define IX_CALL
#define IX_STREAMING
#define IX_CONNSERVER
#define IX_OPENGL_ES
#define IX_AUDIOBUFFER
#define IX_SEGMENTED_DATA
#define IX_PIM
#define IX_TUTORIAL

Adding the new event type

The MAEvent structure contains a field called "type", this field is an integer which identifies the event type. So next we need to add a constant which identifies our custom event. At the end of the block called "constset int EVENT_TYPE_" we add our custom event type:

#if IX_TUTORIAL
        TUTORIAL = 10001;
#endif

Note: To avoid collisions with future extensions from the MoSync team, we suggest that you always choose a large value for the event constant.

In the version of maapi.idl that we are working with, the entire block looks like this:

constset int EVENT_TYPE_ {
		/**
		* This event is posted when the operating system sends MoSync a command 
          * to exit. Causes include the OS shutting down and OS-controlled user 
         * commands. 
         * \see maGetEvent()
		*/
		CLOSE = 1;

		KEY_PRESSED = 2;
		KEY_RELEASED = 3;
		/// Connection
		CONN = 4;
		/// Bluetooth discovery
		BT = 5;
#if IX_GUIDO
		/// Has MAEvent::key be the identifier for the TTS session, as returned 
        /// by maStartSpeaking().
		TTS = 6;
#endif	//IX_GUIDO
#if IX_WLAN
		/// Uses MAEvent::state.
		WLAN = 7;
#endif	//IX_WLAN

		POINTER_PRESSED = 8;
		POINTER_RELEASED = 9;
		POINTER_DRAGGED = 10;

#if IX_CALL
		/// Has MAEvent::state be one of the \link CALLSTATE \endlink constants.
		CALL = 11;
#endif	//IX_CALL

		/**
		 * While MoSync doesn't have focus, no key events will arrive and the 
          * screen will not be updated. If the keypad is locked, no application 
          * will have focus.
		 * \see maLockKeypad
		 */
		FOCUS_LOST = 13;
		FOCUS_GAINED = 14;

#if IX_STREAMING
		/**
		* Has MAEvent::data point to a MAStreamEventData.
		*/
		STREAM = 15;
#endif	//IX_STREAMING
//#if IX_LOCATION
		/// Has MAEvent::data point to an MALocation.
		LOCATION = 16;

		/// MAEvent::state is one of the \link #MA_LPS_AVAILABLE MA_LPS 
          /// \endlink constants.
		LOCATION_PROVIDER = 17;
//#endif	//IX_LOCATION
#if IX_AUDIOBUFFER
		/// MAEvent::state is \> 0 when the audio stream is waiting for more data,
		/// or \< 0 on error.
		AUDIOBUFFER_FILL = 18;
#endif
		SCREEN_CHANGED = 21;

#if IX_TUTORIAL
         /// Just a simple event type added for the tutorial
         TUTORIAL = 10001;
#endif
	}

Adding the new inner structure to the event structure

Now that we have a new event type, it is time to create a way through which we can return data to the user from an event. This can be accomplished in two ways, one is to allocate dynamic memory in the runtime and let the runtime copy that memory to user memory. The runtime will also update the data pointer in the MAEvent struct. To do this, you have to write a few macros in the right places. The second method is to create a new struct with the data that you need to return to the user and embed this in the MAEvent struct. The second method has the advantage of being a bit simpler, but at the cost of bloating the MAEvent struct. For the sake of simplicity, we will only go through the later method in this document. So let's get started.

First, we define our struct. We can call it MATutorialEvenData, it needs to contain 16 chars. The next step is to embed it in the MAEvent struct, in the revision of the code base that the author is working with, the entire code will look like this.

struct MATutorialEventData {
	char m_data[16];
}

struct MAEvent {
	/**
	* One of the \link #EVENT_TYPE_CLOSE EVENT_TYPE 
	* \endlink constants.
	*/
	int type;
	union {
		struct {
			/**
			* In KEY events, this will be one of the 
			* \link #MAK_UNKNOWN MAK \endlink constants.
			*/
			int key;
			/**
			* In KEY events, this will be the native keycode.
			*/
			int nativeKey;
		} ked;

		/**
		 * Tutorial structure
		 */
		MATutorialEvenData tutorial;

		/**
		 * In POINTER events, this will be the location of the pointer.
		 */
		MAPoint2d point;

		/**
		* In \link #EVENT_TYPE_BT BT \endlink events, this will be a value \>= 0 or
		* one of the \link #CONNERR_GENERIC CONNERR \endlink constants.
		*/
		int state;

		/**
		* Valid only in CONN events.
		*/
		MAConnEventData conn;

		/**
		* Used by custom events. See invididual event descriptions.
		*/
		void* data;
	}
}

Adding the new maIOCtl

 

At this point we have all the parts in place to return data through events, now we just need a way of making a call to the runtime. This is where syscalls and maIOCtl comes in. We will define a new IOCtl in the maIOCtl block (after the opening bracket and before the closing bracket). The maIOCtl will look like this:

#if IX_TUTORIAL
  int maSyscallTutorial(in MAString data);
#endif //IX_TUTORIAL

where MAString is a defined as 'typedef char* MAString' at the top of the maapi.idl file.

Step 2 — Running the idl2 tool

Now that we've made actual changes to the syscall interface, we need to regenerate all the auto generated files that have to do with syscalls. In Visual Studio, build tools/idl2 and run it. Your output should look like this,

If you instead get this:

D:\workspace\repo-new\MoSync2.3\tools\idl2>Debug\idl2.exe
Error opening file "opengl_generated.idl" in
maapi.idl on line 2383
Exception: Unknown exception

Then you need to build the opengl_generated.idl, run tools\GLWrapperGenerator\build.bat and run the idl2 tool again.

You probably noticed that for most files it generates three different versions of it. For instance, the IX_TUTORIAL extension goes in:

  • intlibs\CPP_IX_TUTORIAL.h
  • libs\MAStd\IX_TUTORIAL.h
  • runtimes\java\shared\generated\IX_TUTORIAL_consts.h
The first one is to be used from the C++ runtimes (MoRE, Symbian, Windows mobile, Linux). The second file is to be used on the user side. The last file is to be used from the Java runtimes (such as J2ME and Android).
So let's look inside the first two files ( since we're only doing the changes for a C++ runtime in this example). In the first file we have:
#ifndef IX_TUTORIAL_DEFS_H #define IX_TUTORIAL_DEFS_H /** \brief A hash of the abstract representation of the API described in this file. * Identifiers, declarations and definitions are included in the calculation of the hash, * but documentation is not. */ #define MAIDL_HASH ((int)0xc2fcb710) /// Just a simple event type added for the tutorial #define EVENT_TYPE_TUTORIAL 10001 #define maIOCtl_maSyscallTutorial 247 #endif //IX_TUTORIAL_DEFS_H

In here we have hash (which is discussed in section 4), the event ID, and syscall ID. We will need these to write the code which handles the maIOCtl call in the runtime. The java version contains similar things.

In the next file we have:

#ifndef IX_TUTORIAL_H
#define IX_TUTORIAL_H

#ifdef __cplusplus
extern "C" {
#endif

/// Just a simple event type added for the tutorial
#define EVENT_TYPE_TUTORIAL 10001

static inline int maSyscallTutorial(const char* data) {
	return maIOCtl(247, (int)data, 0, 0);
}

#ifdef __cplusplus
}
#endif

#endif	//IX_TUTORIAL_H

As you see, this file is a bit different, there is an actual C function in there, this is because we wrote our new syscall/extension in the maIOCtl block. The idl2 tool knows that anything inside that block is not an actual syscall, but goes through the maIOCtl syscall. The first parameter is the maIOCtl ID, and in the previous file you can see that the preproccesor define "maIOCtl_maSyscallTutorial" is set to 247, which is quite practical and the reason that the previously discussed hash is needed to make sure that both the user libraries and the runtime agree on the same syscall interface.

Step 3 — Add your implementation in the runtimes you need

 

By this point, we have all the parts in place to do the maIOCtl on the user side.  Now you need to write the code on the runtime side which actually handles the IOCtl. In this document, the only runtime we will modify is MoRE, but modifying the other runtimes is similar.

In the source directory, there exists a file runtime/cpp/platforms/sdl/SyscallImpl.cpp, this file handles all the syscalls in the SDL runtime (MoRE). In the Visual Studio project you can find it under Platforms/cpp/sdl/SyscallImpl.cpp. Open it, and add the include "#include <helpers/CPP_IX_TUTORIAL.h>", this will include the definitions that have to do with our example.

Next we need to add the actual code for handling the IOCtl. So, scroll down to the part that says:

SYSCALL(int, maIOCtl(int function, int a, int b, int c)) {

This is the actual maIOCtl syscall. In it you will find a big switch statement. We need to add our own handler to this switch case. So just before the default case, we add this piece of code:

        case maIOCtl_maSyscallTutorial:
        {
            char *  data;
            MAEvent myevent;
            
            // Get pointer to memory
            data = (char*)SYSCALL_THIS->GetValidatedMemRange( a, 16 );

            // Copy to event
            myevent.type = EVENT_TYPE_TUTORIAL;
            memcpy( myevent.tutorial.m_data, data, 16 );

            // Put in event queue
            gEventFifo.put( myevent );

            // Return success code
            return 1;   
        }            

Let's go through the parts of this code. Firstly, a pointer to the argument that was passed to the maIOCtl from the user side, is fetched. The method GetValidatedMemRange takes a pointer to the user side memory and the number of bytes that the pointer points to. If the memory is valid, a pointer will be returned. Otherwise a panic will occur.  Secondly, the event type is set and the ASCIIZ string passed by the user is copied to the event structure. Thirdly, the event is put in the event queue, from where it can be fetched by the user through the maGetEvent syscall. And lastly, we return from the maIOCtl syscall.

Assuming that you have Visual Studio correctly set up and you can build MoRE, rebuild MoRE. The executable will be copied to your MoSync installation path (MOSYNCDIR/bin).

Step 4 — Rebuilding all the user libraries

This step is very important. Any change you make to the IDL file might change the binary interface of syscalls (specfically syscall IDs). Because of this there is a hash in the generated maapi.h file that is verified against the runtime to make sure the user code and the runtime have been built with the same interface. So we need to remove all the old files and rebuild the libs:

  1. In your MoSync installation dir (let's call it MOSYNCDIR), delete include/*
  2. delete MOSYNCDIR/lib/pipe/*
  3. In the source directory (let's call it MOSYNCSRC), do cd libs
  4. ruby workfile.rb
  5. ruby workfile.rb CONFIG=""

Step 5 — Trying it out

If you have gotten everything to work this far, there should be only one last step remaining, and that is to actually test that it works as expected. In the MoSync IDE, create a project and create in it a file called main.c containing the following code:

#include <ma.h>
#include <IX_TUTORIAL.h>

int MAMain ( )
{
	while ( 1 )
	{
		MAEvent e;
		while ( maGetEvent( &e ) != 0 )
		{
			switch ( e.type )
			{
				case EVENT_TYPE_CLOSE:
					return 0;

				case EVENT_TYPE_KEY_PRESSED:
				case EVENT_TYPE_POINTER_PRESSED:
					maSyscallTutorial( "world!" );
					break;

				case EVENT_TYPE_TUTORIAL:
					printf( "Hello %s\n", e.tutorial.m_data );
					break;
			}
		}
	}

	return 0;
}

If you've done everything correctly, this code will compile and run just fine. And in that case, congratulations, you've just written your first MoSync extension!

MoSync Glossary

MoSync introduces a few new terms, and uses some others in a new way. Here we briefly explain these terms and how we interpret them.

API

A set of syscalls and other low-level functions that provide access to one or more basic features of a device. We sometimes use this term rather broadly. Examples include the Sockets API and the Map API.

Connection

A serial connection to a remote machine, identified by a Handle. There are different types of connections, including TCP, HTTP and Bluetooth.

Device

A model of machine that can execute arbitrary programs, typically a mobile phone (cell phone).

Event

MoSync Events deliver notification of keypresses, stylus movements and the results of asynchronous operations like networking and Bluetooth scans.

Handle

An identifier to a resource (e.g. binary file) or object (e.g. a connection) managed by the runtime. There are different types of handles, not interchangable.

Panic

A notification sent when an unrecoverable error occurs in a MoSync runtime, library, or user application. See the Panics Reference Guide.

Pipe-tool

Pipe-tool is our powerful code transformation engine. It takes the output of the GCC compiler, links it with the MoSync libraries, and outputs binary code and/or Java source code depending on the target platforms and devices. Pipe-tool combines the functions of a resource compiler, linker, and code optimizer. It is configured through build configurations.

Placeholder

A resource handle that doesn't identify any resource. It is used in the creation of new resources. The concept of a "placeholder" is used in two contexts. The first is in resource files, to specify "markers" to identify ranges of resources. The second is to create handles for dynamically allocated data objects (using maCreatePlaceholder/maDestoryPlaceholder).

Platform

An operating system, or version thereof, that is used by one or more devices. Examples include J2ME, Symbian Series 60, Android, and Windows Mobile.

Resource

A piece of data managed by the runtime, for example an image, an audio file, or a binary data file. Each resource is identified by a handle. Resources are compiled with the rest of the program and loaded at startup. They can also be created and destroyed when the program is executing.

Runtime

Libraries or programs that execute MoSync programs on a given target device. They also provide a uniform interface to low-level system APIs, including graphics, audio, event handling and communications.  Platforms supported by MoSync have different runtimes.

Syscall

System Call - a function that is implemented by a MoSync runtime in code native to a platform.

Screencasts