Click here to Skip to main content
12,350,443 members (24,632 online)
Click here to Skip to main content
Add your own
alternative version

Stats

6.4K views
89 downloads
2 bookmarked
Posted

Roger’s Clock - your first application with MoSync

, 2 Aug 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Article #3: Getting started with MoSync for Android (and other mobile platforms)

Download RogersClock.zip

Android Tutorial Contest

This is my submission for Article #3: Creating a Simple Hello World Android Project. Its main claim to distinction is that it describes how to use MoSync, a development environment which isn't very well known, but has the advantage of supporting a wide range of mobile platforms, not just Android.

Introduction

Roger is my father in law. Like a lot of people his age, he has problems with short term memory, leading to considerable frustration for his family, who have to cope with his repeated questions:

  • Is it Tuesday today?

  • Yes, it’s Tuesday.

  • (two minutes later) Is it Wednesday today?

  • No, it’s Tuesday.

After a bit of searching, I found the solution: a calendar clock (http://www.dayclox.com).

Order it.

Out of stock. Availability unknown.

But wait! I’ve got an old Android tablet lying around unused (because it has a very slow processor and only about 1GB memory). This is my chance to start using MoSync for Android development.

Download and install MoSync

I’ve described in a separate article how to download and install MoSync. The installation includes several sample projects which are worth studying for tips. Much of what I present here can be found in those samples.

Specification

Rule one of software development: decide what you want to do before you start coding.

In this case, the design is quite simple. I want a continuous display of the day of the week, the time of day and the date. This obviously needs to be updated regularly, and tapping the Back button should quit the program. Oh, and it would be a nice idea to have it adapt to the screen size and orientation.

Step One: Create a new project

Start MoSync and select the Menu File – New – Project:

The Project Wizard will appear:

Select the project type (C++ MoSysnc Project in this case) and click Next:

Choose among the project types on offer. Here I’ve selected a C++ Moblet Project. Moblet is MoSync’s event handling framework, making it easy to handle screen taps and other events. Then click Next:

Supply a name for the project. The name mustn't contain spaces or punctuation characters, otherwise you'll have problems with the generated code. You also specify the folder to store the project. Here I’ve just kept the default path. Then click on Finish, and MoSync will build the project.

The project explorer on the left shows the files which make up the project (in this case, just main.cpp). By expanding the file, you can see the included files, namespaces, classes and member functions. The editor window shows the code; the wizard has already filled in a few event handlers.

Try out the default project

Since we’ve already got this code free, we might as well try it out. Press Ctrl-B to build the project:

A new entry (“Release Packages”) will be added to the Project Explorer. You can unfold it to find RogersClock.apk and right-click to run it in the emulator. Click anywhere you like in the emulator windows or type on the keyboard to test the event handlers provided.

After you close the app (use the Escape key in the emulator), take a look at the Applications page in the emulator. You’ll see Roger’s Clock with the default MoSync icon:

Look at the event handlers supplied

What’s going on here? Here’s the main.cpp file supplied by MoSync,

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

using namespace MAUtil;

/**
 * A Moblet is a high-level class that defines the
 * behaviour of a MoSync program.
 */

class MyMoblet : public Moblet
{
public:
      /**
       * Initialize the application in the constructor.
       */

      MyMoblet()
      {
            printf("Press zero or back to exit\n");
      }

      /**
       * 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();
            }

            // Print the key character.
            printf("You typed: %c\n", keyCode);
      }

      /**
       * Called when a key is released.
       */

      void keyReleaseEvent(int keyCode, int nativeCode)
      {
      }

      /**
       * Called when the screen is touched.
       */

      void pointerPressEvent(MAPoint2d point)
      {
            // Print the x and y coordinate.
            printf("You touched: %i %i\n", point.x, point.y);
      }
};

/**
 * 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;
}

At its heart is a single class (MyMoblet) with a constructor and three event handlers. The constructor prints the initial welcome message “Press zero or back to exit”. Notice the standard printf() function is used, so it’s really quite easy to get started with a simple application.

The event handler keyPressEvent() is called when a key is pressed (naturally), with the keycode as a parameter. If the character is 0 or Back, then it ends the app by calling close(). That’s handy, it fulfils one part of our specification already. Otherwise, it just displays the key (using printf() again).

The corresponding event handler KeyReleaseEvent() is also defined, but it doesn’t do anything.

Finally the event handler pointerPressEvent() is called when the screen is tapped, with the position of the tap as a parameter. It just displays the coordinates of the tap.

Apart from that, there’s just a main() function which creates an instance of MyMoblet.

Rename the Moblet class

I don’t really like the idea of a small mob running around in my tablet, it sounds too violent. So let’s begin by changing the word MyMoblet to RogersClock everywhere in the source code.

From the menu, choose Edit-Find/Replace or type Ctrl-F.  Fill in the details (don’t forget Case Sensitive and Whole Word) and then click on Replace All. Notice that we could have used a regular expression.

Compile and run the code again, just to check.

Step Two: Displaying the date and time

Let’s make this look a bit more like a clock. Instead of just displaying “you pressed…”, why not have it display the date and time, updating it when the screen is tapped.

For that, we just need to change the pointerPressEvent() handler.

Problem. How do we get the date and time?

MoSync includes a complete C++ API. The documentation is installed with MoSync; you can read it by selecting the menu Help – MoSync C++ API Reference:

The documentation opens in Internet Explorer. Type Ctrl-F to search, then type Time.

From that, we can see that there are three functions handling time and date: maGetMilliSecondCount(), maTime() and maLocalTime(), defined in matime.h. By clicking on maLocalTime() we see that it returns the number of seconds that have passed since 1970. Clicking on matime.h shows that there are various conversion functions which may be helpful, in particular sprint_time() which converts a time value to a statically allocated string.

So it’s looking quite easy now. We just need to include matime.h and change the pointerPressEvent() handler to get the time and convert it to a string instead of printing a fixed message:

void pointerPressEvent(MAPoint2d point)
{
      // get the current time
      time_t now = maLocalTime();

      // display the time
      printf("%s\n", sprint_time(now));
}

And here’s the result:

Step Three: Updating the screen regularly

OK, so now we can see the date and time whenever we tap the screen. But it would be nicer if it was updated automatically. What we need is something to send a signal to the application every second or so, and update the display. A timer, in fact. Fortunately, MoSync allows us to set an alarm which sends an event when requested.

The functions of interest here are:

  • addTimer() - request a regular timer event

  • runTimerEvent() - the handler called when the timer event is received

We need to make three changes to the code:

  • The main RogersClock class must inherit from TimerListener in order to receive timer events

  • The pointerPressEvent() handler must become runTimerEvent() (without parameters)

  • The constructor should call addTimer()

Here are the changes:

class RogersClock : public Moblet, public TimerListener
{
public:
      /**
       * Initialize the application in the constructor.
       */

      RogersClock()
      {
            printf("Press zero or back to exit\n");

            // Arrange to call runTimerEvent() every second
            addTimer(this, 1000, 0);
      }

...

      /**
       * Called when the alarm rings
       */

      void runTimerEvent()
      {
            // get the current time
            time_t now = maLocalTime();

            // display the time
            printf("%s\n", sprint_time(now));
      }
};

And here’s the result:

I admit it doesn't look much different, but it updates itself every second instead of waiting for me to tap the screen.

Step Four: Make it look nicer

It all works, but we really want a cleaner, clearer display. So instead of using printf(), we’re going to have to start working with fonts and a graphic display. Also, the abbreviations (Sat, Jul) aren’t very helpful.

Fonts

To handle the text size, we can create our own fonts. They should be based on the size of the screen, and fortunately, MoSync provides the function maGetScrSize() for this.

MAExtent ex = maGetScrSize();
mScreenWidth = EXTENT_X(ex);
mScreenHeight = EXTENT_Y(ex)

Let’s say we want three lines of text one for the date, one for the day of the week, one for the time. We’ll make them different sizes, and allow some space between them.

A simple way to do this is to take the smaller of the height and the width, divide it by ten and use that as the basis for the font size. Then the size of each font can be a multiple of that value.

mLineHeight = ((mScreenWidth < mScreenHeight) ? mScreenWidth : mScreenHeight) / 10;

// Prepare fonts
mDateFont = maFontLoadDefault(FONT_TYPE_SANS_SERIF, FONT_STYLE_BOLD, mLineHeight);
mDayFont = maFontLoadDefault(FONT_TYPE_SANS_SERIF, FONT_STYLE_BOLD, 3 * mLineHeight);
mTimeFont = maFontLoadDefault(FONT_TYPE_SANS_SERIF, 0, 2 * mLineHeight);

Obviously we only want to do this once, so we’ll put it into a separate function (GetScreenParameters()) and call it from the constructor.

Colours

The Hello World example supplied with MoSync shows a simple example of how to write coloured text to the screen:

//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();

We will use these functions to set the color of each line separately.

Graphic display

As with most graphic displays, MoSync does not draw directly to the screen. It builds up the new image on a back buffer, and then writes it to the screen on request. So we need to clear this buffer before writing the new time to it. This is all it takes:

maSetColor(0);                                 // black pen
maFillRect(0, 0, mScreenWidth, mScreenHeight); // cover the screen with black

Keep the screen on

Android devices turn off the backlight after a few seconds, to save the battery. That’s not really desirable for this app. Just adding one line after updating the screen will reset the backlight timer and keep the display on.

maResetBacklight();

Don’t use abbreviations

It’s time to say goodbye to sprint_time(). In its place, we can use split_time(), which returns the familiar tm structure with the time broken down into its components. We’ll have to include our own table of day and month names. We really ought to localize them, but that's asking too much of an introductory project.

Tidying up

Updating the screen is beginning to get a bit complicated, so we’ll move all the code into a separate function and have the handler call it. That makes things rather neater. Putting together the previous sections, here's the resulting code:

// Days of the week. We should localize this
char *Day[] =
{
 "Sunday", "Monday", "Tuesday", "Wednesday",
 "Thursday", "Friday", "Saturday"
};

// Days of the week. Ditto
char *Month[] =
{
 "January", "February", "March", "April", "May", "June",
 "July", "August", "September", "October", "November", "December"
};

// Display colours. We should make these into parameters
#define DAY_COLOUR 0x0000FF
#define TIME_COLOUR 0x00FF00
#define DATE_COLOUR 0xFF0000

 /**
  * Get the size of the screen and create fonts based on it
  */

 void GetScreenParameters()
 {
  // Calculate the font size
  MAExtent ex = maGetScrSize();
  mScreenWidth = EXTENT_X(ex);
  mScreenHeight = EXTENT_Y(ex);
  mLineHeight = ((mScreenWidth < mScreenHeight) ? mScreenWidth : mScreenHeight) / 10;

  // Prepare fonts
  mDateFont = maFontLoadDefault(FONT_TYPE_SANS_SERIF, FONT_STYLE_BOLD, mLineHeight);
  mDayFont = maFontLoadDefault(FONT_TYPE_SANS_SERIF, FONT_STYLE_BOLD, 3 * mLineHeight);
  mTimeFont = maFontLoadDefault(FONT_TYPE_SANS_SERIF, 0, 2 * mLineHeight);
 }

 /**
  * Display a line of text, centered, with the specified font,
  * colour and vertical position
  */

 void CentreText(MAHandle font, int colour, int y, char *text)
 {
  maFontSetCurrent(font);
  maSetColor(colour);
  maDrawText((mScreenWidth - EXTENT_X(maGetTextSize(text))) / 2, y, text);
 }

 /**
  * Display the current date and time
  */
 void UpdateDisplay()
 {
  // get the current time
  time_t timenow = maLocalTime();

  // split the time into its components
  struct tm now;
  split_time(timenow, &now);

  //Print it
  CentreText(mDayFont, DAY_COLOUR, mLineHeight, Day[now.tm_wday]);

  sprintf(line, "%02d:%02d:%02d", now.tm_hour, now.tm_min, now.tm_sec);
  CentreText(mTimeFont, TIME_COLOUR, 5 * mLineHeight, line);

  sprintf(line, "%d %s %d", now.tm_mday, Month[now.tm_mon], 1900 + now.tm_year);
  CentreText(mDateFont, DATE_COLOUR, 8 * mLineHeight, line);

  // Update the screen
  maUpdateScreen();
  maResetBacklight();

  // Clear the screen, ready for the next update
  maSetColor(0);
  maFillRect(0, 0, mScreenWidth, mScreenHeight);
}

and this is the display:

Step Five: Respecting the screen orientation

Well, we’re getting there, but obviously there’s a problem with the screen orientation (which you’d probably noticed anyway, as I had to turn all the screen copies round. Again, this isn’t hard to do.

The simplest approach, and quite appropriate for this kind of app, is to force the app into landscape mode. Adding this line to the constructor will do the trick:

maScreenSetOrientation(SCREEN_ORIENTATION_LANDSCAPE);

However, handling changes in orientation doesn’t take much more work. First, we change the constructor:

maScreenSetOrientation(SCREEN_ORIENTATION_DYNAMIC);

Then we need an event handler to deal with changes in orientation. There isn’t a specific handler for this, just a catch-all called customEvent(). It needs to detect an orientation change and then delete and recreate the fonts.

void customEvent(const MAEvent& event)
{
      //If the event type is screen changed...
      if (event.type == EVENT_TYPE_SCREEN_CHANGED)
      {
            // release the fonts
            maFontDelete(mDayFont);
            maFontDelete(mDateFont);
            maFontDelete(mTimeFont);

            // fix parameters and display the clock
            GetScreenParameters();
      }
}

And here’s the final app:

Not bad for such a simple app, and a great help to Roger when he came to visit us.

Install and run the app on your device

If your device is connected and in debug mode, you can download the app to it. Here's how it looks on my Galaxy Tab (sorry it isn't a great photo):

 

Future improvements

Obviously, there are several more things to be done to improve the app. However, this article isn’t about this app in particular; it’s just intended to help you get started using MoSync. So I’ll just mention a couple of things I would like to change:

  • The names of days and months are built-in. It would be better to get them from the system or from a configuration file, and to allow for a choice of languages and colours.

  • The font size is based exclusively on the height of the screen. As a result, long words like Wednesday tend to get cut off in portrait mode.

End note

By the way, the Dayclox Digital Day clock is now available again. It’s a bit expensive, but it has been a godsend for my father in law and his family, and I strongly recommend it. My only connection with the company is as a customer.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

K Stock
France France
Kevin's introduction to computing was when studying O Level Maths in Belfast, NI.
His first computer was a Science of Cambridge (Sinclair) MK14 with 512 bytes of RAM and a hexadecimal keyboard. He is the creator of Oraperl, the first database-enabled version of Perl, which eventually led to the better-known DBI module.

You may also be interested in...

Comments and Discussions

 
GeneralThanks! Pin
Kevin Priddle6-Aug-14 4:24
staffKevin Priddle6-Aug-14 4:24 
GeneralRe: Thanks! Pin
K Stock6-Aug-14 4:30
memberK Stock6-Aug-14 4:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160621.1 | Last Updated 2 Aug 2014
Article Copyright 2014 by K Stock
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid