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:
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;
class MyMoblet : public Moblet
{
public:
MyMoblet()
{
printf("Press zero or back to exit\n");
}
void keyPressEvent(int keyCode, int nativeCode)
{
if (MAK_BACK == keyCode || MAK_0 == keyCode)
{
close();
}
printf("You typed: %c\n", keyCode);
}
void keyReleaseEvent(int keyCode, int nativeCode)
{
}
void pointerPressEvent(MAPoint2d point)
{
printf("You touched: %i %i\n", point.x, point.y);
}
};
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)
{
time_t now = maLocalTime();
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:
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:
RogersClock()
{
printf("Press zero or back to exit\n");
addTimer(this, 1000, 0);
}
...
void runTimerEvent()
{
time_t now = maLocalTime();
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;
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:
maSetColor(0xFFFFFF),
maDrawText(0, 32, "Hello World!");
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);
maFillRect(0, 0, mScreenWidth, mScreenHeight);
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:
char *Day[] =
{
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
char *Month[] =
{
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
#define DAY_COLOUR 0x0000FF
#define TIME_COLOUR 0x00FF00
#define DATE_COLOUR 0xFF0000
void GetScreenParameters()
{
MAExtent ex = maGetScrSize();
mScreenWidth = EXTENT_X(ex);
mScreenHeight = EXTENT_Y(ex);
mLineHeight = ((mScreenWidth < mScreenHeight) ? mScreenWidth : mScreenHeight) / 10;
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);
}
void CentreText(MAHandle font, int colour, int y, char *text)
{
maFontSetCurrent(font);
maSetColor(colour);
maDrawText((mScreenWidth - EXTENT_X(maGetTextSize(text))) / 2, y, text);
}
void UpdateDisplay()
{
time_t timenow = maLocalTime();
struct tm now;
split_time(timenow, &now);
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);
maUpdateScreen();
maResetBacklight();
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 (event.type == EVENT_TYPE_SCREEN_CHANGED)
{
maFontDelete(mDayFont);
maFontDelete(mDateFont);
maFontDelete(mTimeFont);
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.